posthtml-component 1.0.0-beta.9 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/readme.md +120 -114
- package/src/find-path.js +3 -3
- package/src/index.js +102 -34
- package/src/log.js +27 -0
- package/src/process-attributes.js +115 -0
- package/src/process-props.js +83 -0
- package/src/process-script.js +49 -0
- package/src/{slots.js → process-slots.js} +5 -5
- package/src/{stacks.js → process-stacks.js} +6 -1
- package/src/valid-attributes.js +3560 -0
- package/src/attributes.js +0 -64
- package/src/locals.js +0 -99
package/src/index.js
CHANGED
|
@@ -3,21 +3,32 @@
|
|
|
3
3
|
const {readFileSync, existsSync} = require('fs');
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const {parser} = require('posthtml-parser');
|
|
6
|
-
const {match} = require('posthtml/lib/api');
|
|
6
|
+
const {match, walk} = require('posthtml/lib/api');
|
|
7
7
|
const expressions = require('posthtml-expressions');
|
|
8
8
|
const findPathFromTag = require('./find-path');
|
|
9
|
-
const
|
|
10
|
-
const processAttributes = require('./attributes');
|
|
11
|
-
const {processPushes, processStacks} = require('./stacks');
|
|
12
|
-
const {setFilledSlots, processSlotContent, processFillContent} = require('./slots');
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
9
|
+
const processProps = require('./process-props');
|
|
10
|
+
const processAttributes = require('./process-attributes');
|
|
11
|
+
const {processPushes, processStacks} = require('./process-stacks');
|
|
12
|
+
const {setFilledSlots, processSlotContent, processFillContent} = require('./process-slots');
|
|
13
|
+
const log = require('./log');
|
|
14
|
+
const each = require('lodash/each');
|
|
15
|
+
const defaults = require('lodash/defaults');
|
|
16
|
+
const assignWith = require('lodash/assignWith');
|
|
17
|
+
const mergeWith = require('lodash/mergeWith');
|
|
18
|
+
const template = require('lodash/template');
|
|
19
|
+
const get = require('lodash/get');
|
|
20
|
+
const has = require('lodash/has');
|
|
21
|
+
const isObjectLike = require('lodash/isObjectLike');
|
|
22
|
+
const isArray = require('lodash/isArray');
|
|
23
|
+
const isEmpty = require('lodash/isEmpty');
|
|
24
|
+
const isBoolean = require('lodash/isBoolean');
|
|
25
|
+
const isUndefined = require('lodash/isUndefined'); // value === undefined
|
|
26
|
+
const isNull = require('lodash/isNull'); // value === null
|
|
27
|
+
const isNil = require('lodash/isNil'); // value == null
|
|
28
|
+
const uniqueId = require('lodash/uniqueId');
|
|
29
|
+
const transform = require('lodash/transform');
|
|
30
|
+
const assign = require('lodash/assign');
|
|
31
|
+
const isPlainObject = require('lodash/isPlainObject');
|
|
21
32
|
|
|
22
33
|
/* eslint-disable complexity */
|
|
23
34
|
module.exports = (options = {}) => tree => {
|
|
@@ -35,14 +46,43 @@ module.exports = (options = {}) => tree => {
|
|
|
35
46
|
options.slotSeparator = options.slotSeparator || ':';
|
|
36
47
|
options.push = options.push || 'push';
|
|
37
48
|
options.stack = options.stack || 'stack';
|
|
38
|
-
options.
|
|
49
|
+
options.propsScriptAttribute = options.propsScriptAttribute || 'props';
|
|
50
|
+
options.propsContext = options.propsContext || 'props';
|
|
51
|
+
options.propsAttribute = options.propsAttribute || 'props';
|
|
52
|
+
options.propsSlot = options.propsSlot || 'props';
|
|
39
53
|
options.expressions = options.expressions || {};
|
|
40
54
|
options.plugins = options.plugins || [];
|
|
41
55
|
options.attrsParserRules = options.attrsParserRules || {};
|
|
42
56
|
options.strict = typeof options.strict === 'undefined' ? true : options.strict;
|
|
57
|
+
options.utilities = options.utilities || {
|
|
58
|
+
each,
|
|
59
|
+
defaults,
|
|
60
|
+
assign: assignWith,
|
|
61
|
+
merge: mergeWith,
|
|
62
|
+
template,
|
|
63
|
+
get,
|
|
64
|
+
has,
|
|
65
|
+
isPlainObject,
|
|
66
|
+
isObject: isObjectLike,
|
|
67
|
+
isArray,
|
|
68
|
+
isEmpty,
|
|
69
|
+
isBoolean,
|
|
70
|
+
isUndefined,
|
|
71
|
+
isNull,
|
|
72
|
+
isNil,
|
|
73
|
+
uniqueId,
|
|
74
|
+
isEnabled: prop => prop === true || prop === ''
|
|
75
|
+
};
|
|
76
|
+
// Additional element attributes, in case already exist in valid-attributes.js it will replace all attributes
|
|
77
|
+
// It should be an object with key as tag name and as value a function modifier which receive
|
|
78
|
+
// the default attributes and return an array of attributes. Example:
|
|
79
|
+
// { TAG: (attributes) => { attributes[] = 'attribute-name'; return attributes; } }
|
|
80
|
+
options.elementAttributes = isPlainObject(options.elementAttributes) ? options.elementAttributes : {};
|
|
81
|
+
options.safelistAttributes = Array.isArray(options.safelistAttributes) ? options.safelistAttributes : [];
|
|
82
|
+
options.blacklistAttributes = Array.isArray(options.blacklistAttributes) ? options.blacklistAttributes : [];
|
|
43
83
|
|
|
44
84
|
// Merge customizer callback passed to lodash mergeWith
|
|
45
|
-
// for merge attribute `
|
|
85
|
+
// for merge attribute `props` and all attributes starting with `merge:`
|
|
46
86
|
// @see https://lodash.com/docs/4.17.15#mergeWith
|
|
47
87
|
options.mergeCustomizer = options.mergeCustomizer || ((objectValue, sourceValue) => {
|
|
48
88
|
if (Array.isArray(objectValue)) {
|
|
@@ -86,11 +126,13 @@ module.exports = (options = {}) => tree => {
|
|
|
86
126
|
}
|
|
87
127
|
});
|
|
88
128
|
|
|
89
|
-
options.
|
|
129
|
+
options.props = {...options.expressions.locals};
|
|
90
130
|
options.aware = {};
|
|
91
131
|
|
|
92
132
|
const pushedContent = {};
|
|
93
133
|
|
|
134
|
+
log('Start of processing..', 'init', 'success');
|
|
135
|
+
|
|
94
136
|
return processStacks(
|
|
95
137
|
processPushes(
|
|
96
138
|
processTree(options)(
|
|
@@ -105,21 +147,27 @@ module.exports = (options = {}) => tree => {
|
|
|
105
147
|
};
|
|
106
148
|
/* eslint-enable complexity */
|
|
107
149
|
|
|
150
|
+
// Used for reset aware props
|
|
151
|
+
let processCounter = 0;
|
|
152
|
+
|
|
108
153
|
/**
|
|
109
154
|
* @param {Object} options Plugin options
|
|
110
155
|
* @return {Object} PostHTML tree
|
|
111
156
|
*/
|
|
157
|
+
|
|
112
158
|
function processTree(options) {
|
|
113
159
|
const filledSlots = {};
|
|
114
160
|
|
|
115
|
-
// let processCounter = 0;
|
|
116
|
-
|
|
117
161
|
return function (tree) {
|
|
162
|
+
log(`Processing tree number ${processCounter}..`, 'processTree');
|
|
163
|
+
|
|
118
164
|
if (options.plugins.length > 0) {
|
|
119
165
|
tree = applyPluginsToTree(tree, options.plugins);
|
|
120
166
|
}
|
|
121
167
|
|
|
122
168
|
match.call(tree, options.matcher, currentNode => {
|
|
169
|
+
log(`Match found for tag "${currentNode.tag}"..`, 'processTree');
|
|
170
|
+
|
|
123
171
|
if (!currentNode.attrs) {
|
|
124
172
|
currentNode.attrs = {};
|
|
125
173
|
}
|
|
@@ -130,20 +178,21 @@ function processTree(options) {
|
|
|
130
178
|
return currentNode;
|
|
131
179
|
}
|
|
132
180
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
// log(currentNode, 'currentNode');
|
|
181
|
+
log(`${++processCounter}) Processing "${currentNode.tag}" from "${componentPath}"`, 'processTree');
|
|
136
182
|
|
|
137
183
|
let nextNode = parser(readFileSync(componentPath, 'utf8'));
|
|
138
184
|
|
|
139
185
|
// Set filled slots
|
|
140
186
|
setFilledSlots(currentNode, filledSlots, options);
|
|
141
|
-
// setFilledSlots(nextNode, filledSlots, options);
|
|
142
187
|
|
|
143
|
-
|
|
144
|
-
|
|
188
|
+
const aware = transform(options.aware, (result, value) => {
|
|
189
|
+
assign(result, value);
|
|
190
|
+
}, {});
|
|
145
191
|
|
|
146
|
-
|
|
192
|
+
// Reset options.expressions.locals and keep aware locals
|
|
193
|
+
options.expressions.locals = {...options.props, ...aware};
|
|
194
|
+
|
|
195
|
+
const {attributes, props} = processProps(currentNode, nextNode, filledSlots, options, componentPath, processCounter);
|
|
147
196
|
|
|
148
197
|
options.expressions.locals = attributes;
|
|
149
198
|
options.expressions.locals.$slots = filledSlots;
|
|
@@ -172,21 +221,37 @@ function processTree(options) {
|
|
|
172
221
|
currentNode.tag = false;
|
|
173
222
|
currentNode.content = content;
|
|
174
223
|
|
|
175
|
-
processAttributes(currentNode, attributes,
|
|
224
|
+
processAttributes(currentNode, attributes, props, options, aware);
|
|
225
|
+
|
|
226
|
+
// Remove attributes when value is 'null' or 'undefined'
|
|
227
|
+
// so we can conditionally add an attribute by setting value to 'undefined' or 'null'.
|
|
228
|
+
walk.call(currentNode, node => {
|
|
229
|
+
if (node && node.attrs) {
|
|
230
|
+
each(node.attrs, (value, key) => {
|
|
231
|
+
if (['undefined', 'null'].includes(value)) {
|
|
232
|
+
delete node.attrs[key];
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return node;
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
log(`Done processing number ${processCounter}.`, 'processTree', 'success');
|
|
176
241
|
|
|
177
|
-
//
|
|
178
|
-
|
|
179
|
-
// currentNode.attrs.data = JSON.stringify({ attributes, locals });
|
|
242
|
+
// Reset options.aware for current processCounter
|
|
243
|
+
delete options.aware[processCounter];
|
|
180
244
|
|
|
181
|
-
//
|
|
182
|
-
|
|
183
|
-
// file: componentPath,
|
|
184
|
-
// from: options.root
|
|
185
|
-
// });
|
|
245
|
+
// Decrement counter
|
|
246
|
+
processCounter--;
|
|
186
247
|
|
|
187
248
|
return currentNode;
|
|
188
249
|
});
|
|
189
250
|
|
|
251
|
+
if (processCounter === 0) {
|
|
252
|
+
log('End of processing', 'processTree', 'success');
|
|
253
|
+
}
|
|
254
|
+
|
|
190
255
|
return tree;
|
|
191
256
|
};
|
|
192
257
|
}
|
|
@@ -205,6 +270,9 @@ function getComponentPath(currentNode, options) {
|
|
|
205
270
|
}
|
|
206
271
|
}
|
|
207
272
|
|
|
273
|
+
// Delete attribute used as path
|
|
274
|
+
delete currentNode.attrs[options.attribute];
|
|
275
|
+
|
|
208
276
|
return componentPath;
|
|
209
277
|
}
|
|
210
278
|
|
package/src/log.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const {inspect} = require('util');
|
|
2
|
+
|
|
3
|
+
const debug = false;
|
|
4
|
+
|
|
5
|
+
// Colors
|
|
6
|
+
const colors = {
|
|
7
|
+
reset: '\u001B[0m',
|
|
8
|
+
error: '\u001B[31m',
|
|
9
|
+
success: '\u001B[32m',
|
|
10
|
+
warning: '\u001B[33m',
|
|
11
|
+
|
|
12
|
+
errorHighlight: '\u001B[41m',
|
|
13
|
+
successHighlight: '\u001B[42m',
|
|
14
|
+
warningHighlight: '\u001B[43m',
|
|
15
|
+
|
|
16
|
+
highlighted: '\u001B[45m'
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
module.exports = (message, method, level = 'reset', object = null) => {
|
|
20
|
+
if (debug === true || method === debug) {
|
|
21
|
+
if (object) {
|
|
22
|
+
console.log(`[${colors.highlighted}x-components ${method}()${colors.reset}]`, colors[level] || 'reset', message, inspect(object, false, null, true), colors.reset);
|
|
23
|
+
} else {
|
|
24
|
+
console.log(`[${colors.highlighted}x-components ${method}()${colors.reset}]`, colors[level] || 'reset', message, colors.reset);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
};
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const {match} = require('posthtml/lib/api');
|
|
4
|
+
const parseAttrs = require('posthtml-attrs-parser');
|
|
5
|
+
const styleToObject = require('style-to-object');
|
|
6
|
+
const validAttributes = require('./valid-attributes');
|
|
7
|
+
const keys = require('lodash/keys');
|
|
8
|
+
const union = require('lodash/union');
|
|
9
|
+
const pick = require('lodash/pick');
|
|
10
|
+
const difference = require('lodash/difference');
|
|
11
|
+
const each = require('lodash/each');
|
|
12
|
+
const has = require('lodash/has');
|
|
13
|
+
const extend = require('lodash/extend');
|
|
14
|
+
const isString = require('lodash/isString');
|
|
15
|
+
const isObject = require('lodash/isObject');
|
|
16
|
+
const isEmpty = require('lodash/isEmpty');
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Map component attributes that it's not defined as props to first element of node
|
|
20
|
+
*
|
|
21
|
+
* @param {Object} currentNode
|
|
22
|
+
* @param {Object} attributes
|
|
23
|
+
* @param {Object} props
|
|
24
|
+
* @param {Object} options
|
|
25
|
+
* @param {Object} aware
|
|
26
|
+
* @return {void}
|
|
27
|
+
*/
|
|
28
|
+
module.exports = (currentNode, attributes, props, options, aware) => {
|
|
29
|
+
let mainNode;
|
|
30
|
+
match.call(currentNode, {attrs: {attributes: ''}}, node => {
|
|
31
|
+
delete node.attrs.attributes;
|
|
32
|
+
mainNode = node;
|
|
33
|
+
|
|
34
|
+
return node;
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
if (!mainNode) {
|
|
38
|
+
const index = currentNode.content.findIndex(content => typeof content === 'object');
|
|
39
|
+
|
|
40
|
+
if (index === -1) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
mainNode = currentNode.content[index];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const nodeAttrs = parseAttrs(mainNode.attrs, options.attrsParserRules);
|
|
48
|
+
|
|
49
|
+
// Merge elementAttributes and blacklistAttributes with options provided
|
|
50
|
+
validAttributes.blacklistAttributes = union(validAttributes.blacklistAttributes, options.blacklistAttributes);
|
|
51
|
+
validAttributes.safelistAttributes = union(validAttributes.safelistAttributes, options.safelistAttributes);
|
|
52
|
+
|
|
53
|
+
// Merge or override elementAttributes from options provided
|
|
54
|
+
if (!isEmpty(options.elementAttributes)) {
|
|
55
|
+
each(options.elementAttributes, (modifier, tagName) => {
|
|
56
|
+
if (typeof modifier === 'function' && isString(tagName)) {
|
|
57
|
+
tagName = tagName.toUpperCase();
|
|
58
|
+
const attributes = modifier(validAttributes.elementAttributes[tagName]);
|
|
59
|
+
if (Array.isArray(attributes)) {
|
|
60
|
+
validAttributes.elementAttributes[tagName] = attributes;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Attributes to be excluded
|
|
67
|
+
const excludeAttributes = union(validAttributes.blacklistAttributes, keys(props), keys(aware), keys(options.props), ['$slots']);
|
|
68
|
+
// All valid HTML attributes for the main element
|
|
69
|
+
const allValidElementAttributes = isString(mainNode.tag) && has(validAttributes.elementAttributes, mainNode.tag.toUpperCase()) ? validAttributes.elementAttributes[mainNode.tag.toUpperCase()] : [];
|
|
70
|
+
// Valid HTML attributes without the excluded
|
|
71
|
+
const validElementAttributes = difference(allValidElementAttributes, excludeAttributes);
|
|
72
|
+
// Add override attributes
|
|
73
|
+
validElementAttributes.push('override:style');
|
|
74
|
+
validElementAttributes.push('override:class');
|
|
75
|
+
// Pick valid attributes from passed
|
|
76
|
+
const mainNodeAttributes = pick(attributes, validElementAttributes);
|
|
77
|
+
|
|
78
|
+
// Get additional specified attributes
|
|
79
|
+
each(attributes, (value, attr) => {
|
|
80
|
+
each(validAttributes.safelistAttributes, additionalAttr => {
|
|
81
|
+
if (additionalAttr === attr || (additionalAttr.endsWith('*') && attr.startsWith(additionalAttr.replace('*', '')))) {
|
|
82
|
+
mainNodeAttributes[attr] = value;
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
each(mainNodeAttributes, (value, key) => {
|
|
88
|
+
if (['class', 'style'].includes(key)) {
|
|
89
|
+
if (!has(nodeAttrs, key)) {
|
|
90
|
+
nodeAttrs[key] = key === 'class' ? [] : {};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (key === 'class') {
|
|
94
|
+
nodeAttrs.class.push(attributes.class);
|
|
95
|
+
} else {
|
|
96
|
+
nodeAttrs.style = extend(nodeAttrs.style, styleToObject(attributes.style));
|
|
97
|
+
}
|
|
98
|
+
} else {
|
|
99
|
+
nodeAttrs[key.replace('override:', '')] = attributes[key];
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
delete attributes[key];
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
// The plugin posthtml-attrs-parser compose() method expects a string,
|
|
106
|
+
// but since we are JSON parsing, values like "-1" become number -1.
|
|
107
|
+
// So below we convert non string values to string.
|
|
108
|
+
each(nodeAttrs, (value, key) => {
|
|
109
|
+
if (key !== 'compose' && !isObject(nodeAttrs[key]) && !isString(nodeAttrs[key])) {
|
|
110
|
+
nodeAttrs[key] = nodeAttrs[key].toString();
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
mainNode.attrs = nodeAttrs.compose();
|
|
115
|
+
};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const processScript = require('./process-script');
|
|
4
|
+
const pick = require('lodash/pick');
|
|
5
|
+
const each = require('lodash/each');
|
|
6
|
+
const assign = require('lodash/assign');
|
|
7
|
+
const mergeWith = require('lodash/mergeWith');
|
|
8
|
+
|
|
9
|
+
const attributeTypes = ['aware', 'merge'];
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Parse props from attributes, globals and via script
|
|
13
|
+
*
|
|
14
|
+
* @param {Object} currentNode - PostHTML tree
|
|
15
|
+
* @param {Array} nextNode - PostHTML tree
|
|
16
|
+
* @param {Object} filledSlots - Filled slots
|
|
17
|
+
* @param {Object} options - Plugin options
|
|
18
|
+
* @param {string} componentPath - Component path
|
|
19
|
+
* @param {number} processCounter
|
|
20
|
+
* @return {Object} - Attribute props and script props
|
|
21
|
+
*/
|
|
22
|
+
module.exports = (currentNode, nextNode, filledSlots, options, componentPath, processCounter) => {
|
|
23
|
+
let attributes = {...currentNode.attrs};
|
|
24
|
+
|
|
25
|
+
const attributesByTypeName = {};
|
|
26
|
+
each(attributeTypes, type => {
|
|
27
|
+
attributesByTypeName[type] = [];
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
each(attributes, (value, key, attrs) => {
|
|
31
|
+
let newKey = key;
|
|
32
|
+
each(attributeTypes, type => {
|
|
33
|
+
if (key.startsWith(`${type}:`)) {
|
|
34
|
+
newKey = newKey.replace(`${type}:`, '');
|
|
35
|
+
attributesByTypeName[type].push(newKey);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
if (newKey !== key) {
|
|
40
|
+
attrs[newKey] = value;
|
|
41
|
+
delete attrs[key];
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// Parse JSON attributes
|
|
46
|
+
each(attributes, (value, key, attrs) => {
|
|
47
|
+
try {
|
|
48
|
+
attrs[key] = JSON.parse(value);
|
|
49
|
+
} catch {}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// Merge or extend attribute props
|
|
53
|
+
if (attributes[options.propsAttribute]) {
|
|
54
|
+
if (attributesByTypeName.merge.includes(options.propsAttribute)) {
|
|
55
|
+
attributesByTypeName.merge.splice(attributesByTypeName.merge.indexOf(options.propsAttribute), 1);
|
|
56
|
+
mergeWith(attributes, attributes[options.propsAttribute], options.mergeCustomizer);
|
|
57
|
+
} else {
|
|
58
|
+
assign(attributes, attributes[options.propsAttribute]);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
delete attributes[options.propsAttribute];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Merge with global
|
|
65
|
+
attributes = mergeWith({}, options.expressions.locals, attributes, options.mergeCustomizer);
|
|
66
|
+
|
|
67
|
+
// Process props from <script props>
|
|
68
|
+
const {props} = processScript(nextNode, {props: {...attributes}, $slots: filledSlots, propsScriptAttribute: options.propsScriptAttribute, propsContext: options.propsContext, utilities: options.utilities}, componentPath.replace(`.${options.fileExtension}`, '.js'));
|
|
69
|
+
|
|
70
|
+
if (props) {
|
|
71
|
+
assign(attributes, props);
|
|
72
|
+
// if (attributesByTypeName.merge.length > 0) {
|
|
73
|
+
// assign(attributes, mergeWith(pick(locals, attributesByTypeName.merge), pick(attributes, attributesByTypeName.merge), options.mergeCustomizer));
|
|
74
|
+
// }
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Set aware attributes
|
|
78
|
+
if (attributesByTypeName.aware.length > 0) {
|
|
79
|
+
options.aware[processCounter] = pick(attributes, attributesByTypeName.aware);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return {attributes, props};
|
|
83
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const vm = require('vm');
|
|
4
|
+
const {existsSync, readFileSync} = require('fs');
|
|
5
|
+
const {render} = require('posthtml-render');
|
|
6
|
+
const {match} = require('posthtml/lib/api');
|
|
7
|
+
|
|
8
|
+
const ctx = vm.createContext({module, require});
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Get the script tag with props from a node list and return process props.
|
|
12
|
+
* Custom posthtml-expressions/lib/locals.
|
|
13
|
+
*
|
|
14
|
+
* @param {Array} tree Nodes
|
|
15
|
+
* @param {Object} options Options
|
|
16
|
+
* @param {string} scriptPath - Component path
|
|
17
|
+
* @return {Object} {} Locals
|
|
18
|
+
*/
|
|
19
|
+
module.exports = (tree, options, scriptPath) => {
|
|
20
|
+
const props = {};
|
|
21
|
+
const propsContext = options.props;
|
|
22
|
+
const utilities = {...options.utilities};
|
|
23
|
+
const context = {...utilities, ...ctx, [options.propsContext]: propsContext, $slots: options.$slots};
|
|
24
|
+
|
|
25
|
+
const runInContext = code => {
|
|
26
|
+
try {
|
|
27
|
+
const parsedContext = vm.createContext(context);
|
|
28
|
+
const parsedProps = vm.runInContext(code, parsedContext);
|
|
29
|
+
|
|
30
|
+
Object.assign(props, parsedProps);
|
|
31
|
+
} catch {}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
if (existsSync(scriptPath)) {
|
|
35
|
+
runInContext(readFileSync(scriptPath, 'utf8'));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
match.call(tree, {tag: 'script', attrs: {[options.propsScriptAttribute]: ''}}, node => {
|
|
39
|
+
if (node.content) {
|
|
40
|
+
runInContext(render(node.content));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return '';
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
props
|
|
48
|
+
};
|
|
49
|
+
};
|
|
@@ -14,7 +14,7 @@ const omit = require('lodash/omit');
|
|
|
14
14
|
* @param {String} slotSeparator Slot separator
|
|
15
15
|
* @return {void}
|
|
16
16
|
*/
|
|
17
|
-
function setFilledSlots(currentNode, filledSlots, {fill, slotSeparator}) {
|
|
17
|
+
function setFilledSlots(currentNode, filledSlots, {fill, slotSeparator, propsSlot}) {
|
|
18
18
|
match.call(currentNode, {tag: fill}, fillNode => {
|
|
19
19
|
if (!fillNode.attrs) {
|
|
20
20
|
fillNode.attrs = {};
|
|
@@ -22,10 +22,10 @@ function setFilledSlots(currentNode, filledSlots, {fill, slotSeparator}) {
|
|
|
22
22
|
|
|
23
23
|
const name = fillNode.tag.split(slotSeparator)[1];
|
|
24
24
|
|
|
25
|
-
const
|
|
25
|
+
const props = omit(fillNode.attrs, ['append', 'prepend', 'aware']);
|
|
26
26
|
|
|
27
|
-
if (
|
|
28
|
-
each(
|
|
27
|
+
if (props) {
|
|
28
|
+
each(props, (value, key, attrs) => {
|
|
29
29
|
try {
|
|
30
30
|
attrs[key] = JSON.parse(value);
|
|
31
31
|
} catch {}
|
|
@@ -39,7 +39,7 @@ function setFilledSlots(currentNode, filledSlots, {fill, slotSeparator}) {
|
|
|
39
39
|
attrs: fillNode.attrs,
|
|
40
40
|
content: fillNode.content,
|
|
41
41
|
source: render(fillNode.content),
|
|
42
|
-
|
|
42
|
+
[propsSlot]: props
|
|
43
43
|
};
|
|
44
44
|
|
|
45
45
|
return fillNode;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const {match} = require('posthtml/lib/api');
|
|
4
4
|
const {render} = require('posthtml-render');
|
|
5
|
+
const get = require('lodash/get');
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Process <push> tag
|
|
@@ -13,8 +14,12 @@ const {render} = require('posthtml-render');
|
|
|
13
14
|
*/
|
|
14
15
|
function processPushes(tree, content, push) {
|
|
15
16
|
match.call(tree, {tag: push}, pushNode => {
|
|
17
|
+
if (get(pushNode, 'attrs.name') === '') {
|
|
18
|
+
throw new Error(`[components] <${push}> tag requires a value for the "name" attribute.`);
|
|
19
|
+
}
|
|
20
|
+
|
|
16
21
|
if (!pushNode.attrs || !pushNode.attrs.name) {
|
|
17
|
-
throw new Error(`[components]
|
|
22
|
+
throw new Error(`[components] <${push}> tag requires a "name" attribute.`);
|
|
18
23
|
}
|
|
19
24
|
|
|
20
25
|
if (!content[pushNode.attrs.name]) {
|