@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.
@@ -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), nativeHandlers: [], effectHandlers: [], reactiveBindings: [], listExpressions: [], clientImports: new Set() }
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, nativeHandlers, effectHandlers, reactiveBindings, listExpressions, clientImports } = semantic
13
- function compileListExpression(read, expression, item, index, states = new Set()) {
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) clientImports.add(entry.target)
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
- return { compileConditional, compileEffectCallback, compileEvent, compileListConditional, compileListValue, compileReactiveBinding }
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
- import ts from "typescript"
2
- import { isFunctionLike, isReferenceIdentifier, isShadowedByParameter, isShadowedIdentifier, sourceNodeError } from "./ast-helpers.mjs"
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([...callbacks, ...reactiveBindings].flatMap(handler => handler.imports ?? []), handlerPath),
8
- ...callbacks.map(handler => printNativeHandler(handler)),
9
- ...reactiveBindings.map(entry => printReactiveBinding(entry)),
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 unique = new Map(entries.map(entry => [`${entry.target}:${entry.kind}:${entry.imported ?? ""}:${entry.local}`, entry]))
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
  }