@katabatic/compiler 1.1.6 → 1.1.7

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@katabatic/compiler",
3
3
  "license": "MIT",
4
- "version": "1.1.6",
4
+ "version": "1.1.7",
5
5
  "type": "module",
6
6
  "repository": {
7
7
  "type": "git",
@@ -4,7 +4,9 @@ export function CustomElement(node, ctx) {
4
4
  ctx.next()
5
5
 
6
6
  const hasClass = node.attributes.some(is.classAttribute)
7
+ const hasExpressionAttribute = node.attributes.some(is.expressionAttribute)
7
8
 
8
9
  node.metadata ??= {}
9
10
  node.metadata.hasClass = hasClass
11
+ node.metadata.hasExpressionAttribute = hasExpressionAttribute
10
12
  }
@@ -3,14 +3,12 @@ import * as is from '../../checkers.js'
3
3
  export function Element(node, ctx) {
4
4
  ctx.next()
5
5
 
6
+ const isVoid = is.voidElement(node)
6
7
  const hasClass = node.attributes.some(is.classAttribute)
7
-
8
- const bindAttribute = node.attributes.find(is.bindAttribute)
9
- const hasBinding = !!bindAttribute
10
- const bindExpression = bindAttribute?.value[0].expression
8
+ const hasExpressionAttribute = node.attributes.some(is.expressionAttribute)
11
9
 
12
10
  node.metadata ??= {}
11
+ node.metadata.isVoid = isVoid
13
12
  node.metadata.hasClass = hasClass
14
- node.metadata.hasBinding = hasBinding
15
- node.metadata.bindExpression = bindExpression
13
+ node.metadata.hasExpressionAttribute = hasExpressionAttribute
16
14
  }
package/src/builders.js CHANGED
@@ -227,6 +227,10 @@ export function array(elements) {
227
227
  }
228
228
 
229
229
  export function call(callee, args = [], { optional = false } = {}) {
230
+ if (typeof callee === 'string') {
231
+ callee = { type: 'Identifier', name: callee }
232
+ }
233
+
230
234
  return { type: 'CallExpression', callee, arguments: args, optional }
231
235
  }
232
236
 
@@ -577,14 +581,7 @@ export function $set(object, nodeId, attribute, value) {
577
581
  computed: false,
578
582
  optional: false
579
583
  },
580
- arguments: [
581
- nodeId,
582
- {
583
- type: 'Literal',
584
- value: attribute
585
- },
586
- value
587
- ],
584
+ arguments: [nodeId, attribute, value],
588
585
  optional: false
589
586
  }
590
587
  }
@@ -1064,6 +1061,82 @@ export function $effect(body) {
1064
1061
  }
1065
1062
  }
1066
1063
 
1064
+ export function $bind(elementId, body) {
1065
+ return {
1066
+ type: 'ExpressionStatement',
1067
+ expression: {
1068
+ type: 'CallExpression',
1069
+ callee: {
1070
+ type: 'MemberExpression',
1071
+ object: {
1072
+ type: 'MemberExpression',
1073
+ object: {
1074
+ type: 'ThisExpression'
1075
+ },
1076
+ property: {
1077
+ type: 'Identifier',
1078
+ name: '$'
1079
+ },
1080
+ computed: false,
1081
+ optional: false
1082
+ },
1083
+ property: {
1084
+ type: 'Identifier',
1085
+ name: 'bind'
1086
+ },
1087
+ computed: false,
1088
+ optional: false
1089
+ },
1090
+ arguments: [
1091
+ elementId,
1092
+ {
1093
+ type: 'ArrowFunctionExpression',
1094
+ id: null,
1095
+ expression: false,
1096
+ generator: false,
1097
+ async: false,
1098
+ params: [{ type: 'Identifier', name: 'opts' }],
1099
+ body
1100
+ },
1101
+ {
1102
+ type: 'Identifier',
1103
+ name: '$'
1104
+ }
1105
+ ],
1106
+ optional: false
1107
+ }
1108
+ }
1109
+ }
1110
+
1111
+ export function $getBinding(elementId) {
1112
+ return {
1113
+ type: 'CallExpression',
1114
+ callee: {
1115
+ type: 'MemberExpression',
1116
+ object: {
1117
+ type: 'MemberExpression',
1118
+ object: {
1119
+ type: 'ThisExpression'
1120
+ },
1121
+ property: {
1122
+ type: 'Identifier',
1123
+ name: '$'
1124
+ },
1125
+ computed: false,
1126
+ optional: false
1127
+ },
1128
+ property: {
1129
+ type: 'Identifier',
1130
+ name: 'getBinding'
1131
+ },
1132
+ computed: false,
1133
+ optional: false
1134
+ },
1135
+ arguments: [elementId],
1136
+ optional: false
1137
+ }
1138
+ }
1139
+
1067
1140
  export function $animate(value, body) {
1068
1141
  return {
1069
1142
  type: 'ExpressionStatement',
@@ -1099,7 +1172,7 @@ export function $animate(value, body) {
1099
1172
  }
1100
1173
  }
1101
1174
 
1102
- export function addEventListener(object, value, body) {
1175
+ export function addEventListener(object, value, body, { optional = false } = {}) {
1103
1176
  return {
1104
1177
  type: 'ExpressionStatement',
1105
1178
  expression: {
@@ -1134,7 +1207,7 @@ export function addEventListener(object, value, body) {
1134
1207
  }
1135
1208
  }
1136
1209
  ],
1137
- optional: false
1210
+ optional
1138
1211
  }
1139
1212
  }
1140
1213
  }
package/src/checkers.js CHANGED
@@ -121,6 +121,10 @@ export function classAttribute(node, withExpressionTag) {
121
121
  return result
122
122
  }
123
123
 
124
+ export function expressionAttribute(node) {
125
+ return node.type === 'Attribute' && node.value[0]?.type === 'ExpressionTag'
126
+ }
127
+
124
128
  export function idAttribute(node, withExpressionTag) {
125
129
  let result = node.type === 'Attribute' && node.name === 'id'
126
130
  if (withExpressionTag === true) {
@@ -129,12 +133,6 @@ export function idAttribute(node, withExpressionTag) {
129
133
  return result
130
134
  }
131
135
 
132
- export function bindAttribute(node) {
133
- return (
134
- node.type === 'Attribute' && node.name === 'bind' && node.value[0]?.type === 'ExpressionTag'
135
- )
136
- }
137
-
138
136
  export function staticAttribute(node) {
139
137
  return node.type === 'Attribute' && node.name === 'static'
140
138
  }
@@ -146,3 +144,25 @@ export function shadowRootModeAttribute(node) {
146
144
  export function elseIfBlock(node) {
147
145
  return node.type === 'IfBlock' && node.elseif
148
146
  }
147
+
148
+ export function voidElement(node) {
149
+ return (
150
+ node.type === 'Element' &&
151
+ [
152
+ 'area',
153
+ 'base',
154
+ 'br',
155
+ 'col',
156
+ 'embed',
157
+ 'hr',
158
+ 'img',
159
+ 'input',
160
+ 'link',
161
+ 'meta',
162
+ 'param',
163
+ 'source',
164
+ 'track',
165
+ 'wbr'
166
+ ].includes(node.name)
167
+ )
168
+ }
@@ -54,21 +54,21 @@ function matchSelectors(selectors, template) {
54
54
  classAttribute ??= node.attributes.find((a) => a.name === 'class')
55
55
  scopedElement = node
56
56
  scopedClassAttribute = classAttribute
57
- break
58
57
  }
59
- return
58
+ break
60
59
  case 'PseudoClassSelector':
61
- classAttribute ??= node.attributes.find((a) => a.name === 'class')
62
- scopedElement = node
63
- scopedClassAttribute = classAttribute
60
+ if (selector.name !== 'host') {
61
+ classAttribute ??= node.attributes.find((a) => a.name === 'class')
62
+ scopedElement = node
63
+ scopedClassAttribute = classAttribute
64
+ }
64
65
  break
65
66
  case 'IdSelector':
66
67
  const idAttribute = node.attributes.find((a) => a.name === 'id')
67
68
  if (idAttribute?.value[0].data === selector.name) {
68
69
  scopedIdAttribute = idAttribute
69
- break
70
70
  }
71
- return
71
+ break
72
72
  case 'ClassSelector':
73
73
  classAttribute ??= node.attributes.find((a) => a.name === 'class')
74
74
  const expression = classAttribute?.value[0].expression
@@ -76,9 +76,8 @@ function matchSelectors(selectors, template) {
76
76
 
77
77
  if (expression || classes?.includes(selector.name)) {
78
78
  scopedClassAttribute = classAttribute
79
- break
80
79
  }
81
- return
80
+ break
82
81
  }
83
82
  }
84
83
 
package/src/css.js CHANGED
@@ -1,57 +1,3 @@
1
- export function match(element, stylesheet) {
2
- for (const rule of stylesheet.children) {
3
- if (matchSelectorList(element, rule.prelude)) return true
4
- }
5
- return false
6
- }
7
-
8
- function matchSelectorList(element, selectorList) {
9
- if (selectorList) {
10
- for (const selector of selectorList.children) {
11
- if (matchSelector(element, selector)) return true
12
- }
13
- }
14
- return false
15
- }
16
-
17
- function matchSelector(element, selector) {
18
- let selectors = []
19
- for (const child of selector.children) {
20
- switch (child.type) {
21
- case 'TypeSelector':
22
- case 'ClassSelector':
23
- selectors.push(child)
24
- break
25
- case 'PseudoClassSelector':
26
- if (matchSelectorList(element, child.children?.first)) return true
27
- selectors.push(child)
28
- break
29
- case 'Combinator':
30
- if (matchSelectors(element, selectors)) return true
31
- selectors = []
32
- break
33
- }
34
- }
35
- return matchSelectors(element, selectors)
36
- }
37
-
38
- function matchSelectors(element, selectors) {
39
- if (selectors.length === 0) return false
40
-
41
- for (const selector of selectors) {
42
- switch (selector.type) {
43
- case 'TypeSelector':
44
- if (selector.name !== element.name) return false
45
- break
46
- case 'ClassSelector':
47
- const classAtt = element.attributes.find((a) => a.name === 'class')?.value.data
48
- const classes = classAtt?.split(/\s+/)
49
- if (!classes?.includes(selector.name)) return false
50
- break
51
- }
52
- }
53
- return true
54
- }
55
1
 
56
2
  /**
57
3
  * Dead simple clx assuming class2 is always set
@@ -12,21 +12,28 @@ export function matchExpression(expression, program, blocks) {
12
12
  if (parentNode.callee === node) {
13
13
  setMethodMetadata(node)
14
14
  } else {
15
- setMetadata(node)
15
+ setMetadata(node, blocks)
16
16
  }
17
17
  break
18
18
  case 'MemberExpression':
19
19
  if (parentNode.object === node) {
20
+ setMetadata(node, blocks)
21
+ }
22
+ break
23
+ case 'AssignmentExpression':
24
+ if (parentNode.left === node) {
20
25
  setMetadata(node)
26
+ } else {
27
+ setMetadata(node, blocks)
21
28
  }
22
29
  break
23
30
  case 'Property':
24
31
  if (parentNode.value === node) {
25
- setMetadata(node)
32
+ setMetadata(node, blocks)
26
33
  }
27
34
  break
28
35
  default:
29
- setMetadata(node)
36
+ setMetadata(node, blocks)
30
37
  }
31
38
  }
32
39
  })
@@ -49,10 +56,10 @@ export function matchExpression(expression, program, blocks) {
49
56
  node.metadata.isData = true
50
57
  }
51
58
 
52
- function setMetadata(node) {
59
+ function setMetadata(node, blocks) {
53
60
  node.metadata ??= {}
54
61
 
55
- if (blocks.some((b) => b.context?.name === node.name)) {
62
+ if (blocks?.some((b) => b.context?.name === node.name)) {
56
63
  node.metadata.isBlockVar = true
57
64
  return
58
65
  }
@@ -12,10 +12,6 @@ export function nextBlockId(ctx) {
12
12
  return b.id(`block_${ctx.state.blocks.length + 1}`)
13
13
  }
14
14
 
15
- export function nextBindingId(ctx) {
16
- return b.id(`binding_${ctx.state.init.binding.length + 1}`)
17
- }
18
-
19
15
  export function pathStmt(ctx, nodes) {
20
16
  const subPath = []
21
17
 
@@ -58,7 +54,9 @@ function position(fragment, node) {
58
54
  (nodes[i - 1]?.type === 'ExpressionTag' || nodes[i - 1]?.type === 'Text')
59
55
  ) {
60
56
  // Text and ExpressionTag siblings are collapsed in one node in the html template
61
- } else {
57
+ } else if (nodes[i].type === 'Comment') {
58
+ // Comments are skiped in the html template
59
+ } else {
62
60
  position++
63
61
  }
64
62
 
@@ -24,47 +24,73 @@ export function Attribute(node, ctx) {
24
24
  ctx.visit(val, { ...ctx.state, template, pretty: false })
25
25
  }
26
26
 
27
+ if (node.name === ':in' || node.name === ':out' || node.name === ':animate') {
28
+ const elementId = ctx.state.getElementId()
29
+ const expression = template.expressions[0]
30
+ const direction = node.name === ':animate' ? 'both' : node.name.slice(1)
31
+ const stmt = b.$animate(direction, {
32
+ ...expression,
33
+ arguments: [elementId, expression.arguments[0] ?? b.object(), b.id('o')]
34
+ })
35
+ ctx.state.animates.push(stmt)
36
+ return
37
+ }
38
+
39
+ if (node.name === ':use') {
40
+ const elementId = ctx.state.getElementId()
41
+ const expression = template.expressions[0]
42
+ const bindExpression = {
43
+ ...expression,
44
+ arguments: [elementId, b.call('opts', expression.arguments[0])]
45
+ }
46
+ const stmt = b.$bind(elementId, bindExpression)
47
+ ctx.state.binds.push(stmt)
48
+ return
49
+ }
50
+
51
+ if (node.name.startsWith(':on')) {
52
+ const elementId = ctx.state.getElementId()
53
+ const expression = template.expressions[0]
54
+ const event = node.name.slice(3)
55
+ const stmt = b.addEventListener(b.$getBinding(elementId), event, [expression], {
56
+ optional: true
57
+ })
58
+ ctx.state.eventListeners.push(stmt)
59
+ return
60
+ }
61
+
62
+ if (node.name.startsWith(':')) {
63
+ const elementId = ctx.state.getElementId()
64
+ const property = node.name.slice(1)
65
+ const stmt = b.$effect([
66
+ b.assignment(b.member(b.$getBinding(elementId), property), b.template(template))
67
+ ])
68
+ ctx.state.effects.push(stmt)
69
+ return
70
+ }
71
+
72
+ if (node.name.startsWith('on')) {
73
+ const elementId = ctx.state.getElementId()
74
+ const expression = template.expressions[0]
75
+ const event = node.name.slice(2)
76
+ const stmt = b.addEventListener(elementId, event, [expression])
77
+ ctx.state.eventListeners.push(stmt)
78
+ return
79
+ }
80
+
27
81
  if (hasExpression(template)) {
28
- // expression attribute
29
82
  const elementId = ctx.state.getElementId()
30
83
  const moduleId = ctx.state.getModuleId?.()
31
- const bindingId = ctx.state.getBindingId?.()
32
84
 
33
- if (node.name === 'bind') {
34
- // binding handled in element visitor
35
- } else if (node.name === 'in' || node.name === 'out' || node.name === 'animate') {
36
- const direction = node.name === 'animate' ? 'both' : node.name
37
- const animateExpression = template.expressions[0]
38
- const stmt = b.$animate(direction, {
39
- ...animateExpression,
40
- arguments: [elementId, animateExpression.arguments[0] ?? b.object(), b.id('o')]
41
- })
42
- ctx.state.animates.push(stmt)
43
- } else if (node.name.startsWith('on')) {
44
- const stmt = b.assignment(
45
- b.member(elementId, node.name),
46
- b.eventListener(template.expressions[0])
47
- )
48
- ctx.state.handlers.push(stmt)
49
- } else if (moduleId) {
50
- const stmt = b.$effect([b.$set(moduleId, elementId, node.name, b.template(template))])
51
- ctx.state.effects.push(stmt)
52
- } else if (bindingId) {
53
- const stmt1 = b.$effect([
54
- b.assignment(b.member(bindingId, node.name), b.template(template))
55
- ])
56
- const stmt2 = b.$effect([
57
- b.assignment(template.expressions[0], b.member(bindingId, node.name))
58
- ])
85
+ const setStmt = moduleId
86
+ ? b.$set(moduleId, elementId, b.literal(node.name), b.template(template))
87
+ : b.setAttribute(elementId, b.literal(node.name), b.template(template))
88
+ const stmt = b.$effect([setStmt])
89
+ ctx.state.effects.push(stmt)
90
+ return
91
+ }
59
92
 
60
- ctx.state.effects.push(stmt1, stmt2)
61
- } else {
62
- const stmt = b.$effect([
63
- b.setAttribute(elementId, b.literal(node.name), b.template(template))
64
- ])
65
- ctx.state.effects.push(stmt)
66
- }
67
- } else if (isEmpty(template)) {
93
+ if (isEmpty(template)) {
68
94
  appendText(ctx.state.template, ` ${node.name}`)
69
95
  } else {
70
96
  appendText(ctx.state.template, ` ${node.name}="${template.text[0]}"`)
@@ -9,13 +9,10 @@ export function CustomElement(node, ctx) {
9
9
  }
10
10
 
11
11
  let elementId
12
- function getElementId() {
13
- if (!elementId) {
14
- elementId = nextElementId(ctx)
15
- const stmt = b.declaration(elementId, pathStmt(ctx))
16
- ctx.state.init.elem.push(stmt)
17
- }
18
- return elementId
12
+ if (node.metadata?.hasExpressionAttribute) {
13
+ elementId = nextElementId(ctx)
14
+ const stmt = b.declaration(elementId, pathStmt(ctx, node))
15
+ ctx.state.init.elem.push(stmt)
19
16
  }
20
17
 
21
18
  if (node.metadata?.isModule) {
@@ -24,7 +21,11 @@ export function CustomElement(node, ctx) {
24
21
  appendText(ctx.state.template, '<')
25
22
  appendExpression(ctx.state.template, b.$name(moduleId))
26
23
  for (const attribute of attributes) {
27
- ctx.visit(attribute, { ...ctx.state, getElementId, getModuleId: () => moduleId })
24
+ ctx.visit(attribute, {
25
+ ...ctx.state,
26
+ getElementId: () => elementId,
27
+ getModuleId: () => moduleId
28
+ })
28
29
  }
29
30
  appendText(ctx.state.template, '>')
30
31
  ctx.visit(node.fragment)
@@ -34,7 +35,7 @@ export function CustomElement(node, ctx) {
34
35
  } else {
35
36
  appendText(ctx.state.template, `<${node.name}`)
36
37
  for (const attribute of attributes) {
37
- ctx.visit(attribute, { ...ctx.state, getElementId })
38
+ ctx.visit(attribute, { ...ctx.state, getElementId: () => elementId })
38
39
  }
39
40
  appendText(ctx.state.template, '>')
40
41
  ctx.visit(node.fragment)
@@ -4,13 +4,23 @@ import { nextElementId, pathStmt } from '../context.js'
4
4
 
5
5
  export function EachBlock(node, ctx) {
6
6
  const template = { text: [''], expressions: [] }
7
- const init = { elem: [], text: [], binding: [] }
7
+ const init = { elem: [], text: [] }
8
+ const binds = []
8
9
  const effects = []
9
10
  const animates = []
10
- const handlers = []
11
+ const eventListeners = []
11
12
  const blocks = []
12
13
 
13
- ctx.visit(node.body, { ...ctx.state, template, init, effects, animates, handlers, blocks })
14
+ ctx.visit(node.body, {
15
+ ...ctx.state,
16
+ template,
17
+ init,
18
+ binds,
19
+ effects,
20
+ animates,
21
+ eventListeners,
22
+ blocks
23
+ })
14
24
 
15
25
  const stmts1 = [
16
26
  b.declaration('template', b.createElement('template')),
@@ -19,10 +29,10 @@ export function EachBlock(node, ctx) {
19
29
  const stmts2 = [
20
30
  ...init.elem,
21
31
  ...init.text,
22
- ...init.binding,
32
+ ...binds,
23
33
  ...effects,
24
34
  ...animates,
25
- ...handlers,
35
+ ...eventListeners,
26
36
  ...blocks
27
37
  ]
28
38
  const stmt3 = b.insertBefore('anchor', b.member('template', 'content'))
@@ -1,6 +1,6 @@
1
1
  import * as b from '../../builders.js'
2
2
  import { appendText } from '../../utils/template.js'
3
- import { nextBindingId, nextElementId, pathStmt } from '../context.js'
3
+ import { nextElementId, pathStmt } from '../context.js'
4
4
 
5
5
  export function Element(node, ctx) {
6
6
  let attributes = node.attributes
@@ -8,41 +8,22 @@ export function Element(node, ctx) {
8
8
  attributes = [...attributes, b.attribute('class', '', { isScoped: true })]
9
9
  }
10
10
 
11
- const changed = []
12
-
13
11
  let elementId
14
- function getElementId() {
15
- if (!elementId) {
16
- elementId = nextElementId(ctx)
17
- const stmt = b.declaration(elementId, pathStmt(ctx))
18
- ctx.state.init.elem.push(stmt)
19
- }
20
- return elementId
21
- }
22
-
23
- let bindingId
24
- function getBindingId() {
25
- if (!bindingId && node.metadata?.hasBinding) {
26
- bindingId = nextBindingId(ctx)
27
- const stmt = b.declaration(bindingId, {
28
- ...node.metadata?.bindExpression,
29
- arguments: [getElementId(), ...node.metadata?.bindExpression.arguments]
30
- })
31
- ctx.state.init.binding.push(stmt)
32
- }
33
- return bindingId
12
+ if (node.metadata?.hasExpressionAttribute) {
13
+ elementId = nextElementId(ctx)
14
+ const stmt = b.declaration(elementId, pathStmt(ctx, node))
15
+ ctx.state.init.elem.push(stmt)
34
16
  }
35
17
 
36
18
  appendText(ctx.state.template, `<${node.name}`)
37
19
  for (const attribute of attributes) {
38
- ctx.visit(attribute, { ...ctx.state, getElementId, getBindingId, changed })
20
+ ctx.visit(attribute, { ...ctx.state, getElementId: () => elementId })
39
21
  }
40
- appendText(ctx.state.template, '>')
41
- ctx.visit(node.fragment, { ...ctx.state, getElementId })
42
- appendText(ctx.state.template, `</${node.name}>`)
43
-
44
- if (changed.length > 0) {
45
- const stmt = b.addEventListener(bindingId, 'changed', changed)
46
- ctx.state.handlers.push(stmt)
22
+ if (node.metadata?.isVoid) {
23
+ appendText(ctx.state.template, '/>')
24
+ } else {
25
+ appendText(ctx.state.template, '>')
26
+ ctx.visit(node.fragment, { ...ctx.state, getElementId: () => elementId })
27
+ appendText(ctx.state.template, `</${node.name}>`)
47
28
  }
48
29
  }
@@ -6,13 +6,23 @@ export function IfBlock(node, ctx) {
6
6
  function branchStmt(node, hasElseif = false) {
7
7
  if (node) {
8
8
  const template = { text: [''], expressions: [] }
9
- const init = { elem: [], text: [], binding: [] }
9
+ const init = { elem: [], text: [] }
10
+ const binds = []
10
11
  const effects = []
11
12
  const animates = []
12
- const handlers = []
13
+ const eventListeners = []
13
14
  const blocks = []
14
15
 
15
- ctx.visit(node, { ...ctx.state, template, init, effects, animates, handlers, blocks })
16
+ ctx.visit(node, {
17
+ ...ctx.state,
18
+ template,
19
+ init,
20
+ binds,
21
+ effects,
22
+ animates,
23
+ eventListeners,
24
+ blocks
25
+ })
16
26
 
17
27
  if (!hasElseif) {
18
28
  const stmts1 = [
@@ -22,10 +32,10 @@ export function IfBlock(node, ctx) {
22
32
  const stmts2 = [
23
33
  ...init.elem,
24
34
  ...init.text,
25
- ...init.binding,
35
+ ...binds,
26
36
  ...effects,
27
37
  ...animates,
28
- ...handlers,
38
+ ...eventListeners,
29
39
  ...blocks
30
40
  ]
31
41
  const stmt3 = b.insertBefore('anchor', b.member('template', 'content'))
@@ -3,10 +3,11 @@ import * as b from '../../builders.js'
3
3
  export function Template(node, ctx) {
4
4
  const style = { text: [''], expressions: [] }
5
5
  const template = { text: [''], expressions: [] }
6
- const init = { elem: [], text: [], binding: [] }
6
+ const init = { elem: [], text: [] }
7
+ const binds = []
7
8
  const effects = []
8
9
  const animates = []
9
- const handlers = []
10
+ const eventListeners = []
10
11
  const blocks = []
11
12
 
12
13
  ctx.visit(node.fragment, {
@@ -14,9 +15,10 @@ export function Template(node, ctx) {
14
15
  style,
15
16
  template,
16
17
  init,
18
+ binds,
17
19
  effects,
18
20
  animates,
19
- handlers,
21
+ eventListeners,
20
22
  blocks
21
23
  })
22
24
 
@@ -29,10 +31,10 @@ export function Template(node, ctx) {
29
31
  const stmts2 = [
30
32
  ...init.elem,
31
33
  ...init.text,
32
- ...init.binding,
34
+ ...binds,
33
35
  ...effects,
34
36
  ...animates,
35
- ...handlers,
37
+ ...eventListeners,
36
38
  ...blocks
37
39
  ]
38
40
  const stmt3 = b.replaceChildren(rootId, b.member('template', 'content'))