@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.
- package/README.md +3 -0
- package/package.json +32 -0
- package/src/analyse/context.js +31 -0
- package/src/analyse/index.js +47 -0
- package/src/analyse/visitors/AssignmentExpression.js +14 -0
- package/src/analyse/visitors/Attribute.js +11 -0
- package/src/analyse/visitors/CallExpression.js +36 -0
- package/src/analyse/visitors/ClassBody.js +20 -0
- package/src/analyse/visitors/ClassDeclaration.js +5 -0
- package/src/analyse/visitors/CustomElement.js +10 -0
- package/src/analyse/visitors/EachBlock.js +10 -0
- package/src/analyse/visitors/Element.js +16 -0
- package/src/analyse/visitors/ExpressionTag.js +9 -0
- package/src/analyse/visitors/IfBlock.js +16 -0
- package/src/analyse/visitors/ImportDeclaration.js +35 -0
- package/src/analyse/visitors/MethodDefinition.js +17 -0
- package/src/analyse/visitors/Program.js +24 -0
- package/src/analyse/visitors/PropertyDefinition.js +12 -0
- package/src/analyse/visitors/Selector.js +23 -0
- package/src/analyse/visitors/Template.js +14 -0
- package/src/builders.js +1663 -0
- package/src/checkers.js +120 -0
- package/src/css-matcher.js +100 -0
- package/src/css-transform.js +21 -0
- package/src/css.js +65 -0
- package/src/exp-matcher.js +65 -0
- package/src/id-matcher.js +17 -0
- package/src/index.js +19 -0
- package/src/module-matcher.js +17 -0
- package/src/parser/attributes.js +66 -0
- package/src/parser/each-block.js +73 -0
- package/src/parser/element.js +115 -0
- package/src/parser/expression.js +44 -0
- package/src/parser/if-block.js +71 -0
- package/src/parser/index.js +10 -0
- package/src/parser/parser.js +259 -0
- package/src/parser/root.js +33 -0
- package/src/parser/script.js +59 -0
- package/src/parser/style.js +57 -0
- package/src/parser/template.js +30 -0
- package/src/parser/tokentype.js +75 -0
- package/src/router/html.js +18 -0
- package/src/router/index.js +130 -0
- package/src/transform/context.js +74 -0
- package/src/transform/index.js +52 -0
- package/src/transform/static/index.js +40 -0
- package/src/transform/static/visitors/Attribute.js +27 -0
- package/src/transform/static/visitors/EachBlock.js +23 -0
- package/src/transform/static/visitors/Element.js +17 -0
- package/src/transform/static/visitors/ExpressionTag.js +6 -0
- package/src/transform/static/visitors/IfBlock.js +28 -0
- package/src/transform/static/visitors/Program.js +10 -0
- package/src/transform/static/visitors/Script.js +9 -0
- package/src/transform/static/visitors/SlotElement.js +8 -0
- package/src/transform/static/visitors/Style.js +9 -0
- package/src/transform/static/visitors/Template.js +12 -0
- package/src/transform/static/visitors/Text.js +5 -0
- package/src/transform/visitors/AssignmentExpression.js +7 -0
- package/src/transform/visitors/Attribute.js +79 -0
- package/src/transform/visitors/CallExpression.js +17 -0
- package/src/transform/visitors/ClassBody.js +36 -0
- package/src/transform/visitors/CustomElement.js +43 -0
- package/src/transform/visitors/EachBlock.js +39 -0
- package/src/transform/visitors/Element.js +49 -0
- package/src/transform/visitors/ExpressionTag.js +6 -0
- package/src/transform/visitors/Fragment.js +42 -0
- package/src/transform/visitors/Identifier.js +10 -0
- package/src/transform/visitors/IfBlock.js +55 -0
- package/src/transform/visitors/ImportDeclaration.js +21 -0
- package/src/transform/visitors/MethodDefinition.js +115 -0
- package/src/transform/visitors/Program.js +65 -0
- package/src/transform/visitors/PropertyDefinition.js +7 -0
- package/src/transform/visitors/Selector.js +41 -0
- package/src/transform/visitors/Style.js +11 -0
- package/src/transform/visitors/Template.js +44 -0
- package/src/transform/visitors/Text.js +5 -0
- package/src/utils/misc.js +9 -0
- package/src/utils/template.js +15 -0
package/README.md
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@katabatic/compiler",
|
|
3
|
+
"license": "MIT",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/katabatic-js/katabatic.git",
|
|
9
|
+
"directory": "packages/compiler"
|
|
10
|
+
},
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"import": "./src/index.js"
|
|
14
|
+
},
|
|
15
|
+
"./router": {
|
|
16
|
+
"import": "./src/router/index.js"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@types/css-tree": "^2.3.10",
|
|
21
|
+
"@types/node": "^22.15.21",
|
|
22
|
+
"typescript": "~5.7.2",
|
|
23
|
+
"vitest": "^3.2.4"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@web/dev-server": "^0.4.6",
|
|
27
|
+
"acorn": "^8.14.1",
|
|
28
|
+
"css-tree": "^3.1.0",
|
|
29
|
+
"esrap": "^1.4.6",
|
|
30
|
+
"zimmerframe": "^1.1.2"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export function getStyleSheet(ctx) {
|
|
2
|
+
const root = ctx.path[0]
|
|
3
|
+
return root.template.fragment.nodes.find((n) => n.type === 'Style')?.content
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export function getTemplate(ctx) {
|
|
7
|
+
const root = ctx.path[0]
|
|
8
|
+
return root.template
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function getProgram(ctx) {
|
|
12
|
+
const root = ctx.path[0]
|
|
13
|
+
return root.script.content
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function getScript(ctx) {
|
|
17
|
+
return ctx.path.toReversed().find((n) => n.type === 'Script')
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function getBlocks(ctx) {
|
|
21
|
+
return ctx.path.toReversed().filter((n) => n.type === 'EachBlock')
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function isInConstructor(ctx) {
|
|
25
|
+
for (const node of ctx.path.toReversed()) {
|
|
26
|
+
if (node.type === 'ArrowFunctionExpression') return false
|
|
27
|
+
if (node.type === 'FunctionDeclaration') return false
|
|
28
|
+
if (node.type === 'MethodDefinition') return node.kind === 'constructor'
|
|
29
|
+
}
|
|
30
|
+
return false
|
|
31
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { walk } from 'zimmerframe'
|
|
2
|
+
import { CallExpression } from './visitors/CallExpression.js'
|
|
3
|
+
import { ClassDeclaration } from './visitors/ClassDeclaration.js'
|
|
4
|
+
import { Template } from './visitors/Template.js'
|
|
5
|
+
import { Element } from './visitors/Element.js'
|
|
6
|
+
import { CssTree, Selector } from './visitors/Selector.js'
|
|
7
|
+
import { Attribute } from './visitors/Attribute.js'
|
|
8
|
+
import { IfBlock } from './visitors/IfBlock.js'
|
|
9
|
+
import { ClassBody } from './visitors/ClassBody.js'
|
|
10
|
+
import { Program } from './visitors/Program.js'
|
|
11
|
+
import { ExpressionTag } from './visitors/ExpressionTag.js'
|
|
12
|
+
import { EachBlock } from './visitors/EachBlock.js'
|
|
13
|
+
import { AssignmentExpression } from './visitors/AssignmentExpression.js'
|
|
14
|
+
import { MethodDefinition } from './visitors/MethodDefinition.js'
|
|
15
|
+
import { PropertyDefinition } from './visitors/PropertyDefinition.js'
|
|
16
|
+
import { ImportDeclaration } from './visitors/ImportDeclaration.js'
|
|
17
|
+
import { CustomElement } from './visitors/CustomElement.js'
|
|
18
|
+
|
|
19
|
+
const visitors = {
|
|
20
|
+
Program,
|
|
21
|
+
ImportDeclaration,
|
|
22
|
+
CallExpression,
|
|
23
|
+
ClassDeclaration,
|
|
24
|
+
ClassBody,
|
|
25
|
+
AssignmentExpression,
|
|
26
|
+
MethodDefinition,
|
|
27
|
+
PropertyDefinition,
|
|
28
|
+
Template,
|
|
29
|
+
Element,
|
|
30
|
+
CustomElement,
|
|
31
|
+
Attribute,
|
|
32
|
+
ExpressionTag,
|
|
33
|
+
IfBlock,
|
|
34
|
+
EachBlock,
|
|
35
|
+
Selector,
|
|
36
|
+
...CssTree
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function analyse(ast) {
|
|
40
|
+
const state = {
|
|
41
|
+
isStatic: false
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
walk(ast, state, visitors)
|
|
45
|
+
|
|
46
|
+
return state
|
|
47
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import * as is from '../../checkers.js'
|
|
2
|
+
import { isInConstructor } from '../context.js'
|
|
3
|
+
|
|
4
|
+
export function AssignmentExpression(node, ctx) {
|
|
5
|
+
if (is.thisMember(node.left) && isInConstructor(ctx)) {
|
|
6
|
+
const isPrivate = is.privateId(node.left.property)
|
|
7
|
+
const customElement = isPrivate ? ctx.state.customElement.private : ctx.state.customElement
|
|
8
|
+
|
|
9
|
+
node.metadata ??= {}
|
|
10
|
+
node.metadata.isPrivate = isPrivate
|
|
11
|
+
node.metadata.isProperty = true
|
|
12
|
+
customElement.properties.push(node.left.property.name)
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as is from '../../checkers.js'
|
|
2
|
+
import { matchQuerySelector } from '../../css-matcher.js'
|
|
3
|
+
import { matchElementById } from '../../id-matcher.js'
|
|
4
|
+
import { getTemplate } from '../context.js'
|
|
5
|
+
|
|
6
|
+
export function CallExpression(node, ctx) {
|
|
7
|
+
ctx.next()
|
|
8
|
+
|
|
9
|
+
if (is.getElementById(node)) {
|
|
10
|
+
const id = node.arguments[0].value
|
|
11
|
+
const template = getTemplate(ctx)
|
|
12
|
+
const isScoped = matchElementById(id, template)
|
|
13
|
+
|
|
14
|
+
node.metadata ??= {}
|
|
15
|
+
node.metadata.isGetElementById = true
|
|
16
|
+
node.metadata.isScoped = isScoped
|
|
17
|
+
return
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (is.querySelector(node)) {
|
|
21
|
+
const selector = node.arguments[0].value
|
|
22
|
+
const template = getTemplate(ctx)
|
|
23
|
+
const [isScoped, selectorList] = matchQuerySelector(selector, template)
|
|
24
|
+
|
|
25
|
+
node.metadata ??= {}
|
|
26
|
+
node.metadata.isQuerySelector= true
|
|
27
|
+
node.metadata.isScoped = isScoped
|
|
28
|
+
node.metadata.selectorList = selectorList
|
|
29
|
+
return
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (is.defineCustomElement(node)) {
|
|
33
|
+
const name = node.arguments[0].value
|
|
34
|
+
ctx.state.customElement.name = name
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import * as is from '../../checkers.js'
|
|
2
|
+
|
|
3
|
+
export function ClassBody(node, ctx) {
|
|
4
|
+
ctx.next()
|
|
5
|
+
|
|
6
|
+
const hasConstructor = node.body.some(is.constructor)
|
|
7
|
+
const hasConnectedCallbackMethod = node.body.some(is.connectedCallback)
|
|
8
|
+
const hasDisconnectedCallbackMethod = node.body.some(is.disconnectedCallback)
|
|
9
|
+
const hasGetAttribute = node.body.some(is.getAttribute)
|
|
10
|
+
const hasAttributeChangedCallback = node.body.some(is.attributeChangedCallback)
|
|
11
|
+
const hasObservedAttributes = node.body.some(is.observedAttributes)
|
|
12
|
+
|
|
13
|
+
node.metadata ??= {}
|
|
14
|
+
node.metadata.hasConstructor = hasConstructor
|
|
15
|
+
node.metadata.hasConnectedCallbackMethod = hasConnectedCallbackMethod
|
|
16
|
+
node.metadata.hasDisconnectedCallbackMethod = hasDisconnectedCallbackMethod
|
|
17
|
+
node.metadata.hasGetAttribute = hasGetAttribute
|
|
18
|
+
node.metadata.hasAttributeChangedCallback = hasAttributeChangedCallback
|
|
19
|
+
node.metadata.hasObservedAttributes = hasObservedAttributes
|
|
20
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { matchExpression } from '../../exp-matcher.js'
|
|
2
|
+
import { getBlocks, getProgram } from '../context.js'
|
|
3
|
+
|
|
4
|
+
export function EachBlock(node, ctx) {
|
|
5
|
+
ctx.next()
|
|
6
|
+
|
|
7
|
+
const program = getProgram(ctx)
|
|
8
|
+
const blocks = getBlocks(ctx)
|
|
9
|
+
matchExpression(node.expression, program, blocks)
|
|
10
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import * as is from '../../checkers.js'
|
|
2
|
+
|
|
3
|
+
export function Element(node, ctx) {
|
|
4
|
+
ctx.next()
|
|
5
|
+
|
|
6
|
+
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
|
|
11
|
+
|
|
12
|
+
node.metadata ??= {}
|
|
13
|
+
node.metadata.hasClass = hasClass
|
|
14
|
+
node.metadata.hasBinding = hasBinding
|
|
15
|
+
node.metadata.bindExpression = bindExpression
|
|
16
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { matchExpression } from "../../exp-matcher.js";
|
|
2
|
+
import { getBlocks, getProgram } from "../context.js";
|
|
3
|
+
|
|
4
|
+
export function ExpressionTag(node, ctx) {
|
|
5
|
+
const program = getProgram(ctx)
|
|
6
|
+
const blocks = getBlocks(ctx)
|
|
7
|
+
|
|
8
|
+
matchExpression(node.expression, program, blocks)
|
|
9
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import * as is from '../../checkers.js'
|
|
2
|
+
import { matchExpression } from '../../exp-matcher.js'
|
|
3
|
+
import { getBlocks, getProgram } from '../context.js'
|
|
4
|
+
|
|
5
|
+
export function IfBlock(node, ctx) {
|
|
6
|
+
ctx.next()
|
|
7
|
+
|
|
8
|
+
const program = getProgram(ctx)
|
|
9
|
+
const blocks = getBlocks(ctx)
|
|
10
|
+
matchExpression(node.test, program, blocks)
|
|
11
|
+
|
|
12
|
+
const hasElseif = node.alternate?.nodes.some(is.ifBlock) ?? false
|
|
13
|
+
|
|
14
|
+
node.metadata ??= {}
|
|
15
|
+
node.metadata.hasElseif = hasElseif
|
|
16
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { matchModule } from '../../module-matcher.js'
|
|
2
|
+
import { getTemplate } from '../context.js'
|
|
3
|
+
|
|
4
|
+
export function ImportDeclaration(node, ctx) {
|
|
5
|
+
const template = getTemplate(ctx)
|
|
6
|
+
|
|
7
|
+
const { dirname, filename, extension } = parse(node.source.value)
|
|
8
|
+
if (filename.includes('-')) {
|
|
9
|
+
const index = ctx.state.modules.length
|
|
10
|
+
const isModule = matchModule(filename, index, template)
|
|
11
|
+
|
|
12
|
+
if (isModule) {
|
|
13
|
+
node.metadata ??= {}
|
|
14
|
+
node.metadata.isModule = isModule
|
|
15
|
+
node.metadata.dirname = dirname
|
|
16
|
+
node.metadata.filename = filename
|
|
17
|
+
node.metadata.extension = extension
|
|
18
|
+
node.metadata.index = index
|
|
19
|
+
|
|
20
|
+
ctx.state.modules.push(filename)
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function parse(path) {
|
|
26
|
+
const dirnameTokens = path.split('/')
|
|
27
|
+
const filenameTokens = dirnameTokens.pop().split('.')
|
|
28
|
+
const extension = filenameTokens.pop()
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
dirname: dirnameTokens.join('/'),
|
|
32
|
+
filename: filenameTokens.join('.'),
|
|
33
|
+
extension
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import * as is from '../../checkers.js'
|
|
2
|
+
|
|
3
|
+
export function MethodDefinition(node, ctx) {
|
|
4
|
+
ctx.next()
|
|
5
|
+
|
|
6
|
+
const isPrivate = is.privateId(node.key)
|
|
7
|
+
const customElement = isPrivate ? ctx.state.customElement.private : ctx.state.customElement
|
|
8
|
+
|
|
9
|
+
node.metadata ??= {}
|
|
10
|
+
node.metadata.isPrivate = isPrivate
|
|
11
|
+
|
|
12
|
+
if (node.kind === 'method') {
|
|
13
|
+
customElement.methods.push(node.key.name)
|
|
14
|
+
} else if (node.kind === 'set') {
|
|
15
|
+
customElement.setters.push(node.key.name)
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import * as is from '../../checkers.js'
|
|
2
|
+
|
|
3
|
+
export function Program(node, ctx) {
|
|
4
|
+
const modules = []
|
|
5
|
+
const customElement = {
|
|
6
|
+
properties: [],
|
|
7
|
+
methods: ['getAttribute'],
|
|
8
|
+
setters: [],
|
|
9
|
+
private: {
|
|
10
|
+
properties: [],
|
|
11
|
+
methods: [],
|
|
12
|
+
setters: []
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
ctx.next({ ...ctx.state, modules, customElement })
|
|
17
|
+
|
|
18
|
+
const hasDefineCustomElement = node.body.some(is.defineCustomElement)
|
|
19
|
+
|
|
20
|
+
node.metadata ??= {}
|
|
21
|
+
node.metadata.hasDefineCustomElement = hasDefineCustomElement
|
|
22
|
+
node.metadata.modules = modules
|
|
23
|
+
node.metadata.customElement = customElement
|
|
24
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import * as is from '../../checkers.js'
|
|
2
|
+
|
|
3
|
+
export function PropertyDefinition(node, ctx) {
|
|
4
|
+
if (!node.static) {
|
|
5
|
+
const isPrivate = is.privateId(node.key)
|
|
6
|
+
const customElement = isPrivate ? ctx.state.customElement.private : ctx.state.customElement
|
|
7
|
+
|
|
8
|
+
node.metadata ??= {}
|
|
9
|
+
node.metadata.isPrivate = isPrivate
|
|
10
|
+
customElement.properties.push(node.key.name)
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { matchSelector } from '../../css-matcher.js'
|
|
2
|
+
import { getTemplate } from '../context.js'
|
|
3
|
+
|
|
4
|
+
export function Selector(node, ctx) {
|
|
5
|
+
node = CssTreeNodeFix(node, ctx)
|
|
6
|
+
|
|
7
|
+
const template = getTemplate(ctx)
|
|
8
|
+
const isUsed = matchSelector(node, template)
|
|
9
|
+
|
|
10
|
+
node.metadata ??= {}
|
|
11
|
+
node.metadata.isUsed = isUsed
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const CssTree = {
|
|
15
|
+
StyleSheet: CssTreeNodeFix,
|
|
16
|
+
SelectorList: CssTreeNodeFix,
|
|
17
|
+
PseudoClassSelector: CssTreeNodeFix
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function CssTreeNodeFix(node, ctx) {
|
|
21
|
+
const children = node.children?.map((c) => ctx.visit(c)) ?? null
|
|
22
|
+
return { ...node, children }
|
|
23
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import * as is from '../../checkers.js'
|
|
2
|
+
|
|
3
|
+
export function Template(node, ctx) {
|
|
4
|
+
ctx.next()
|
|
5
|
+
|
|
6
|
+
const isStatic = node.attributes.some(is.staticAttribute)
|
|
7
|
+
const shadowRootMode = node.attributes.find(is.shadowRootModeAttribute)?.value[0]?.data
|
|
8
|
+
|
|
9
|
+
node.metadata ??= {}
|
|
10
|
+
node.metadata.shadowRootMode = shadowRootMode
|
|
11
|
+
|
|
12
|
+
ctx.state.isStatic = isStatic
|
|
13
|
+
}
|
|
14
|
+
|