Skip to content

Commit 8321415

Browse files
Fix parse attributes
1 parent 44a6154 commit 8321415

File tree

5 files changed

+103
-38
lines changed

5 files changed

+103
-38
lines changed

package-lock.json

+28-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@
2525
"posthtml-attrs-parser": "^0.1.1",
2626
"posthtml-expressions": "^1.9.0",
2727
"posthtml-match-helper": "^1.0.3",
28-
"posthtml-parser": "^0.11.0"
28+
"posthtml-parser": "^0.11.0",
29+
"style-to-object": "^0.3.0"
2930
},
3031
"devDependencies": {
3132
"@commitlint/cli": "^12.0.1",

src/index.js

+69-32
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ const fs = require('fs');
44
const path = require('path');
55
const {inspect} = require('util');
66
const {sha256} = require('js-sha256');
7+
const styleToObject = require('style-to-object');
78
const expressions = require('posthtml-expressions');
89
const scriptDataLocals = require('posthtml-expressions/lib/locals');
910
const {parser: parseToPostHtml} = require('posthtml-parser');
@@ -12,6 +13,7 @@ const parseAttrs = require('posthtml-attrs-parser');
1213
const {match} = require('posthtml/lib/api');
1314
const merge = require('deepmerge');
1415
const findPathFromTagName = require('./find-path');
16+
// const posthtml = require('posthtml');
1517

1618
const debug = true;
1719

@@ -63,7 +65,7 @@ function processNodes(tree, options, messages) {
6365
options.expressions.locals = {...options.locals, ...options.aware};
6466

6567
const defaultSlotName = sha256(filePath);
66-
const slotsLocals = parseSlotsLocals(options.slotTagName, html, node.content, defaultSlotName);
68+
const slotsLocals = parseSlotsLocals(options.fillTagName, html, node.content, defaultSlotName);
6769
const {attributes, locals} = parseLocals(options, slotsLocals, node, html);
6870

6971
options.expressions.locals = attributes;
@@ -74,33 +76,9 @@ function processNodes(tree, options, messages) {
7476
const layoutTree = processNodes(applyPluginsToTree(html, plugins), options, messages);
7577

7678
node.tag = false;
77-
node.content = mergeSlots(layoutTree, node, options.strict, options, defaultSlotName);
78-
79-
const index = node.content.findIndex(content => typeof content === 'object');
80-
81-
if (index !== -1) {
82-
// Map component attributes that it's not defined
83-
// as locals to first element of node
84-
// for now only class and style
85-
const nodeAttrs = parseAttrs(node.content[index].attrs);
86-
87-
Object.keys(attributes).forEach(attr => {
88-
if (typeof locals[attr] === 'undefined') {
89-
if (['class'].includes(attr)) {
90-
if (typeof nodeAttrs[attr] === 'undefined') {
91-
nodeAttrs[attr] = [];
92-
}
93-
94-
nodeAttrs[attr].push(attributes[attr]);
95-
} else if (['style'].includes(attr)) {
96-
// Append style ?
97-
nodeAttrs[attr] = attributes[attr];
98-
}
99-
}
100-
});
79+
node.content = mergeSlots(layoutTree, node, options, defaultSlotName);
10180

102-
node.content[index].attrs = nodeAttrs.compose();
103-
}
81+
parseAttributes(node, attributes, locals, options);
10482

10583
messages.push({
10684
type: 'dependency',
@@ -266,26 +244,77 @@ function parseSlotsLocals(tag, html, content, defaultSlotName) {
266244
return slots;
267245
}
268246

247+
/**
248+
* Map component attributes that it's not defined as locals to first element of node
249+
* @param {Object} node
250+
* @param {Object} attributes
251+
* @param {Object} locals
252+
* @param {Object} options
253+
* @return {void}
254+
*/
255+
function parseAttributes(node, attributes, locals, options) {
256+
const index = node.content.findIndex(content => typeof content === 'object');
257+
258+
if (index !== -1) {
259+
const nodeAttrs = parseAttrs(node.content[index].attrs, options.attrsParserRules);
260+
261+
Object.keys(attributes).forEach(attr => {
262+
if (typeof locals[attr] === 'undefined') {
263+
if (['class'].includes(attr)) {
264+
// Merge class
265+
if (typeof nodeAttrs.class === 'undefined') {
266+
nodeAttrs.class = [];
267+
}
268+
269+
nodeAttrs.class.push(attributes.class);
270+
271+
delete attributes.class;
272+
} else if (['override:class'].includes(attr)) {
273+
// Override class
274+
nodeAttrs.class = attributes['override:class'];
275+
276+
delete attributes['override:class'];
277+
} else if (['style'].includes(attr)) {
278+
// Merge style
279+
if (typeof nodeAttrs.style === 'undefined') {
280+
nodeAttrs.style = {};
281+
}
282+
283+
nodeAttrs.style = Object.assign(nodeAttrs.style, styleToObject(attributes.style));
284+
delete attributes.style;
285+
} else if (['override:style'].includes(attr)) {
286+
// Override style
287+
nodeAttrs.style = attributes['override:style'];
288+
delete attributes['override:style'];
289+
}
290+
}
291+
});
292+
293+
node.content[index].attrs = nodeAttrs.compose();
294+
}
295+
}
296+
269297
/**
270298
* Merge slots content
271299
* @param {Object} tree
272300
* @param {Object} node
273301
* @param {Boolean} strict
274302
* @param {String} slotTagName
303+
* @param {String} fillTagName
275304
* @param {Boolean|String} fallbackSlotTagName
276305
* @param defaultSlotName
277306
* @return {Object} tree
278307
*/
279-
function mergeSlots(tree, node, strict, {slotTagName, fallbackSlotTagName}, defaultSlotName) {
308+
function mergeSlots(tree, node, {strict, slotTagName, fillTagName, fallbackSlotTagName}, defaultSlotName) {
280309
const slots = getSlots(slotTagName, tree, defaultSlotName, fallbackSlotTagName); // Slot in component.html
281-
const fillSlots = getSlots(slotTagName, node.content, defaultSlotName); // Slot in page.html
310+
const fillSlots = getSlots(fillTagName, node.content, defaultSlotName); // Slot in page.html
282311
const clean = content => content.replace(/(\n|\t)/g, '').trim();
283312

284313
// Retrieve main content, means everything that is not inside slots
285314
if (node.content) {
286-
const contentOutsideSlots = node.content.filter(content => (content.tag !== slotTagName));
315+
const contentOutsideSlots = node.content.filter(content => (content.tag !== fillTagName));
287316
if (contentOutsideSlots.filter(c => typeof c !== 'string' || clean(c) !== '').length > 0) {
288-
fillSlots[defaultSlotName] = [{tag: slotTagName, attrs: {name: defaultSlotName}, content: [...contentOutsideSlots]}];
317+
fillSlots[defaultSlotName] = [{tag: fillTagName, attrs: {name: defaultSlotName}, content: [...contentOutsideSlots]}];
289318
}
290319

291320
// Replace <content> with <block>
@@ -429,6 +458,12 @@ function applyPluginsToTree(tree, plugins) {
429458
}, tree);
430459
}
431460

461+
// function processWithPostHtml(html, options = {}, plugins = []) {
462+
// return posthtml(plugins)
463+
// .process(html, options)
464+
// .then(result => result.tree);
465+
// }
466+
432467
module.exports = (options = {}) => {
433468
options = {
434469
...{
@@ -441,6 +476,7 @@ module.exports = (options = {}) => {
441476
tagPrefix: 'x-',
442477
tagRegExp: new RegExp(`^${options.tagPrefix || 'x-'}`, 'i'),
443478
slotTagName: 'slot',
479+
fillTagName: 'slot',
444480
// Used for compatibility with modules plugin <content> slot, set to true only if you have migrated from modules plugin
445481
fallbackSlotTagName: false,
446482
tagName: 'component',
@@ -452,7 +488,8 @@ module.exports = (options = {}) => {
452488
encoding: 'utf8',
453489
scriptLocalAttribute: 'props',
454490
matcher: [],
455-
strict: true
491+
strict: true,
492+
attrsParserRules: {}
456493
},
457494
...options
458495
};

test/test-locals.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ test('Must process attributes as locals', async t => {
2424
});
2525

2626
test('Must process default attributes and map style and class for the first node', async t => {
27-
const actual = `<component src="components/component-mapped-attributes.html" title="My Title" class="bg-light p-2" style="display: flex; font-size: 20px;"></component>`;
28-
const expected = `<div class="text-dark m-3 bg-light p-2" style="display: flex; font-size: 20px;">My Title Default body</div>`;
27+
const actual = `<component src="components/component-mapped-attributes.html" title="My Title" class="bg-light p-2" style="display: flex; font-size: 20px"></component>`;
28+
const expected = `<div class="text-dark m-3 bg-light p-2" style="display: flex; font-size: 20px">My Title Default body</div>`;
2929

3030
const html = await posthtml([plugin({root: './test/templates'})]).process(actual).then(result => clean(result.html));
3131

test/test-plugins.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ test('Must work with posthtml-extend syntax', async t => {
99
const actual = `<extends src="layouts/extend.html"><block name="content">My Content</block></extends>`;
1010
const expected = `<html><head><title>Extend Layout</title></head><body><main>My Content</main><footer>footer content</footer></body></html>`;
1111

12-
const html = await posthtml([plugin({root: './test/templates', tagName: 'extends', attribute: 'src', slotTagName: 'block'})]).process(actual).then(result => clean(result.html));
12+
const html = await posthtml([plugin({root: './test/templates', tagName: 'extends', attribute: 'src', slotTagName: 'block', fillTagName: 'block'})]).process(actual).then(result => clean(result.html));
1313

1414
t.is(html, expected);
1515
});
@@ -27,7 +27,7 @@ test('Must work with posthtml-extend and posthtml-modules syntax together', asyn
2727
const actual = `<extends src="layouts/extend-with-module.html"><block name="content"><module href="components/module-with-extend.html">My Module Content</module></block></extends>`;
2828
const expected = `<html><head><title>Extend With Module Layout</title></head><body><main><div>My Module Content</div></main><footer>footer content</footer></body></html>`;
2929

30-
const html = await posthtml([plugin({root: './test/templates', tagNames: ['extends', 'module'], attributes: ['src', 'href'], slotTagName: 'block', fallbackSlotTagName: true})]).process(actual).then(result => clean(result.html));
30+
const html = await posthtml([plugin({root: './test/templates', tagNames: ['extends', 'module'], attributes: ['src', 'href'], slotTagName: 'block', fillTagName: 'block', fallbackSlotTagName: true})]).process(actual).then(result => clean(result.html));
3131

3232
t.is(html, expected);
3333
});

0 commit comments

Comments
 (0)