@kudzujs/core 0.8.17 → 0.8.19
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 +79 -0
- package/README.md +1 -1
- package/RELEASES.md +66 -0
- package/docs/next-architecture/README.md +1 -1
- package/docs/next-architecture/compiler-current-architecture.md +7 -5
- package/docs/next-architecture/goal-a-compiler-foundation.md +2 -2
- package/docs/next-architecture/versioning.md +1 -1
- package/framework/README.md +4 -2
- package/framework/build.mjs +194 -49
- package/framework/compiler/analysis/component-analysis.mjs +49 -0
- package/framework/compiler/descriptor-session.mjs +88 -21
- package/framework/compiler/handler-codegen.mjs +6 -279
- package/framework/compiler/handler-lowering.mjs +278 -0
- package/framework/compiler/ir/module-ir.mjs +23 -5
- package/package.json +1 -1
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export function createComponentAnalysis(file) {
|
|
2
|
+
return { version: 1, file, owners: [], specializations: [] }
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export function createComponentAnalysisSession(analysis) {
|
|
6
|
+
const owners = new WeakMap()
|
|
7
|
+
|
|
8
|
+
function registerOwner(identity, descriptor = {}) {
|
|
9
|
+
let owner = owners.get(identity)
|
|
10
|
+
if (!owner) {
|
|
11
|
+
owner = { slot: analysis.owners.length, kind: descriptor.kind ?? "component", name: descriptor.name ?? "anonymous", props: descriptor.props ?? [], states: [], setters: [], refs: [], ids: [], ...(descriptor.source ? { source: descriptor.source } : {}) }
|
|
12
|
+
owners.set(identity, owner)
|
|
13
|
+
analysis.owners.push(owner)
|
|
14
|
+
}
|
|
15
|
+
return owner
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function registerState(identity, descriptor) {
|
|
19
|
+
const owner = owners.get(identity)
|
|
20
|
+
if (!owner) throw new Error("Component owner must be registered before its state")
|
|
21
|
+
let state = owner.states.find(entry => entry.name === descriptor.name && entry.owner === descriptor.owner)
|
|
22
|
+
if (!state) {
|
|
23
|
+
state = { slot: owner.states.length, name: descriptor.name, kind: descriptor.kind, ...(descriptor.owner ? { owner: descriptor.owner } : {}), ...(descriptor.source ? { source: descriptor.source } : {}) }
|
|
24
|
+
owner.states.push(state)
|
|
25
|
+
}
|
|
26
|
+
if (descriptor.setter && !owner.setters.some(entry => entry.name === descriptor.setter)) owner.setters.push({ name: descriptor.setter, signal: state.slot, kind: descriptor.kind })
|
|
27
|
+
return state
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function registerRef(identity, descriptor) {
|
|
31
|
+
const owner = owners.get(identity)
|
|
32
|
+
if (!owner) throw new Error("Component owner must be registered before its ref")
|
|
33
|
+
if (!owner.refs.some(entry => entry.name === descriptor.name)) owner.refs.push({ slot: owner.refs.length, ...descriptor })
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function registerId(identity, descriptor) {
|
|
37
|
+
const owner = owners.get(identity)
|
|
38
|
+
if (!owner) throw new Error("Component owner must be registered before its ID")
|
|
39
|
+
if (!owner.ids.some(entry => entry.name === descriptor.name)) owner.ids.push({ slot: owner.ids.length, ...descriptor })
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function registerSpecialization(descriptor) {
|
|
43
|
+
const specialization = { slot: analysis.specializations.length, ...descriptor }
|
|
44
|
+
analysis.specializations.push(specialization)
|
|
45
|
+
return specialization
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return { owner: identity => owners.get(identity), registerId, registerOwner, registerRef, registerSpecialization, registerState }
|
|
49
|
+
}
|
|
@@ -1,20 +1,24 @@
|
|
|
1
1
|
import ts from "typescript"
|
|
2
|
+
import { createComponentAnalysis } from "./analysis/component-analysis.mjs"
|
|
2
3
|
import { bindingNames, isFunctionLike, isReferenceIdentifier, isShadowedByParameter, isShadowedIdentifier, unwrapExpression } from "./ast-helpers.mjs"
|
|
3
4
|
import { generateCommandBehavior } from "./codegen/command-codegen.mjs"
|
|
4
|
-
import { createModuleIR, registerCommandHandler } from "./ir/module-ir.mjs"
|
|
5
|
+
import { createModuleIR, registerBinding, registerCommandHandler, registerDerived, registerModuleHandler } from "./ir/module-ir.mjs"
|
|
5
6
|
|
|
6
7
|
export function createSemanticArtifact(file) {
|
|
7
|
-
return {
|
|
8
|
+
return { componentAnalysis: createComponentAnalysis(file), moduleIR: createModuleIR(file) }
|
|
8
9
|
}
|
|
9
10
|
|
|
10
|
-
export function createDescriptorSession({ semantic, handlerUrl, factory, context, compileEventCommand, isPrimitiveLiteral, rejectWorkerConstructions, sourceName = source => source.fileName }) {
|
|
11
|
-
const { moduleIR
|
|
12
|
-
const
|
|
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()
|
|
14
18
|
|
|
15
19
|
function compileListExpression(read, expression, item, index, states = new Set()) {
|
|
16
20
|
const exportName = `listExpression${listExpressions.length}`
|
|
17
|
-
listExpressions.push({ exportName, expression, item, index, states })
|
|
21
|
+
listExpressions.push({ exportName, expression, item, index, states, role: "list-expression" })
|
|
18
22
|
const arguments_ = [read, factory.createStringLiteral(handlerUrl), factory.createStringLiteral(exportName)]
|
|
19
23
|
if (states.size) arguments_.push(factory.createArrayLiteralExpression([...states].map(name => factory.createArrayLiteralExpression([factory.createStringLiteral(name), factory.createIdentifier(name)]))))
|
|
20
24
|
return factory.createCallExpression(factory.createIdentifier("__kListExpression"), undefined, arguments_)
|
|
@@ -22,7 +26,7 @@ export function createDescriptorSession({ semantic, handlerUrl, factory, context
|
|
|
22
26
|
|
|
23
27
|
function compileListConditional(entry) {
|
|
24
28
|
const exportName = `listExpression${listExpressions.length}`
|
|
25
|
-
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" })
|
|
26
30
|
const read = factory.createArrowFunction(undefined, undefined, [], undefined, factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), entry.condition)
|
|
27
31
|
const thunk = branch => factory.createArrowFunction(undefined, undefined, [], undefined, factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), branch)
|
|
28
32
|
return factory.createCallExpression(factory.createIdentifier("__kListConditional"), undefined, [
|
|
@@ -67,7 +71,7 @@ export function createDescriptorSession({ semantic, handlerUrl, factory, context
|
|
|
67
71
|
registerClientImports(imports)
|
|
68
72
|
const captures = new Set([...captureNames(expression, expression, setters)].filter(name => !importedNames.has(name)))
|
|
69
73
|
const exportName = `binding${reactiveBindings.length}`
|
|
70
|
-
reactiveBindings.push({ exportName, expression, captures, states: usedStates, imports })
|
|
74
|
+
reactiveBindings.push({ exportName, expression, captures, states: usedStates, imports, role: "binding" })
|
|
71
75
|
const states = [...usedStates].map(name => factory.createArrayLiteralExpression([factory.createStringLiteral(name), factory.createIdentifier(name)]))
|
|
72
76
|
const scope = [...captures].map(name => factory.createArrayLiteralExpression([factory.createStringLiteral(name), factory.createIdentifier(name)]))
|
|
73
77
|
const stateNames = new Set(usedStates)
|
|
@@ -87,23 +91,23 @@ export function createDescriptorSession({ semantic, handlerUrl, factory, context
|
|
|
87
91
|
]
|
|
88
92
|
}
|
|
89
93
|
|
|
90
|
-
function compileEvent(expression, { setters, reducers, functions, listItem, importBindings }) {
|
|
94
|
+
function compileEvent(expression, { owner = "module", stateOwners = new Map(), setters, reducers, functions, listItem, importBindings }) {
|
|
91
95
|
if (ts.isIdentifier(expression)) expression = functions.get(expression.text)
|
|
92
96
|
if (!expression || (!ts.isArrowFunction(expression) && !ts.isFunctionExpression(expression) && !ts.isFunctionDeclaration(expression))) return undefined
|
|
93
|
-
const optimized = referencedReducerDispatches(expression.body, reducers, expression).size ? undefined : compileOptimizedEvent(expression, setters)
|
|
97
|
+
const optimized = referencedReducerDispatches(expression.body, reducers, expression).size ? undefined : compileOptimizedEvent(expression, setters, stateOwners, owner)
|
|
94
98
|
if (optimized) return optimized
|
|
95
99
|
rejectWorkerConstructions(expression)
|
|
96
|
-
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 })
|
|
97
101
|
return factory.createCallExpression(factory.createIdentifier("__kNativeBehavior"), undefined, [
|
|
98
102
|
factory.createStringLiteral(handlerUrl), factory.createStringLiteral(descriptor.exportName), descriptor.states, descriptor.scope
|
|
99
103
|
])
|
|
100
104
|
}
|
|
101
105
|
|
|
102
106
|
function compileEffectCallback(expression, options) {
|
|
103
|
-
return compileNativeCallback(expression, { ...options, entries: effectHandlers, prefix: "effect" })
|
|
107
|
+
return compileNativeCallback(expression, { ...options, entries: effectHandlers, prefix: "effect", role: "effect" })
|
|
104
108
|
}
|
|
105
109
|
|
|
106
|
-
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, deferValues = false, snapshotNested = false, liveStates = new Set() }) {
|
|
107
111
|
const allCaptures = nativeCaptureNames(expression, setters)
|
|
108
112
|
const usedReducers = referencedReducerDispatches(expression.body, reducers, expression)
|
|
109
113
|
const imports = [...referencedImportedBindings(expression, importBindings)].map(name => importBindings.get(name))
|
|
@@ -116,7 +120,7 @@ export function createDescriptorSession({ semantic, handlerUrl, factory, context
|
|
|
116
120
|
if (reducer.contextAction) for (const state of referencedStateNames(reducer.contextAction.body, reducer.states, reducer.contextAction)) usedStates.add(state)
|
|
117
121
|
}
|
|
118
122
|
const exportName = `${prefix}${entries.length}`
|
|
119
|
-
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, liveStates, role, setters: new Map([...setters].filter(([, state]) => usedStates.has(state))), reducers: new Map([...reducers].filter(([name]) => usedReducers.has(name))), snapshotNested, usedStates })
|
|
120
124
|
const value = name => deferValues
|
|
121
125
|
? factory.createArrowFunction(undefined, undefined, [], undefined, factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), factory.createIdentifier(name))
|
|
122
126
|
: factory.createIdentifier(name)
|
|
@@ -132,23 +136,86 @@ export function createDescriptorSession({ semantic, handlerUrl, factory, context
|
|
|
132
136
|
}
|
|
133
137
|
}
|
|
134
138
|
|
|
135
|
-
function compileOptimizedEvent(expression, setters) {
|
|
139
|
+
function compileOptimizedEvent(expression, setters, stateOwners, owner) {
|
|
136
140
|
const statements = ts.isBlock(expression.body) ? expression.body.statements : [factory.createExpressionStatement(expression.body)]
|
|
137
141
|
const commands = statements.map(statement => ts.isExpressionStatement(statement) ? compileEventCommand(statement.expression, setters) : undefined)
|
|
138
142
|
if (!commands.length || commands.some(command => !command)) return undefined
|
|
139
143
|
const original = ts.getOriginalNode(expression)
|
|
140
144
|
const source = original.pos >= 0 && original.end >= 0 ? { file: sourceName(original.getSourceFile()), start: original.getStart(), end: original.end } : undefined
|
|
141
|
-
|
|
142
|
-
const scope = stateScopes.get(setters)
|
|
143
|
-
const handler = registerCommandHandler(moduleIR, commands, source, scope)
|
|
145
|
+
const handler = registerCommandHandler(moduleIR, commands.map(command => ({ ...command, owner: stateOwners.get(command.state) ?? owner })), source, owner)
|
|
144
146
|
return generateCommandBehavior(moduleIR, handler, factory)
|
|
145
147
|
}
|
|
146
148
|
|
|
147
149
|
function registerClientImports(imports) {
|
|
148
|
-
for (const entry of imports) if (!entry.package)
|
|
150
|
+
for (const entry of imports) if (!entry.package) clientModules.add(entry.target)
|
|
149
151
|
}
|
|
150
152
|
|
|
151
|
-
|
|
153
|
+
function registerDerivedResult(kind, value, states = [], node) {
|
|
154
|
+
const normalized = JSON.parse(JSON.stringify(value))
|
|
155
|
+
return registerDerived(moduleIR, { kind, [kind]: normalized, states: [...states], ...(source(node) ? { source: source(node) } : {}) })
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function finalize() {
|
|
159
|
+
const callbacks = [...nativeHandlers, ...effectHandlers]
|
|
160
|
+
for (const entry of callbacks) {
|
|
161
|
+
const lowered = handlerLowering.lowerNativeHandler(entry)
|
|
162
|
+
const setters = Map.groupBy(entry.setters, ([, state]) => state)
|
|
163
|
+
registerModuleHandler(moduleIR, {
|
|
164
|
+
role: entry.role,
|
|
165
|
+
exportName: entry.exportName,
|
|
166
|
+
async: Boolean(entry.expression.modifiers?.some(modifier => modifier.kind === ts.SyntaxKind.AsyncKeyword)),
|
|
167
|
+
generator: Boolean(entry.expression.asteriskToken),
|
|
168
|
+
signals: [...entry.usedStates].map(name => ({
|
|
169
|
+
name,
|
|
170
|
+
setters: (setters.get(name) ?? []).map(([setter]) => setter),
|
|
171
|
+
value: entry.deferValues ? "deferred" : "direct",
|
|
172
|
+
snapshot: lowered.stateSnapshots.includes(name)
|
|
173
|
+
})),
|
|
174
|
+
captures: [...entry.captures].map(name => ({
|
|
175
|
+
name,
|
|
176
|
+
source: name === (typeof entry.listItem === "string" ? entry.listItem : entry.listItem?.item) ? "list-item" : name === entry.listItem?.index ? "list-index" : "scope",
|
|
177
|
+
value: entry.deferValues ? "deferred" : "direct",
|
|
178
|
+
snapshot: lowered.captureSnapshots.includes(name)
|
|
179
|
+
})),
|
|
180
|
+
imports: entry.imports.map(importRecord),
|
|
181
|
+
code: lowered.code,
|
|
182
|
+
...(source(entry.expression) ? { source: source(entry.expression) } : {})
|
|
183
|
+
})
|
|
184
|
+
}
|
|
185
|
+
for (const entry of reactiveBindings) registerBinding(moduleIR, {
|
|
186
|
+
role: entry.role,
|
|
187
|
+
exportName: entry.exportName,
|
|
188
|
+
parameters: ["__k"],
|
|
189
|
+
states: [...entry.states],
|
|
190
|
+
captures: [...entry.captures].map(name => ({ name, source: "scope" })),
|
|
191
|
+
imports: entry.imports.map(importRecord),
|
|
192
|
+
code: handlerLowering.lowerReactiveBinding(entry),
|
|
193
|
+
...(source(entry.expression) ? { source: source(entry.expression) } : {})
|
|
194
|
+
})
|
|
195
|
+
for (const entry of listExpressions) registerBinding(moduleIR, {
|
|
196
|
+
role: entry.role,
|
|
197
|
+
exportName: entry.exportName,
|
|
198
|
+
parameters: [entry.item, entry.index ?? "__kIndex", "__k"],
|
|
199
|
+
states: [...(entry.states ?? [])],
|
|
200
|
+
captures: [],
|
|
201
|
+
imports: [],
|
|
202
|
+
code: handlerLowering.lowerListExpression(entry),
|
|
203
|
+
...(source(entry.expression) ? { source: source(entry.expression) } : {})
|
|
204
|
+
})
|
|
205
|
+
const imports = [...callbacks, ...reactiveBindings].flatMap(entry => entry.imports ?? []).map(importRecord)
|
|
206
|
+
moduleIR.imports = [...new Map(imports.map(entry => [`${entry.target}:${entry.kind}:${entry.imported ?? ""}:${entry.local}`, entry])).values()]
|
|
207
|
+
moduleIR.clientModules = [...clientModules]
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function source(node) {
|
|
211
|
+
if (!node) return undefined
|
|
212
|
+
const original = ts.getOriginalNode(node)
|
|
213
|
+
return original.pos >= 0 && original.end >= 0 ? { file: sourceName(original.getSourceFile()), start: original.getStart(), end: original.end } : undefined
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const importRecord = entry => ({ target: entry.target, kind: entry.kind, local: entry.local, ...(entry.imported ? { imported: entry.imported } : {}), package: Boolean(entry.package) })
|
|
217
|
+
|
|
218
|
+
return { compileConditional, compileEffectCallback, compileEvent, compileListConditional, compileListValue, compileReactiveBinding, finalize, registerDerived: registerDerivedResult }
|
|
152
219
|
}
|
|
153
220
|
|
|
154
221
|
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
|
}
|