boxwood 0.57.2 → 0.59.1
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 +0 -126
- package/package.json +10 -11
- package/src/Linter.js +11 -3
- package/src/Statistics.js +0 -4
- package/src/bundlers/esbuild/index.js +55 -0
- package/src/bundlers/esbuild/plugins/css.js +40 -0
- package/src/bundlers/esbuild/plugins/html.js +27 -0
- package/src/bundlers/esbuild/plugins/image.js +38 -0
- package/src/bundlers/esbuild/plugins/resolve.js +11 -0
- package/src/bundlers/esbuild/plugins/yaml.js +38 -0
- package/src/bundlers/esbuild/utilities/asset.js +19 -0
- package/src/compilers/html/Renderer.js +32 -28
- package/src/compilers/js/Compiler/index.js +7 -3
- package/src/optimizers/html.js +5 -0
- package/src/plugins/InlinePlugin/css.js +2 -3
- package/src/plugins/ScopedStylesPlugin/css.js +2 -2
- package/src/plugins/ScopedStylesPlugin/index.js +12 -1
- package/src/render.js +5 -3
- package/src/tags/img.js +1 -8
- package/src/tags/index.js +0 -2
- package/src/tags/script/index.js +1 -19
- package/src/transpilers/css/index.js +34 -0
- package/src/transpilers/{expression.js → html/expression.js} +43 -22
- package/src/transpilers/{html.js → html/index.js} +16 -7
- package/src/transpilers/html/node.js +244 -0
- package/src/transpilers/{tags → html/tags}/doctype.js +0 -0
- package/src/transpilers/{tags → html/tags}/import.js +0 -0
- package/src/transpilers/{tags → html/tags}/index.js +2 -1
- package/src/transpilers/{tags → html/tags}/partial.js +1 -1
- package/src/transpilers/html/tags/slot.js +7 -0
- package/src/utilities/collect.js +6 -23
- package/src/utilities/convert.js +7 -10
- package/src/utilities/errors.js +0 -8
- package/src/utilities/node.js +2 -32
- package/src/utilities/options.js +0 -11
- package/src/utilities/string.js +8 -9
- package/src/vdom/browser/render.js +6 -0
- package/src/vdom/nodes.js +4 -1
- package/src/vdom/server/render.js +10 -2
- package/src/vdom/tag.js +1 -1
- package/src/bundlers/esbuild.js +0 -75
- package/src/plugins/RoutesPlugin/index.js +0 -29
- package/src/tags/svg.js +0 -18
- package/src/transpilers/node.js +0 -139
- package/src/utilities/css.js +0 -64
- package/src/utilities/routes.js +0 -69
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const { parse, walk, generate } = require('css-tree')
|
|
4
|
-
const hash = require('string
|
|
4
|
+
const { hash } = require('../../utilities/string')
|
|
5
5
|
const { normalizeNewline } = require('../../utilities/string')
|
|
6
6
|
|
|
7
7
|
function addScopeToCssSelectors (input, scopes) {
|
|
8
8
|
const content = normalizeNewline(input).trim()
|
|
9
|
-
const id =
|
|
9
|
+
const id = hash(content)
|
|
10
10
|
const tree = parse(content)
|
|
11
11
|
const keyframes = {}
|
|
12
12
|
walk(tree, node => {
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
const Plugin = require('../Plugin')
|
|
4
4
|
const { addScopeToCssSelectors } = require('./css')
|
|
5
5
|
const { addScopeToHtmlClassAttribute, addClassAttributeWithScopeToHtmlTag } = require('./html')
|
|
6
|
+
const { transpile: transpileCss, getSelectors } = require('../../transpilers/css')
|
|
6
7
|
|
|
7
8
|
class ScopedStylesPlugin extends Plugin {
|
|
8
9
|
constructor () {
|
|
@@ -14,7 +15,17 @@ class ScopedStylesPlugin extends Plugin {
|
|
|
14
15
|
this.scopes[this.depth] = []
|
|
15
16
|
}
|
|
16
17
|
|
|
17
|
-
prerun ({ tag, keys, children, attributes }) {
|
|
18
|
+
prerun ({ fragment, tag, keys, assets, children, attributes }) {
|
|
19
|
+
if (tag === 'import') {
|
|
20
|
+
const from = attributes.find(attribute => attribute.key === 'from')
|
|
21
|
+
if (from && from.value.endsWith('.css')) {
|
|
22
|
+
fragment.tagName = 'var'
|
|
23
|
+
const { key } = attributes[0]
|
|
24
|
+
/* do we need a better check here? */
|
|
25
|
+
const asset = assets.find(asset => asset.name === key)
|
|
26
|
+
fragment.attributes = [{ key, value: getSelectors(transpileCss(asset.source)) }]
|
|
27
|
+
}
|
|
28
|
+
}
|
|
18
29
|
if (tag === 'style' && keys.includes('scoped')) {
|
|
19
30
|
children.forEach(node => {
|
|
20
31
|
node.content = addScopeToCssSelectors(node.content, this.scopes[this.depth])
|
package/src/render.js
CHANGED
|
@@ -4,11 +4,12 @@ const { readFile } = require('fs')
|
|
|
4
4
|
const { dirname } = require('path')
|
|
5
5
|
const { promisify } = require('util')
|
|
6
6
|
const { print } = require('./utilities/log')
|
|
7
|
+
const { optimize } = require('./optimizers/html')
|
|
7
8
|
|
|
8
9
|
const read = promisify(readFile)
|
|
9
10
|
|
|
10
11
|
function createRender ({
|
|
11
|
-
compilerOptions = {},
|
|
12
|
+
compilerOptions = { paths: [] },
|
|
12
13
|
globals = {},
|
|
13
14
|
cacheEnabled = true,
|
|
14
15
|
log = false
|
|
@@ -32,8 +33,9 @@ function createRender ({
|
|
|
32
33
|
const template = await compileFile(path)
|
|
33
34
|
const params = typeof globals === 'function' ? globals(path, options) : globals
|
|
34
35
|
const html = template({ ...params, ...options }, escape)
|
|
35
|
-
|
|
36
|
-
return
|
|
36
|
+
const optimizedHtml = optimize(html)
|
|
37
|
+
if (callback) return callback(null, optimizedHtml)
|
|
38
|
+
return optimizedHtml
|
|
37
39
|
} catch (exception) {
|
|
38
40
|
if (callback) return callback(exception)
|
|
39
41
|
return exception.message
|
package/src/tags/img.js
CHANGED
|
@@ -1,17 +1,10 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const { convertAttributeToInlineStyle, convertSizeToWidthAndHeight, setAutoDimension } = require('../utilities/css')
|
|
4
3
|
const { findAsset } = require('../utilities/files')
|
|
5
4
|
const { getExtension, getBase64Extension, normalizeNewline } = require('../utilities/string')
|
|
6
5
|
|
|
7
6
|
module.exports = function ({ fragment, attrs, keys, assets, options }) {
|
|
8
|
-
|
|
9
|
-
convertAttributeToInlineStyle(attrs, ['cover'], 'object-fit: cover; object-position: right top;')
|
|
10
|
-
convertAttributeToInlineStyle(attrs, ['contain'], 'object-fit: contain; object-position: center;')
|
|
11
|
-
convertSizeToWidthAndHeight(attrs)
|
|
12
|
-
setAutoDimension(attrs, keys, 'width', assets, options)
|
|
13
|
-
setAutoDimension(attrs, keys, 'height', assets, options)
|
|
14
|
-
if (keys.includes('inline') || options.inline.includes('images')) {
|
|
7
|
+
if (keys.includes('inline')) {
|
|
15
8
|
fragment.attributes = fragment.attributes.map(attr => {
|
|
16
9
|
if (attr.key === 'inline') return null
|
|
17
10
|
if (attr.key === 'src') {
|
package/src/tags/index.js
CHANGED
|
@@ -19,7 +19,6 @@ const slotTag = require('./slot')
|
|
|
19
19
|
const imgTag = require('./img')
|
|
20
20
|
const scriptTag = require('./script')
|
|
21
21
|
const styleTag = require('./style')
|
|
22
|
-
const svgTag = require('./svg')
|
|
23
22
|
const templateTag = require('./template')
|
|
24
23
|
const translateTag = require('./translate')
|
|
25
24
|
const translationTag = require('./translation')
|
|
@@ -46,7 +45,6 @@ module.exports = {
|
|
|
46
45
|
img: imgTag,
|
|
47
46
|
script: scriptTag,
|
|
48
47
|
style: styleTag,
|
|
49
|
-
svg: svgTag,
|
|
50
48
|
template: templateTag,
|
|
51
49
|
translate: translateTag,
|
|
52
50
|
translation: translationTag,
|
package/src/tags/script/index.js
CHANGED
|
@@ -10,7 +10,7 @@ const scoped = require('./scoped')
|
|
|
10
10
|
const script = { scoped }
|
|
11
11
|
|
|
12
12
|
module.exports = async function ({ tree, keys, attrs, fragment, assets, variables, promises, warnings, filters, translations, languages, append, scripts, options }) {
|
|
13
|
-
if (keys.includes('inline')
|
|
13
|
+
if (keys.includes('inline')) {
|
|
14
14
|
if (keys.includes('src')) {
|
|
15
15
|
const { value: path } = attrs.find(attr => attr.key === 'src')
|
|
16
16
|
const asset = findAsset(path, assets, options)
|
|
@@ -24,24 +24,6 @@ module.exports = async function ({ tree, keys, attrs, fragment, assets, variable
|
|
|
24
24
|
ast.each('VariableDeclarator', node => variables.push(node.id.name))
|
|
25
25
|
ast.body.forEach(node => tree.append(node))
|
|
26
26
|
}
|
|
27
|
-
} else if (keys.includes('polyfills')) {
|
|
28
|
-
let content = ''
|
|
29
|
-
const { value } = attrs.find(attr => attr.key === 'polyfills')
|
|
30
|
-
const ast = new AbstractSyntaxTree(value)
|
|
31
|
-
const polyfills = AbstractSyntaxTree.serialize(ast.body[0].expression)
|
|
32
|
-
polyfills.forEach(polyfill => {
|
|
33
|
-
const asset = findAsset(polyfill, assets, options)
|
|
34
|
-
if (asset) {
|
|
35
|
-
content += asset.source
|
|
36
|
-
} else {
|
|
37
|
-
warnings.push({ type: 'POLYFILL_NOT_FOUND', message: `${polyfill} polyfill not found` })
|
|
38
|
-
}
|
|
39
|
-
})
|
|
40
|
-
fragment.children.forEach(node => {
|
|
41
|
-
node.used = true
|
|
42
|
-
content += node.content
|
|
43
|
-
})
|
|
44
|
-
scripts.push(content)
|
|
45
27
|
} else if (keys.includes('scoped')) {
|
|
46
28
|
const leaf = fragment.children[0]
|
|
47
29
|
if (!leaf) return
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { parse, walk, generate } = require('css-tree')
|
|
4
|
+
const createHash = require('string-hash')
|
|
5
|
+
const { camelize } = require('pure-utilities/string')
|
|
6
|
+
|
|
7
|
+
function transpile (source) {
|
|
8
|
+
const hash = createHash(source)
|
|
9
|
+
const tree = parse(source)
|
|
10
|
+
walk(tree, node => {
|
|
11
|
+
if (node.type === 'ClassSelector') {
|
|
12
|
+
node.name = `${camelize(node.name)}-${hash}`
|
|
13
|
+
}
|
|
14
|
+
if (node.type === 'TypeSelector') {
|
|
15
|
+
node.type = 'ClassSelector'
|
|
16
|
+
node.name = `${camelize(node.name)}-${hash}`
|
|
17
|
+
}
|
|
18
|
+
})
|
|
19
|
+
return generate(tree)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function getSelectors (source) {
|
|
23
|
+
const tree = parse(source)
|
|
24
|
+
const selectors = {}
|
|
25
|
+
walk(tree, node => {
|
|
26
|
+
if (node.type === 'ClassSelector') {
|
|
27
|
+
const [key] = node.name.split('-')
|
|
28
|
+
selectors[key] = node.name
|
|
29
|
+
}
|
|
30
|
+
})
|
|
31
|
+
return selectors
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
module.exports = { transpile, getSelectors }
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const AbstractSyntaxTree = require('abstract-syntax-tree')
|
|
2
2
|
const { unique } = require('pure-utilities/array')
|
|
3
|
-
const lexer = require('
|
|
4
|
-
const { BUILT_IN_VARIABLES } = require('
|
|
3
|
+
const lexer = require('../../utilities/lexer')
|
|
4
|
+
const { BUILT_IN_VARIABLES } = require('../../utilities/enum')
|
|
5
5
|
|
|
6
6
|
const { ArrayExpression, CallExpression, Identifier, Literal, ObjectPattern, Property, toBinaryExpression } = AbstractSyntaxTree
|
|
7
7
|
|
|
@@ -36,37 +36,58 @@ function findParams (body) {
|
|
|
36
36
|
return unique(nodes.map(node => node.name))
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
function findChildren (body) {
|
|
40
|
+
const tree = new AbstractSyntaxTree(body)
|
|
41
|
+
const node = tree.first('Identifier[name="__children__"]')
|
|
42
|
+
return node
|
|
43
|
+
}
|
|
44
|
+
|
|
39
45
|
function deduceParams (body) {
|
|
40
46
|
const params = findParams(body)
|
|
41
|
-
|
|
42
|
-
|
|
47
|
+
const children = findChildren(body)
|
|
48
|
+
return [
|
|
49
|
+
params.length > 0
|
|
50
|
+
? new ObjectPattern({
|
|
51
|
+
properties: params.map(param => {
|
|
52
|
+
const node = new Identifier(param)
|
|
53
|
+
return new Property({
|
|
54
|
+
key: node,
|
|
55
|
+
value: node,
|
|
56
|
+
kind: 'init',
|
|
57
|
+
computed: false,
|
|
58
|
+
method: false,
|
|
59
|
+
shorthand: true
|
|
60
|
+
})
|
|
61
|
+
})
|
|
62
|
+
})
|
|
63
|
+
: children && new Identifier('__UNUSED_PARAM__'),
|
|
64
|
+
children
|
|
65
|
+
].filter(Boolean)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function normalizeTokenValue (value) {
|
|
69
|
+
if (value.trim().startsWith('{') && value.trim().endsWith('}')) {
|
|
70
|
+
return `(${value})`
|
|
43
71
|
}
|
|
44
|
-
return
|
|
45
|
-
properties: params.map(param => {
|
|
46
|
-
const node = new Identifier(param)
|
|
47
|
-
return new Property({
|
|
48
|
-
key: node,
|
|
49
|
-
value: node,
|
|
50
|
-
kind: 'init',
|
|
51
|
-
computed: false,
|
|
52
|
-
method: false,
|
|
53
|
-
shorthand: true
|
|
54
|
-
})
|
|
55
|
-
})
|
|
56
|
-
})
|
|
72
|
+
return value
|
|
57
73
|
}
|
|
58
74
|
|
|
59
|
-
function transpileExpression (source) {
|
|
75
|
+
function transpileExpression (source, escape = true) {
|
|
60
76
|
const tokens = lexer(source)
|
|
61
77
|
const nodes = tokens.map(token => {
|
|
62
78
|
if (token.type === 'expression') {
|
|
79
|
+
token.value = normalizeTokenValue(token.value)
|
|
63
80
|
const tree = new AbstractSyntaxTree(token.value)
|
|
64
81
|
const { expression } = tree.first('ExpressionStatement')
|
|
65
82
|
markNodes(expression)
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
83
|
+
if (escape) {
|
|
84
|
+
return new CallExpression({
|
|
85
|
+
callee: new Identifier({ name: 'escape' }),
|
|
86
|
+
arguments: [expression]
|
|
87
|
+
})
|
|
88
|
+
} else {
|
|
89
|
+
return expression
|
|
90
|
+
}
|
|
70
91
|
}
|
|
71
92
|
if (token.type === 'text') {
|
|
72
93
|
return new Literal({ value: token.value })
|
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
const AbstractSyntaxTree = require('abstract-syntax-tree')
|
|
4
4
|
const { camelize } = require('pure-utilities/string')
|
|
5
|
-
const { parse, walk } = require('../utilities/html')
|
|
6
|
-
const { findAttributeByKey } = require('../utilities/attributes')
|
|
7
5
|
const { deduceParams } = require('./expression')
|
|
8
|
-
const BoxModelPlugin = require('
|
|
9
|
-
const CurlyStylesPlugin = require('
|
|
6
|
+
const BoxModelPlugin = require('../../plugins/BoxModelPlugin')
|
|
7
|
+
const CurlyStylesPlugin = require('../../plugins/CurlyStylesPlugin')
|
|
8
|
+
const { parse, walk } = require('../../utilities/html')
|
|
9
|
+
const { findAttributeByKey } = require('../../utilities/attributes')
|
|
10
10
|
const { transpileNode } = require('./node')
|
|
11
|
+
// TODO: initial transpilation, move to a separate dir? or inline here after removing the outdated compiler
|
|
12
|
+
const Transpiler = require('../../compilers/html/Transpiler')
|
|
11
13
|
|
|
12
14
|
const {
|
|
13
15
|
ArrayExpression,
|
|
@@ -18,6 +20,10 @@ const {
|
|
|
18
20
|
Literal
|
|
19
21
|
} = AbstractSyntaxTree
|
|
20
22
|
|
|
23
|
+
function pathToIdentifier (path) {
|
|
24
|
+
return `__${camelize(path).replace('.', 'Dot')}__`
|
|
25
|
+
}
|
|
26
|
+
|
|
21
27
|
const program = (body) => {
|
|
22
28
|
const params = deduceParams(body)
|
|
23
29
|
return AbstractSyntaxTree.program(
|
|
@@ -35,7 +41,7 @@ function createComponentImportDeclarations (imports) {
|
|
|
35
41
|
return new ImportDeclaration({
|
|
36
42
|
specifiers: [
|
|
37
43
|
new ImportDefaultSpecifier({
|
|
38
|
-
local: new Identifier(
|
|
44
|
+
local: new Identifier(pathToIdentifier(path))
|
|
39
45
|
})
|
|
40
46
|
],
|
|
41
47
|
source: new Literal(path)
|
|
@@ -112,7 +118,6 @@ function prerunPlugins (tree, plugins) {
|
|
|
112
118
|
|
|
113
119
|
function body (tree, options) {
|
|
114
120
|
const plugins = [
|
|
115
|
-
// new RoutesPlugin(options, errors),
|
|
116
121
|
// new DataPlugin(),
|
|
117
122
|
// new InlinePlugin(),
|
|
118
123
|
new BoxModelPlugin(options),
|
|
@@ -132,6 +137,8 @@ function body (tree, options) {
|
|
|
132
137
|
}
|
|
133
138
|
|
|
134
139
|
function transpile (source, options) {
|
|
140
|
+
source = new Transpiler().transpile(source)
|
|
141
|
+
|
|
135
142
|
const tree = parse(source)
|
|
136
143
|
const outputTree = new AbstractSyntaxTree(
|
|
137
144
|
program(body(tree, options))
|
|
@@ -147,7 +154,7 @@ function transpile (source, options) {
|
|
|
147
154
|
if (node.type === 'CallExpression' && node.callee.name === 'tag') {
|
|
148
155
|
imports.forEach(leaf => {
|
|
149
156
|
if (node.arguments[0].value === leaf.tag) {
|
|
150
|
-
node.callee.name =
|
|
157
|
+
node.callee.name = pathToIdentifier(leaf.path)
|
|
151
158
|
node.arguments.shift()
|
|
152
159
|
}
|
|
153
160
|
})
|
|
@@ -155,6 +162,8 @@ function transpile (source, options) {
|
|
|
155
162
|
})
|
|
156
163
|
}
|
|
157
164
|
|
|
165
|
+
// console.log(outputTree.source)
|
|
166
|
+
|
|
158
167
|
imports = deducePartials(outputTree)
|
|
159
168
|
if (imports.length > 0) {
|
|
160
169
|
outputTree.prepend(createPartialImportDeclarations(imports))
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const AbstractSyntaxTree = require('abstract-syntax-tree')
|
|
4
|
+
const { transpileExpression } = require('./expression')
|
|
5
|
+
const tags = require('./tags')
|
|
6
|
+
|
|
7
|
+
const {
|
|
8
|
+
ArrayExpression,
|
|
9
|
+
BlockStatement,
|
|
10
|
+
CallExpression,
|
|
11
|
+
Identifier,
|
|
12
|
+
IfStatement,
|
|
13
|
+
Literal,
|
|
14
|
+
ObjectExpression,
|
|
15
|
+
Property,
|
|
16
|
+
ReturnStatement,
|
|
17
|
+
TryStatement,
|
|
18
|
+
CatchClause,
|
|
19
|
+
UnaryExpression
|
|
20
|
+
} = AbstractSyntaxTree
|
|
21
|
+
|
|
22
|
+
let FOR_LOOP_INDEX = 0
|
|
23
|
+
|
|
24
|
+
function mapForStatement (htmlNode, parent, index) {
|
|
25
|
+
FOR_LOOP_INDEX++
|
|
26
|
+
const [node] = AbstractSyntaxTree.template(`
|
|
27
|
+
(function () {
|
|
28
|
+
var __output__ = [];
|
|
29
|
+
for (var <%= index %> = 0, <%= length %> = <%= array %>.length; <%= index %> < <%= length %>; <%= index %>++) {
|
|
30
|
+
var <%= item %> = <%= array %>[<%= index %>];
|
|
31
|
+
__output__.push(%= children %);
|
|
32
|
+
}
|
|
33
|
+
return __output__;
|
|
34
|
+
})();
|
|
35
|
+
`, {
|
|
36
|
+
index: new Identifier(`__i${FOR_LOOP_INDEX}__`),
|
|
37
|
+
length: new Identifier(`__ilen${FOR_LOOP_INDEX}__`),
|
|
38
|
+
item: new Identifier(htmlNode.attributes[0].key),
|
|
39
|
+
// TODO we should not mark params which were created on the fly, e.g. for nested loops
|
|
40
|
+
array: new Identifier({ name: htmlNode.attributes[2].key, parameter: true }),
|
|
41
|
+
children: htmlNode.children.map((child, index) =>
|
|
42
|
+
transpileNode({ node: child, parent: htmlNode, index: index })
|
|
43
|
+
)
|
|
44
|
+
})
|
|
45
|
+
return node.expression
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function transpileNode ({ node: htmlNode, parent, index }) {
|
|
49
|
+
function mapAttributes (attributes) {
|
|
50
|
+
function getAttributeValue (value) {
|
|
51
|
+
if (value === null) { return new Literal(true) }
|
|
52
|
+
return transpileExpression(value, false)
|
|
53
|
+
}
|
|
54
|
+
return attributes.length > 0
|
|
55
|
+
? new ObjectExpression({
|
|
56
|
+
properties: attributes.map(attribute => {
|
|
57
|
+
return new Property({
|
|
58
|
+
key: new Identifier(attribute.key),
|
|
59
|
+
value: getAttributeValue(attribute.value),
|
|
60
|
+
kind: 'init',
|
|
61
|
+
computed: false,
|
|
62
|
+
method: false,
|
|
63
|
+
shorthand: false
|
|
64
|
+
})
|
|
65
|
+
})
|
|
66
|
+
})
|
|
67
|
+
: new ObjectExpression({ properties: [] })
|
|
68
|
+
}
|
|
69
|
+
function mapChildren (children) {
|
|
70
|
+
return children.length > 0 && new ArrayExpression({
|
|
71
|
+
elements: children.map((childNode, index) => {
|
|
72
|
+
return transpileNode({ node: childNode, parent: children, index })
|
|
73
|
+
}).filter(Boolean)
|
|
74
|
+
})
|
|
75
|
+
}
|
|
76
|
+
function mapIfStatement (htmlNode, parent, index) {
|
|
77
|
+
function mapAttributesToTest ({ attributes }) {
|
|
78
|
+
if (attributes.length === 1) {
|
|
79
|
+
if (attributes[0].key === 'true' || attributes[0].key === '{true}') {
|
|
80
|
+
return new Literal(true)
|
|
81
|
+
} else if (attributes[0].key === 'false' || attributes[0].key === '{false}') {
|
|
82
|
+
return new Literal(false)
|
|
83
|
+
} else {
|
|
84
|
+
return new Identifier({ name: attributes[0].key, parameter: true })
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
throw new Error('Unsupported length of attributes (if)')
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function mapCurrentNodeToConsequent (htmlNode) {
|
|
91
|
+
const body = htmlNode.children.map((node, index) => transpileNode({ node, parent: htmlNode.children, index })).filter(Boolean)
|
|
92
|
+
const argument = body.pop()
|
|
93
|
+
body.push(new ReturnStatement({ argument }))
|
|
94
|
+
return new BlockStatement({ body })
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function mapNextNodeToAlternate (nextNode) {
|
|
98
|
+
if (nextNode && nextNode.tagName === 'else') {
|
|
99
|
+
const body = nextNode.children.map((node, index) => transpileNode({ node, parent: nextNode.children, index })).filter(Boolean)
|
|
100
|
+
const argument = body.pop()
|
|
101
|
+
body.push(new ReturnStatement({ argument }))
|
|
102
|
+
return new BlockStatement({ body })
|
|
103
|
+
} else if (nextNode && nextNode.tagName === 'elseif') {
|
|
104
|
+
return mapIfStatement(nextNode, parent, index + 1)
|
|
105
|
+
}
|
|
106
|
+
return new BlockStatement({
|
|
107
|
+
body: [
|
|
108
|
+
new ReturnStatement({
|
|
109
|
+
argument: new Literal('')
|
|
110
|
+
})
|
|
111
|
+
]
|
|
112
|
+
})
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return new IfStatement({
|
|
116
|
+
test: mapAttributesToTest(htmlNode),
|
|
117
|
+
consequent: mapCurrentNodeToConsequent(htmlNode),
|
|
118
|
+
alternate: mapNextNodeToAlternate(parent[index + 1])
|
|
119
|
+
})
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function mapUnlessStatement (htmlNode, parent, index) {
|
|
123
|
+
function mapAttributesToTest ({ attributes }) {
|
|
124
|
+
if (attributes.length === 1) {
|
|
125
|
+
if (attributes[0].key === 'true' || attributes[0].key === '{true}') {
|
|
126
|
+
return new Literal(true)
|
|
127
|
+
} else if (attributes[0].key === 'false' || attributes[0].key === '{false}') {
|
|
128
|
+
return new Literal(false)
|
|
129
|
+
} else {
|
|
130
|
+
return new Identifier({ name: attributes[0].key, parameter: true })
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
throw new Error('Unsupported length of attributes (unless)')
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function mapCurrentNodeToConsequent (htmlNode) {
|
|
137
|
+
const body = htmlNode.children.map((node, index) => transpileNode({ node, parent: htmlNode.children, index })).filter(Boolean)
|
|
138
|
+
const argument = body.pop()
|
|
139
|
+
body.push(new ReturnStatement({ argument }))
|
|
140
|
+
return new BlockStatement({ body })
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function mapNextNodeToAlternate (nextNode) {
|
|
144
|
+
if (nextNode && nextNode.tagName === 'else') {
|
|
145
|
+
const body = nextNode.children.map((node, index) => transpileNode({ node, parent: nextNode.children, index })).filter(Boolean)
|
|
146
|
+
const argument = body.pop()
|
|
147
|
+
body.push(new ReturnStatement({ argument }))
|
|
148
|
+
return new BlockStatement({ body })
|
|
149
|
+
} else if (nextNode && nextNode.tagName === 'elseunless') {
|
|
150
|
+
return mapUnlessStatement(nextNode, parent, index + 1)
|
|
151
|
+
} else if (nextNode && nextNode.tagName === 'elseif') {
|
|
152
|
+
return mapIfStatement(nextNode, parent, index + 1)
|
|
153
|
+
}
|
|
154
|
+
return new BlockStatement({
|
|
155
|
+
body: [
|
|
156
|
+
new ReturnStatement({
|
|
157
|
+
argument: new Literal('')
|
|
158
|
+
})
|
|
159
|
+
]
|
|
160
|
+
})
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return new IfStatement({
|
|
164
|
+
test: new UnaryExpression({
|
|
165
|
+
operator: '!',
|
|
166
|
+
argument: mapAttributesToTest(htmlNode),
|
|
167
|
+
prefix: true
|
|
168
|
+
}),
|
|
169
|
+
consequent: mapCurrentNodeToConsequent(htmlNode),
|
|
170
|
+
alternate: mapNextNodeToAlternate(parent[index + 1])
|
|
171
|
+
})
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function mapTryStatement (htmlNode, parent, index) {
|
|
175
|
+
function mapCurrentNodeToBlockStatement (htmlNode) {
|
|
176
|
+
const body = htmlNode.children.map((node, index) => transpileNode({ node, parent: htmlNode.children, index })).filter(Boolean)
|
|
177
|
+
const argument = body.pop()
|
|
178
|
+
body.push(new ReturnStatement({ argument }))
|
|
179
|
+
return new BlockStatement({ body })
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function mapNextNodeToCatchClause (nextNode) {
|
|
183
|
+
if (nextNode && nextNode.tagName === 'catch') {
|
|
184
|
+
const body = nextNode.children.map((node, index) => transpileNode({ node, parent: htmlNode.children, index })).filter(Boolean)
|
|
185
|
+
const argument = body.pop()
|
|
186
|
+
body.push(new ReturnStatement({ argument }))
|
|
187
|
+
return new CatchClause({
|
|
188
|
+
body: new BlockStatement({ body })
|
|
189
|
+
})
|
|
190
|
+
}
|
|
191
|
+
return null
|
|
192
|
+
}
|
|
193
|
+
return new TryStatement({
|
|
194
|
+
block: mapCurrentNodeToBlockStatement(htmlNode),
|
|
195
|
+
handler: mapNextNodeToCatchClause(parent[index + 1])
|
|
196
|
+
})
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (htmlNode.type === 'element' && htmlNode.tagName === 'if') {
|
|
200
|
+
const statement = mapIfStatement(htmlNode, parent, index)
|
|
201
|
+
const { expression } = AbstractSyntaxTree.iife(statement)
|
|
202
|
+
return expression
|
|
203
|
+
} else if (htmlNode.type === 'element' && htmlNode.tagName === 'else') {
|
|
204
|
+
return null
|
|
205
|
+
} else if (htmlNode.type === 'element' && htmlNode.tagName === 'elseif') {
|
|
206
|
+
return null
|
|
207
|
+
} else if (htmlNode.type === 'element' && htmlNode.tagName === 'unless') {
|
|
208
|
+
const statement = mapUnlessStatement(htmlNode, parent, index)
|
|
209
|
+
const { expression } = AbstractSyntaxTree.iife(statement)
|
|
210
|
+
return expression
|
|
211
|
+
} else if (htmlNode.type === 'element' && htmlNode.tagName === 'elseunless') {
|
|
212
|
+
return null
|
|
213
|
+
} else if (htmlNode.type === 'element' && htmlNode.tagName === 'try') {
|
|
214
|
+
const statement = mapTryStatement(htmlNode, parent, index)
|
|
215
|
+
const { expression } = AbstractSyntaxTree.iife(statement)
|
|
216
|
+
return expression
|
|
217
|
+
} else if (htmlNode.type === 'element' && htmlNode.tagName === 'catch') {
|
|
218
|
+
return null
|
|
219
|
+
} else if (htmlNode.type === 'element' && htmlNode.tagName === 'for') {
|
|
220
|
+
return mapForStatement(htmlNode, parent, index)
|
|
221
|
+
} else if (htmlNode.type === 'element') {
|
|
222
|
+
if (htmlNode.tagName === 'import') { return tags.import(htmlNode) }
|
|
223
|
+
if (htmlNode.tagName === '!doctype') { return tags.doctype() }
|
|
224
|
+
if (htmlNode.tagName === 'partial') { return tags.partial(htmlNode) }
|
|
225
|
+
if (htmlNode.tagName === 'slot') { return tags.slot(htmlNode) }
|
|
226
|
+
const { tagName, attributes, children } = htmlNode
|
|
227
|
+
const node = new CallExpression({
|
|
228
|
+
callee: new Identifier('tag'),
|
|
229
|
+
arguments: [
|
|
230
|
+
new Literal(tagName),
|
|
231
|
+
mapAttributes(attributes),
|
|
232
|
+
mapChildren(children)
|
|
233
|
+
].filter(Boolean)
|
|
234
|
+
})
|
|
235
|
+
return node
|
|
236
|
+
} else if (htmlNode.type === 'text') {
|
|
237
|
+
const { content } = htmlNode
|
|
238
|
+
return transpileExpression(content)
|
|
239
|
+
} else if (htmlNode.type === 'comment') {
|
|
240
|
+
return new Literal('')
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
module.exports = { transpileNode }
|
|
File without changes
|
|
File without changes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const AbstractSyntaxTree = require('abstract-syntax-tree')
|
|
2
2
|
const { camelize } = require('pure-utilities/string')
|
|
3
|
-
const { findAttributeByKey } = require('
|
|
3
|
+
const { findAttributeByKey } = require('../../../utilities/attributes')
|
|
4
4
|
|
|
5
5
|
const { CallExpression, Identifier } = AbstractSyntaxTree
|
|
6
6
|
|