@katabatic/compiler 1.0.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 (78) hide show
  1. package/README.md +3 -0
  2. package/package.json +32 -0
  3. package/src/analyse/context.js +31 -0
  4. package/src/analyse/index.js +47 -0
  5. package/src/analyse/visitors/AssignmentExpression.js +14 -0
  6. package/src/analyse/visitors/Attribute.js +11 -0
  7. package/src/analyse/visitors/CallExpression.js +36 -0
  8. package/src/analyse/visitors/ClassBody.js +20 -0
  9. package/src/analyse/visitors/ClassDeclaration.js +5 -0
  10. package/src/analyse/visitors/CustomElement.js +10 -0
  11. package/src/analyse/visitors/EachBlock.js +10 -0
  12. package/src/analyse/visitors/Element.js +16 -0
  13. package/src/analyse/visitors/ExpressionTag.js +9 -0
  14. package/src/analyse/visitors/IfBlock.js +16 -0
  15. package/src/analyse/visitors/ImportDeclaration.js +35 -0
  16. package/src/analyse/visitors/MethodDefinition.js +17 -0
  17. package/src/analyse/visitors/Program.js +24 -0
  18. package/src/analyse/visitors/PropertyDefinition.js +12 -0
  19. package/src/analyse/visitors/Selector.js +23 -0
  20. package/src/analyse/visitors/Template.js +14 -0
  21. package/src/builders.js +1663 -0
  22. package/src/checkers.js +120 -0
  23. package/src/css-matcher.js +100 -0
  24. package/src/css-transform.js +21 -0
  25. package/src/css.js +65 -0
  26. package/src/exp-matcher.js +65 -0
  27. package/src/id-matcher.js +17 -0
  28. package/src/index.js +19 -0
  29. package/src/module-matcher.js +17 -0
  30. package/src/parser/attributes.js +66 -0
  31. package/src/parser/each-block.js +73 -0
  32. package/src/parser/element.js +115 -0
  33. package/src/parser/expression.js +44 -0
  34. package/src/parser/if-block.js +71 -0
  35. package/src/parser/index.js +10 -0
  36. package/src/parser/parser.js +259 -0
  37. package/src/parser/root.js +33 -0
  38. package/src/parser/script.js +59 -0
  39. package/src/parser/style.js +57 -0
  40. package/src/parser/template.js +30 -0
  41. package/src/parser/tokentype.js +75 -0
  42. package/src/router/html.js +18 -0
  43. package/src/router/index.js +130 -0
  44. package/src/transform/context.js +74 -0
  45. package/src/transform/index.js +52 -0
  46. package/src/transform/static/index.js +40 -0
  47. package/src/transform/static/visitors/Attribute.js +27 -0
  48. package/src/transform/static/visitors/EachBlock.js +23 -0
  49. package/src/transform/static/visitors/Element.js +17 -0
  50. package/src/transform/static/visitors/ExpressionTag.js +6 -0
  51. package/src/transform/static/visitors/IfBlock.js +28 -0
  52. package/src/transform/static/visitors/Program.js +10 -0
  53. package/src/transform/static/visitors/Script.js +9 -0
  54. package/src/transform/static/visitors/SlotElement.js +8 -0
  55. package/src/transform/static/visitors/Style.js +9 -0
  56. package/src/transform/static/visitors/Template.js +12 -0
  57. package/src/transform/static/visitors/Text.js +5 -0
  58. package/src/transform/visitors/AssignmentExpression.js +7 -0
  59. package/src/transform/visitors/Attribute.js +79 -0
  60. package/src/transform/visitors/CallExpression.js +17 -0
  61. package/src/transform/visitors/ClassBody.js +36 -0
  62. package/src/transform/visitors/CustomElement.js +43 -0
  63. package/src/transform/visitors/EachBlock.js +39 -0
  64. package/src/transform/visitors/Element.js +49 -0
  65. package/src/transform/visitors/ExpressionTag.js +6 -0
  66. package/src/transform/visitors/Fragment.js +42 -0
  67. package/src/transform/visitors/Identifier.js +10 -0
  68. package/src/transform/visitors/IfBlock.js +55 -0
  69. package/src/transform/visitors/ImportDeclaration.js +21 -0
  70. package/src/transform/visitors/MethodDefinition.js +115 -0
  71. package/src/transform/visitors/Program.js +65 -0
  72. package/src/transform/visitors/PropertyDefinition.js +7 -0
  73. package/src/transform/visitors/Selector.js +41 -0
  74. package/src/transform/visitors/Style.js +11 -0
  75. package/src/transform/visitors/Template.js +44 -0
  76. package/src/transform/visitors/Text.js +5 -0
  77. package/src/utils/misc.js +9 -0
  78. package/src/utils/template.js +15 -0
@@ -0,0 +1,130 @@
1
+ import { print } from 'esrap'
2
+ import * as b from '../builders.js'
3
+
4
+ export * from './html.js'
5
+
6
+ export function createRouter(registry) {
7
+ const routes = b.object()
8
+ const imports = { page: [], layout: [] }
9
+
10
+ for (const entry of Object.values(registry)) {
11
+ if (entry.isPage) {
12
+ const node = makeNode(routes, entry.route)
13
+ const pageId = b.id(`page_${imports.page.length + 1}`)
14
+ imports.page.push(b.importNamespace(pageId, entry.routerImport))
15
+ node.properties.push(b.property('_page', pageId))
16
+ continue
17
+ }
18
+
19
+ if (entry.isLayout) {
20
+ const node = makeNode(routes, entry.route)
21
+ const layoutId = b.id(`layout_${imports.layout.length + 1}`)
22
+ imports.layout.push(b.importNamespace(layoutId, entry.routerImport))
23
+ node.properties.push(b.property('_layout', layoutId))
24
+ continue
25
+ }
26
+
27
+ if (entry.isIndex) {
28
+ imports.index = b.importSpecifier('renderIndex', entry.routerImport)
29
+ continue
30
+ }
31
+ }
32
+
33
+ return printRouter({
34
+ imports: print(b.program([...imports.layout, ...imports.page, imports.index])),
35
+ routes: print(b.program([routes]))
36
+ })
37
+ }
38
+
39
+ function makeNode(node, path) {
40
+ const tokens = path.split('/').filter((t) => t !== '')
41
+
42
+ for (let token of tokens) {
43
+ const param = token.startsWith('[') && token.endsWith(']') ? token.slice(1, -1) : undefined
44
+ token = param ? '*' : token
45
+
46
+ const prop = node.properties.find((p) => p.key.value === token)
47
+ if (prop) {
48
+ node = prop.value
49
+ } else {
50
+ const child = b.object()
51
+ if (param) {
52
+ child.properties.push(b.property('_param', b.literal(param)))
53
+ }
54
+ node.properties.push(b.property(b.literal(token), child))
55
+ node = child
56
+ }
57
+ }
58
+ return node
59
+ }
60
+
61
+ function printRouter({ imports, routes }) {
62
+ return {
63
+ code: `${imports.code}
64
+
65
+ const ROUTES = ${routes.code}
66
+
67
+ export async function route(path) {
68
+ const route = resolve(path)
69
+
70
+ if (route) {
71
+ const { modules, ...context } = route
72
+ const data = await load(modules, context)
73
+ const result = render(modules, data)
74
+
75
+ return renderIndex({ body: result })
76
+ }
77
+ }
78
+
79
+ function load(modules, context) {
80
+ const data = []
81
+ for (let i = 0; i < modules.length; i++) {
82
+ data.push(modules[i].load(context, data[i - 1]))
83
+ }
84
+ return Promise.all(data)
85
+ }
86
+
87
+ function render(modules, data) {
88
+ let result
89
+ for (let i = modules.length - 1; i >= 0; i--) {
90
+ result = modules[i].render(data[i], { default: result })
91
+ }
92
+ return result
93
+ }
94
+
95
+ function resolve(path) {
96
+ const modules = []
97
+ const params = {}
98
+ const route = []
99
+ const tokens = path.split('/').filter((t) => t !== '')
100
+
101
+ let node = ROUTES
102
+
103
+ if (node._layout) {
104
+ modules.push(node._layout)
105
+ }
106
+
107
+ for (const token of tokens) {
108
+ node = node[token] ?? node['*']
109
+ if (!node) break
110
+
111
+ if (node._layout) {
112
+ modules.push(node._layout)
113
+ }
114
+
115
+ if (node._param) {
116
+ params[node._param] = token
117
+ route.push(\`[\${node._param}]\`)
118
+ } else{
119
+ route.push(token)
120
+ }
121
+ }
122
+
123
+ if (node?._page) {
124
+ modules.push(node._page)
125
+ return { modules, route: \`/\${route.join('/')}\`, params }
126
+ }
127
+ }
128
+ `
129
+ }
130
+ }
@@ -0,0 +1,74 @@
1
+ import * as b from '../builders.js'
2
+
3
+ export function nextElementId(ctx) {
4
+ return b.id(`elem_${ctx.state.init.elem.length + 1}`)
5
+ }
6
+
7
+ export function nextTextId(ctx) {
8
+ return b.id(`text_${ctx.state.init.text.length + 1}`)
9
+ }
10
+
11
+ export function nextBlockId(ctx) {
12
+ return b.id(`block_${ctx.state.blocks.length + 1}`)
13
+ }
14
+
15
+ export function nextBindingId(ctx) {
16
+ return b.id(`binding_${ctx.state.init.binding.length + 1}`)
17
+ }
18
+
19
+ export function pathStmt(ctx, nodes) {
20
+ const subPath = []
21
+
22
+ for (const node of ctx.path.toReversed()) {
23
+ if (node.type === 'IfBlock' || node.type === 'EachBlock' || node.type === 'Template') break
24
+ subPath.unshift(node)
25
+ }
26
+
27
+ if (nodes) {
28
+ subPath.push(...(Array.isArray(nodes) ? nodes : [nodes]))
29
+ }
30
+
31
+ console.log(subPath)
32
+
33
+ let stmt = b.member('template', 'content')
34
+ let fragment
35
+ for (const node of subPath) {
36
+ switch (node.type) {
37
+ case 'Fragment':
38
+ fragment = node
39
+ break
40
+ case 'Text':
41
+ case 'ExpressionTag':
42
+ case 'Element':
43
+ case 'CustomElement':
44
+ case 'IfBlock':
45
+ case 'EachBlock':
46
+ stmt = b.sibling(b.child(stmt), position(fragment, node))
47
+ break
48
+ }
49
+ }
50
+ return stmt
51
+ }
52
+
53
+ function position(fragment, node) {
54
+ let position = -1
55
+
56
+ const nodes = fragment.nodes
57
+ for (let i = 0; i < nodes.length; i++) {
58
+ if (
59
+ (nodes[i].type === 'ExpressionTag' || nodes[i].type === 'Text') &&
60
+ (nodes[i - 1]?.type === 'ExpressionTag' || nodes[i - 1]?.type === 'Text')
61
+ ) {
62
+ // Text and ExpressionTag siblings are collapsed in one node in the html template
63
+ continue
64
+ }
65
+
66
+ position++
67
+ if (nodes[i] === node) break
68
+ }
69
+ return position
70
+ }
71
+
72
+ export function getProgram(ctx) {
73
+ return ctx.path[0]
74
+ }
@@ -0,0 +1,52 @@
1
+ import { walk } from 'zimmerframe'
2
+ import { Program } from './visitors/Program.js'
3
+ import { Identifier } from './visitors/Identifier.js'
4
+ import { ExpressionTag } from './visitors/ExpressionTag.js'
5
+ import { Template } from './visitors/Template.js'
6
+ import { MethodDefinition } from './visitors/MethodDefinition.js'
7
+ import { Element } from './visitors/Element.js'
8
+ import { Text } from './visitors/Text.js'
9
+ import { Fragment } from './visitors/Fragment.js'
10
+ import { Style } from './visitors/Style.js'
11
+ import { Attribute } from './visitors/Attribute.js'
12
+ import { ClassBody } from './visitors/ClassBody.js'
13
+ import { ImportDeclaration } from './visitors/ImportDeclaration.js'
14
+ import { CssTree, Selector } from './visitors/Selector.js'
15
+ import { CallExpression } from './visitors/CallExpression.js'
16
+ import { IfBlock } from './visitors/IfBlock.js'
17
+ import { EachBlock } from './visitors/EachBlock.js'
18
+ import { CustomElement } from './visitors/CustomElement.js'
19
+ import { AssignmentExpression } from './visitors/AssignmentExpression.js'
20
+ import { PropertyDefinition } from './visitors/PropertyDefinition.js'
21
+
22
+ const templateVisitors = {
23
+ Template,
24
+ Identifier,
25
+ ExpressionTag,
26
+ Element,
27
+ CustomElement,
28
+ Text,
29
+ Fragment,
30
+ Style,
31
+ Attribute,
32
+ CallExpression,
33
+ IfBlock,
34
+ EachBlock,
35
+ Selector,
36
+ ...CssTree
37
+ }
38
+
39
+ const scriptVisitors = {
40
+ Program,
41
+ MethodDefinition,
42
+ ClassBody,
43
+ ImportDeclaration,
44
+ CallExpression,
45
+ AssignmentExpression,
46
+ PropertyDefinition
47
+ }
48
+
49
+ export function transform(ast, analysis, context) {
50
+ const template = walk(ast.template, { analysis, context }, templateVisitors)
51
+ return walk(ast.script.content, { analysis, template, context }, scriptVisitors)
52
+ }
@@ -0,0 +1,40 @@
1
+ import { walk } from 'zimmerframe'
2
+ import { Element } from './visitors/Element.js'
3
+ import { Text } from './visitors/Text.js'
4
+ import { Program } from './visitors/Program.js'
5
+ import { Template } from './visitors/Template.js'
6
+ import { ExpressionTag } from './visitors/ExpressionTag.js'
7
+ import { SlotElement } from './visitors/SlotElement.js'
8
+ import { Attribute } from './visitors/Attribute.js'
9
+ import { Style } from './visitors/Style.js'
10
+ import { Script } from './visitors/Script.js'
11
+ import { CssTree, Selector } from '../visitors/Selector.js'
12
+ import { CallExpression } from '../visitors/CallExpression.js'
13
+ import { IfBlock } from './visitors/IfBlock.js'
14
+ import { EachBlock } from './visitors/EachBlock.js'
15
+
16
+ const templateVisitors = {
17
+ Attribute,
18
+ Element,
19
+ SlotElement,
20
+ Text,
21
+ ExpressionTag,
22
+ Template,
23
+ Style,
24
+ Script,
25
+ CallExpression,
26
+ IfBlock,
27
+ EachBlock,
28
+ // common visitors
29
+ Selector,
30
+ ...CssTree
31
+ }
32
+
33
+ const scriptVisitors = {
34
+ Program
35
+ }
36
+
37
+ export function transform(ast, analysis, context) {
38
+ const template = walk(ast.template, { analysis, context }, templateVisitors)
39
+ return walk(ast.script.content, { analysis, template, context }, scriptVisitors)
40
+ }
@@ -0,0 +1,27 @@
1
+ import * as b from '../../../builders.js'
2
+ import { append } from '../../../utils/misc.js'
3
+ import { clx } from '../../../css.js'
4
+
5
+ export function Attribute(node, ctx) {
6
+ let value = node.value
7
+ if (node.name === 'id' && node.metadata?.isScoped) {
8
+ if (value[0].type === 'Text') {
9
+ value = [b.text(`${value[0].data}-${ctx.state.context.hash}`)]
10
+ } else {
11
+ value = [...value, b.text(`-${ctx.state.context.hash}`)]
12
+ }
13
+ }
14
+ if (node.name === 'class' && node.metadata?.isScoped) {
15
+ if (value[0].type === 'Text') {
16
+ value = [b.text(clx(value[0].data, `ktb-${ctx.state.context.hash}`))]
17
+ } else {
18
+ value = [...value, b.text(` ktb-${ctx.state.context.hash}`)]
19
+ }
20
+ }
21
+
22
+ append(ctx.state.text, ` ${node.name}="`)
23
+ for (const val of value) {
24
+ ctx.visit(val)
25
+ }
26
+ append(ctx.state.text, `"`)
27
+ }
@@ -0,0 +1,23 @@
1
+ import * as b from '../../../builders.js'
2
+ import { nextBlockId } from '../../context.js'
3
+
4
+ export function EachBlock(node, ctx) {
5
+ const text = ['']
6
+ const expressions = []
7
+
8
+ ctx.visit(node.body, { ...ctx.state, text, expressions })
9
+
10
+ const blockId = nextBlockId(ctx)
11
+ const resultId = b.id('result_')
12
+ const block = b.func(blockId, [
13
+ b.declaration(resultId, b.literal(''), 'let'),
14
+ b.forStmt(node.context, node.expression, [
15
+ b.assignment(resultId, b.template({ text, expressions }), '+=')
16
+ ]),
17
+ b.returnStmt(resultId)
18
+ ])
19
+
20
+ ctx.state.blocks.push(block)
21
+ ctx.state.text.push('')
22
+ ctx.state.expressions.push(b.call(blockId))
23
+ }
@@ -0,0 +1,17 @@
1
+ import * as b from '../../../builders.js'
2
+ import { append } from '../../../utils/misc.js'
3
+
4
+ export function Element(node, ctx) {
5
+ let attributes = node.attributes
6
+ if (node.metadata?.isScoped && !node.metadata?.hasClass) {
7
+ attributes = [...attributes, b.attribute('class', '', { isScoped: true })]
8
+ }
9
+
10
+ append(ctx.state.text, `<${node.name}`)
11
+ for (const attribute of attributes) {
12
+ ctx.visit(attribute)
13
+ }
14
+ append(ctx.state.text, '>')
15
+ ctx.visit(node.fragment)
16
+ append(ctx.state.text, `</${node.name}>`)
17
+ }
@@ -0,0 +1,6 @@
1
+ export function ExpressionTag(node, ctx) {
2
+ node = ctx.next() ?? node
3
+
4
+ ctx.state.text.push('')
5
+ ctx.state.expressions.push(node.expression)
6
+ }
@@ -0,0 +1,28 @@
1
+ import * as b from '../../../builders.js'
2
+ import { nextBlockId } from '../../context.js'
3
+
4
+ export function IfBlock(node, ctx) {
5
+ const text = ['']
6
+ const expressions = []
7
+
8
+ ctx.visit(node.consequent, { ...ctx.state, text, expressions })
9
+ const stmt1 = b.returnStmt(b.template({ text, expressions }))
10
+
11
+ let stmt2
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(''))
20
+ }
21
+
22
+ const blockId = nextBlockId(ctx)
23
+ const block = b.func(blockId, [b.ifStmt(node.test, [stmt1]), stmt2])
24
+
25
+ ctx.state.blocks.push(block)
26
+ ctx.state.text.push('')
27
+ ctx.state.expressions.push(b.call(blockId))
28
+ }
@@ -0,0 +1,10 @@
1
+ import * as b from '../../../builders.js'
2
+
3
+ export function Program(node, ctx) {
4
+ node = ctx.next() ?? node
5
+
6
+ const stmt = b.exportDec(
7
+ b.render([...ctx.state.template.blocks, b.returnStmt(ctx.state.template.template)])
8
+ )
9
+ return { ...node, body: [...node.body, stmt] }
10
+ }
@@ -0,0 +1,9 @@
1
+ import { print } from 'esrap'
2
+ import { append } from '../../../utils/misc.js'
3
+
4
+ export function Script(node, ctx) {
5
+ node = ctx.next() ?? node
6
+
7
+ const { code } = print(node.content)
8
+ append(ctx.state.text, `<script type="module">${code}</script>`)
9
+ }
@@ -0,0 +1,8 @@
1
+ import * as b from '../../../builders.js'
2
+
3
+ export function SlotElement(node, ctx) {
4
+ node = ctx.next() ?? node
5
+
6
+ ctx.state.text.push('')
7
+ ctx.state.expressions.push(b.member(b.id('slot'), b.id('default')))
8
+ }
@@ -0,0 +1,9 @@
1
+ import { generate } from 'css-tree'
2
+ import { append } from '../../../utils/misc.js'
3
+
4
+ export function Style(node, ctx) {
5
+ node = ctx.next() ?? node
6
+
7
+ const css = generate(node.content)
8
+ append(ctx.state.text, `<style>${css}</style>`)
9
+ }
@@ -0,0 +1,12 @@
1
+ import * as b from '../../../builders.js'
2
+
3
+ export function Template(node, ctx) {
4
+ const text = ['']
5
+ const expressions = []
6
+ const blocks = []
7
+
8
+ ctx.visit(node.fragment, { ...ctx.state, text, expressions, blocks })
9
+
10
+ const template = b.template({ text, expressions })
11
+ return { type: 'TemplateMod', template, blocks }
12
+ }
@@ -0,0 +1,5 @@
1
+ import { append } from '../../../utils/misc.js'
2
+
3
+ export function Text(node, ctx) {
4
+ append(ctx.state.text, node.data)
5
+ }
@@ -0,0 +1,7 @@
1
+ import * as b from '../../builders.js'
2
+
3
+ export function AssignmentExpression(node) {
4
+ if (node.metadata?.isProperty && !node.metadata?.isPrivate) {
5
+ return { ...node, right: b.$$init(node.left.property.name, node.right) }
6
+ }
7
+ }
@@ -0,0 +1,79 @@
1
+ import * as b from '../../builders.js'
2
+ import { clx } from '../../css.js'
3
+ import { appendText, hasExpression } from '../../utils/template.js'
4
+
5
+ export function Attribute(node, ctx) {
6
+ let value = node.value
7
+ if (node.name === 'id' && node.metadata?.isScoped) {
8
+ if (value[0].type === 'Text') {
9
+ value = [b.text(`${value[0].data}-${ctx.state.context.hash}`)]
10
+ } else {
11
+ value = [...value, b.text(`-${ctx.state.context.hash}`)]
12
+ }
13
+ }
14
+ if (node.name === 'class' && node.metadata?.isScoped) {
15
+ if (value[0].type === 'Text') {
16
+ value = [b.text(clx(value[0].data, `ktb-${ctx.state.context.hash}`))]
17
+ } else {
18
+ value = [...value, b.text(` ktb-${ctx.state.context.hash}`)]
19
+ }
20
+ }
21
+
22
+ const template = { text: [''], expressions: [] }
23
+ for (const val of value) {
24
+ ctx.visit(val, { ...ctx.state, template })
25
+ }
26
+
27
+ if (hasExpression(template)) {
28
+ // expression attribute
29
+ const elementId = ctx.state.getElementId()
30
+ const moduleId = ctx.state.getModuleId?.()
31
+ const bindingId = ctx.state.getBindingId?.()
32
+
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([
51
+ b.$set(moduleId, elementId, node.name, template.expressions[0])
52
+ ])
53
+ ctx.state.effects.push(stmt)
54
+ } else if (bindingId) {
55
+ const stmt = b.$effect([
56
+ b.logical(
57
+ '||',
58
+ b.$setBindingProp(bindingId, node.name, template.expressions[0]),
59
+ b.setAttribute(elementId, b.literal(node.name), template.expressions[0])
60
+ )
61
+ ])
62
+ ctx.state.effects.push(stmt)
63
+
64
+ const stmt2 = b.$getBindingProp(
65
+ bindingId,
66
+ node.name,
67
+ b.assignment(template.expressions[0], b.id('v'))
68
+ )
69
+ ctx.state.changed.push(stmt2)
70
+ } else {
71
+ const stmt = b.$effect([
72
+ b.setAttribute(elementId, b.literal(node.name), template.expressions[0])
73
+ ])
74
+ ctx.state.effects.push(stmt)
75
+ }
76
+ } else {
77
+ appendText(ctx.state.template, ` ${node.name}="${template.text[0] ?? 'true'}"`)
78
+ }
79
+ }
@@ -0,0 +1,17 @@
1
+ import * as b from '../../builders.js'
2
+ import { transformQuerySelector } from '../../css-transform.js'
3
+
4
+ export function CallExpression(node, ctx) {
5
+ ctx.next()
6
+
7
+ if (node.metadata?.isGetElementById && node.metadata?.isScoped) {
8
+ const id = node.arguments[0].value
9
+ return { ...node, arguments: [b.literal(id + '-' + ctx.state.context.hash)] }
10
+ }
11
+
12
+ if (node.metadata?.isQuerySelector && node.metadata?.isScoped) {
13
+ const selectorList = node.metadata.selectorList
14
+ const query = transformQuerySelector(selectorList, ctx.state.context)
15
+ return { ...node, arguments: [b.literal(query)] }
16
+ }
17
+ }
@@ -0,0 +1,36 @@
1
+ import * as b from '../../builders.js'
2
+
3
+ export function ClassBody(node, ctx) {
4
+ node = ctx.next() ?? node
5
+
6
+ const stmts1 = []
7
+ const stmts2 = []
8
+ if (!node.metadata?.hasConstructor) {
9
+ const stmt = ctx.visit(b.constructor())
10
+ stmts1.push(stmt)
11
+ }
12
+
13
+ if (!node.metadata?.hasConnectedCallbackMethod) {
14
+ const stmt = ctx.visit(b.connectedCallback())
15
+ stmts2.push(stmt)
16
+ }
17
+
18
+ if (!node.metadata?.hasDisconnectedCallbackMethod) {
19
+ const stmt = ctx.visit(b.disconnectedCallback())
20
+ stmts2.push(stmt)
21
+ }
22
+
23
+ if (!node.metadata?.hasAttributeChangedCallback && node.metadata?.hasObservedAttributes) {
24
+ const stmt = ctx.visit(b.attributeChangedCallback())
25
+ stmts2.push(stmt)
26
+ }
27
+
28
+ if (!node.metadata?.hasGetAttribute && node.metadata?.hasObservedAttributes) {
29
+ const stmt = ctx.visit(b.getAttribute())
30
+ stmts2.push(stmt)
31
+ }
32
+
33
+ if (stmts2.length >= 0) {
34
+ return { ...node, body: [...stmts1, ...node.body, ...stmts2] }
35
+ }
36
+ }
@@ -0,0 +1,43 @@
1
+ import * as b from '../../builders.js'
2
+ import { appendExpression, appendText } from '../../utils/template.js'
3
+ import { nextElementId, pathStmt } from '../context.js'
4
+
5
+ export function CustomElement(node, ctx) {
6
+ let attributes = node.attributes
7
+ if (node.metadata?.isScoped && !node.metadata?.hasClass) {
8
+ attributes = [...attributes, b.attribute('class', '', { isScoped: true })]
9
+ }
10
+
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
19
+ }
20
+
21
+ if (node.metadata?.isModule) {
22
+ const moduleId = b.id(`$Module_${node.metadata.index + 1}`)
23
+
24
+ appendText(ctx.state.template, '<')
25
+ appendExpression(ctx.state.template, b.$name(moduleId))
26
+ for (const attribute of attributes) {
27
+ ctx.visit(attribute, { ...ctx.state, getElementId, getModuleId: () => moduleId })
28
+ }
29
+ appendText(ctx.state.template, '>')
30
+ ctx.visit(node.fragment)
31
+ appendText(ctx.state.template, '</')
32
+ appendExpression(ctx.state.template, b.$name(moduleId))
33
+ appendText(ctx.state.template, '>')
34
+ } else {
35
+ appendText(ctx.state.template, `<${node.name}`)
36
+ for (const attribute of attributes) {
37
+ ctx.visit(attribute, { ...ctx.state, getElementId })
38
+ }
39
+ appendText(ctx.state.template, '>')
40
+ ctx.visit(node.fragment)
41
+ appendText(ctx.state.template, `</${node.name}>`)
42
+ }
43
+ }