boxwood 0.60.2 → 0.61.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.
Files changed (139) hide show
  1. package/README.md +0 -358
  2. package/index.js +402 -4
  3. package/package.json +12 -37
  4. package/src/Bundler.js +0 -13
  5. package/src/Cache.js +0 -35
  6. package/src/Compiler.js +0 -28
  7. package/src/Importer.js +0 -142
  8. package/src/Optimizer.js +0 -96
  9. package/src/Scope.js +0 -20
  10. package/src/Statistics.js +0 -58
  11. package/src/bundlers/esbuild/index.js +0 -55
  12. package/src/bundlers/esbuild/plugins/css.js +0 -40
  13. package/src/bundlers/esbuild/plugins/html.js +0 -27
  14. package/src/bundlers/esbuild/plugins/image.js +0 -38
  15. package/src/bundlers/esbuild/plugins/resolve.js +0 -11
  16. package/src/bundlers/esbuild/plugins/yaml.js +0 -38
  17. package/src/bundlers/esbuild/utilities/asset.js +0 -19
  18. package/src/bundlers/rollup.js +0 -38
  19. package/src/compile.js +0 -6
  20. package/src/compilers/any/Compiler.js +0 -16
  21. package/src/compilers/compile.js +0 -17
  22. package/src/compilers/html/Analyzer.js +0 -24
  23. package/src/compilers/html/Compiler.js +0 -47
  24. package/src/compilers/html/Generator.js +0 -19
  25. package/src/compilers/html/Parser.js +0 -11
  26. package/src/compilers/html/Preprocessor.js +0 -130
  27. package/src/compilers/html/Renderer.js +0 -167
  28. package/src/compilers/html/Transpiler.js +0 -76
  29. package/src/compilers/js/Bundler.js +0 -84
  30. package/src/compilers/js/Compiler/index.js +0 -27
  31. package/src/conditions/isEqual.js +0 -5
  32. package/src/lexers/html.js +0 -49
  33. package/src/lexers/internal.js +0 -49
  34. package/src/linters/html/bracket.js +0 -36
  35. package/src/linters/html/component.js +0 -43
  36. package/src/linters/html/import.js +0 -35
  37. package/src/linters/html/index.js +0 -19
  38. package/src/linters/html/tag.js +0 -36
  39. package/src/optimizers/html.js +0 -15
  40. package/src/plugins/BoxModelPlugin/index.js +0 -109
  41. package/src/plugins/CurlyStylesPlugin/index.js +0 -39
  42. package/src/plugins/DataPlugin/index.js +0 -53
  43. package/src/plugins/InlinePlugin/css.js +0 -131
  44. package/src/plugins/InlinePlugin/index.js +0 -39
  45. package/src/plugins/InternationalizationPlugin/index.js +0 -111
  46. package/src/plugins/Plugin.js +0 -22
  47. package/src/plugins/ScopedStylesPlugin/css.js +0 -56
  48. package/src/plugins/ScopedStylesPlugin/html.js +0 -112
  49. package/src/plugins/ScopedStylesPlugin/index.js +0 -48
  50. package/src/plugins/SwappedStylesPlugin/index.js +0 -20
  51. package/src/render.js +0 -48
  52. package/src/tags/case.js +0 -28
  53. package/src/tags/catch.js +0 -17
  54. package/src/tags/data.js +0 -7
  55. package/src/tags/default.js +0 -20
  56. package/src/tags/doctype.js +0 -8
  57. package/src/tags/else.js +0 -18
  58. package/src/tags/elseif.js +0 -25
  59. package/src/tags/elseunless.js +0 -30
  60. package/src/tags/font.js +0 -37
  61. package/src/tags/for.js +0 -90
  62. package/src/tags/foreach.js +0 -80
  63. package/src/tags/if.js +0 -19
  64. package/src/tags/img.js +0 -25
  65. package/src/tags/index.js +0 -53
  66. package/src/tags/link.js +0 -14
  67. package/src/tags/script/index.js +0 -83
  68. package/src/tags/script/scoped.js +0 -26
  69. package/src/tags/slot.js +0 -9
  70. package/src/tags/style.js +0 -9
  71. package/src/tags/switch.js +0 -13
  72. package/src/tags/template.js +0 -22
  73. package/src/tags/translate.js +0 -22
  74. package/src/tags/translation.js +0 -38
  75. package/src/tags/try.js +0 -17
  76. package/src/tags/unless.js +0 -24
  77. package/src/tags/var.js +0 -32
  78. package/src/transpilers/css/index.js +0 -34
  79. package/src/transpilers/html/expression.js +0 -101
  80. package/src/transpilers/html/index.js +0 -189
  81. package/src/transpilers/html/node.js +0 -46
  82. package/src/transpilers/html/tags/any.js +0 -53
  83. package/src/transpilers/html/tags/comment.js +0 -7
  84. package/src/transpilers/html/tags/doctype.js +0 -6
  85. package/src/transpilers/html/tags/for.js +0 -30
  86. package/src/transpilers/html/tags/if.js +0 -55
  87. package/src/transpilers/html/tags/import.js +0 -7
  88. package/src/transpilers/html/tags/index.js +0 -13
  89. package/src/transpilers/html/tags/partial.js +0 -17
  90. package/src/transpilers/html/tags/slot.js +0 -7
  91. package/src/transpilers/html/tags/text.js +0 -6
  92. package/src/transpilers/html/tags/try.js +0 -30
  93. package/src/transpilers/html/tags/unless.js +0 -58
  94. package/src/transpilers/html/utilities/path.js +0 -9
  95. package/src/utilities/action.js +0 -208
  96. package/src/utilities/array.js +0 -80
  97. package/src/utilities/assets.js +0 -18
  98. package/src/utilities/ast.js +0 -13
  99. package/src/utilities/attributes.js +0 -23
  100. package/src/utilities/collect.js +0 -404
  101. package/src/utilities/conditions.js +0 -159
  102. package/src/utilities/convert.js +0 -359
  103. package/src/utilities/data.js +0 -55
  104. package/src/utilities/enum.js +0 -103
  105. package/src/utilities/errors.js +0 -96
  106. package/src/utilities/factory.js +0 -237
  107. package/src/utilities/files.js +0 -55
  108. package/src/utilities/filters.js +0 -140
  109. package/src/utilities/globals.js +0 -43
  110. package/src/utilities/html.js +0 -60
  111. package/src/utilities/inline.js +0 -200
  112. package/src/utilities/js.js +0 -15
  113. package/src/utilities/keywords.js +0 -35
  114. package/src/utilities/log.js +0 -31
  115. package/src/utilities/node.js +0 -149
  116. package/src/utilities/object.js +0 -8
  117. package/src/utilities/optimize.js +0 -268
  118. package/src/utilities/options.js +0 -99
  119. package/src/utilities/request.js +0 -13
  120. package/src/utilities/string.js +0 -144
  121. package/src/utilities/style.js +0 -15
  122. package/src/utilities/translations.js +0 -78
  123. package/src/utilities/url.js +0 -24
  124. package/src/vdom/browser/app.js +0 -19
  125. package/src/vdom/browser/diff.js +0 -104
  126. package/src/vdom/browser/index.js +0 -15
  127. package/src/vdom/browser/mount.js +0 -10
  128. package/src/vdom/browser/render.js +0 -66
  129. package/src/vdom/browser/replace.js +0 -7
  130. package/src/vdom/node.js +0 -10
  131. package/src/vdom/nodes.js +0 -116
  132. package/src/vdom/server/State.js +0 -21
  133. package/src/vdom/server/boxwood.js +0 -5
  134. package/src/vdom/server/index.js +0 -19
  135. package/src/vdom/server/render.js +0 -66
  136. package/src/vdom/tag.js +0 -33
  137. package/src/vdom/utilities/classes.js +0 -22
  138. package/src/vdom/utilities/css.js +0 -17
  139. package/src/vdom/utilities/escape.js +0 -50
package/index.js CHANGED
@@ -1,9 +1,407 @@
1
- const compile = require('./src/compile')
2
- const escape = require('./src/vdom/utilities/escape')
3
- const { createRender } = require('./src/render')
1
+ const csstree = require('css-tree')
2
+ const toHash = require('string-hash')
3
+
4
+ async function compile(path) {
5
+ const fn = require(path)
6
+ return {
7
+ template() {
8
+ const tree = fn(...arguments)
9
+ const nodes = {}
10
+ const styles = []
11
+ const scripts = []
12
+ const find = (node) => {
13
+ if (node.name === 'head') {
14
+ nodes.head = node
15
+ }
16
+ if (node.name === 'style') {
17
+ styles.push(node.children)
18
+ node.ignore = true
19
+ }
20
+ if (node.name === 'script') {
21
+ scripts.push(node.children)
22
+ node.ignore = true
23
+ }
24
+ if (Array.isArray(node)) {
25
+ node.forEach(find)
26
+ }
27
+ }
28
+ walk(tree, find)
29
+ if (nodes.head) {
30
+ if (styles.length > 0) {
31
+ nodes.head.children.push({
32
+ name: 'style',
33
+ children: styles.join(''),
34
+ })
35
+ }
36
+ if (scripts.length > 0) {
37
+ nodes.head.children.push({
38
+ name: 'script',
39
+ children: scripts.join(''),
40
+ })
41
+ }
42
+ }
43
+ return render(tree)
44
+ },
45
+ }
46
+ }
47
+
48
+ function walk(tree, callback) {
49
+ callback(tree)
50
+ if (Array.isArray(tree.children)) {
51
+ tree.children.map((node) => walk(node, callback))
52
+ }
53
+ }
54
+
55
+ const ENTITIES = {
56
+ '&': '&',
57
+ '<': '&lt;',
58
+ '>': '&gt;',
59
+ "'": '&#39;',
60
+ '"': '&quot;',
61
+ }
62
+
63
+ const REGEXP = /[&<>'"]/g
64
+
65
+ const escape = (string) => {
66
+ return String.prototype.replace.call(string, REGEXP, function (character) {
67
+ return ENTITIES[character]
68
+ })
69
+ }
70
+
71
+ const BOOLEAN_ATTRIBUTES = [
72
+ 'async',
73
+ 'autofocus',
74
+ 'autoplay',
75
+ 'border',
76
+ 'challenge',
77
+ 'checked',
78
+ 'compact',
79
+ 'contenteditable',
80
+ 'controls',
81
+ 'default',
82
+ 'defer',
83
+ 'disabled',
84
+ 'formnovalidate',
85
+ 'frameborder',
86
+ 'hidden',
87
+ 'indeterminate',
88
+ 'ismap',
89
+ 'loop',
90
+ 'multiple',
91
+ 'muted',
92
+ 'nohref',
93
+ 'noresize',
94
+ 'noshade',
95
+ 'novalidate',
96
+ 'nowrap',
97
+ 'open',
98
+ 'readonly',
99
+ 'required',
100
+ 'reversed',
101
+ 'scoped',
102
+ 'scrolling',
103
+ 'seamless',
104
+ 'selected',
105
+ 'sortable',
106
+ 'spellcheck',
107
+ 'translate',
108
+ ]
109
+
110
+ const attributes = (options) => {
111
+ const result = []
112
+ for (const key in options) {
113
+ if (BOOLEAN_ATTRIBUTES.includes(key)) {
114
+ result.push(key)
115
+ } else {
116
+ result.push(key + '=' + '"' + options[key] + '"')
117
+ }
118
+ }
119
+ return result.join(' ')
120
+ }
121
+
122
+ const SELF_CLOSING_TAGS = [
123
+ 'area',
124
+ 'base',
125
+ 'br',
126
+ 'col',
127
+ 'command',
128
+ 'embed',
129
+ 'hr',
130
+ 'img',
131
+ 'input',
132
+ 'keygen',
133
+ 'link',
134
+ 'meta',
135
+ 'param',
136
+ 'source',
137
+ 'track',
138
+ 'wbr',
139
+ '!DOCTYPE html',
140
+ ]
141
+
142
+ const render = (input) => {
143
+ if (input.ignore) {
144
+ return ''
145
+ }
146
+ if (Array.isArray(input)) {
147
+ return input.filter(Boolean).map(render).join('')
148
+ }
149
+ if (typeof input === 'number') {
150
+ return input.toString()
151
+ }
152
+ if (typeof input === 'string') {
153
+ return escape(input)
154
+ }
155
+ if (input.name === 'fragment') {
156
+ return render(input.children)
157
+ }
158
+ if (SELF_CLOSING_TAGS.includes(input.name)) {
159
+ if (input.attributes) {
160
+ return `<${input.name} ` + attributes(input.attributes) + '>'
161
+ }
162
+ return `<${input.name}>`
163
+ }
164
+ if (input.attributes && input.children) {
165
+ return (
166
+ `<${input.name} ` +
167
+ attributes(input.attributes) +
168
+ '>' +
169
+ render(input.children) +
170
+ `</${input.name}>`
171
+ )
172
+ }
173
+ if (input.attributes) {
174
+ return (
175
+ `<${input.name} ` + attributes(input.attributes) + `></${input.name}>`
176
+ )
177
+ }
178
+ if (input.children) {
179
+ return `<${input.name}>` + render(input.children) + `</${input.name}>`
180
+ }
181
+ return `<${input.name}></${input.name}>`
182
+ }
183
+
184
+ const fragment = (children) => {
185
+ return { name: 'fragment', children }
186
+ }
187
+
188
+ const tag = (a, b, c) => {
189
+ if (a && b && c) {
190
+ const name = a
191
+ const attributes = b
192
+ const children = c
193
+ return {
194
+ name,
195
+ children,
196
+ attributes,
197
+ }
198
+ }
199
+ const name = a
200
+ const children = b
201
+ if (SELF_CLOSING_TAGS.includes(name)) {
202
+ return {
203
+ name,
204
+ attributes: children,
205
+ }
206
+ }
207
+ return {
208
+ name,
209
+ children,
210
+ }
211
+ }
212
+
213
+ function css(inputs) {
214
+ let result = ''
215
+ for (let i = 0, ilen = inputs.length; i < ilen; i += 1) {
216
+ const input = inputs[i]
217
+ const value = arguments[i + 1]
218
+ if (value) {
219
+ result += input + value
220
+ } else {
221
+ result += input
222
+ }
223
+ }
224
+ const hash = toHash(result).toString(36).substr(0, 5)
225
+ const tree = csstree.parse(result)
226
+ const classes = {}
227
+
228
+ csstree.walk(tree, (node) => {
229
+ if (node.type === 'ClassSelector') {
230
+ const name = `__${node.name}__${hash}`
231
+ classes[node.name] = name
232
+ node.name = name
233
+ }
234
+ })
235
+
236
+ return {
237
+ ...classes,
238
+ css: tag('style', csstree.generate(tree)),
239
+ }
240
+ }
241
+
242
+ function js(inputs) {
243
+ let result = ''
244
+ for (let i = 0, ilen = inputs.length; i < ilen; i += 1) {
245
+ const input = inputs[i]
246
+ const value = arguments[i + 1]
247
+ if (value) {
248
+ result += input + value
249
+ } else {
250
+ result += input
251
+ }
252
+ }
253
+ return {
254
+ js: tag('script', result),
255
+ }
256
+ }
257
+
258
+ const node = (name) => (options, children) => tag(name, options, children)
259
+ const doctype = node('!DOCTYPE html')
260
+
261
+ const nodes = [
262
+ 'a',
263
+ 'abbr',
264
+ 'address',
265
+ 'area',
266
+ 'article',
267
+ 'aside',
268
+ 'audio',
269
+ 'b',
270
+ 'base',
271
+ 'bdi',
272
+ 'bdo',
273
+ 'blockquote',
274
+ 'body',
275
+ 'br',
276
+ 'button',
277
+ 'canvas',
278
+ 'caption',
279
+ 'cite',
280
+ 'code',
281
+ 'col',
282
+ 'colgroup',
283
+ 'data',
284
+ 'datalist',
285
+ 'dd',
286
+ 'del',
287
+ 'details',
288
+ 'dfn',
289
+ 'dialog',
290
+ 'div',
291
+ 'dl',
292
+ 'dt',
293
+ 'em',
294
+ 'embed',
295
+ 'fieldset',
296
+ 'figcaption',
297
+ 'figure',
298
+ 'footer',
299
+ 'form',
300
+ 'h1',
301
+ 'h2',
302
+ 'h3',
303
+ 'h4',
304
+ 'h5',
305
+ 'h6',
306
+ 'head',
307
+ 'header',
308
+ 'hr',
309
+ 'html',
310
+ 'i',
311
+ 'iframe',
312
+ 'img',
313
+ 'input',
314
+ 'ins',
315
+ 'kbd',
316
+ 'label',
317
+ 'legend',
318
+ 'li',
319
+ 'link',
320
+ 'main',
321
+ 'map',
322
+ 'mark',
323
+ 'meta',
324
+ 'meter',
325
+ 'nav',
326
+ 'noscript',
327
+ 'object',
328
+ 'ol',
329
+ 'optgroup',
330
+ 'option',
331
+ 'output',
332
+ 'p',
333
+ 'param',
334
+ 'picture',
335
+ 'pre',
336
+ 'progress',
337
+ 'q',
338
+ 'rp',
339
+ 'rt',
340
+ 'ruby',
341
+ 's',
342
+ 'samp',
343
+ 'script',
344
+ 'section',
345
+ 'select',
346
+ 'small',
347
+ 'source',
348
+ 'span',
349
+ 'strong',
350
+ 'style',
351
+ 'sub',
352
+ 'summary',
353
+ 'sup',
354
+ 'svg',
355
+ 'table',
356
+ 'tbody',
357
+ 'td',
358
+ 'template',
359
+ 'textarea',
360
+ 'tfoot',
361
+ 'th',
362
+ 'thead',
363
+ 'time',
364
+ 'title',
365
+ 'tr',
366
+ 'track',
367
+ 'u',
368
+ 'ul',
369
+ 'var',
370
+ 'video',
371
+ 'wbr',
372
+ ].reduce((result, name) => {
373
+ result[name] = node(name)
374
+ return result
375
+ }, {})
376
+
377
+ function classes() {
378
+ const array = []
379
+ for (let i = 0, ilen = arguments.length; i < ilen; i += 1) {
380
+ const arg = arguments[i]
381
+ if (!arg) {
382
+ continue
383
+ }
384
+ const type = typeof arg
385
+ if (type === 'string') {
386
+ array.push(arg)
387
+ } else if (type === 'object') {
388
+ for (const key in arg) {
389
+ if (arg[key]) {
390
+ array.push(key)
391
+ }
392
+ }
393
+ }
394
+ }
395
+ return array.join(' ')
396
+ }
4
397
 
5
398
  module.exports = {
6
399
  compile,
400
+ classes,
401
+ doctype,
7
402
  escape,
8
- createRender
403
+ fragment,
404
+ css,
405
+ js,
406
+ ...nodes,
9
407
  }
package/package.json CHANGED
@@ -1,16 +1,14 @@
1
1
  {
2
2
  "name": "boxwood",
3
- "version": "0.60.2",
3
+ "version": "0.61.0",
4
4
  "description": "Compile HTML templates into JS",
5
5
  "main": "index.js",
6
6
  "scripts": {
7
- "lint": "standard",
8
- "test": "ava 'test/spec/**/*.spec.js' 'src/**/*.spec.js' --no-worker-threads",
7
+ "test": "ava '**/*.spec.js'",
9
8
  "coverage": "nyc npm test",
10
- "benchmark": "ava test/benchmark.spec.js --verbose",
9
+ "benchmark": "ava test/benchmark/index.js --verbose",
11
10
  "watch": "npm test -- --watch",
12
- "prepush": "npm run lint && npm test",
13
- "typecheck": "tsc --checkJs index.js --noEmit --esModuleInterop --target es5"
11
+ "prepush": "npm test"
14
12
  },
15
13
  "ava": {
16
14
  "files": [
@@ -46,43 +44,16 @@
46
44
  "url": "https://github.com/buxlabs/boxwood/issues"
47
45
  },
48
46
  "homepage": "https://github.com/buxlabs/boxwood#readme",
49
- "dependencies": {
50
- "@rollup/plugin-commonjs": "^21.0.1",
51
- "@rollup/plugin-node-resolve": "^13.1.3",
52
- "abstract-syntax-tree": "^2.20.5",
53
- "ansi-colors": "^4.1.1",
54
- "axios": "^0.25.0",
55
- "axios-extensions": "3.1.3",
56
- "css-tree": "^1.1.3",
57
- "csso": "^5.0.2",
58
- "esbuild": "^0.14.11",
59
- "himalaya": "1.1.0",
60
- "himalaya-walk": "1.0.0",
61
- "html-lexer": "^0.4.1",
62
- "html-minifier": "4.0.0",
63
- "memoizee": "0.4.15",
64
- "negate-sentence": "0.2.0",
65
- "path-to-regexp": "6.2.0",
66
- "pure-conditions": "1.2.1",
67
- "pure-utilities": "^1.2.4",
68
- "rollup": "^2.64.0",
69
- "rollup-plugin-includepaths": "0.2.4",
70
- "string-hash": "1.1.3",
71
- "yaml": "^1.10.2"
72
- },
73
47
  "devDependencies": {
74
- "ava": "^4.0.1",
48
+ "ava": "^5.1.0",
75
49
  "benchmark": "2.1.4",
76
- "browser-env": "3.3.0",
77
- "express": "^4.17.2",
50
+ "express": "^4.18.2",
78
51
  "handlebars": "^4.7.7",
52
+ "jsdom": "^20.0.3",
79
53
  "lodash.template": "4.5.0",
80
54
  "mustache": "^4.2.0",
81
55
  "nyc": "15.1.0",
82
- "puppeteer": "^13.1.1",
83
- "standard": "^16.0.4",
84
- "typescript": "^4.5.4",
85
- "underscore": "^1.13.2"
56
+ "underscore": "^1.13.6"
86
57
  },
87
58
  "standard": {
88
59
  "ignore": [
@@ -90,5 +61,9 @@
90
61
  "test/spec/**/*",
91
62
  "src/**/*.spec.js"
92
63
  ]
64
+ },
65
+ "dependencies": {
66
+ "css-tree": "^2.3.0",
67
+ "string-hash": "^1.1.3"
93
68
  }
94
69
  }
package/src/Bundler.js DELETED
@@ -1,13 +0,0 @@
1
- 'use strict'
2
-
3
- const esbuild = require('./bundlers/esbuild')
4
- const rollup = require('./bundlers/rollup')
5
-
6
- class Bundler {
7
- async bundle (source, options = {}) {
8
- const bundler = options.bundler === 'rollup' ? rollup : esbuild
9
- return bundler.bundle(source, options)
10
- }
11
- }
12
-
13
- module.exports = Bundler
package/src/Cache.js DELETED
@@ -1,35 +0,0 @@
1
- 'use strict'
2
-
3
- // basic cache implementation
4
-
5
- // ideally the cache should be dependent not only on the key, but also on the options that are passed to the compiler
6
- // you can get a different output depending on the options
7
-
8
- // there are other edge cases too:
9
- // - http calls could fail (might want to retry them?)
10
- // - errors and/or warnings could be present (could avoid caching in this case)
11
- // - timeouts?
12
-
13
- class Cache {
14
- constructor () {
15
- this.memory = {}
16
- }
17
-
18
- set (key, value) {
19
- this.memory[key] = value
20
- }
21
-
22
- get (key) {
23
- return this.memory[key]
24
- }
25
-
26
- has (key) {
27
- return !!this.get(key)
28
- }
29
-
30
- remove (key) {
31
- delete this.memory[key]
32
- }
33
- }
34
-
35
- module.exports = Cache
package/src/Compiler.js DELETED
@@ -1,28 +0,0 @@
1
- 'use strict'
2
-
3
- const Cache = require('./Cache')
4
- const { getOptions } = require('./utilities/options')
5
- const compile = require('./compilers/compile')
6
-
7
- const cache = new Cache()
8
-
9
- class Compiler {
10
- constructor (options) {
11
- this.options = getOptions(options)
12
- }
13
-
14
- async compile (input) {
15
- const { options } = this
16
- if (options.cache === true && cache.has(input)) { return { ...cache.get(input), from: 'cache' } }
17
- const output = await compile(input, options)
18
- if (output.dynamic === false) {
19
- output.html = output.template()
20
- }
21
- if (options.cache === true) {
22
- cache.set(input, output)
23
- }
24
- return { ...output, from: 'generator' }
25
- }
26
- }
27
-
28
- module.exports = Compiler
package/src/Importer.js DELETED
@@ -1,142 +0,0 @@
1
- 'use strict'
2
-
3
- const { join, dirname } = require('path')
4
- const { readFile, readFileWithCache, resolveAlias } = require('./utilities/files')
5
- const { flatten } = require('pure-utilities/collection')
6
- const Transpiler = require('./compilers/html/Transpiler')
7
- const { lint } = require('./linters/html')
8
- const request = require('./utilities/request')
9
- const { getFullRemoteUrl, isRemotePath } = require('./utilities/url')
10
- const { mergeAssets } = require('./utilities/assets')
11
-
12
- const { getComponentNames } = require('./utilities/attributes')
13
- const { getAssetPaths, getImportNodes } = require('./utilities/node')
14
- const { parse } = require('./utilities/html')
15
- const transpiler = new Transpiler()
16
-
17
- let id = 1
18
-
19
- function getBase64Data (buffer) {
20
- return buffer.toString('base64')
21
- }
22
-
23
- async function loadComponent (node, path, isRemote, remoteUrl, options, paths = []) {
24
- path = resolveAlias(path, options.aliases)
25
- if (isRemotePath(path) || isRemote) {
26
- try {
27
- const url = getFullRemoteUrl(remoteUrl, path)
28
- const response = await request.get(url, {
29
- responseType: 'arraybuffer',
30
- cache: options.cache
31
- })
32
- if (response.status === 200) {
33
- const buffer = Buffer.from(response.data, 'binary') // TODO: parse response to the buffer
34
- const base64 = getBase64Data(buffer)
35
- id += 1
36
- return {
37
- path,
38
- source: transpiler.transpile(buffer.toString()),
39
- buffer,
40
- base64,
41
- remote: true,
42
- url,
43
- id
44
- }
45
- }
46
- } catch (exception) {}
47
- } else {
48
- const read = options.cache ? readFileWithCache : readFile
49
- for (const option of paths) {
50
- try {
51
- const location = join(option, path)
52
- options.hooks.onBeforeFile(location)
53
- const file = {}
54
- file.path = location
55
- file.buffer = await read(location)
56
- file.base64 = getBase64Data(file.buffer)
57
- // TODO: Read once convert base64
58
- const source = await read(location, 'utf8')
59
- file.source = transpiler.transpile(source)
60
- file.remote = false
61
- id += 1
62
- file.id = id
63
- options.hooks.onAfterFile(file)
64
- return file
65
- } catch (exception) {}
66
- }
67
- }
68
- return {}
69
- }
70
-
71
- async function fetch (node, kind, context, isRemote, remoteUrl, options) {
72
- const paths = options.paths || []
73
- const names = kind === 'IMPORT' ? getComponentNames(node.attributes) : ['']
74
- return Promise.all(names.map(async name => {
75
- const type = kind === 'IMPORT' ? 'COMPONENT' : kind
76
- const dir = dirname(context)
77
- const assetPaths = getAssetPaths(node, name)
78
- return Promise.all(assetPaths.map(async assetPath => {
79
- const { source, path, base64, remote, url, buffer, id } = await loadComponent(node, assetPath, isRemote, remoteUrl, options, [dir, ...paths])
80
- if (!path) {
81
- const isNodeStylesheet = node.attributes.some(({ key, value }) => key === 'rel' && value === 'stylesheet') && node.attributes.some(({ key }) => key === 'inline')
82
-
83
- if (isNodeStylesheet) {
84
- return {
85
- warnings: [{ type: 'STYLESHEET_NOT_FOUND', message: `Stylesheet not found: ${isRemotePath(assetPath) ? assetPath : name}` }]
86
- }
87
- } else {
88
- return {
89
- warnings: [{ type: 'COMPONENT_NOT_FOUND', message: `Component not found: ${isRemotePath(assetPath) ? assetPath : name}` }]
90
- }
91
- }
92
- }
93
- const tree = parse(source)
94
- const files = [context]
95
- const warnings = []
96
- return { name, source, base64, remote, url, buffer, path, files, warnings, tree, type, id }
97
- }))
98
- }))
99
- }
100
- const MAXIMUM_IMPORT_DEPTH = 50
101
- async function recursiveImport (tree, source, path, options, depth, remote, url) {
102
- if (depth > MAXIMUM_IMPORT_DEPTH) {
103
- return {
104
- assets: [],
105
- warnings: [{
106
- type: 'MAXIMUM_IMPORT_DEPTH_EXCEEDED',
107
- message: 'Maximum import depth exceeded',
108
- severity: 'critical'
109
- }]
110
- }
111
- }
112
- const imports = getImportNodes(tree, options)
113
- const isHtmlPath = path === '.' || path.endsWith('.html')
114
- const warnings = isHtmlPath ? lint(source, imports.map(({ node }) => node), options) : []
115
- const assets = await Promise.all(imports.map(({ node, kind }) => fetch(node, kind, path, remote, url, options)))
116
- const current = flatten(assets)
117
- const nested = await Promise.all(current.filter(element => element.tree).map(async element => {
118
- return recursiveImport(element.tree, element.source, element.path, options, depth + 1, element.remote, element.url)
119
- }))
120
- let nestedAssets = current.concat(flatten(nested.map(object => object.assets)))
121
- nestedAssets = mergeAssets(nestedAssets)
122
- const nestedWarnings = warnings.concat(flatten(nested.map(object => object.warnings)))
123
- return {
124
- assets: nestedAssets,
125
- warnings: nestedWarnings.concat(
126
- flatten(nestedAssets.map(file => file.warnings)),
127
- flatten(current.map(element => element.warnings))
128
- )
129
- }
130
- }
131
-
132
- module.exports = class Importer {
133
- constructor (source, options = {}) {
134
- this.source = source
135
- this.tree = parse(source)
136
- this.options = options
137
- }
138
-
139
- async import () {
140
- return recursiveImport(this.tree, this.source, '.', this.options, 0, false, null)
141
- }
142
- }