@katabatic/compiler 1.1.3 → 1.1.5
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 +1 -1
- package/src/analyse/context.js +1 -1
- package/src/analyse/visitors/Program.js +2 -0
- package/src/builders.js +43 -13
- package/src/checkers.js +10 -6
- package/src/css-matcher.js +1 -0
- package/src/exp-matcher.js +14 -5
- package/src/parser/attributes.js +28 -8
- package/src/parser/element.js +1 -1
- package/src/parser/expression.js +18 -3
- package/src/parser/tokentype.js +39 -20
- package/src/router/index.js +2 -1
- package/src/transform/context.js +3 -3
- package/src/transform/index.js +7 -2
- package/src/transform/static/index.js +9 -2
- package/src/transform/static/visitors/Attribute.js +8 -5
- package/src/transform/static/visitors/CustomElement.js +33 -0
- package/src/transform/static/visitors/EachBlock.js +11 -11
- package/src/transform/static/visitors/Element.js +4 -4
- package/src/transform/static/visitors/ExpressionTag.js +8 -2
- package/src/transform/static/visitors/Identifier.js +7 -0
- package/src/transform/static/visitors/IfBlock.js +15 -18
- package/src/transform/static/visitors/Program.js +4 -1
- package/src/transform/static/visitors/Script.js +3 -3
- package/src/transform/static/visitors/SlotElement.js +2 -2
- package/src/transform/static/visitors/Style.js +2 -2
- package/src/transform/static/visitors/Template.js +2 -6
- package/src/transform/static/visitors/Text.js +2 -2
- package/src/transform/visitors/Attribute.js +5 -3
- package/src/transform/visitors/ExpressionTag.js +8 -2
- package/src/transform/visitors/MethodDefinition.js +13 -8
- package/src/transform/visitors/Program.js +52 -38
- package/src/utils/template.js +9 -1
- package/src/utils/misc.js +0 -9
package/package.json
CHANGED
package/src/analyse/context.js
CHANGED
|
@@ -15,9 +15,11 @@ export function Program(node, ctx) {
|
|
|
15
15
|
|
|
16
16
|
ctx.next({ ...ctx.state, modules, customElement })
|
|
17
17
|
|
|
18
|
+
const hasCustomElementClass = node.body.some(is.customElement)
|
|
18
19
|
const hasDefineCustomElement = node.body.some(is.defineCustomElement)
|
|
19
20
|
|
|
20
21
|
node.metadata ??= {}
|
|
22
|
+
node.metadata.hasCustomElementClass = hasCustomElementClass
|
|
21
23
|
node.metadata.hasDefineCustomElement = hasDefineCustomElement
|
|
22
24
|
node.metadata.modules = modules
|
|
23
25
|
node.metadata.customElement = customElement
|
package/src/builders.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { hasExpression, hasOnlyExpression } from './utils/template.js'
|
|
2
|
+
|
|
1
3
|
export function declaration(id, init, kind = 'const') {
|
|
2
4
|
if (typeof id === 'string') {
|
|
3
5
|
id = { type: 'Identifier', name: id }
|
|
@@ -29,10 +31,18 @@ export function binary(operator, left, right) {
|
|
|
29
31
|
}
|
|
30
32
|
}
|
|
31
33
|
|
|
32
|
-
export function template(
|
|
34
|
+
export function template(template) {
|
|
35
|
+
if (hasOnlyExpression(template)) {
|
|
36
|
+
return template.expressions[0]
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (!hasExpression(template)) {
|
|
40
|
+
return { type: 'Literal', value: template.text[0] }
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const { text, expressions } = template
|
|
33
44
|
if (text.length === expressions.length) {
|
|
34
|
-
// template literals must start and end with a text
|
|
35
|
-
text.push('')
|
|
45
|
+
text.push('') // template literals must start and end with a text
|
|
36
46
|
}
|
|
37
47
|
|
|
38
48
|
const quasis = text.map((raw) => ({
|
|
@@ -56,7 +66,7 @@ export function assignment(left, right, operator = '=') {
|
|
|
56
66
|
}
|
|
57
67
|
}
|
|
58
68
|
|
|
59
|
-
export function member(object, property) {
|
|
69
|
+
export function member(object, property, { optional = false } = {}) {
|
|
60
70
|
if (typeof object === 'string') {
|
|
61
71
|
object = { type: 'Identifier', name: object }
|
|
62
72
|
}
|
|
@@ -70,7 +80,7 @@ export function member(object, property) {
|
|
|
70
80
|
object,
|
|
71
81
|
property,
|
|
72
82
|
computed: false,
|
|
73
|
-
optional
|
|
83
|
+
optional
|
|
74
84
|
}
|
|
75
85
|
}
|
|
76
86
|
|
|
@@ -160,8 +170,8 @@ export function property(key, value) {
|
|
|
160
170
|
return { type: 'Property', key, value }
|
|
161
171
|
}
|
|
162
172
|
|
|
163
|
-
export function object() {
|
|
164
|
-
return { type: 'ObjectExpression', properties
|
|
173
|
+
export function object(properties = []) {
|
|
174
|
+
return { type: 'ObjectExpression', properties }
|
|
165
175
|
}
|
|
166
176
|
|
|
167
177
|
export function id(name, isPrivate = false) {
|
|
@@ -216,8 +226,8 @@ export function array(elements) {
|
|
|
216
226
|
return { type: 'ArrayExpression', elements }
|
|
217
227
|
}
|
|
218
228
|
|
|
219
|
-
export function call(callee) {
|
|
220
|
-
return { type: 'CallExpression', callee, arguments:
|
|
229
|
+
export function call(callee, args = [], { optional = false } = {}) {
|
|
230
|
+
return { type: 'CallExpression', callee, arguments: args, optional }
|
|
221
231
|
}
|
|
222
232
|
|
|
223
233
|
export function ifStmt(test, consequent, alternate) {
|
|
@@ -460,6 +470,24 @@ export function includes(object, value) {
|
|
|
460
470
|
}
|
|
461
471
|
}
|
|
462
472
|
|
|
473
|
+
export function customElement(name) {
|
|
474
|
+
return {
|
|
475
|
+
type: 'ClassDeclaration',
|
|
476
|
+
id: {
|
|
477
|
+
type: 'Identifier',
|
|
478
|
+
name
|
|
479
|
+
},
|
|
480
|
+
superClass: {
|
|
481
|
+
type: 'Identifier',
|
|
482
|
+
name: 'HTMLElement'
|
|
483
|
+
},
|
|
484
|
+
body: {
|
|
485
|
+
type: 'ClassBody',
|
|
486
|
+
body: []
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
463
491
|
export function defineCustomElement(elementName, className) {
|
|
464
492
|
return {
|
|
465
493
|
type: 'ExpressionStatement',
|
|
@@ -1276,12 +1304,14 @@ export function render(body = []) {
|
|
|
1276
1304
|
async: false,
|
|
1277
1305
|
params: [
|
|
1278
1306
|
{
|
|
1279
|
-
type: '
|
|
1280
|
-
name: 'data'
|
|
1307
|
+
type: 'AssignmentPattern',
|
|
1308
|
+
left: { type: 'Identifier', name: 'data' },
|
|
1309
|
+
right: { type: 'ObjectExpression', properties: [] }
|
|
1281
1310
|
},
|
|
1282
1311
|
{
|
|
1283
|
-
type: '
|
|
1284
|
-
name: 'slot'
|
|
1312
|
+
type: 'AssignmentPattern',
|
|
1313
|
+
left: { type: 'Identifier', name: 'slot' },
|
|
1314
|
+
right: { type: 'ObjectExpression', properties: [] }
|
|
1285
1315
|
}
|
|
1286
1316
|
],
|
|
1287
1317
|
body: {
|
package/src/checkers.js
CHANGED
|
@@ -62,6 +62,14 @@ export function querySelector(node) {
|
|
|
62
62
|
)
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
export function customElement(node) {
|
|
66
|
+
if (node.type === 'ExportNamedDeclaration') {
|
|
67
|
+
node = node.declaration
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return node.type === 'ClassDeclaration'
|
|
71
|
+
}
|
|
72
|
+
|
|
65
73
|
export function defineCustomElement(node) {
|
|
66
74
|
if (node.type === 'ExpressionStatement') {
|
|
67
75
|
node = node.expression
|
|
@@ -128,11 +136,7 @@ export function bindAttribute(node) {
|
|
|
128
136
|
}
|
|
129
137
|
|
|
130
138
|
export function staticAttribute(node) {
|
|
131
|
-
return
|
|
132
|
-
node.type === 'Attribute' &&
|
|
133
|
-
node.name === 'static' &&
|
|
134
|
-
(node.value === true || (node.value[0]?.type === 'Text' && node.value[0]?.data === 'true'))
|
|
135
|
-
)
|
|
139
|
+
return node.type === 'Attribute' && node.name === 'static'
|
|
136
140
|
}
|
|
137
141
|
|
|
138
142
|
export function shadowRootModeAttribute(node) {
|
|
@@ -141,4 +145,4 @@ export function shadowRootModeAttribute(node) {
|
|
|
141
145
|
|
|
142
146
|
export function elseIfBlock(node) {
|
|
143
147
|
return node.type === 'IfBlock' && node.elseif
|
|
144
|
-
}
|
|
148
|
+
}
|
package/src/css-matcher.js
CHANGED
package/src/exp-matcher.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { walk } from 'zimmerframe'
|
|
2
2
|
|
|
3
3
|
export function matchExpression(expression, program, blocks) {
|
|
4
|
-
let customElement = program
|
|
4
|
+
let customElement = program?.metadata?.customElement
|
|
5
5
|
|
|
6
6
|
walk(expression, undefined, {
|
|
7
7
|
Identifier(node, ctx) {
|
|
@@ -20,6 +20,11 @@ export function matchExpression(expression, program, blocks) {
|
|
|
20
20
|
setMetadata(node)
|
|
21
21
|
}
|
|
22
22
|
break
|
|
23
|
+
case 'Property':
|
|
24
|
+
if (parentNode.value === node) {
|
|
25
|
+
setMetadata(node)
|
|
26
|
+
}
|
|
27
|
+
break
|
|
23
28
|
default:
|
|
24
29
|
setMetadata(node)
|
|
25
30
|
}
|
|
@@ -29,17 +34,19 @@ export function matchExpression(expression, program, blocks) {
|
|
|
29
34
|
function setMethodMetadata(node) {
|
|
30
35
|
node.metadata ??= {}
|
|
31
36
|
|
|
32
|
-
if (customElement
|
|
37
|
+
if (customElement?.methods.includes(node.name)) {
|
|
33
38
|
node.metadata.isPrivate = false
|
|
34
39
|
node.metadata.isMethod = true
|
|
35
40
|
return
|
|
36
41
|
}
|
|
37
42
|
|
|
38
|
-
if (customElement
|
|
43
|
+
if (customElement?.private.methods.includes(node.name)) {
|
|
39
44
|
node.metadata.isPrivate = true
|
|
40
45
|
node.metadata.isMethod = true
|
|
41
46
|
return
|
|
42
47
|
}
|
|
48
|
+
|
|
49
|
+
node.metadata.isData = true
|
|
43
50
|
}
|
|
44
51
|
|
|
45
52
|
function setMetadata(node) {
|
|
@@ -50,16 +57,18 @@ export function matchExpression(expression, program, blocks) {
|
|
|
50
57
|
return
|
|
51
58
|
}
|
|
52
59
|
|
|
53
|
-
if (customElement
|
|
60
|
+
if (customElement?.properties.includes(node.name)) {
|
|
54
61
|
node.metadata.isPrivate = false
|
|
55
62
|
node.metadata.isProperty = true
|
|
56
63
|
return
|
|
57
64
|
}
|
|
58
65
|
|
|
59
|
-
if (customElement
|
|
66
|
+
if (customElement?.private.properties.includes(node.name)) {
|
|
60
67
|
node.metadata.isPrivate = true
|
|
61
68
|
node.metadata.isProperty = true
|
|
62
69
|
return
|
|
63
70
|
}
|
|
71
|
+
|
|
72
|
+
node.metadata.isData = true
|
|
64
73
|
}
|
|
65
74
|
}
|
package/src/parser/attributes.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { parseDoubleQuotedExpressionTag, parseQuotedExpressionTag } from './expression.js'
|
|
2
2
|
import { Parser } from './parser.js'
|
|
3
3
|
import { TokenTypes } from './tokentype.js'
|
|
4
4
|
|
|
@@ -35,12 +35,16 @@ function parseAttribute(p) {
|
|
|
35
35
|
let value
|
|
36
36
|
switch (punctToken?.type) {
|
|
37
37
|
case TokenTypes.quoteBraceL:
|
|
38
|
+
value = [parseQuotedExpressionTag(p)]
|
|
39
|
+
break
|
|
38
40
|
case TokenTypes.doubleQuoteBraceL:
|
|
39
|
-
value = [
|
|
41
|
+
value = [parseDoubleQuotedExpressionTag(p)]
|
|
40
42
|
break
|
|
41
43
|
case TokenTypes.quote:
|
|
44
|
+
value = [parseQuotedText(p)]
|
|
45
|
+
break
|
|
42
46
|
case TokenTypes.doubleQuote:
|
|
43
|
-
value = [
|
|
47
|
+
value = [parseDoubleQuotedText(p)]
|
|
44
48
|
break
|
|
45
49
|
default:
|
|
46
50
|
p.raiseUnexpectedToken()
|
|
@@ -48,7 +52,7 @@ function parseAttribute(p) {
|
|
|
48
52
|
}
|
|
49
53
|
return { type: 'Attribute', name, value, start, end: p.pos }
|
|
50
54
|
}
|
|
51
|
-
return { type: 'Attribute', name, value:
|
|
55
|
+
return { type: 'Attribute', name, value: [], start, end: p.pos }
|
|
52
56
|
}
|
|
53
57
|
|
|
54
58
|
/**
|
|
@@ -56,11 +60,27 @@ function parseAttribute(p) {
|
|
|
56
60
|
* @param {Parser} p
|
|
57
61
|
* @returns
|
|
58
62
|
*/
|
|
59
|
-
function
|
|
63
|
+
function parseQuotedText(p) {
|
|
60
64
|
const start = p.pos
|
|
61
|
-
p.expectToken([TokenTypes.quote
|
|
62
|
-
p.expectToken([TokenTypes.
|
|
65
|
+
p.expectToken([TokenTypes.quote])
|
|
66
|
+
p.expectToken([TokenTypes.quotedText])
|
|
63
67
|
const data = p.value
|
|
64
|
-
p.expectToken([TokenTypes.quote
|
|
68
|
+
p.expectToken([TokenTypes.quote])
|
|
69
|
+
|
|
65
70
|
return { type: 'Text', start, end: p.end, data }
|
|
66
71
|
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
*
|
|
75
|
+
* @param {Parser} p
|
|
76
|
+
* @returns
|
|
77
|
+
*/
|
|
78
|
+
function parseDoubleQuotedText(p) {
|
|
79
|
+
const start = p.pos
|
|
80
|
+
p.expectToken([TokenTypes.doubleQuote])
|
|
81
|
+
p.expectToken([TokenTypes.doubleQuotedText])
|
|
82
|
+
const data = p.value
|
|
83
|
+
p.expectToken([TokenTypes.doubleQuote])
|
|
84
|
+
|
|
85
|
+
return { type: 'Text', start, end: p.end, data }
|
|
86
|
+
}
|
package/src/parser/element.js
CHANGED
|
@@ -34,7 +34,7 @@ export function parseElement(p) {
|
|
|
34
34
|
|
|
35
35
|
return { type, name, attributes, fragment, start, end: p.pos }
|
|
36
36
|
}
|
|
37
|
-
return { type, name, attributes, start, end: p.pos }
|
|
37
|
+
return { type, name, attributes, fragment: { type: 'Fragment', nodes: [] }, start, end: p.pos }
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
/**
|
package/src/parser/expression.js
CHANGED
|
@@ -22,12 +22,27 @@ export function parseExpressionTag(p) {
|
|
|
22
22
|
* @param {Parser} p
|
|
23
23
|
* @returns
|
|
24
24
|
*/
|
|
25
|
-
export function
|
|
26
|
-
p.expectToken([TokenTypes.quoteBraceL
|
|
25
|
+
export function parseQuotedExpressionTag(p) {
|
|
26
|
+
p.expectToken([TokenTypes.quoteBraceL])
|
|
27
27
|
p.skipWhitespaces()
|
|
28
28
|
const expression = parseExpression(p)
|
|
29
29
|
p.skipWhitespaces()
|
|
30
|
-
p.expectToken([TokenTypes.braceRQuote
|
|
30
|
+
p.expectToken([TokenTypes.braceRQuote])
|
|
31
|
+
|
|
32
|
+
return { type: 'ExpressionTag', expression }
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
*
|
|
37
|
+
* @param {Parser} p
|
|
38
|
+
* @returns
|
|
39
|
+
*/
|
|
40
|
+
export function parseDoubleQuotedExpressionTag(p) {
|
|
41
|
+
p.expectToken([TokenTypes.doubleQuoteBraceL])
|
|
42
|
+
p.skipWhitespaces()
|
|
43
|
+
const expression = parseExpression(p)
|
|
44
|
+
p.skipWhitespaces()
|
|
45
|
+
p.expectToken([TokenTypes.braceRDoubleQuote])
|
|
31
46
|
|
|
32
47
|
return { type: 'ExpressionTag', expression }
|
|
33
48
|
}
|
package/src/parser/tokentype.js
CHANGED
|
@@ -27,6 +27,8 @@ function stringTT(label, test) {
|
|
|
27
27
|
|
|
28
28
|
export const TokenTypes = {
|
|
29
29
|
name: stringTT('name', name),
|
|
30
|
+
quotedText: stringTT('text', quotedText),
|
|
31
|
+
doubleQuotedText: stringTT('text', doubleQuotedText),
|
|
30
32
|
text: stringTT('text', text),
|
|
31
33
|
|
|
32
34
|
// html punctuation
|
|
@@ -35,13 +37,13 @@ export const TokenTypes = {
|
|
|
35
37
|
slashGte: charTT('/>', [47, 62]),
|
|
36
38
|
lteSlash: charTT('</', [60, 47]),
|
|
37
39
|
eq: charTT('=', [61]),
|
|
38
|
-
quote: charTT('
|
|
40
|
+
quote: charTT("'", [39]),
|
|
39
41
|
doubleQuote: charTT('"', [34]),
|
|
40
42
|
|
|
41
43
|
// block punctuation
|
|
42
|
-
quoteBraceL: charTT('
|
|
44
|
+
quoteBraceL: charTT("'{", [39, 123]),
|
|
43
45
|
doubleQuoteBraceL: charTT('"{', [34, 123]),
|
|
44
|
-
braceRQuote: charTT(
|
|
46
|
+
braceRQuote: charTT("}'", [125, 39]),
|
|
45
47
|
braceRDoubleQuote: charTT('}"', [125, 34]),
|
|
46
48
|
braceL: charTT('{', [123]),
|
|
47
49
|
braceR: charTT('}', [125]),
|
|
@@ -49,27 +51,44 @@ export const TokenTypes = {
|
|
|
49
51
|
braceLColumn: charTT('{:', [123, 58]),
|
|
50
52
|
braceLSlash: charTT('{/', [123, 47]),
|
|
51
53
|
parenthesesL: charTT('(', [40]),
|
|
52
|
-
parenthesesR: charTT(')', [41])
|
|
54
|
+
parenthesesR: charTT(')', [41])
|
|
53
55
|
}
|
|
54
56
|
|
|
55
57
|
function name(code) {
|
|
56
|
-
|
|
57
|
-
if (code
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
if (code
|
|
61
|
-
|
|
62
|
-
|
|
58
|
+
// Automatically allow all extended ASCII and UTF-16 surrogate bytes
|
|
59
|
+
if (code > 127) return true
|
|
60
|
+
|
|
61
|
+
// Fast check for control characters, whitespace, and quotes
|
|
62
|
+
if (code <= 47) {
|
|
63
|
+
return (
|
|
64
|
+
code !== 0 && // NULL
|
|
65
|
+
code !== 9 && // Tab (\t)
|
|
66
|
+
code !== 10 && // Line Feed (\n)
|
|
67
|
+
code !== 12 && // Form Feed (\f)
|
|
68
|
+
code !== 13 && // Carriage Return (\r)
|
|
69
|
+
code !== 32 && // Space ( )
|
|
70
|
+
code !== 34 && // Double Quote (")
|
|
71
|
+
code !== 39 && // Single Quote (')
|
|
72
|
+
code !== 47 // Forward Slash (/)
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Blocks Equals (=), Greater-Than (>), and Right-Brace (})
|
|
77
|
+
return code !== 61 && code !== 62 && code !== 125
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function quotedText(code) {
|
|
81
|
+
return code > 0 && code !== 39 // Blocks NULL (0) and Single Quote (39)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function doubleQuotedText(code) {
|
|
85
|
+
return code > 0 && code !== 34 // Blocks NULL (0) and Double Quote (34)
|
|
63
86
|
}
|
|
64
87
|
|
|
65
88
|
function text(code) {
|
|
66
|
-
|
|
67
|
-
if (code
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
if (code < 65) return false
|
|
72
|
-
if (code < 91) return true
|
|
73
|
-
if (code < 123) return true
|
|
74
|
-
return false
|
|
89
|
+
// Quickly allow all extended ASCII and UTF-16 surrogate bytes
|
|
90
|
+
if (code > 127) return true
|
|
91
|
+
|
|
92
|
+
// Blocks NULL (0), Less-Than (<), Left-Brace ({) and Ampersand (&)
|
|
93
|
+
return code !== 0 && code !== 60 && code !== 123 && code !== 38
|
|
75
94
|
}
|
package/src/router/index.js
CHANGED
|
@@ -79,7 +79,8 @@ export async function route(path) {
|
|
|
79
79
|
function load(modules, context) {
|
|
80
80
|
const data = []
|
|
81
81
|
for (let i = 0; i < modules.length; i++) {
|
|
82
|
-
|
|
82
|
+
const parentData = { ...data[i - 1] }
|
|
83
|
+
data.push(modules[i].load?.(parentData, context) ?? parentData)
|
|
83
84
|
}
|
|
84
85
|
return Promise.all(data)
|
|
85
86
|
}
|
package/src/transform/context.js
CHANGED
|
@@ -58,10 +58,10 @@ function position(fragment, node) {
|
|
|
58
58
|
(nodes[i - 1]?.type === 'ExpressionTag' || nodes[i - 1]?.type === 'Text')
|
|
59
59
|
) {
|
|
60
60
|
// Text and ExpressionTag siblings are collapsed in one node in the html template
|
|
61
|
-
|
|
61
|
+
} else {
|
|
62
|
+
position++
|
|
62
63
|
}
|
|
63
64
|
|
|
64
|
-
position++
|
|
65
65
|
if (nodes[i] === node) break
|
|
66
66
|
}
|
|
67
67
|
return position
|
|
@@ -73,4 +73,4 @@ export function getProgram(ctx) {
|
|
|
73
73
|
|
|
74
74
|
export function getTemplate(ctx) {
|
|
75
75
|
return ctx.path[0]
|
|
76
|
-
}
|
|
76
|
+
}
|
package/src/transform/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { walk } from 'zimmerframe'
|
|
2
|
+
import * as b from '../builders.js'
|
|
2
3
|
import { Program } from './visitors/Program.js'
|
|
3
4
|
import { Identifier } from './visitors/Identifier.js'
|
|
4
5
|
import { ExpressionTag } from './visitors/ExpressionTag.js'
|
|
@@ -51,6 +52,10 @@ const scriptVisitors = {
|
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
export function transform(ast, analysis, context) {
|
|
54
|
-
|
|
55
|
-
|
|
55
|
+
let template
|
|
56
|
+
|
|
57
|
+
if (ast.template) {
|
|
58
|
+
template = walk(ast.template, { analysis, context }, templateVisitors)
|
|
59
|
+
}
|
|
60
|
+
return walk(ast.script?.content ?? b.program(), { analysis, template, context }, scriptVisitors)
|
|
56
61
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { walk } from 'zimmerframe'
|
|
2
|
+
import * as b from '../../builders.js'
|
|
2
3
|
import { Element } from './visitors/Element.js'
|
|
3
4
|
import { Text } from './visitors/Text.js'
|
|
4
5
|
import { Program } from './visitors/Program.js'
|
|
@@ -12,14 +13,19 @@ import { CssTree, Selector } from '../visitors/Selector.js'
|
|
|
12
13
|
import { CallExpression } from '../visitors/CallExpression.js'
|
|
13
14
|
import { IfBlock } from './visitors/IfBlock.js'
|
|
14
15
|
import { EachBlock } from './visitors/EachBlock.js'
|
|
16
|
+
import { Identifier } from './visitors/Identifier.js'
|
|
17
|
+
import { CustomElement } from './visitors/CustomElement.js'
|
|
18
|
+
import { ImportDeclaration } from '../visitors/ImportDeclaration.js'
|
|
15
19
|
|
|
16
20
|
const templateVisitors = {
|
|
17
21
|
Attribute,
|
|
18
22
|
Element,
|
|
23
|
+
CustomElement,
|
|
19
24
|
SlotElement,
|
|
20
25
|
Text,
|
|
21
26
|
ExpressionTag,
|
|
22
27
|
Template,
|
|
28
|
+
Identifier,
|
|
23
29
|
Style,
|
|
24
30
|
Script,
|
|
25
31
|
CallExpression,
|
|
@@ -31,10 +37,11 @@ const templateVisitors = {
|
|
|
31
37
|
}
|
|
32
38
|
|
|
33
39
|
const scriptVisitors = {
|
|
34
|
-
Program
|
|
40
|
+
Program,
|
|
41
|
+
ImportDeclaration
|
|
35
42
|
}
|
|
36
43
|
|
|
37
44
|
export function transform(ast, analysis, context) {
|
|
38
45
|
const template = walk(ast.template, { analysis, context }, templateVisitors)
|
|
39
|
-
return walk(ast.script.
|
|
46
|
+
return walk(ast.script?.content ?? b.program(), { analysis, template, context }, scriptVisitors)
|
|
40
47
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as b from '../../../builders.js'
|
|
2
|
-
import {
|
|
2
|
+
import { appendText } from '../../../utils/template.js'
|
|
3
3
|
import { clx } from '../../../css.js'
|
|
4
4
|
|
|
5
5
|
export function Attribute(node, ctx) {
|
|
@@ -19,9 +19,12 @@ export function Attribute(node, ctx) {
|
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
ctx.
|
|
22
|
+
appendText(ctx.state.template, ` ${node.name}`)
|
|
23
|
+
if (value.length > 0) {
|
|
24
|
+
appendText(ctx.state.template, `="`)
|
|
25
|
+
for (const val of value) {
|
|
26
|
+
ctx.visit(val)
|
|
27
|
+
}
|
|
28
|
+
appendText(ctx.state.template, `"`)
|
|
25
29
|
}
|
|
26
|
-
append(ctx.state.text, `"`)
|
|
27
30
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import * as b from '../../../builders.js'
|
|
2
|
+
import { appendText, appendExpression } from '../../../utils/template.js'
|
|
3
|
+
|
|
4
|
+
export function CustomElement(node, ctx) {
|
|
5
|
+
if (node.metadata?.isModule) {
|
|
6
|
+
const dataStmt = b.object()
|
|
7
|
+
for (const attribute of node.attributes) {
|
|
8
|
+
const { name, value } = attribute
|
|
9
|
+
|
|
10
|
+
const template = { text: [''], expressions: [] }
|
|
11
|
+
for (const val of value) {
|
|
12
|
+
ctx.visit(val, { ...ctx.state, template, pretty: false })
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const stmt = b.property(name, b.template(template))
|
|
16
|
+
dataStmt.properties.push(stmt)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const moduleId = b.id(`$Module_${node.metadata.index + 1}`)
|
|
20
|
+
const loadStmt = b.call(b.member(moduleId, 'load'), [dataStmt], { optional: true })
|
|
21
|
+
const renderStmt = b.call(b.member(moduleId, 'render'), [loadStmt])
|
|
22
|
+
|
|
23
|
+
appendExpression(ctx.state.template, renderStmt)
|
|
24
|
+
} else {
|
|
25
|
+
appendText(ctx.state.template, `<${node.name}`)
|
|
26
|
+
for (const attribute of node.attributes) {
|
|
27
|
+
ctx.visit(attribute)
|
|
28
|
+
}
|
|
29
|
+
appendText(ctx.state.template, '>')
|
|
30
|
+
ctx.visit(node.fragment)
|
|
31
|
+
appendText(ctx.state.template, `</${node.name}>`)
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
import * as b from '../../../builders.js'
|
|
2
|
+
import { appendExpression } from '../../../utils/template.js'
|
|
2
3
|
import { nextBlockId } from '../../context.js'
|
|
3
4
|
|
|
4
5
|
export function EachBlock(node, ctx) {
|
|
5
|
-
const
|
|
6
|
-
const expressions = []
|
|
6
|
+
const template = { text: [''], expressions: [] }
|
|
7
7
|
|
|
8
|
-
ctx.visit(node.body, { ...ctx.state,
|
|
8
|
+
ctx.visit(node.body, { ...ctx.state, template })
|
|
9
9
|
|
|
10
10
|
const blockId = nextBlockId(ctx)
|
|
11
|
-
const resultId = b.id('
|
|
12
|
-
const
|
|
11
|
+
const resultId = b.id('_result')
|
|
12
|
+
const expressionStmt = ctx.visit(node.expression)
|
|
13
|
+
const bodyStmt = [b.assignment(resultId, b.template(template), '+=')]
|
|
14
|
+
|
|
15
|
+
const blockStmt = b.func(blockId, [
|
|
13
16
|
b.declaration(resultId, b.literal(''), 'let'),
|
|
14
|
-
b.forStmt(node.context,
|
|
15
|
-
b.assignment(resultId, b.template({ text, expressions }), '+=')
|
|
16
|
-
]),
|
|
17
|
+
b.forStmt(node.context, expressionStmt, bodyStmt),
|
|
17
18
|
b.returnStmt(resultId)
|
|
18
19
|
])
|
|
19
20
|
|
|
20
|
-
ctx.state.blocks.push(
|
|
21
|
-
ctx.state.
|
|
22
|
-
ctx.state.expressions.push(b.call(blockId))
|
|
21
|
+
ctx.state.blocks.push(blockStmt)
|
|
22
|
+
appendExpression(ctx.state.template, b.call(blockId))
|
|
23
23
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as b from '../../../builders.js'
|
|
2
|
-
import {
|
|
2
|
+
import { appendText } from '../../../utils/template.js'
|
|
3
3
|
|
|
4
4
|
export function Element(node, ctx) {
|
|
5
5
|
let attributes = node.attributes
|
|
@@ -7,11 +7,11 @@ export function Element(node, ctx) {
|
|
|
7
7
|
attributes = [...attributes, b.attribute('class', '', { isScoped: true })]
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
appendText(ctx.state.template, `<${node.name}`)
|
|
11
11
|
for (const attribute of attributes) {
|
|
12
12
|
ctx.visit(attribute)
|
|
13
13
|
}
|
|
14
|
-
|
|
14
|
+
appendText(ctx.state.template, '>')
|
|
15
15
|
ctx.visit(node.fragment)
|
|
16
|
-
|
|
16
|
+
appendText(ctx.state.template, `</${node.name}>`)
|
|
17
17
|
}
|
|
@@ -1,6 +1,12 @@
|
|
|
1
|
+
import * as b from '../../../builders.js'
|
|
2
|
+
import { appendExpression } from '../../../utils/template.js'
|
|
3
|
+
|
|
1
4
|
export function ExpressionTag(node, ctx) {
|
|
2
5
|
node = ctx.next() ?? node
|
|
3
6
|
|
|
4
|
-
|
|
5
|
-
ctx.state.
|
|
7
|
+
let expression = node.expression
|
|
8
|
+
if (ctx.state.pretty ?? true) {
|
|
9
|
+
expression = b.logical('??', expression, b.literal(''))
|
|
10
|
+
}
|
|
11
|
+
appendExpression(ctx.state.template, expression)
|
|
6
12
|
}
|
|
@@ -1,28 +1,25 @@
|
|
|
1
1
|
import * as b from '../../../builders.js'
|
|
2
|
+
import { appendExpression } from '../../../utils/template.js'
|
|
2
3
|
import { nextBlockId } from '../../context.js'
|
|
3
4
|
|
|
4
5
|
export function IfBlock(node, ctx) {
|
|
5
|
-
|
|
6
|
-
|
|
6
|
+
function branchStmt(node) {
|
|
7
|
+
if (node) {
|
|
8
|
+
const template = { text: [''], expressions: [] }
|
|
7
9
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
if (node.alternate) {
|
|
13
|
-
const text = ['']
|
|
14
|
-
const expressions = []
|
|
15
|
-
|
|
16
|
-
ctx.visit(node.alternate, { ...ctx.state, text, expressions })
|
|
17
|
-
stmt2 = b.returnStmt(b.template({ text, expressions }))
|
|
18
|
-
} else {
|
|
19
|
-
stmt2 = b.returnStmt(b.literal(''))
|
|
10
|
+
ctx.visit(node, { ...ctx.state, template })
|
|
11
|
+
return b.returnStmt(b.template(template))
|
|
12
|
+
}
|
|
13
|
+
return b.returnStmt(b.literal(''))
|
|
20
14
|
}
|
|
21
15
|
|
|
16
|
+
const testStmt = ctx.visit(node.test)
|
|
17
|
+
const consequentStmt = branchStmt(node.consequent)
|
|
18
|
+
const alternateStmt = branchStmt(node.alternate)
|
|
19
|
+
|
|
22
20
|
const blockId = nextBlockId(ctx)
|
|
23
|
-
const
|
|
21
|
+
const blockStmt = b.func(blockId, [b.ifStmt(testStmt, [consequentStmt]), alternateStmt])
|
|
24
22
|
|
|
25
|
-
ctx.state.blocks.push(
|
|
26
|
-
ctx.state.
|
|
27
|
-
ctx.state.expressions.push(b.call(blockId))
|
|
23
|
+
ctx.state.blocks.push(blockStmt)
|
|
24
|
+
appendExpression(ctx.state.template, b.call(blockId))
|
|
28
25
|
}
|
|
@@ -4,7 +4,10 @@ export function Program(node, ctx) {
|
|
|
4
4
|
node = ctx.next() ?? node
|
|
5
5
|
|
|
6
6
|
const stmt = b.exp(
|
|
7
|
-
b.render([
|
|
7
|
+
b.render([
|
|
8
|
+
...ctx.state.template.blocks,
|
|
9
|
+
b.returnStmt(b.template(ctx.state.template.template))
|
|
10
|
+
])
|
|
8
11
|
)
|
|
9
12
|
return { ...node, body: [...node.body, stmt] }
|
|
10
13
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { print } from 'esrap'
|
|
2
|
-
import {
|
|
2
|
+
import { appendText } from '../../../utils/template.js'
|
|
3
3
|
|
|
4
4
|
export function Script(node, ctx) {
|
|
5
5
|
node = ctx.next() ?? node
|
|
6
|
-
|
|
6
|
+
|
|
7
7
|
const { code } = print(node.content)
|
|
8
|
-
|
|
8
|
+
appendText(ctx.state.template, `<script type="module">${code}</script>`)
|
|
9
9
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as b from '../../../builders.js'
|
|
2
|
+
import { appendExpression } from '../../../utils/template.js'
|
|
2
3
|
|
|
3
4
|
export function SlotElement(node, ctx) {
|
|
4
5
|
node = ctx.next() ?? node
|
|
5
6
|
|
|
6
|
-
ctx.state.
|
|
7
|
-
ctx.state.expressions.push(b.member(b.id('slot'), b.id('default')))
|
|
7
|
+
appendExpression(ctx.state.template, b.member(b.id('slot'), b.id('default')))
|
|
8
8
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { generate } from 'css-tree'
|
|
2
|
-
import {
|
|
2
|
+
import { appendText } from '../../../utils/template.js'
|
|
3
3
|
|
|
4
4
|
export function Style(node, ctx) {
|
|
5
5
|
node = ctx.next() ?? node
|
|
6
6
|
|
|
7
7
|
const css = generate(node.content)
|
|
8
|
-
|
|
8
|
+
appendText(ctx.state.template, `<style>${css}</style>`)
|
|
9
9
|
}
|
|
@@ -1,12 +1,8 @@
|
|
|
1
|
-
import * as b from '../../../builders.js'
|
|
2
|
-
|
|
3
1
|
export function Template(node, ctx) {
|
|
4
|
-
const
|
|
5
|
-
const expressions = []
|
|
2
|
+
const template = { text: [''], expressions: [] }
|
|
6
3
|
const blocks = []
|
|
7
4
|
|
|
8
|
-
ctx.visit(node.fragment, { ...ctx.state,
|
|
5
|
+
ctx.visit(node.fragment, { ...ctx.state, template, blocks })
|
|
9
6
|
|
|
10
|
-
const template = b.template({ text, expressions })
|
|
11
7
|
return { type: 'TemplateMod', template, blocks }
|
|
12
8
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as b from '../../builders.js'
|
|
2
2
|
import { clx } from '../../css.js'
|
|
3
|
-
import { appendText, hasExpression } from '../../utils/template.js'
|
|
3
|
+
import { appendText, hasExpression, isEmpty } from '../../utils/template.js'
|
|
4
4
|
|
|
5
5
|
export function Attribute(node, ctx) {
|
|
6
6
|
let value = node.value
|
|
@@ -21,7 +21,7 @@ export function Attribute(node, ctx) {
|
|
|
21
21
|
|
|
22
22
|
const template = { text: [''], expressions: [] }
|
|
23
23
|
for (const val of value) {
|
|
24
|
-
ctx.visit(val, { ...ctx.state, template })
|
|
24
|
+
ctx.visit(val, { ...ctx.state, template, pretty: false })
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
if (hasExpression(template)) {
|
|
@@ -66,7 +66,9 @@ export function Attribute(node, ctx) {
|
|
|
66
66
|
])
|
|
67
67
|
ctx.state.effects.push(stmt)
|
|
68
68
|
}
|
|
69
|
+
} else if (isEmpty(template)) {
|
|
70
|
+
appendText(ctx.state.template, ` ${node.name}`)
|
|
69
71
|
} else {
|
|
70
|
-
appendText(ctx.state.template, ` ${node.name}="${template.text[0]
|
|
72
|
+
appendText(ctx.state.template, ` ${node.name}="${template.text[0]}"`)
|
|
71
73
|
}
|
|
72
74
|
}
|
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as b from '../../builders.js'
|
|
2
|
+
import { appendExpression } from '../../utils/template.js'
|
|
2
3
|
|
|
3
4
|
export function ExpressionTag(node, ctx) {
|
|
4
5
|
node = ctx.next() ?? node
|
|
5
|
-
|
|
6
|
+
|
|
7
|
+
let expression = node.expression
|
|
8
|
+
if (ctx.state.pretty ?? true) {
|
|
9
|
+
expression = b.logical('??', expression, b.literal(''))
|
|
10
|
+
}
|
|
11
|
+
appendExpression(ctx.state.template, expression)
|
|
6
12
|
}
|
|
@@ -6,7 +6,8 @@ export function MethodDefinition(node, ctx) {
|
|
|
6
6
|
|
|
7
7
|
if (node.key.name === 'constructor') {
|
|
8
8
|
const program = getProgram(ctx)
|
|
9
|
-
const
|
|
9
|
+
const properties = program.metadata?.customElement.properties ?? []
|
|
10
|
+
const setters = program.metadata?.customElement.setters ?? []
|
|
10
11
|
|
|
11
12
|
const stmt1 = b.assignment(b.$(), b.$$())
|
|
12
13
|
const stmts2 = []
|
|
@@ -30,18 +31,22 @@ export function MethodDefinition(node, ctx) {
|
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
if (node.key.name === 'connectedCallback') {
|
|
33
|
-
const
|
|
34
|
+
const stmts = []
|
|
34
35
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
if (ctx.state.template?.metadata?.shadowRootMode) {
|
|
37
|
+
const { shadowRootMode } = ctx.state.template.metadata
|
|
38
|
+
stmts.push(b.assignment(b.shadow(), b.attachShadow(shadowRootMode), '??='))
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (ctx.state.template?.block) {
|
|
42
|
+
stmts.push(ctx.state.template.block)
|
|
39
43
|
}
|
|
44
|
+
|
|
40
45
|
if (node.value.body.body.length > 0) {
|
|
41
|
-
|
|
46
|
+
stmts.push(b.$boundary(node.value.body.body))
|
|
42
47
|
}
|
|
43
48
|
|
|
44
|
-
const stmt = b.$lifecycle('connected',
|
|
49
|
+
const stmt = b.$lifecycle('connected', stmts)
|
|
45
50
|
|
|
46
51
|
return {
|
|
47
52
|
...node,
|
|
@@ -8,43 +8,47 @@ export function Program(node, ctx) {
|
|
|
8
8
|
const stmts1 = []
|
|
9
9
|
const stmts2 = []
|
|
10
10
|
|
|
11
|
+
if (!node.metadata?.hasCustomElementClass) {
|
|
12
|
+
const stmt = ctx.visit(b.customElement(ctx.state.context.customElementClassName))
|
|
13
|
+
stmts2.push(stmt)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (!node.metadata?.hasDefineCustomElement) {
|
|
17
|
+
stmt = b.defineCustomElement(
|
|
18
|
+
ctx.state.context.customElementName,
|
|
19
|
+
node.metadata?.customElement.className ?? ctx.state.context.customElementClassName
|
|
20
|
+
)
|
|
21
|
+
stmts2.push(stmt)
|
|
22
|
+
}
|
|
23
|
+
|
|
11
24
|
// import
|
|
12
25
|
stmt = b.importSpecifier('$$', '@katabatic/runtime')
|
|
13
26
|
stmts1.push(stmt)
|
|
14
27
|
|
|
15
28
|
// html template
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
29
|
+
if (ctx.state.template?.template) {
|
|
30
|
+
const { template } = ctx.state.template
|
|
31
|
+
stmt = b.declaration('TEMPLATE', b.template(template))
|
|
32
|
+
stmts1.push(stmt)
|
|
33
|
+
}
|
|
19
34
|
|
|
20
35
|
// style
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
stmt = b.exp(
|
|
27
|
-
b.declaration(
|
|
28
|
-
'$name',
|
|
29
|
-
b.literal(node.metadata?.customElement.name ?? ctx.state.context.customElementName)
|
|
30
|
-
)
|
|
31
|
-
)
|
|
32
|
-
stmts1.push(stmt)
|
|
33
|
-
|
|
34
|
-
// $class
|
|
35
|
-
stmt = b.exp(b.declaration('$class', b.id(node.metadata?.customElement.className)))
|
|
36
|
-
stmts2.push(stmt)
|
|
36
|
+
if (ctx.state.template?.style) {
|
|
37
|
+
const { style } = ctx.state.template
|
|
38
|
+
stmt = b.declaration('STYLE', b.template(style))
|
|
39
|
+
stmts1.push(stmt)
|
|
40
|
+
}
|
|
37
41
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
42
|
+
//$hot
|
|
43
|
+
if (ctx.state.context.hot) {
|
|
44
|
+
const stmt = $hot({ ...node, body: [...node.body, ...stmts2] })
|
|
45
|
+
stmts2.push(stmt)
|
|
46
|
+
}
|
|
43
47
|
|
|
44
48
|
// $set
|
|
45
49
|
const properties = [
|
|
46
|
-
...node.metadata?.customElement.properties,
|
|
47
|
-
...node.metadata?.customElement.setters
|
|
50
|
+
...(node.metadata?.customElement.properties ?? []),
|
|
51
|
+
...(node.metadata?.customElement.setters ?? [])
|
|
48
52
|
]
|
|
49
53
|
if (properties.length > 0) {
|
|
50
54
|
stmt = b.$setDecl([
|
|
@@ -57,21 +61,31 @@ export function Program(node, ctx) {
|
|
|
57
61
|
} else {
|
|
58
62
|
stmt = b.$setDecl([b.setAttribute('node', 'attribute', 'value')])
|
|
59
63
|
}
|
|
60
|
-
|
|
64
|
+
stmts2.push(stmt)
|
|
61
65
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
+
// $name
|
|
67
|
+
stmt = b.exp(
|
|
68
|
+
b.declaration(
|
|
69
|
+
'$name',
|
|
70
|
+
b.literal(node.metadata?.customElement.name ?? ctx.state.context.customElementName)
|
|
71
|
+
)
|
|
72
|
+
)
|
|
73
|
+
stmts2.push(stmt)
|
|
66
74
|
|
|
67
|
-
//
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
node.metadata?.customElement.className
|
|
75
|
+
// $class
|
|
76
|
+
stmt = b.exp(
|
|
77
|
+
b.declaration(
|
|
78
|
+
'$class',
|
|
79
|
+
b.id(node.metadata?.customElement.className ?? ctx.state.context.customElementClassName)
|
|
72
80
|
)
|
|
73
|
-
|
|
74
|
-
|
|
81
|
+
)
|
|
82
|
+
stmts2.push(stmt)
|
|
83
|
+
|
|
84
|
+
// $shadowRootMode
|
|
85
|
+
stmt = b.exp(
|
|
86
|
+
b.declaration('$shadowRootMode', b.literal(ctx.state.template?.metadata?.shadowRootMode))
|
|
87
|
+
)
|
|
88
|
+
stmts2.push(stmt)
|
|
75
89
|
|
|
76
90
|
return {
|
|
77
91
|
...node,
|
package/src/utils/template.js
CHANGED
|
@@ -10,6 +10,14 @@ export function hasExpression(template) {
|
|
|
10
10
|
return template.expressions.length > 0
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
export function hasOnlyExpression(template) {
|
|
14
|
+
return (
|
|
15
|
+
template.expressions.length == 1 &&
|
|
16
|
+
template.text[0] === '' &&
|
|
17
|
+
(template.text[1] === undefined || template.text[1] === '')
|
|
18
|
+
)
|
|
19
|
+
}
|
|
20
|
+
|
|
13
21
|
export function isEmpty(template) {
|
|
14
|
-
return template.expressions.length == 0 && template.text
|
|
22
|
+
return template.expressions.length == 0 && template.text[0] === ''
|
|
15
23
|
}
|