@kudzujs/core 0.8.18 → 0.8.20
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/MIGRATION_ROADMAP.md +14 -0
- package/PERFORMANCE.md +88 -0
- package/README.md +1 -1
- package/RELEASES.md +65 -0
- package/docs/next-architecture/README.md +1 -1
- package/docs/next-architecture/compiler-current-architecture.md +6 -5
- package/docs/next-architecture/goal-a-compiler-foundation.md +2 -2
- package/docs/next-architecture/versioning.md +1 -1
- package/framework/README.md +5 -4
- package/framework/build.mjs +140 -76
- package/framework/compiler/descriptor-session.mjs +100 -21
- package/framework/compiler/handler-codegen.mjs +6 -279
- package/framework/compiler/handler-lowering.mjs +278 -0
- package/framework/compiler/ir/module-ir.mjs +25 -1
- package/framework/core.d.ts +52 -1
- package/package.json +1 -1
|
@@ -2,17 +2,23 @@ import ts from "typescript"
|
|
|
2
2
|
import { createComponentAnalysis } from "./analysis/component-analysis.mjs"
|
|
3
3
|
import { bindingNames, isFunctionLike, isReferenceIdentifier, isShadowedByParameter, isShadowedIdentifier, unwrapExpression } from "./ast-helpers.mjs"
|
|
4
4
|
import { generateCommandBehavior } from "./codegen/command-codegen.mjs"
|
|
5
|
-
import { createModuleIR, registerCommandHandler } from "./ir/module-ir.mjs"
|
|
5
|
+
import { createModuleIR, registerBinding, registerCommandHandler, registerDerived, registerKeyedBlock, registerModuleHandler } from "./ir/module-ir.mjs"
|
|
6
6
|
|
|
7
7
|
export function createSemanticArtifact(file) {
|
|
8
|
-
return { componentAnalysis: createComponentAnalysis(file), moduleIR: createModuleIR(file)
|
|
8
|
+
return { componentAnalysis: createComponentAnalysis(file), moduleIR: createModuleIR(file) }
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
export function createDescriptorSession({ semantic, handlerUrl, factory, context, compileEventCommand, isPrimitiveLiteral, rejectWorkerConstructions, sourceName = source => source.fileName }) {
|
|
12
|
-
const { moduleIR
|
|
13
|
-
|
|
11
|
+
export function createDescriptorSession({ semantic, handlerUrl, factory, context, compileEventCommand, handlerLowering, isPrimitiveLiteral, rejectWorkerConstructions, sourceName = source => source.fileName }) {
|
|
12
|
+
const { moduleIR } = semantic
|
|
13
|
+
const nativeHandlers = []
|
|
14
|
+
const effectHandlers = []
|
|
15
|
+
const reactiveBindings = []
|
|
16
|
+
const listExpressions = []
|
|
17
|
+
const clientModules = new Set()
|
|
18
|
+
|
|
19
|
+
function compileListExpression(read, expression, item, index, states = new Set(), keyedBlock) {
|
|
14
20
|
const exportName = `listExpression${listExpressions.length}`
|
|
15
|
-
listExpressions.push({ exportName, expression, item, index, states })
|
|
21
|
+
listExpressions.push({ exportName, expression, item, index, states, role: "list-expression", keyedBlock })
|
|
16
22
|
const arguments_ = [read, factory.createStringLiteral(handlerUrl), factory.createStringLiteral(exportName)]
|
|
17
23
|
if (states.size) arguments_.push(factory.createArrayLiteralExpression([...states].map(name => factory.createArrayLiteralExpression([factory.createStringLiteral(name), factory.createIdentifier(name)]))))
|
|
18
24
|
return factory.createCallExpression(factory.createIdentifier("__kListExpression"), undefined, arguments_)
|
|
@@ -20,7 +26,7 @@ export function createDescriptorSession({ semantic, handlerUrl, factory, context
|
|
|
20
26
|
|
|
21
27
|
function compileListConditional(entry) {
|
|
22
28
|
const exportName = `listExpression${listExpressions.length}`
|
|
23
|
-
listExpressions.push({ exportName, expression: entry.condition, item: entry.item, index: entry.index })
|
|
29
|
+
listExpressions.push({ exportName, expression: entry.condition, item: entry.item, index: entry.index, role: "list-conditional", keyedBlock: entry.keyedBlock })
|
|
24
30
|
const read = factory.createArrowFunction(undefined, undefined, [], undefined, factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), entry.condition)
|
|
25
31
|
const thunk = branch => factory.createArrowFunction(undefined, undefined, [], undefined, factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), branch)
|
|
26
32
|
return factory.createCallExpression(factory.createIdentifier("__kListConditional"), undefined, [
|
|
@@ -38,16 +44,16 @@ export function createDescriptorSession({ semantic, handlerUrl, factory, context
|
|
|
38
44
|
const read = factory.createArrowFunction(undefined, undefined, [], undefined, factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), initial)
|
|
39
45
|
return entry.field
|
|
40
46
|
? factory.createCallExpression(factory.createIdentifier("__kListField"), undefined, [read, factory.createStringLiteral(entry.field)])
|
|
41
|
-
: compileListExpression(read, expression, entry.item, entry.index, entry.states)
|
|
47
|
+
: compileListExpression(read, expression, entry.item, entry.index, entry.states, entry.keyedBlock)
|
|
42
48
|
}
|
|
43
49
|
|
|
44
|
-
function compileReactiveBinding(expression, { setters, importBindings = new Map() }) {
|
|
50
|
+
function compileReactiveBinding(expression, { setters, importBindings = new Map(), keyedBlock }) {
|
|
45
51
|
const parts = conditionalParts(expression)
|
|
46
52
|
const state = parts && directStateIdentifier(parts.condition, setters)
|
|
47
53
|
if (state && isPrimitiveLiteral(parts.truthy) && isPrimitiveLiteral(parts.falsy)) {
|
|
48
54
|
return factory.createCallExpression(factory.createIdentifier("__kSelect"), undefined, [state, parts.truthy, parts.falsy])
|
|
49
55
|
}
|
|
50
|
-
return factory.createCallExpression(factory.createIdentifier("__kBinding"), undefined, compileReactiveExpression(expression, setters, importBindings))
|
|
56
|
+
return factory.createCallExpression(factory.createIdentifier("__kBinding"), undefined, compileReactiveExpression(expression, setters, importBindings, keyedBlock))
|
|
51
57
|
}
|
|
52
58
|
|
|
53
59
|
function compileConditional(kind, expression, truthy, falsy, setters) {
|
|
@@ -58,14 +64,14 @@ export function createDescriptorSession({ semantic, handlerUrl, factory, context
|
|
|
58
64
|
return factory.createCallExpression(factory.createIdentifier("__kConditional"), undefined, [factory.createStringLiteral(kind), initial, thunk(truthy), thunk(falsy), ...descriptor])
|
|
59
65
|
}
|
|
60
66
|
|
|
61
|
-
function compileReactiveExpression(expression, setters, importBindings = new Map()) {
|
|
67
|
+
function compileReactiveExpression(expression, setters, importBindings = new Map(), keyedBlock) {
|
|
62
68
|
const usedStates = referencedStateNames(expression, setters)
|
|
63
69
|
const importedNames = referencedImportedBindings(expression, importBindings)
|
|
64
70
|
const imports = [...importedNames].map(name => importBindings.get(name))
|
|
65
71
|
registerClientImports(imports)
|
|
66
72
|
const captures = new Set([...captureNames(expression, expression, setters)].filter(name => !importedNames.has(name)))
|
|
67
73
|
const exportName = `binding${reactiveBindings.length}`
|
|
68
|
-
reactiveBindings.push({ exportName, expression, captures, states: usedStates, imports })
|
|
74
|
+
reactiveBindings.push({ exportName, expression, captures, states: usedStates, imports, role: "binding", keyedBlock })
|
|
69
75
|
const states = [...usedStates].map(name => factory.createArrayLiteralExpression([factory.createStringLiteral(name), factory.createIdentifier(name)]))
|
|
70
76
|
const scope = [...captures].map(name => factory.createArrayLiteralExpression([factory.createStringLiteral(name), factory.createIdentifier(name)]))
|
|
71
77
|
const stateNames = new Set(usedStates)
|
|
@@ -85,23 +91,23 @@ export function createDescriptorSession({ semantic, handlerUrl, factory, context
|
|
|
85
91
|
]
|
|
86
92
|
}
|
|
87
93
|
|
|
88
|
-
function compileEvent(expression, { owner = "module", stateOwners = new Map(), setters, reducers, functions, listItem, importBindings }) {
|
|
94
|
+
function compileEvent(expression, { owner = "module", stateOwners = new Map(), setters, reducers, functions, listItem, keyedBlock, importBindings }) {
|
|
89
95
|
if (ts.isIdentifier(expression)) expression = functions.get(expression.text)
|
|
90
96
|
if (!expression || (!ts.isArrowFunction(expression) && !ts.isFunctionExpression(expression) && !ts.isFunctionDeclaration(expression))) return undefined
|
|
91
|
-
const optimized = referencedReducerDispatches(expression.body, reducers, expression).size ? undefined : compileOptimizedEvent(expression, setters, stateOwners, owner)
|
|
97
|
+
const optimized = referencedReducerDispatches(expression.body, reducers, expression).size ? undefined : compileOptimizedEvent(expression, setters, stateOwners, owner, keyedBlock)
|
|
92
98
|
if (optimized) return optimized
|
|
93
99
|
rejectWorkerConstructions(expression)
|
|
94
|
-
const descriptor = compileNativeCallback(expression, { setters, reducers, entries: nativeHandlers, importBindings, prefix: "handler", listItem })
|
|
100
|
+
const descriptor = compileNativeCallback(expression, { setters, reducers, entries: nativeHandlers, importBindings, prefix: "handler", role: "native", listItem, keyedBlock })
|
|
95
101
|
return factory.createCallExpression(factory.createIdentifier("__kNativeBehavior"), undefined, [
|
|
96
102
|
factory.createStringLiteral(handlerUrl), factory.createStringLiteral(descriptor.exportName), descriptor.states, descriptor.scope
|
|
97
103
|
])
|
|
98
104
|
}
|
|
99
105
|
|
|
100
106
|
function compileEffectCallback(expression, options) {
|
|
101
|
-
return compileNativeCallback(expression, { ...options, entries: effectHandlers, prefix: "effect" })
|
|
107
|
+
return compileNativeCallback(expression, { ...options, entries: effectHandlers, prefix: "effect", role: "effect" })
|
|
102
108
|
}
|
|
103
109
|
|
|
104
|
-
function compileNativeCallback(expression, { setters, reducers, entries, importBindings, prefix, listItem, deferValues = false, snapshotNested = false, liveStates = new Set() }) {
|
|
110
|
+
function compileNativeCallback(expression, { setters, reducers, entries, importBindings, prefix, role, listItem, keyedBlock, deferValues = false, snapshotNested = false, liveStates = new Set() }) {
|
|
105
111
|
const allCaptures = nativeCaptureNames(expression, setters)
|
|
106
112
|
const usedReducers = referencedReducerDispatches(expression.body, reducers, expression)
|
|
107
113
|
const imports = [...referencedImportedBindings(expression, importBindings)].map(name => importBindings.get(name))
|
|
@@ -114,7 +120,7 @@ export function createDescriptorSession({ semantic, handlerUrl, factory, context
|
|
|
114
120
|
if (reducer.contextAction) for (const state of referencedStateNames(reducer.contextAction.body, reducer.states, reducer.contextAction)) usedStates.add(state)
|
|
115
121
|
}
|
|
116
122
|
const exportName = `${prefix}${entries.length}`
|
|
117
|
-
entries.push({ exportName, expression, captures, imports, liveStates, setters: new Map([...setters].filter(([, state]) => usedStates.has(state))), reducers: new Map([...reducers].filter(([name]) => usedReducers.has(name))), snapshotNested })
|
|
123
|
+
entries.push({ exportName, expression, captures, deferValues, imports, listItem, keyedBlock, liveStates, role, setters: new Map([...setters].filter(([, state]) => usedStates.has(state))), reducers: new Map([...reducers].filter(([name]) => usedReducers.has(name))), snapshotNested, usedStates })
|
|
118
124
|
const value = name => deferValues
|
|
119
125
|
? factory.createArrowFunction(undefined, undefined, [], undefined, factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), factory.createIdentifier(name))
|
|
120
126
|
: factory.createIdentifier(name)
|
|
@@ -130,21 +136,94 @@ export function createDescriptorSession({ semantic, handlerUrl, factory, context
|
|
|
130
136
|
}
|
|
131
137
|
}
|
|
132
138
|
|
|
133
|
-
function compileOptimizedEvent(expression, setters, stateOwners, owner) {
|
|
139
|
+
function compileOptimizedEvent(expression, setters, stateOwners, owner, keyedBlock) {
|
|
134
140
|
const statements = ts.isBlock(expression.body) ? expression.body.statements : [factory.createExpressionStatement(expression.body)]
|
|
135
141
|
const commands = statements.map(statement => ts.isExpressionStatement(statement) ? compileEventCommand(statement.expression, setters) : undefined)
|
|
136
142
|
if (!commands.length || commands.some(command => !command)) return undefined
|
|
137
143
|
const original = ts.getOriginalNode(expression)
|
|
138
144
|
const source = original.pos >= 0 && original.end >= 0 ? { file: sourceName(original.getSourceFile()), start: original.getStart(), end: original.end } : undefined
|
|
139
145
|
const handler = registerCommandHandler(moduleIR, commands.map(command => ({ ...command, owner: stateOwners.get(command.state) ?? owner })), source, owner)
|
|
146
|
+
if (keyedBlock !== undefined) handler.keyedBlock = keyedBlock
|
|
140
147
|
return generateCommandBehavior(moduleIR, handler, factory)
|
|
141
148
|
}
|
|
142
149
|
|
|
143
150
|
function registerClientImports(imports) {
|
|
144
|
-
for (const entry of imports) if (!entry.package)
|
|
151
|
+
for (const entry of imports) if (!entry.package) clientModules.add(entry.target)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function registerDerivedResult(kind, value, states = [], node) {
|
|
155
|
+
const normalized = JSON.parse(JSON.stringify(value))
|
|
156
|
+
return registerDerived(moduleIR, { kind, [kind]: normalized, states: [...states], ...(source(node) ? { source: source(node) } : {}) })
|
|
145
157
|
}
|
|
146
158
|
|
|
147
|
-
|
|
159
|
+
function registerKeyedBlockResult(descriptor) {
|
|
160
|
+
return registerKeyedBlock(moduleIR, descriptor)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function finalize() {
|
|
164
|
+
const callbacks = [...nativeHandlers, ...effectHandlers]
|
|
165
|
+
for (const entry of callbacks) {
|
|
166
|
+
const lowered = handlerLowering.lowerNativeHandler(entry)
|
|
167
|
+
const setters = Map.groupBy(entry.setters, ([, state]) => state)
|
|
168
|
+
registerModuleHandler(moduleIR, {
|
|
169
|
+
role: entry.role,
|
|
170
|
+
...(entry.keyedBlock !== undefined ? { keyedBlock: entry.keyedBlock } : {}),
|
|
171
|
+
exportName: entry.exportName,
|
|
172
|
+
async: Boolean(entry.expression.modifiers?.some(modifier => modifier.kind === ts.SyntaxKind.AsyncKeyword)),
|
|
173
|
+
generator: Boolean(entry.expression.asteriskToken),
|
|
174
|
+
signals: [...entry.usedStates].map(name => ({
|
|
175
|
+
name,
|
|
176
|
+
setters: (setters.get(name) ?? []).map(([setter]) => setter),
|
|
177
|
+
value: entry.deferValues ? "deferred" : "direct",
|
|
178
|
+
snapshot: lowered.stateSnapshots.includes(name)
|
|
179
|
+
})),
|
|
180
|
+
captures: [...entry.captures].map(name => ({
|
|
181
|
+
name,
|
|
182
|
+
source: name === (typeof entry.listItem === "string" ? entry.listItem : entry.listItem?.item) ? "list-item" : name === entry.listItem?.index ? "list-index" : "scope",
|
|
183
|
+
value: entry.deferValues ? "deferred" : "direct",
|
|
184
|
+
snapshot: lowered.captureSnapshots.includes(name)
|
|
185
|
+
})),
|
|
186
|
+
imports: entry.imports.map(importRecord),
|
|
187
|
+
code: lowered.code,
|
|
188
|
+
...(source(entry.expression) ? { source: source(entry.expression) } : {})
|
|
189
|
+
})
|
|
190
|
+
}
|
|
191
|
+
for (const entry of reactiveBindings) registerBinding(moduleIR, {
|
|
192
|
+
role: entry.role,
|
|
193
|
+
...(entry.keyedBlock !== undefined ? { keyedBlock: entry.keyedBlock } : {}),
|
|
194
|
+
exportName: entry.exportName,
|
|
195
|
+
parameters: ["__k"],
|
|
196
|
+
states: [...entry.states],
|
|
197
|
+
captures: [...entry.captures].map(name => ({ name, source: "scope" })),
|
|
198
|
+
imports: entry.imports.map(importRecord),
|
|
199
|
+
code: handlerLowering.lowerReactiveBinding(entry),
|
|
200
|
+
...(source(entry.expression) ? { source: source(entry.expression) } : {})
|
|
201
|
+
})
|
|
202
|
+
for (const entry of listExpressions) registerBinding(moduleIR, {
|
|
203
|
+
role: entry.role,
|
|
204
|
+
...(entry.keyedBlock !== undefined ? { keyedBlock: entry.keyedBlock } : {}),
|
|
205
|
+
exportName: entry.exportName,
|
|
206
|
+
parameters: [entry.item, entry.index ?? "__kIndex", "__k"],
|
|
207
|
+
states: [...(entry.states ?? [])],
|
|
208
|
+
captures: [],
|
|
209
|
+
imports: [],
|
|
210
|
+
code: handlerLowering.lowerListExpression(entry),
|
|
211
|
+
...(source(entry.expression) ? { source: source(entry.expression) } : {})
|
|
212
|
+
})
|
|
213
|
+
const imports = [...callbacks, ...reactiveBindings].flatMap(entry => entry.imports ?? []).map(importRecord)
|
|
214
|
+
moduleIR.imports = [...new Map(imports.map(entry => [`${entry.target}:${entry.kind}:${entry.imported ?? ""}:${entry.local}`, entry])).values()]
|
|
215
|
+
moduleIR.clientModules = [...clientModules]
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function source(node) {
|
|
219
|
+
if (!node) return undefined
|
|
220
|
+
const original = ts.getOriginalNode(node)
|
|
221
|
+
return original.pos >= 0 && original.end >= 0 ? { file: sourceName(original.getSourceFile()), start: original.getStart(), end: original.end } : undefined
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const importRecord = entry => ({ target: entry.target, kind: entry.kind, local: entry.local, ...(entry.imported ? { imported: entry.imported } : {}), package: Boolean(entry.package) })
|
|
225
|
+
|
|
226
|
+
return { compileConditional, compileEffectCallback, compileEvent, compileListConditional, compileListValue, compileReactiveBinding, finalize, registerDerived: registerDerivedResult, registerKeyedBlock: registerKeyedBlockResult }
|
|
148
227
|
}
|
|
149
228
|
|
|
150
229
|
function directStateIdentifier(expression, setters) {
|
|
@@ -1,19 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export function createHandlerCodegen({ cloneAst, synthesizeTree, resolveClientImport }) {
|
|
5
|
-
return function printHandlerModule({ callbacks, reactiveBindings, listExpressions, handlerPath }) {
|
|
1
|
+
export function createHandlerCodegen({ resolveClientImport }) {
|
|
2
|
+
return function printHandlerModule({ moduleIR, handlerPath }) {
|
|
6
3
|
return [
|
|
7
|
-
printClientImports(
|
|
8
|
-
...
|
|
9
|
-
...
|
|
10
|
-
...listExpressions.map(entry => printListExpression(entry))
|
|
4
|
+
printClientImports(moduleIR.imports, handlerPath),
|
|
5
|
+
...moduleIR.handlers.filter(handler => handler.kind === "module-export").map(handler => handler.code),
|
|
6
|
+
...moduleIR.bindings.map(binding => binding.code)
|
|
11
7
|
].join("\n")
|
|
12
8
|
}
|
|
13
9
|
|
|
14
10
|
function printClientImports(entries, handlerPath) {
|
|
15
|
-
const
|
|
16
|
-
const groups = Map.groupBy(unique.values(), entry => entry.target)
|
|
11
|
+
const groups = Map.groupBy(entries, entry => entry.target)
|
|
17
12
|
const imports = []
|
|
18
13
|
for (const [target, group] of groups) {
|
|
19
14
|
const specifier = resolveClientImport(group[0], handlerPath)
|
|
@@ -25,272 +20,4 @@ export function createHandlerCodegen({ cloneAst, synthesizeTree, resolveClientIm
|
|
|
25
20
|
}
|
|
26
21
|
return imports.join("\n")
|
|
27
22
|
}
|
|
28
|
-
|
|
29
|
-
function printNativeHandler({ exportName, expression, captures, setters, reducers = new Map(), snapshotNested, liveStates = new Set() }) {
|
|
30
|
-
const factory = ts.factory
|
|
31
|
-
const stateNames = new Set(setters.values())
|
|
32
|
-
const snapshotNames = snapshotNested ? nestedStateNames(expression, setters, liveStates) : new Set()
|
|
33
|
-
const snapshots = new Map([...snapshotNames].map(name => [name, factory.createUniqueName("__kEffectState")]))
|
|
34
|
-
const captureSnapshotNames = snapshotNested ? nestedCaptureNames(expression, captures) : new Set()
|
|
35
|
-
const captureSnapshots = new Map([...captureSnapshotNames].map(name => [name, factory.createUniqueName("__kEffectCapture")]))
|
|
36
|
-
const transformer = context => root => {
|
|
37
|
-
const visitor = node => {
|
|
38
|
-
if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && reducers.has(node.expression.text) && !isShadowedIdentifier(node.expression, expression)) {
|
|
39
|
-
const reducer = reducers.get(node.expression.text)
|
|
40
|
-
if (reducer.contextAction) {
|
|
41
|
-
const action = synthesizeTree(cloneAst(reducer.contextAction, factory, context))
|
|
42
|
-
const call = factory.createCallExpression(action, undefined, node.arguments)
|
|
43
|
-
ts.setParentRecursive(call, false)
|
|
44
|
-
return ts.visitNode(call, visitor)
|
|
45
|
-
}
|
|
46
|
-
if (reducer.store) return zustandActionDispatch(factory, reducer, node.arguments.map(argument => ts.visitNode(argument, visitor)))
|
|
47
|
-
if (node.arguments.length !== 1) throw sourceNodeError(node, expression.getSourceFile(), "Reducer dispatches require exactly one action")
|
|
48
|
-
return reducerDispatch(factory, reducer, ts.visitNode(node.arguments[0], visitor))
|
|
49
|
-
}
|
|
50
|
-
if (ts.isShorthandPropertyAssignment(node) && reducers.has(node.name.text) && !isShadowedIdentifier(node.name, expression)) {
|
|
51
|
-
if (reducers.get(node.name.text).contextAction) throw sourceNodeError(node, expression.getSourceFile(), "Context actions must be called directly inside an event handler")
|
|
52
|
-
if (reducers.get(node.name.text).store) throw sourceNodeError(node, expression.getSourceFile(), "Zustand actions must be called directly inside an event handler")
|
|
53
|
-
return factory.createPropertyAssignment(node.name, reducerReference(factory, reducers.get(node.name.text)))
|
|
54
|
-
}
|
|
55
|
-
if (ts.isIdentifier(node) && reducers.has(node.text) && isReferenceIdentifier(node) && !isShadowedIdentifier(node, expression)) {
|
|
56
|
-
if (reducers.get(node.text).contextAction) throw sourceNodeError(node, expression.getSourceFile(), "Context actions must be called directly inside an event handler")
|
|
57
|
-
if (reducers.get(node.text).store) throw sourceNodeError(node, expression.getSourceFile(), "Zustand actions must be called directly inside an event handler")
|
|
58
|
-
return reducerReference(factory, reducers.get(node.text))
|
|
59
|
-
}
|
|
60
|
-
if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && setters.has(node.expression.text) && !isShadowedIdentifier(node.expression, expression)) {
|
|
61
|
-
return factory.createCallExpression(
|
|
62
|
-
factory.createPropertyAccessExpression(factory.createIdentifier("__k"), "set"),
|
|
63
|
-
undefined,
|
|
64
|
-
[factory.createStringLiteral(setters.get(node.expression.text)), ...node.arguments.map(argument => ts.visitNode(argument, visitor))]
|
|
65
|
-
)
|
|
66
|
-
}
|
|
67
|
-
if (ts.isShorthandPropertyAssignment(node) && setters.has(node.name.text) && !isShadowedIdentifier(node.name, expression)) {
|
|
68
|
-
return factory.createPropertyAssignment(node.name, setterReference(factory, setters.get(node.name.text)))
|
|
69
|
-
}
|
|
70
|
-
if (ts.isIdentifier(node) && setters.has(node.text) && isReferenceIdentifier(node) && !isShadowedIdentifier(node, expression)) {
|
|
71
|
-
return setterReference(factory, setters.get(node.text))
|
|
72
|
-
}
|
|
73
|
-
if (ts.isShorthandPropertyAssignment(node) && stateNames.has(node.name.text) && !isShadowedIdentifier(node.name, expression)) {
|
|
74
|
-
if (snapshots.has(node.name.text) && insideNestedFunction(node, expression)) return factory.createPropertyAssignment(node.name, snapshots.get(node.name.text))
|
|
75
|
-
return factory.createPropertyAssignment(node.name, factory.createCallExpression(factory.createPropertyAccessExpression(factory.createIdentifier("__k"), "get"), undefined, [factory.createStringLiteral(node.name.text)]))
|
|
76
|
-
}
|
|
77
|
-
if (ts.isIdentifier(node) && stateNames.has(node.text) && isReferenceIdentifier(node) && !isShadowedIdentifier(node, expression)) {
|
|
78
|
-
if (snapshots.has(node.text) && insideNestedFunction(node, expression)) return snapshots.get(node.text)
|
|
79
|
-
return factory.createCallExpression(
|
|
80
|
-
factory.createPropertyAccessExpression(factory.createIdentifier("__k"), "get"),
|
|
81
|
-
undefined,
|
|
82
|
-
[factory.createStringLiteral(node.text)]
|
|
83
|
-
)
|
|
84
|
-
}
|
|
85
|
-
if (ts.isShorthandPropertyAssignment(node) && captures.has(node.name.text) && !isShadowedIdentifier(node.name, expression)) {
|
|
86
|
-
if (captureSnapshots.has(node.name.text) && insideNestedFunction(node, expression)) return factory.createPropertyAssignment(node.name, captureSnapshots.get(node.name.text))
|
|
87
|
-
return factory.createPropertyAssignment(node.name, scopeRead(factory, node.name.text))
|
|
88
|
-
}
|
|
89
|
-
if (ts.isIdentifier(node) && captures.has(node.text) && isReferenceIdentifier(node) && !isShadowedIdentifier(node, expression)) {
|
|
90
|
-
if (captureSnapshots.has(node.text) && insideNestedFunction(node, expression)) return captureSnapshots.get(node.text)
|
|
91
|
-
return scopeRead(factory, node.text)
|
|
92
|
-
}
|
|
93
|
-
return ts.visitEachChild(node, visitor, context)
|
|
94
|
-
}
|
|
95
|
-
return ts.visitNode(root, visitor)
|
|
96
|
-
}
|
|
97
|
-
const transformed = ts.transform(expression.body, [transformer])
|
|
98
|
-
try {
|
|
99
|
-
let body = ts.isBlock(expression.body)
|
|
100
|
-
? transformed.transformed[0]
|
|
101
|
-
: factory.createBlock([factory.createReturnStatement(transformed.transformed[0])], true)
|
|
102
|
-
const snapshotDeclarations = [
|
|
103
|
-
...[...snapshots].map(([name, identifier]) => factory.createVariableDeclaration(identifier, undefined, undefined, factory.createCallExpression(factory.createPropertyAccessExpression(factory.createIdentifier("__k"), "get"), undefined, [factory.createStringLiteral(name)]))),
|
|
104
|
-
...[...captureSnapshots].map(([name, identifier]) => factory.createVariableDeclaration(identifier, undefined, undefined, factory.createCallExpression(factory.createPropertyAccessExpression(factory.createIdentifier("__k"), "scope"), undefined, [factory.createStringLiteral(name)])))
|
|
105
|
-
]
|
|
106
|
-
if (snapshotDeclarations.length) body = factory.updateBlock(body, [
|
|
107
|
-
factory.createVariableStatement(undefined, factory.createVariableDeclarationList(snapshotDeclarations, ts.NodeFlags.Const)),
|
|
108
|
-
...body.statements
|
|
109
|
-
])
|
|
110
|
-
const modifiers = [factory.createModifier(ts.SyntaxKind.ExportKeyword)]
|
|
111
|
-
if (expression.modifiers?.some(modifier => modifier.kind === ts.SyntaxKind.AsyncKeyword)) modifiers.push(factory.createModifier(ts.SyntaxKind.AsyncKeyword))
|
|
112
|
-
const declaration = factory.createFunctionDeclaration(
|
|
113
|
-
modifiers,
|
|
114
|
-
expression.asteriskToken,
|
|
115
|
-
exportName,
|
|
116
|
-
undefined,
|
|
117
|
-
[factory.createParameterDeclaration(undefined, undefined, "__k"), ...expression.parameters],
|
|
118
|
-
undefined,
|
|
119
|
-
body
|
|
120
|
-
)
|
|
121
|
-
return ts.createPrinter().printNode(ts.EmitHint.Unspecified, declaration, expression.getSourceFile())
|
|
122
|
-
} finally {
|
|
123
|
-
transformed.dispose()
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
function nestedCaptureNames(expression, captures) {
|
|
128
|
-
const names = new Set()
|
|
129
|
-
const visit = node => {
|
|
130
|
-
if (ts.isIdentifier(node) && captures.has(node.text) && isReferenceIdentifier(node) && insideNestedFunction(node, expression) && !isShadowedIdentifier(node, expression)) names.add(node.text)
|
|
131
|
-
ts.forEachChild(node, visit)
|
|
132
|
-
}
|
|
133
|
-
visit(expression.body)
|
|
134
|
-
return names
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
function nestedStateNames(expression, setters, liveStates = new Set()) {
|
|
138
|
-
const states = new Set(setters.values())
|
|
139
|
-
const names = new Set()
|
|
140
|
-
const visit = node => {
|
|
141
|
-
if (ts.isIdentifier(node) && states.has(node.text) && !liveStates.has(node.text) && isReferenceIdentifier(node) && insideNestedFunction(node, expression) && !isShadowedIdentifier(node, expression)) names.add(node.text)
|
|
142
|
-
ts.forEachChild(node, visit)
|
|
143
|
-
}
|
|
144
|
-
visit(expression.body)
|
|
145
|
-
return names
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
function insideNestedFunction(node, root) {
|
|
149
|
-
for (let current = node.parent; current && current !== root; current = current.parent) {
|
|
150
|
-
if (isFunctionLike(current)) return true
|
|
151
|
-
}
|
|
152
|
-
return false
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
function setterReference(factory, stateName) {
|
|
156
|
-
return factory.createArrowFunction(
|
|
157
|
-
undefined,
|
|
158
|
-
undefined,
|
|
159
|
-
[factory.createParameterDeclaration(undefined, undefined, "value")],
|
|
160
|
-
undefined,
|
|
161
|
-
factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
|
|
162
|
-
factory.createCallExpression(factory.createPropertyAccessExpression(factory.createIdentifier("__k"), "set"), undefined, [factory.createStringLiteral(stateName), factory.createIdentifier("value")])
|
|
163
|
-
)
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
function reducerReference(factory, reducer) {
|
|
167
|
-
const action = factory.createUniqueName("__kAction")
|
|
168
|
-
return factory.createArrowFunction(undefined, undefined, [factory.createParameterDeclaration(undefined, undefined, action)], undefined, factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), reducerDispatch(factory, reducer, action))
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
function reducerDispatch(factory, reducer, action) {
|
|
172
|
-
if (reducer.store) return zustandActionDispatch(factory, reducer, [action])
|
|
173
|
-
const previous = factory.createUniqueName("__kPrevious")
|
|
174
|
-
const update = factory.createArrowFunction(undefined, undefined, [factory.createParameterDeclaration(undefined, undefined, previous)], undefined, factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), factory.createCallExpression(factory.createIdentifier(reducer.reducer), undefined, [previous, action]))
|
|
175
|
-
return factory.createCallExpression(factory.createPropertyAccessExpression(factory.createIdentifier("__k"), "set"), undefined, [factory.createStringLiteral(reducer.state), update])
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
function zustandActionDispatch(factory, reducer, args) {
|
|
179
|
-
const previous = factory.createUniqueName("__kPrevious")
|
|
180
|
-
const current = factory.createUniqueName("__kStore")
|
|
181
|
-
const updateValue = factory.createUniqueName("__kUpdate")
|
|
182
|
-
const partial = factory.createUniqueName("__kPartial")
|
|
183
|
-
const action = factory.createUniqueName("__kAction")
|
|
184
|
-
const set = factory.createIdentifier(reducer.store.setName)
|
|
185
|
-
const merge = factory.createExpressionStatement(factory.createBinaryExpression(current, factory.createToken(ts.SyntaxKind.EqualsToken), factory.createObjectLiteralExpression([
|
|
186
|
-
factory.createSpreadAssignment(current),
|
|
187
|
-
factory.createSpreadAssignment(partial)
|
|
188
|
-
])))
|
|
189
|
-
const setBody = factory.createBlock([
|
|
190
|
-
factory.createVariableStatement(undefined, factory.createVariableDeclarationList([factory.createVariableDeclaration(partial, undefined, undefined, factory.createConditionalExpression(
|
|
191
|
-
factory.createBinaryExpression(factory.createTypeOfExpression(updateValue), factory.createToken(ts.SyntaxKind.EqualsEqualsEqualsToken), factory.createStringLiteral("function")),
|
|
192
|
-
undefined,
|
|
193
|
-
factory.createCallExpression(updateValue, undefined, [current]),
|
|
194
|
-
undefined,
|
|
195
|
-
updateValue
|
|
196
|
-
))], ts.NodeFlags.Const)),
|
|
197
|
-
merge
|
|
198
|
-
], true)
|
|
199
|
-
const body = factory.createBlock([
|
|
200
|
-
factory.createVariableStatement(undefined, factory.createVariableDeclarationList([factory.createVariableDeclaration(current, undefined, undefined, factory.createObjectLiteralExpression([factory.createPropertyAssignment(reducer.store.field, previous)]))], ts.NodeFlags.Let)),
|
|
201
|
-
factory.createVariableStatement(undefined, factory.createVariableDeclarationList([factory.createVariableDeclaration(set, undefined, undefined, factory.createArrowFunction(undefined, undefined, [factory.createParameterDeclaration(undefined, undefined, updateValue)], undefined, factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), setBody))], ts.NodeFlags.Const)),
|
|
202
|
-
factory.createVariableStatement(undefined, factory.createVariableDeclarationList([factory.createVariableDeclaration(action, undefined, undefined, synthesizeTree(reducer.store.actions.get(reducer.action)))], ts.NodeFlags.Const)),
|
|
203
|
-
factory.createExpressionStatement(factory.createCallExpression(action, undefined, args)),
|
|
204
|
-
factory.createReturnStatement(factory.createPropertyAccessExpression(current, reducer.store.field))
|
|
205
|
-
], true)
|
|
206
|
-
const update = factory.createArrowFunction(undefined, undefined, [factory.createParameterDeclaration(undefined, undefined, previous)], undefined, factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), body)
|
|
207
|
-
return factory.createCallExpression(factory.createPropertyAccessExpression(factory.createIdentifier("__k"), "set"), undefined, [factory.createStringLiteral(reducer.state), update])
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
function printReactiveBinding({ exportName, expression, captures, states }) {
|
|
211
|
-
const factory = ts.factory
|
|
212
|
-
const transformer = context => root => {
|
|
213
|
-
const visitor = node => {
|
|
214
|
-
if (ts.isShorthandPropertyAssignment(node) && states.has(node.name.text)) {
|
|
215
|
-
return factory.createPropertyAssignment(
|
|
216
|
-
node.name,
|
|
217
|
-
factory.createCallExpression(
|
|
218
|
-
factory.createPropertyAccessExpression(factory.createIdentifier("__k"), "get"),
|
|
219
|
-
undefined,
|
|
220
|
-
[factory.createStringLiteral(node.name.text)]
|
|
221
|
-
)
|
|
222
|
-
)
|
|
223
|
-
}
|
|
224
|
-
if (ts.isIdentifier(node) && states.has(node.text) && isReferenceIdentifier(node) && !isShadowedByParameter(node, expression)) {
|
|
225
|
-
return factory.createCallExpression(
|
|
226
|
-
factory.createPropertyAccessExpression(factory.createIdentifier("__k"), "get"),
|
|
227
|
-
undefined,
|
|
228
|
-
[factory.createStringLiteral(node.text)]
|
|
229
|
-
)
|
|
230
|
-
}
|
|
231
|
-
if (ts.isShorthandPropertyAssignment(node) && captures.has(node.name.text)) {
|
|
232
|
-
return factory.createPropertyAssignment(node.name, scopeRead(factory, node.name.text))
|
|
233
|
-
}
|
|
234
|
-
if (ts.isIdentifier(node) && captures.has(node.text) && isReferenceIdentifier(node)) {
|
|
235
|
-
return scopeRead(factory, node.text)
|
|
236
|
-
}
|
|
237
|
-
return ts.visitEachChild(node, visitor, context)
|
|
238
|
-
}
|
|
239
|
-
return ts.visitNode(root, visitor)
|
|
240
|
-
}
|
|
241
|
-
const transformed = ts.transform(expression, [transformer])
|
|
242
|
-
try {
|
|
243
|
-
const declaration = factory.createFunctionDeclaration(
|
|
244
|
-
[factory.createModifier(ts.SyntaxKind.ExportKeyword)],
|
|
245
|
-
undefined,
|
|
246
|
-
exportName,
|
|
247
|
-
undefined,
|
|
248
|
-
[factory.createParameterDeclaration(undefined, undefined, "__k")],
|
|
249
|
-
undefined,
|
|
250
|
-
factory.createBlock([factory.createReturnStatement(transformed.transformed[0])], true)
|
|
251
|
-
)
|
|
252
|
-
return ts.createPrinter().printNode(ts.EmitHint.Unspecified, declaration, expression.getSourceFile())
|
|
253
|
-
} finally {
|
|
254
|
-
transformed.dispose()
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
function printListExpression({ exportName, expression, item, index, states = new Set() }) {
|
|
259
|
-
const factory = ts.factory
|
|
260
|
-
const transformer = context => root => {
|
|
261
|
-
const visitor = node => {
|
|
262
|
-
if (ts.isShorthandPropertyAssignment(node) && states.has(node.name.text)) {
|
|
263
|
-
return factory.createPropertyAssignment(node.name, factory.createCallExpression(factory.createPropertyAccessExpression(factory.createIdentifier("__k"), "get"), undefined, [factory.createStringLiteral(node.name.text)]))
|
|
264
|
-
}
|
|
265
|
-
if (ts.isIdentifier(node) && states.has(node.text) && isReferenceIdentifier(node) && !isShadowedByParameter(node, expression)) {
|
|
266
|
-
return factory.createCallExpression(factory.createPropertyAccessExpression(factory.createIdentifier("__k"), "get"), undefined, [factory.createStringLiteral(node.text)])
|
|
267
|
-
}
|
|
268
|
-
return ts.visitEachChild(node, visitor, context)
|
|
269
|
-
}
|
|
270
|
-
return ts.visitNode(root, visitor)
|
|
271
|
-
}
|
|
272
|
-
const transformed = ts.transform(expression, [transformer])
|
|
273
|
-
const declaration = ts.factory.createFunctionDeclaration(
|
|
274
|
-
[ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)],
|
|
275
|
-
undefined,
|
|
276
|
-
exportName,
|
|
277
|
-
undefined,
|
|
278
|
-
[ts.factory.createParameterDeclaration(undefined, undefined, item), ts.factory.createParameterDeclaration(undefined, undefined, index ?? "__kIndex"), ts.factory.createParameterDeclaration(undefined, undefined, "__k")],
|
|
279
|
-
undefined,
|
|
280
|
-
ts.factory.createBlock([ts.factory.createReturnStatement(transformed.transformed[0])], true)
|
|
281
|
-
)
|
|
282
|
-
try {
|
|
283
|
-
return ts.createPrinter().printNode(ts.EmitHint.Unspecified, declaration, expression.getSourceFile())
|
|
284
|
-
} finally {
|
|
285
|
-
transformed.dispose()
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
function scopeRead(factory, name) {
|
|
290
|
-
return factory.createCallExpression(
|
|
291
|
-
factory.createPropertyAccessExpression(factory.createIdentifier("__k"), "scope"),
|
|
292
|
-
undefined,
|
|
293
|
-
[factory.createStringLiteral(name)]
|
|
294
|
-
)
|
|
295
|
-
}
|
|
296
23
|
}
|