posthtml-component 2.0.0 → 2.1.0-beta.2

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/README.md CHANGED
@@ -58,7 +58,7 @@ Init PostHTML:
58
58
  ```js
59
59
  // index.js
60
60
  const posthtml = require('posthtml')
61
- const components = require('posthtml-components')
61
+ const components = require('posthtml-component')
62
62
  const { readFileSync, writeFileSync } = require('node:fs')
63
63
 
64
64
  posthtml([
@@ -117,37 +117,37 @@ You can clone this repo and run `npm run build` to compile them.
117
117
 
118
118
  ## Options
119
119
 
120
- | Name | Type | Default | Description |
121
- |--------------------------|-------------------|----------------------------------------------|-----------------------------------------------------------------------------------------|
122
- | **root** | `String` | `'./'` | Root path for components lookup. |
123
- | **folders** | `String[]` | `['']` | Array of paths relative to `options.root` or defined namespaces. |
124
- | **tagPrefix** | `String` | `'x-'` | Tag prefix. |
125
- | **tag** | `String\|Boolean` | `false` | Component tag. Use with `options.attribute`. Boolean only `false`. |
126
- | **attribute** | `String` | `'src'` | Component attribute for setting path. |
127
- | **namespaces** | `String[]` | `[]` | Array of namespace root paths, fallback paths, and custom override paths. |
128
- | **namespaceSeparator** | `String` | `'::'` | Namespace separator for tag names. |
129
- | **fileExtension** | `String` | `'html'` | File extension for component files. |
130
- | **yield** | `String` | `'yield'` | Tag name for injecting main component content. |
131
- | **slot** | `String` | `'slot'` | Tag name for slots. |
132
- | **fill** | `String` | `'fill'` | Tag name for filling slots. |
133
- | **slotSeparator** | `String` | `':'` | Name separator for `<slot>` and `<fill>` tags. |
134
- | **push** | `String` | `'push'` | Tag name for `<push>`. |
135
- | **stack** | `String` | `'stack'` | Tag name for `<stack>`. |
136
- | **propsScriptAttribute** | `String` | `'props'` | Attribute in `<script props>` for retrieving component props. |
137
- | **propsContext** | `String` | `'props'` | Name of the object inside the script for processing props. |
138
- | **propsAttribute** | `String` | `'props'` | Attribute to define props as JSON. |
139
- | **propsSlot** | `String` | `'props'` | Used to retrieve props passed to slot via `$slots.slotName.props`. |
140
- | **parserOptions** | `Object` | `{recognizeSelfClosing: true}` | Pass options to `posthtml-parser`. |
141
- | **expressions** | `Object` | `{}` | Pass options to `posthtml-expressions`. |
142
- | **plugins** | `Array` | `[]` | PostHTML plugins to apply to every parsed component. |
143
- | **matcher** | `Object` | `[{tag: options.tagPrefix}]` | Array of objects used to match tags. |
144
- | **attrsParserRules** | `Object` | `{}` | Additional rules for attributes parser plugin. |
145
- | **strict** | `Boolean` | `true` | Toggle exception throwing. |
146
- | **mergeCustomizer** | `Function` | `function` | Callback for lodash `mergeWith` to merge `options.expressions.locals` and props. |
147
- | **utilities** | `Object` | `{merge: _.mergeWith, template: _.template}` | Utility methods passed to `<script props>`. |
148
- | **elementAttributes** | `Object` | `{}` | Object with tag names and function modifiers of `valid-attributes.js`. |
149
- | **safelistAttributes** | `String[]` | `['data-*']` | Array of attribute names to add to default valid attributes. |
150
- | **blocklistAttributes** | `String[]` | `[]` | Array of attribute names to remove from default valid attributes. |
120
+ | Name | Type | Default | Description |
121
+ |--------------------------|--------------------|----------------------------------------------|----------------------------------------------------------------------------------|
122
+ | **root** | `String` | `'./'` | Root path where to look for components. |
123
+ | **folders** | `String[]` | `['']` | Array of paths relative to `options.root` or defined namespaces. |
124
+ | **fileExtension** | `String\|String[]` | `'html'` | Component file extensions to look for. |
125
+ | **tagPrefix** | `String` | `'x-'` | Tag prefix. |
126
+ | **tag** | `String\|Boolean` | `false` | Component tag. Use with `options.attribute`. Boolean only `false`. |
127
+ | **attribute** | `String` | `'src'` | Attribute to use for defining path to component file. |
128
+ | **namespaces** | `String[]` | `[]` | Array of namespace root paths, fallback paths, and custom override paths. |
129
+ | **namespaceSeparator** | `String` | `'::'` | Namespace separator for tag names. |
130
+ | **yield** | `String` | `'yield'` | Tag name for injecting main component content. |
131
+ | **slot** | `String` | `'slot'` | Tag name for [slots](#slots) |
132
+ | **fill** | `String` | `'fill'` | Tag name for filling slots. |
133
+ | **slotSeparator** | `String` | `':'` | Name separator for `<slot>` and `<fill>` tags. |
134
+ | **stack** | `String` | `'stack'` | Tag name for [`<stack>`](#stacks). |
135
+ | **push** | `String` | `'push'` | Tag name for `<push>`. |
136
+ | **propsScriptAttribute** | `String` | `'props'` | Attribute in `<script props>` for retrieving [component props](#props). |
137
+ | **propsContext** | `String` | `'props'` | Name of the object inside the script for processing props. |
138
+ | **propsAttribute** | `String` | `'props'` | Attribute name to define props as JSON on a component tag. |
139
+ | **propsSlot** | `String` | `'props'` | Used to retrieve props passed to slot via `$slots.slotName.props`. |
140
+ | **parserOptions** | `Object` | `{recognizeSelfClosing: true}` | Pass options to `posthtml-parser`. |
141
+ | **expressions** | `Object` | `{}` | Pass options to `posthtml-expressions`. |
142
+ | **plugins** | `Array` | `[]` | PostHTML plugins to apply to every parsed component. |
143
+ | **matcher** | `Object` | `[{tag: options.tagPrefix}]` | Array of objects used to match tags. |
144
+ | **attrsParserRules** | `Object` | `{}` | Additional rules for attributes parser plugin. |
145
+ | **strict** | `Boolean` | `true` | Toggle exception throwing. |
146
+ | **mergeCustomizer** | `Function` | `function` | Callback for lodash `mergeWith` to merge `options.expressions.locals` and props. |
147
+ | **utilities** | `Object` | `{merge: _.mergeWith, template: _.template}` | Utility methods passed to `<script props>`. |
148
+ | **elementAttributes** | `Object` | `{}` | Object with tag names and function modifiers of `valid-attributes.js`. |
149
+ | **safelistAttributes** | `String[]` | `['data-*']` | Array of attribute names to add to default valid attributes. |
150
+ | **blocklistAttributes** | `String[]` | `[]` | Array of attribute names to remove from default valid attributes. |
151
151
 
152
152
  ## Features
153
153
 
@@ -155,9 +155,9 @@ You can clone this repo and run `npm run build` to compile them.
155
155
 
156
156
  You can use the components in multiple ways, or also a combination of them.
157
157
 
158
- If you to use components as 'includes', you may define a tag and src attribute name.
158
+ If you want to use components as 'includes', you can define tag and `src` attribute names.
159
159
 
160
- Using our previous button component example, we can define the tag and attribute names and then use it in this way:
160
+ Using our previous button component example, we can define the tag and attribute names and then use it like this:
161
161
 
162
162
  ```hbs
163
163
  <!-- src/index.html -->
@@ -174,7 +174,7 @@ Init PostHTML:
174
174
  // index.js
175
175
 
176
176
  require('posthtml')(
177
- require('posthtml-components')({
177
+ require('posthtml-component')({
178
178
  root: './src',
179
179
  tag: 'component',
180
180
  attribute: 'src'
@@ -183,16 +183,20 @@ require('posthtml')(
183
183
  .then(/* ... */)
184
184
  ```
185
185
 
186
- If you need more control over tag matching, you can pass an array of matcher or single object via `options.matcher` like this:
186
+ If you need more control over tag matching, you may pass an array of matcher or single object via `options.matcher`:
187
187
 
188
188
  ```js
189
189
  // index.js
190
190
  const options = {
191
191
  root: './src',
192
- matcher: [{tag: 'a-tag'}, {tag: 'another-one'}, {tag: new RegExp(`^app-`, 'i')}]
192
+ matcher: [
193
+ {tag: 'a-tag'},
194
+ {tag: 'another-one'},
195
+ {tag: new RegExp(`^app-`, 'i')},
196
+ ]
193
197
  };
194
198
 
195
- require('posthtml')(require('posthtml-components')(options))
199
+ require('posthtml')(require('posthtml-component')(options))
196
200
  .process(/* ... */)
197
201
  .then(/* ... */)
198
202
  ```
@@ -208,7 +212,7 @@ const options = {
208
212
  tagPrefix: 'x-'
209
213
  };
210
214
 
211
- require('posthtml')(require('posthtml-components')(options))
215
+ require('posthtml')(require('posthtml-component')(options))
212
216
  .process(/* ... */)
213
217
  .then(/* ... */)
214
218
  ```
@@ -254,12 +258,13 @@ const options = {
254
258
  parserOptions: { decodeEntities: true }
255
259
  };
256
260
 
257
- require('posthtml')(require('posthtml-components')(options))
261
+ require('posthtml')(require('posthtml-component')(options))
258
262
  .process('some HTML', options.parserOptions)
259
263
  .then(/* ... */)
260
264
  ```
261
265
 
262
- Important: as you can see, whatever `parserOptions` you pass to the plugin, must also be passed in the `process` method in your code, otherwise your PostHTML build will use `posthtml-parser` defaults and will override anything you've passed to `posthtml-component`.
266
+ > [!IMPORTANT]
267
+ > The `parserOptions` that you pass to the plugin must also be passed in the `process` method in your code, otherwise your PostHTML build will use `posthtml-parser` defaults and will override anything you've passed to `posthtml-component`.
263
268
 
264
269
  #### Self-closing tags
265
270
 
@@ -267,7 +272,7 @@ The plugin supports self-closing tags by default, but you need to make sure to e
267
272
 
268
273
  ```js
269
274
  // index.js
270
- require('posthtml')(require('posthtml-components')({root: './src'}))
275
+ require('posthtml')(require('posthtml-component')({root: './src'}))
271
276
  .process('your HTML...', {recognizeSelfClosing: true})
272
277
  .then(/* ... */)
273
278
  ```
@@ -287,7 +292,7 @@ const options = {
287
292
  folders: ['components', 'layouts']
288
293
  };
289
294
 
290
- require('posthtml')(require('posthtml-components')(options))
295
+ require('posthtml')(require('posthtml-component')(options))
291
296
  .process(/* ... */)
292
297
  .then(/* ... */)
293
298
  ```
@@ -826,7 +831,7 @@ If default configurations for valid attributes are not right for you, you may co
826
831
  const { readFileSync, writeFileSync } = require('fs')
827
832
 
828
833
  const posthtml = require('posthtml')
829
- const components = require('posthtml-components')
834
+ const components = require('posthtml-component')
830
835
 
831
836
  const options = {
832
837
  root: './src',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "posthtml-component",
3
- "version": "2.0.0",
3
+ "version": "2.1.0-beta.2",
4
4
  "description": "Laravel Blade-inspired components for PostHTML with slots, attributes as props, custom tags and more.",
5
5
  "license": "MIT",
6
6
  "repository": "posthtml/posthtml-components",
@@ -39,7 +39,7 @@
39
39
  "style-to-object": "^1.0.6"
40
40
  },
41
41
  "devDependencies": {
42
- "@biomejs/biome": "1.8.3",
42
+ "@biomejs/biome": "1.9.4",
43
43
  "@vitest/coverage-v8": "^2.0.4",
44
44
  "conventional-changelog-cli": "^5.0.0",
45
45
  "highlight.js": "^11.6.0",
package/src/find-path.js CHANGED
@@ -13,16 +13,20 @@ const folderSeparator = '.';
13
13
  * @return {String|boolean} Full path or boolean false
14
14
  */
15
15
  module.exports = (tag, options) => {
16
- const fileNameFromTag = tag
17
- .replace(options.tagPrefix, '')
18
- .split(folderSeparator)
19
- .join(path.sep)
20
- .concat(folderSeparator, options.fileExtension);
16
+ const extensions = Array.isArray(options.fileExtension) ? options.fileExtension : [options.fileExtension];
17
+
18
+ const fileNamesFromTag = extensions.map(ext =>
19
+ tag
20
+ .replace(options.tagPrefix, '')
21
+ .split(folderSeparator)
22
+ .join(path.sep)
23
+ .concat(folderSeparator, ext)
24
+ )
21
25
 
22
26
  try {
23
- return tag.includes(options.namespaceSeparator) ?
24
- searchInNamespaces(tag, fileNameFromTag.split(options.namespaceSeparator), options) :
25
- searchInFolders(tag, fileNameFromTag, options);
27
+ return tag.includes(options.namespaceSeparator)
28
+ ? searchInNamespaces(tag, fileNamesFromTag, options)
29
+ : searchInFolders(tag, fileNamesFromTag, options);
26
30
  } catch (error) {
27
31
  if (options.strict) {
28
32
  throw new Error(error.message);
@@ -36,15 +40,17 @@ module.exports = (tag, options) => {
36
40
  * Search component file in root
37
41
  *
38
42
  * @param {String} tag [tag name]
39
- * @param {String} fileNameFromTag [filename converted from tag name]
43
+ * @param {Array} fileNamesFromTag [filename converted from tag name]
40
44
  * @param {Object} options [posthtml options]
41
45
  * @return {String|boolean} [custom tag root where the module is found]
42
46
  */
43
- function searchInFolders(tag, fileNameFromTag, options) {
44
- const componentPath = search(options.root, options.folders, fileNameFromTag, options.fileExtension);
47
+ function searchInFolders(tag, fileNamesFromTag, options) {
48
+ const componentPath = search(options.root, options.folders, fileNamesFromTag, options.fileExtension);
45
49
 
46
50
  if (!componentPath) {
47
- throw new Error(`[components] <${tag}> could not find ${fileNameFromTag} in the defined root paths (${options.folders.join(', ')})`);
51
+ throw new Error(
52
+ `[components] <${tag}> could not find ${fileNamesFromTag} in the defined root paths (${options.folders.join(', ')})`
53
+ );
48
54
  }
49
55
 
50
56
  return componentPath;
@@ -54,65 +60,89 @@ function searchInFolders(tag, fileNameFromTag, options) {
54
60
  * Search component file within all defined namespaces
55
61
  *
56
62
  * @param {String} tag [tag name with namespace]
57
- * @param {String} namespace [tag's namespace]
58
- * @param {String} fileNameFromTag [filename converted from tag name]
63
+ * @param {Array} namespaceAndFileNames Array of [namespace]::[filename]
59
64
  * @param {Object} options [posthtml options]
60
65
  * @return {String|boolean} [custom tag root where the module is found]
61
66
  */
62
- function searchInNamespaces(tag, [namespace, fileNameFromTag], options) {
63
- const namespaceOption = options.namespaces.find(n => n.name === namespace.replace(options.tagPrefix, ''));
67
+ function searchInNamespaces(tag, namespaceAndFileNames, options) {
68
+ let result = '';
64
69
 
65
- if (!namespaceOption) {
66
- throw new Error(`[components] Unknown component namespace: ${namespace}.`);
67
- }
70
+ for (const namespaceAndFileName of namespaceAndFileNames) {
71
+ const [namespace, fileNameFromTag] = namespaceAndFileName.split('::');
68
72
 
69
- let componentPath;
73
+ const namespaceOption = options.namespaces.find(n => n.name === namespace.replace(options.tagPrefix, ''));
70
74
 
71
- // 1) Check in custom root
72
- if (namespaceOption.custom) {
73
- componentPath = search(namespaceOption.custom, options.folders, fileNameFromTag, options.fileExtension);
74
- }
75
+ if (!namespaceOption) {
76
+ throw new Error(`[components] Unknown component namespace: ${namespace}.`);
77
+ }
75
78
 
76
- // 2) Check in base root
77
- if (!componentPath) {
78
- componentPath = search(namespaceOption.root, options.folders, fileNameFromTag, options.fileExtension);
79
- }
79
+ let componentPath;
80
80
 
81
- // 3) Check in fallback root
82
- if (!componentPath && namespaceOption.fallback) {
83
- componentPath = search(namespaceOption.fallback, options.folders, fileNameFromTag, options.fileExtension);
84
- }
81
+ // 1) Check in custom root
82
+ if (namespaceOption.custom) {
83
+ componentPath = search(namespaceOption.custom, options.folders, fileNameFromTag, options.fileExtension);
84
+ }
85
+
86
+ // 2) Check in base root
87
+ if (!componentPath) {
88
+ componentPath = search(namespaceOption.root, options.folders, fileNameFromTag, options.fileExtension);
89
+ }
90
+
91
+ // 3) Check in fallback root
92
+ if (!componentPath && namespaceOption.fallback) {
93
+ componentPath = search(namespaceOption.fallback, options.folders, fileNameFromTag, options.fileExtension);
94
+ }
85
95
 
86
- if (!componentPath && options.strict) {
87
- throw new Error(`[components] <${tag}> could not find ${fileNameFromTag} in the defined namespace base path ${namespaceOption.root}`);
96
+ if (!componentPath) {
97
+ throw new Error(`[components] <${tag}> could not find ${fileNameFromTag} in the defined namespace paths.`);
98
+ }
99
+
100
+ result = componentPath;
88
101
  }
89
102
 
90
- return componentPath;
103
+ return result;
91
104
  }
92
105
 
93
106
  /**
94
- * Main search component file function
107
+ * Main component file search function
95
108
  *
96
- * @param {String} root Base root or namespace root from options
97
- * @param {Array} folders Folders from options
98
- * @param {String} fileName Filename converted from tag name
99
- * @param {String} extension File extension from options
109
+ * @param {String} root Base root or namespace root from `options`
110
+ * @param {Array} folders Folders to search in from `options`
111
+ * @param {Array} fileNames Filenames converted from tag name
112
+ * @param {Array} extensions File extension(s) from `options`
100
113
  * @return {String|boolean} [custom tag root where the module is found]
101
114
  */
102
- function search(root, folders, fileName, extension) {
115
+ function search(root, folders, fileNames, extensions) {
103
116
  let componentPath;
117
+ let componentFound = false;
104
118
 
105
- let componentFound = folders.some(folder => {
106
- componentPath = path.join(path.resolve(root, folder), fileName);
107
- return existsSync(componentPath);
108
- });
119
+ fileNames = Array.isArray(fileNames) ? fileNames : [fileNames];
109
120
 
110
- if (!componentFound) {
111
- fileName = fileName.replace(`.${extension}`, `${path.sep}index.${extension}`);
121
+ for (const fileName of fileNames) {
112
122
  componentFound = folders.some(folder => {
113
123
  componentPath = path.join(path.resolve(root, folder), fileName);
124
+
114
125
  return existsSync(componentPath);
115
126
  });
127
+
128
+ if (componentFound) break;
129
+ }
130
+
131
+ if (!componentFound) {
132
+ for (const extension of extensions) {
133
+ for (const fileName of fileNames) {
134
+ const newFileName = fileName.replace(`.${extension}`, `${path.sep}index.${extension}`);
135
+
136
+ componentFound = folders.some(folder => {
137
+ componentPath = path.join(path.resolve(root, folder), newFileName);
138
+ return existsSync(componentPath);
139
+ });
140
+
141
+ if (componentFound) break;
142
+ }
143
+
144
+ if (componentFound) break;
145
+ }
116
146
  }
117
147
 
118
148
  return componentFound ? componentPath : false;
package/src/index.d.ts CHANGED
@@ -60,7 +60,7 @@ export type PostHTMLComponents = {
60
60
  *
61
61
  * @default 'html'
62
62
  */
63
- fileExtension?: string;
63
+ fileExtension?: string|string[];
64
64
 
65
65
  /**
66
66
  * Name of the tag that will be replaced with the content that is passed to the component.
package/src/index.js CHANGED
@@ -74,17 +74,26 @@ module.exports = (options = {}) => tree => {
74
74
  uniqueId,
75
75
  isEnabled: prop => prop === true || prop === ''
76
76
  };
77
- // Additional element attributes, in case already exist in valid-attributes.js it will replace all attributes
78
- // It should be an object with key as tag name and as value a function modifier which receive
79
- // the default attributes and return an array of attributes. Example:
80
- // { TAG: (attributes) => { attributes[] = 'attribute-name'; return attributes; } }
77
+
78
+ /**
79
+ * Additional element attributes. If they already exist in valid-attributes.js,
80
+ * it will replace all attributes. It should be an object with tag name as
81
+ * the key and a function modifier as the value, which will receive the
82
+ * default attributes and return an array of attributes.
83
+ *
84
+ * Example:
85
+ * { TAG: (attributes) => { attributes[] = 'attribute-name'; return attributes; } }
86
+ */
81
87
  options.elementAttributes = isPlainObject(options.elementAttributes) ? options.elementAttributes : {};
82
88
  options.safelistAttributes = Array.isArray(options.safelistAttributes) ? options.safelistAttributes : [];
83
89
  options.blocklistAttributes = Array.isArray(options.blocklistAttributes) ? options.blocklistAttributes : [];
84
90
 
85
- // Merge customizer callback passed to lodash mergeWith
86
- // for merge attribute `props` and all attributes starting with `merge:`
87
- // @see https://lodash.com/docs/4.17.15#mergeWith
91
+ /**
92
+ * Merge customizer callback passed to `lodash.mergeWith` for merging
93
+ * attribute `props` and all attributes starting with `merge:`.
94
+ *
95
+ * @see {@link https://lodash.com/docs/4.17.15#mergeWith|Lodash}
96
+ */
88
97
  options.mergeCustomizer = options.mergeCustomizer || ((objectValue, sourceValue) => {
89
98
  if (Array.isArray(objectValue)) {
90
99
  return objectValue.concat(sourceValue);
@@ -105,6 +114,7 @@ module.exports = (options = {}) => tree => {
105
114
 
106
115
  if (!Array.isArray(options.matcher)) {
107
116
  options.matcher = [];
117
+
108
118
  if (options.tagPrefix) {
109
119
  options.matcher.push({tag: options.tagPrefix});
110
120
  }
@@ -116,8 +126,10 @@ module.exports = (options = {}) => tree => {
116
126
 
117
127
  options.folders = Array.isArray(options.folders) ? options.folders : [options.folders];
118
128
  options.namespaces = Array.isArray(options.namespaces) ? options.namespaces : [options.namespaces];
129
+
119
130
  options.namespaces.forEach((namespace, index) => {
120
131
  options.namespaces[index].root = path.resolve(namespace.root);
132
+
121
133
  if (namespace.fallback) {
122
134
  options.namespaces[index].fallback = path.resolve(namespace.fallback);
123
135
  }
@@ -148,14 +160,13 @@ module.exports = (options = {}) => tree => {
148
160
  };
149
161
  /* eslint-enable complexity */
150
162
 
151
- // Used for reset aware props
163
+ // Used to reset aware props
152
164
  let processCounter = 0;
153
165
 
154
166
  /**
155
167
  * @param {Object} options Plugin options
156
168
  * @return {Object} PostHTML tree
157
169
  */
158
-
159
170
  function processTree(options) {
160
171
  const filledSlots = {};
161
172
 
@@ -227,15 +238,18 @@ function processTree(options) {
227
238
 
228
239
  processAttributes(currentNode, attributes, props, options, aware);
229
240
 
230
- // Remove attributes when value is 'null' or 'undefined'
231
- // so we can conditionally add an attribute by setting value to 'undefined' or 'null'.
241
+ /**
242
+ * Remove attributes when value is 'null' or 'undefined' so we can
243
+ * conditionally add an attribute by setting the value to
244
+ * 'undefined' or 'null'.
245
+ */
232
246
  walk.call(currentNode, node => {
233
247
  if (node && node.attrs) {
234
- each(node.attrs, (value, key) => {
235
- if (['undefined', 'null'].includes(value)) {
248
+ for (const key in node.attrs) {
249
+ if (node.attrs[key] === 'undefined' || node.attrs[key] === 'null') {
236
250
  delete node.attrs[key];
237
251
  }
238
- });
252
+ }
239
253
  }
240
254
 
241
255
  return node;
@@ -8,7 +8,6 @@ const keys = require('lodash/keys');
8
8
  const union = require('lodash/union');
9
9
  const pick = require('lodash/pick');
10
10
  const difference = require('lodash/difference');
11
- const each = require('lodash/each');
12
11
  const has = require('lodash/has');
13
12
  const extend = require('lodash/extend');
14
13
  const isString = require('lodash/isString');
@@ -52,15 +51,16 @@ module.exports = (currentNode, attributes, props, options, aware) => {
52
51
 
53
52
  // Merge or override elementAttributes from options provided
54
53
  if (!isEmpty(options.elementAttributes)) {
55
- each(options.elementAttributes, (modifier, tagName) => {
54
+ for (const tagName in options.elementAttributes) {
55
+ const modifier = options.elementAttributes[tagName];
56
56
  if (typeof modifier === 'function' && isString(tagName)) {
57
- tagName = tagName.toUpperCase();
58
- const attributes = modifier(validAttributes.elementAttributes[tagName]);
57
+ const upperTagName = tagName.toUpperCase();
58
+ const attributes = modifier(validAttributes.elementAttributes[upperTagName]);
59
59
  if (Array.isArray(attributes)) {
60
- validAttributes.elementAttributes[tagName] = attributes;
60
+ validAttributes.elementAttributes[upperTagName] = attributes;
61
61
  }
62
62
  }
63
- });
63
+ }
64
64
  }
65
65
 
66
66
  // Attributes to be excluded
@@ -76,15 +76,18 @@ module.exports = (currentNode, attributes, props, options, aware) => {
76
76
  const mainNodeAttributes = pick(attributes, validElementAttributes);
77
77
 
78
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;
79
+ for (const attr in attributes) {
80
+ for (const additionalAttr of validAttributes.safelistAttributes) {
81
+ if (
82
+ additionalAttr === attr
83
+ || (additionalAttr.endsWith('*') && attr.startsWith(additionalAttr.replace('*', '')))
84
+ ) {
85
+ mainNodeAttributes[attr] = attributes[attr];
83
86
  }
84
- });
85
- });
87
+ }
88
+ }
86
89
 
87
- each(mainNodeAttributes, (value, key) => {
90
+ for (const key in mainNodeAttributes) {
88
91
  if (['class', 'style'].includes(key)) {
89
92
  if (!has(nodeAttrs, key)) {
90
93
  nodeAttrs[key] = key === 'class' ? [] : {};
@@ -100,16 +103,16 @@ module.exports = (currentNode, attributes, props, options, aware) => {
100
103
  }
101
104
 
102
105
  delete attributes[key];
103
- });
106
+ }
104
107
 
105
108
  // The plugin posthtml-attrs-parser compose() method expects a string,
106
109
  // but since we are JSON parsing, values like "-1" become number -1.
107
110
  // So below we convert non string values to string.
108
- each(nodeAttrs, (value, key) => {
111
+ for (const key in nodeAttrs) {
109
112
  if (key !== 'compose' && !isObject(nodeAttrs[key]) && !isString(nodeAttrs[key])) {
110
113
  nodeAttrs[key] = nodeAttrs[key].toString();
111
114
  }
112
- });
115
+ }
113
116
 
114
117
  mainNode.attrs = nodeAttrs.compose();
115
118
  };
@@ -1,10 +1,9 @@
1
1
  'use strict';
2
2
 
3
- const processScript = require('./process-script');
4
3
  const pick = require('lodash/pick');
5
- const each = require('lodash/each');
6
4
  const assign = require('lodash/assign');
7
5
  const mergeWith = require('lodash/mergeWith');
6
+ const processScript = require('./process-script');
8
7
 
9
8
  const attributeTypes = ['aware', 'merge'];
10
9
 
@@ -23,31 +22,31 @@ module.exports = (currentNode, nextNode, filledSlots, options, componentPath, pr
23
22
  let attributes = {...currentNode.attrs};
24
23
 
25
24
  const attributesByTypeName = {};
26
- each(attributeTypes, type => {
25
+ for (const type of attributeTypes) {
27
26
  attributesByTypeName[type] = [];
28
- });
27
+ }
29
28
 
30
- each(attributes, (value, key, attrs) => {
29
+ for (const key in attributes) {
31
30
  let newKey = key;
32
- each(attributeTypes, type => {
31
+ for (const type of attributeTypes) {
33
32
  if (key.startsWith(`${type}:`)) {
34
33
  newKey = newKey.replace(`${type}:`, '');
35
34
  attributesByTypeName[type].push(newKey);
36
35
  }
37
- });
36
+ }
38
37
 
39
38
  if (newKey !== key) {
40
- attrs[newKey] = value;
41
- delete attrs[key];
39
+ attributes[newKey] = attributes[key];
40
+ delete attributes[key];
42
41
  }
43
- });
42
+ }
44
43
 
45
44
  // Parse JSON attributes
46
- each(attributes, (value, key, attrs) => {
45
+ for (const key in attributes) {
47
46
  try {
48
- attrs[key] = JSON.parse(value);
47
+ attributes[key] = JSON.parse(attributes[key]);
49
48
  } catch {}
50
- });
49
+ }
51
50
 
52
51
  // Merge or extend attribute props
53
52
  if (attributes[options.propsAttribute]) {
@@ -65,7 +64,17 @@ module.exports = (currentNode, nextNode, filledSlots, options, componentPath, pr
65
64
  attributes = mergeWith({}, options.expressions.locals, attributes, options.mergeCustomizer);
66
65
 
67
66
  // 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'));
67
+ const {props} = processScript(
68
+ nextNode,
69
+ {
70
+ props: {...attributes},
71
+ $slots: filledSlots,
72
+ propsScriptAttribute: options.propsScriptAttribute,
73
+ propsContext: options.propsContext,
74
+ utilities: options.utilities
75
+ },
76
+ componentPath.replace(`.${options.fileExtension}`, '.js')
77
+ );
69
78
 
70
79
  if (props) {
71
80
  assign(attributes, props);
@@ -2,7 +2,6 @@
2
2
 
3
3
  const {match} = require('posthtml/lib/api');
4
4
  const {render} = require('posthtml-render');
5
- const each = require('lodash/each');
6
5
  const omit = require('lodash/omit');
7
6
 
8
7
  /**
@@ -25,11 +24,13 @@ function setFilledSlots(currentNode, filledSlots, {fill, slotSeparator, propsSlo
25
24
  const props = omit(fillNode.attrs, ['append', 'prepend', 'aware']);
26
25
 
27
26
  if (props) {
28
- each(props, (value, key, attrs) => {
29
- try {
30
- attrs[key] = JSON.parse(value);
31
- } catch {}
32
- });
27
+ for (const key in props) {
28
+ if (props.hasOwnProperty(key)) {
29
+ try {
30
+ props[key] = JSON.parse(props[key]);
31
+ } catch {}
32
+ }
33
+ }
33
34
  }
34
35
 
35
36
  filledSlots[name] = {