Skip to content

Commit b89c90a

Browse files
Fix and enhance parseLocals
1 parent 5e7b754 commit b89c90a

File tree

4 files changed

+68
-8
lines changed

4 files changed

+68
-8
lines changed

readme.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -111,4 +111,4 @@ See [PostHTML Guidelines](https://github.com/posthtml/posthtml/tree/master/docs)
111111

112112
## Credits
113113

114-
Thanks to all PostHTML contributors and especially to `posthtml-extends` and `posthtml-modules` contributors, as part of code are ~~stolen~~ borrowed from these plugins.
114+
Thanks to all PostHTML contributors and especially to `posthtml-extend` and `posthtml-modules` contributors, as part of code are ~~stolen~~ borrowed from these plugins.

src/index.js

+37-7
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ function processNodes(tree, options, messages) {
5353

5454
const html = parseToPostHtml(fs.readFileSync(filePath, options.encoding));
5555

56-
const {attributes, defaultLocals} = parseLocals(options, node, html);
56+
const {attributes, locals} = parseLocals(options, node, html);
5757

5858
options.expressions.locals = attributes;
5959

@@ -75,7 +75,7 @@ function processNodes(tree, options, messages) {
7575
const nodeAttrs = parseAttrs(node.content[index].attrs);
7676

7777
Object.keys(attributes).forEach(attr => {
78-
if (typeof defaultLocals[attr] === 'undefined') {
78+
if (typeof locals[attr] === 'undefined') {
7979
if (['class'].includes(attr)) {
8080
nodeAttrs[attr].push(attributes[attr]);
8181
} else if (['style'].includes(attr)) {
@@ -109,26 +109,56 @@ function processNodes(tree, options, messages) {
109109
function parseLocals(options, {attrs}, html) {
110110
let attributes = {...attrs};
111111

112+
// Handle attributes to be merged with default
113+
// only for Array or Objects
114+
const mergeAttributeWithDefault = [];
115+
Object.keys(attributes).forEach(attribute => {
116+
if (attribute.startsWith('merge:')) {
117+
const newAttributeName = attribute.replace('merge:', '');
118+
attributes[newAttributeName] = attributes[attribute];
119+
delete attributes[attribute];
120+
mergeAttributeWithDefault.push(newAttributeName);
121+
}
122+
});
123+
124+
// Parse JSON attributes
112125
Object.keys(attributes).forEach(attribute => {
113126
try {
114127
// Use merge() ?
115-
attributes = {...attributes, ...JSON.parse(attributes[attribute])};
128+
const parsed = JSON.parse(attributes[attribute]);
129+
if (attribute === 'locals') {
130+
if (mergeAttributeWithDefault.includes(attribute)) {
131+
attributes = merge(attributes, parsed);
132+
} else {
133+
Object.assign(attributes, parsed);
134+
}
135+
} else {
136+
attributes[attribute] = parsed;
137+
}
116138
} catch {}
117139
});
118140

119141
delete attributes.locals;
120142

143+
// Merge with global
121144
attributes = merge(options.expressions.locals, attributes);
122145

123146
// Retrieve default locals from <script defaultLocals> and merge with attributes
124-
const {locals: defaultLocals} = scriptDataLocals(html, {localsAttr: options.scriptLocalAttribute, removeScriptLocals: true, locals: attributes});
147+
const {locals} = scriptDataLocals(html, {localsAttr: options.scriptLocalAttribute, removeScriptLocals: true, locals: attributes});
125148

126149
// Merge default locals and attributes
127-
if (defaultLocals) {
128-
attributes = merge(defaultLocals, attributes);
150+
// or overrides locals with attributes
151+
if (locals) {
152+
Object.keys(locals).forEach(local => {
153+
if (mergeAttributeWithDefault.includes(local)) {
154+
attributes[local] = merge({...locals[local]}, {...attributes[local]});
155+
} else if (typeof attributes[local] === 'undefined') {
156+
attributes[local] = locals[local];
157+
}
158+
});
129159
}
130160

131-
return {attributes, defaultLocals};
161+
return {attributes, locals};
132162
}
133163

134164
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<script defaultLocals>
2+
module.exports = {
3+
title: 'Default title',
4+
items: ['default first item', 'default second item'],
5+
titles: {
6+
title: 'This is default main title',
7+
subtitle: 'This is default subtitle',
8+
suptitle: 'This is default suptitle'
9+
}
10+
}
11+
</script>
12+
<div><h1>{{title}}</h1></div>
13+
<div><each loop="item in items"><span>{{ item }}</span></each></div>
14+
<div><each loop="t, i in titles"><span>{{ i }}: {{ t }}</span></each></div>

test/test-locals.js

+16
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,19 @@ test('Must process default attributes and map style and class for the first node
3131

3232
t.is(html, expected);
3333
});
34+
35+
test('Must process component with locals as JSON and string', async t => {
36+
const actual = `<component src="components/component-locals-json-and-string.html"
37+
subsomething2='{"items": [{"one": "First", "two": "Second", "three": "Third"}]}'
38+
merge:titles='{ "suptitle": "This is custom suptitle" }'
39+
something='["one", "two", "three"]'
40+
something2='{"one": "First", "two": "Second", "three": "Third"}'
41+
title="Custom title from JSON and String"
42+
items='["another item", "yet another"]'
43+
merge:locals='{ "items": ["first item", "second item"] }'></component>`;
44+
const expected = `<div><h1>Custom title from JSON and String</h1></div><div><span>another item</span><span>yet another</span><span>first item</span><span>second item</span></div><div><span>title: This is default main title</span><span>subtitle: This is default subtitle</span><span>suptitle: This is custom suptitle</span></div>`;
45+
46+
const html = await posthtml([plugin({root: './test/templates'})]).process(actual).then(result => clean(result.html));
47+
48+
t.is(html, expected);
49+
});

0 commit comments

Comments
 (0)