posthtml-component 1.0.0-beta.8 → 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 +186 -112
- package/src/find-path.js +3 -3
- package/src/index.js +102 -36
- 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/ava.config.js +0 -5
- 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)) {
|
|
@@ -50,8 +90,6 @@ module.exports = (options = {}) => tree => {
|
|
|
50
90
|
}
|
|
51
91
|
});
|
|
52
92
|
|
|
53
|
-
options.mergeCustomizer();
|
|
54
|
-
|
|
55
93
|
if (!(options.slot instanceof RegExp)) {
|
|
56
94
|
options.slot = new RegExp(`^${options.slot}${options.slotSeparator}`, 'i');
|
|
57
95
|
}
|
|
@@ -88,11 +126,13 @@ module.exports = (options = {}) => tree => {
|
|
|
88
126
|
}
|
|
89
127
|
});
|
|
90
128
|
|
|
91
|
-
options.
|
|
129
|
+
options.props = {...options.expressions.locals};
|
|
92
130
|
options.aware = {};
|
|
93
131
|
|
|
94
132
|
const pushedContent = {};
|
|
95
133
|
|
|
134
|
+
log('Start of processing..', 'init', 'success');
|
|
135
|
+
|
|
96
136
|
return processStacks(
|
|
97
137
|
processPushes(
|
|
98
138
|
processTree(options)(
|
|
@@ -107,21 +147,27 @@ module.exports = (options = {}) => tree => {
|
|
|
107
147
|
};
|
|
108
148
|
/* eslint-enable complexity */
|
|
109
149
|
|
|
150
|
+
// Used for reset aware props
|
|
151
|
+
let processCounter = 0;
|
|
152
|
+
|
|
110
153
|
/**
|
|
111
154
|
* @param {Object} options Plugin options
|
|
112
155
|
* @return {Object} PostHTML tree
|
|
113
156
|
*/
|
|
157
|
+
|
|
114
158
|
function processTree(options) {
|
|
115
159
|
const filledSlots = {};
|
|
116
160
|
|
|
117
|
-
// let processCounter = 0;
|
|
118
|
-
|
|
119
161
|
return function (tree) {
|
|
162
|
+
log(`Processing tree number ${processCounter}..`, 'processTree');
|
|
163
|
+
|
|
120
164
|
if (options.plugins.length > 0) {
|
|
121
165
|
tree = applyPluginsToTree(tree, options.plugins);
|
|
122
166
|
}
|
|
123
167
|
|
|
124
168
|
match.call(tree, options.matcher, currentNode => {
|
|
169
|
+
log(`Match found for tag "${currentNode.tag}"..`, 'processTree');
|
|
170
|
+
|
|
125
171
|
if (!currentNode.attrs) {
|
|
126
172
|
currentNode.attrs = {};
|
|
127
173
|
}
|
|
@@ -132,20 +178,21 @@ function processTree(options) {
|
|
|
132
178
|
return currentNode;
|
|
133
179
|
}
|
|
134
180
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
// log(currentNode, 'currentNode');
|
|
181
|
+
log(`${++processCounter}) Processing "${currentNode.tag}" from "${componentPath}"`, 'processTree');
|
|
138
182
|
|
|
139
183
|
let nextNode = parser(readFileSync(componentPath, 'utf8'));
|
|
140
184
|
|
|
141
185
|
// Set filled slots
|
|
142
186
|
setFilledSlots(currentNode, filledSlots, options);
|
|
143
|
-
// setFilledSlots(nextNode, filledSlots, options);
|
|
144
187
|
|
|
145
|
-
|
|
146
|
-
|
|
188
|
+
const aware = transform(options.aware, (result, value) => {
|
|
189
|
+
assign(result, value);
|
|
190
|
+
}, {});
|
|
147
191
|
|
|
148
|
-
|
|
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);
|
|
149
196
|
|
|
150
197
|
options.expressions.locals = attributes;
|
|
151
198
|
options.expressions.locals.$slots = filledSlots;
|
|
@@ -174,21 +221,37 @@ function processTree(options) {
|
|
|
174
221
|
currentNode.tag = false;
|
|
175
222
|
currentNode.content = content;
|
|
176
223
|
|
|
177
|
-
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');
|
|
178
241
|
|
|
179
|
-
//
|
|
180
|
-
|
|
181
|
-
// currentNode.attrs.data = JSON.stringify({ attributes, locals });
|
|
242
|
+
// Reset options.aware for current processCounter
|
|
243
|
+
delete options.aware[processCounter];
|
|
182
244
|
|
|
183
|
-
//
|
|
184
|
-
|
|
185
|
-
// file: componentPath,
|
|
186
|
-
// from: options.root
|
|
187
|
-
// });
|
|
245
|
+
// Decrement counter
|
|
246
|
+
processCounter--;
|
|
188
247
|
|
|
189
248
|
return currentNode;
|
|
190
249
|
});
|
|
191
250
|
|
|
251
|
+
if (processCounter === 0) {
|
|
252
|
+
log('End of processing', 'processTree', 'success');
|
|
253
|
+
}
|
|
254
|
+
|
|
192
255
|
return tree;
|
|
193
256
|
};
|
|
194
257
|
}
|
|
@@ -207,6 +270,9 @@ function getComponentPath(currentNode, options) {
|
|
|
207
270
|
}
|
|
208
271
|
}
|
|
209
272
|
|
|
273
|
+
// Delete attribute used as path
|
|
274
|
+
delete currentNode.attrs[options.attribute];
|
|
275
|
+
|
|
210
276
|
return componentPath;
|
|
211
277
|
}
|
|
212
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]) {
|