@kudzujs/core 0.8.15 → 0.8.16

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.
@@ -7,14 +7,19 @@ import ts from "typescript"
7
7
  import { normalizeEffectAnimationFrameRefs } from "./compiler/animation-frame-pass.mjs"
8
8
  import { bindingNames, containsJsx, effectReturns, functionVarDeclaresName, importDeclarationNames, isFunctionLike, isLocalConst, isReferenceIdentifier, isShadowedByParameter, isShadowedIdentifier, isUnshadowedGlobal, nearestFunction, referenceIdentifiers, referencesIdentifier, sourceLocation, sourceNodeError, statementDeclaresName, unwrapExpression } from "./compiler/ast-helpers.mjs"
9
9
  import { normalizeMediaQueryExternalStores, normalizeNavigatorCapabilityConditions } from "./compiler/browser-signal-passes.mjs"
10
+ import { analyzeCollectionPipeline, collectionExpression, collectionParameters, isArrayFromCall, mutatingCollectionMethods as mutatingListMethods, pureCollectionMathMethods as pureMathMethods, pureCollectionMethods as pureListMethods } from "./compiler/collection-analysis.mjs"
10
11
  import { normalizeCustomHookTimerRefs } from "./compiler/custom-hook-timer-pass.mjs"
12
+ import { captureNames, createDescriptorSession, createSemanticArtifact, nativeCaptureNames, referencedReducerDispatches, referencedStateNames } from "./compiler/descriptor-session.mjs"
11
13
  import { createEffectCodegen } from "./compiler/effect-codegen.mjs"
14
+ import { createEventCommandCompiler } from "./compiler/event-command-pass.mjs"
12
15
  import { createHandlerCodegen } from "./compiler/handler-codegen.mjs"
13
16
  import { applyNormalizationPasses } from "./compiler/normalization-pipeline.mjs"
14
17
  import { createReactMigrationPass, reactMemoExpression } from "./compiler/react-migration-pass.mjs"
15
18
  import { normalizeRenderControlFlow } from "./compiler/render-control-pass.mjs"
16
19
  import { createRouterPass } from "./compiler/router-pass.mjs"
20
+ import { planRouteCapabilities, usesRouteDependencyRuntime } from "./compiler/route-capability-planner.mjs"
17
21
  import { createWorkerCompiler } from "./compiler/worker-compiler.mjs"
22
+ import { createZustandPass } from "./compiler/zustand-pass.mjs"
18
23
  import { renderPage } from "./core.mjs"
19
24
  import { parseDevHost, parseDevPort, startDevServer } from "./dev-server.mjs"
20
25
 
@@ -26,6 +31,8 @@ const pagesDirectory = join(sourceDirectory, "pages")
26
31
  const workDirectory = join(root, ".kudzu")
27
32
  const outputDirectory = join(root, "dist")
28
33
  const staticAssetExtensions = new Set([".avif", ".gif", ".ico", ".jpeg", ".jpg", ".otf", ".png", ".svg", ".ttf", ".webp", ".woff", ".woff2"])
34
+ const compileEventCommand = createEventCommandCompiler({ isPrimitiveLiteral: isPrimitiveDefaultLiteral, synthesizeSerializableStateLiteral })
35
+ const { analyzeZustandStores, normalizeZustandMigrationSyntax } = createZustandPass({ isSerializableStateLiteral, nativeCaptureNames, sourceDirectory })
29
36
 
30
37
  export async function build({ quiet = false, minify = true } = {}) {
31
38
  const config = await loadConfig()
@@ -73,14 +80,8 @@ export async function build({ quiet = false, minify = true } = {}) {
73
80
  if (handlerModule) handlerModules.push(handlerModule)
74
81
  }
75
82
 
76
- let behaviorCount = 0
77
- let regularBehaviorCount = 0
78
- let bindingCount = 0
79
- let listCount = 0
80
- let listStyleCount = 0
81
- let regularStateSeedCount = 0
82
- let dependencyStateSeedCount = 0
83
83
  const plans = []
84
+ const routeCapabilities = new Map()
84
85
  const pageEntries = []
85
86
  const effectEntries = []
86
87
  const nativeEntries = []
@@ -161,27 +162,24 @@ export async function build({ quiet = false, minify = true } = {}) {
161
162
  navigationGroup.hasEffects ||= result.hasEffects
162
163
  navigationGroup.hasParams ||= result.hasParams
163
164
  }
164
- const hasDependencies = result.plan.effects.some(effect => effect.dependencies?.length)
165
- const usesDependencyRuntime = !navigable && hasDependencies && !result.plan.effects.some(effect => effect.owner) && !result.hasBindings && !result.hasLists && !result.plan.events.some(event => event.native)
165
+ const usesDependencyRuntime = usesRouteDependencyRuntime({ plan: result.plan, navigable, hasBindings: result.hasBindings, hasLists: result.hasLists })
166
166
  pageEntries.push({ route, html: result.html, usesDependencyRuntime })
167
167
  plans.push({ route: routePath, ...result.plan })
168
+ routeCapabilities.set(routePath, {
169
+ navigable,
170
+ usesDependencyRuntime,
171
+ hasBehaviors: result.hasBehaviors,
172
+ hasBindings: result.hasBindings,
173
+ hasLists: result.hasLists,
174
+ hasListStyles: result.hasListStyles,
175
+ hasStateSeed: result.hasStateSeed
176
+ })
168
177
  if (result.hasParams) paramEntries.push({ path: paramPath, schema: runtimeSchema, params: result.plan.params, searchParams: result.plan.searchParams, searchParamsWritable: result.plan.searchParamsWritable, usesDependencyRuntime, navigable })
169
178
  if (result.hasEffects) effectEntries.push({ path: effectPath, effects: runtimeEffects(result.plan.effects, navigable), paramPath: result.hasParams ? paramPath : undefined, usesDependencyRuntime, navigable })
170
179
  if (result.plan.events.some(event => event.native)) nativeEntries.push({
171
180
  path: nativePath,
172
181
  modules: [...new Set(result.plan.events.filter(event => event.native).map(event => event.native.module))]
173
182
  })
174
- if (result.hasBehaviors) {
175
- behaviorCount++
176
- if (!usesDependencyRuntime) regularBehaviorCount++
177
- }
178
- if (result.hasBindings) bindingCount++
179
- if (result.hasLists) listCount++
180
- if (result.hasListStyles) listStyleCount++
181
- if (result.hasStateSeed) {
182
- if (usesDependencyRuntime) dependencyStateSeedCount++
183
- else regularStateSeedCount++
184
- }
185
183
  }
186
184
  }
187
185
 
@@ -208,43 +206,41 @@ export async function build({ quiet = false, minify = true } = {}) {
208
206
  }
209
207
  if (module.code.includes("/__kudzu_worker_")) throw new Error(`Worker URL placeholder survived in ${module.path}`)
210
208
  }
211
- const commandEvents = [...new Set(plans.flatMap(plan => plan.events.filter(event => event.commands).map(event => event.event)))].sort()
212
- const nativeEvents = [...new Set(plans.flatMap(plan => plan.events.filter(event => event.native).map(event => event.event)))].sort()
213
- const hasTextBindings = plans.some(plan => plan.bindings.some(binding => binding.target === "text"))
214
- const hasSvgConditions = plans.some(plan => plan.conditions.some(condition => condition.svg))
215
- const hasListConditions = plans.some(plan => plan.lists.some(list => list.conditions))
216
- const hasSvgLists = plans.some(plan => plan.lists.some(list => list.svg))
217
- const hasDeepListConditions = plans.some(plan => plan.lists.some(list => list.conditionHandlers))
218
- const hasListTextRanges = plans.some(plan => plan.lists.some(list => list.textRanges))
219
- const hasListAttributes = plans.some(plan => plan.lists.some(list => list.attributes))
220
- const hasListEvents = plans.some(plan => plan.lists.some(list => list.events))
221
- const hasListExpressions = plans.some(plan => plan.lists.some(list => list.expressions))
222
- const hasListExpressionAttributes = plans.some(plan => plan.lists.some(list => list.expressionAttributes))
223
- const hasListSeeds = plans.some(plan => plan.lists.some(list => list.seed || list.valueSeed))
224
- const hasListEffects = plans.some(plan => plan.lists.some(list => list.effects))
225
- const hasListRowHooks = plans.some(plan => plan.lists.some(list => list.rowStates?.length || list.rowRefs?.length))
226
- const hasListRowRefs = plans.some(plan => plan.lists.some(list => list.rowRefs?.length))
227
- const hasComplexListRowState = plans.some(plan => plan.lists.some(list => list.rowStates?.some(state => state.initialValue !== null && typeof state.initialValue === "object")))
228
- const hasNestedLists = plans.some(plan => plan.lists.some(list => list.ownerField))
229
- const hasCollectionSelectors = plans.some(plan => plan.lists.some(list => list.selector))
230
- const hasCalculatedCollections = plans.some(plan => plan.lists.some(list => list.source))
231
- const hasDerivedEffectDependencies = plans.some(plan => plan.effects.some(effect => effect.dependencyExpressions?.length))
232
- const hasStaticCollections = plans.some(plan => plan.lists.some(list => list.static))
233
- const hasListIndexes = plans.some(plan => plan.lists.some(list => list.indexed))
234
- const hasListStableFastPaths = plans.some(plan => plan.lists.some(list => !list.children && !list.ownerField && list.key !== null && !list.indexed && !list.reducer && !list.selector))
235
- const hasGeneralListRowHooks = hasListRowRefs || hasComplexListRowState || plans.some(plan => plan.lists.some(list => list.ownerField && (list.rowStates?.length || list.rowRefs?.length)))
236
- const hasItemDependencies = plans.some(plan => plan.effects.some(effect => effect.itemDependencies?.length))
237
- const hasListAsyncParts = hasListExpressions || hasListExpressionAttributes || hasListConditions
238
- const hasListMounts = hasListConditions || hasNestedLists || plans.some(plan => plan.lists.some(list => list.mount))
239
- const hasNestedStateCaptures = hasNestedCaptureState(plans)
240
- const hasSetterCaptures = hasCaptureType(plans, "setter")
241
- const hasEffectCaptures = plans.some(plan => plan.effects.some(effect => Object.keys(effect.scope).length))
242
- const hasNativeHandlers = nativeEntries.length > 0
243
- const hasEffects = effectEntries.length > 0
244
- const hasNavigableEffects = effectEntries.some(entry => entry.navigable)
245
- const hasNavigableOwners = effectEntries.some(entry => entry.navigable && entry.effects.some(effect => effect.owner))
246
- const hasSharedRuntime = bindingCount || listCount || hasNativeHandlers || navigationRoutes.length
247
- const hasDependencyRuntime = pageEntries.some(entry => entry.usesDependencyRuntime)
209
+ const capabilityManifest = planRouteCapabilities(plans, { routes: routeCapabilities, navigationRouteCount: navigationRoutes.length })
210
+ const {
211
+ routes: { behaviors: behaviorCount, regularBehaviors: regularBehaviorCount, regularStateSeeds: regularStateSeedCount, dependencyStateSeeds: dependencyStateSeedCount },
212
+ events: { command: commandEvents, native: nativeEvents, hasNativeHandlers },
213
+ bindings: { count: bindingCount, text: hasTextBindings, svgConditions: hasSvgConditions },
214
+ lists: {
215
+ count: listCount,
216
+ styleCount: listStyleCount,
217
+ conditions: hasListConditions,
218
+ svg: hasSvgLists,
219
+ deepConditions: hasDeepListConditions,
220
+ textRanges: hasListTextRanges,
221
+ attributes: hasListAttributes,
222
+ events: hasListEvents,
223
+ expressions: hasListExpressions,
224
+ expressionAttributes: hasListExpressionAttributes,
225
+ seeds: hasListSeeds,
226
+ effects: hasListEffects,
227
+ rowHooks: hasListRowHooks,
228
+ rowRefs: hasListRowRefs,
229
+ complexRowState: hasComplexListRowState,
230
+ nested: hasNestedLists,
231
+ selectors: hasCollectionSelectors,
232
+ calculated: hasCalculatedCollections,
233
+ static: hasStaticCollections,
234
+ indexes: hasListIndexes,
235
+ stableFastPaths: hasListStableFastPaths,
236
+ generalRowHooks: hasGeneralListRowHooks,
237
+ asyncParts: hasListAsyncParts,
238
+ mounts: hasListMounts
239
+ },
240
+ effects: { any: hasEffects, derivedDependencies: hasDerivedEffectDependencies, itemDependencies: hasItemDependencies, captures: hasEffectCaptures, navigable: hasNavigableEffects, navigableOwners: hasNavigableOwners },
241
+ captures: { nestedState: hasNestedStateCaptures, setter: hasSetterCaptures },
242
+ runtime: { shared: hasSharedRuntime, dependency: hasDependencyRuntime }
243
+ } = capabilityManifest
248
244
  const runtimeName = usesDependencyRuntime => usesDependencyRuntime ? "kudzu-deps.js" : "kudzu.js"
249
245
  for (const entry of pageEntries) {
250
246
  const routeDirectory = join(outputDirectory, entry.route)
@@ -631,20 +627,6 @@ addEventListener("popstate", () => ${searchParams.length ? "initializeSearch(loc
631
627
  ${searchInitializer}${prefix}${pathname}${query}${suffix}${writer}`
632
628
  }
633
629
 
634
- function hasCaptureType(value, type) {
635
- if (!value || typeof value !== "object") return false
636
- if (value.type === type) return true
637
- return (Array.isArray(value) ? value : Object.values(value)).some(entry => hasCaptureType(entry, type))
638
- }
639
-
640
- function hasNestedCaptureState(value, insideCapture = false) {
641
- if (!value || typeof value !== "object") return false
642
- if (value.type === "state") return insideCapture
643
- if (value.type === "array") return value.value.some(entry => hasNestedCaptureState(entry, true))
644
- if (value.type === "object") return value.value.some(([, entry]) => hasNestedCaptureState(entry, true))
645
- return (Array.isArray(value) ? value : Object.values(value)).some(entry => hasNestedCaptureState(entry, false))
646
- }
647
-
648
630
  export function specializeRuntime(source, events, hasStateSeed) {
649
631
  const specialized = specializeEvents(source, events)
650
632
  if (hasStateSeed) return specialized
@@ -695,11 +677,7 @@ function escapeAttribute(value) {
695
677
 
696
678
  async function compile(file, sourceFiles, sourceIndex, staticFiles, importedAssets, cssModules, base, workerReferences) {
697
679
  const source = sourceIndex.get(file)
698
- const nativeHandlers = []
699
- const effectHandlers = []
700
- const reactiveBindings = []
701
- const listExpressions = []
702
- const clientImports = new Set()
680
+ const semantic = createSemanticArtifact()
703
681
  const handlerPath = `handlers/${relative(sourceDirectory, file).replaceAll(sep, "/").replace(/\.(?:ts|tsx)$/, ".js")}`
704
682
  const result = ts.transpileModule(source, {
705
683
  fileName: file,
@@ -709,7 +687,7 @@ async function compile(file, sourceFiles, sourceIndex, staticFiles, importedAsse
709
687
  jsx: ts.JsxEmit.ReactJSX,
710
688
  jsxImportSource: "@kudzujs/core"
711
689
  },
712
- transformers: { before: [createKudzuTransformer(nativeHandlers, effectHandlers, reactiveBindings, listExpressions, assetPath(base, `assets/${handlerPath}`), file, sourceFiles, sourceIndex, staticFiles, importedAssets, cssModules, base, clientImports, workerReferences)] },
690
+ transformers: { before: [createKudzuTransformer({ semantic, handlerUrl: assetPath(base, `assets/${handlerPath}`), file, sourceFiles, sourceIndex, staticFiles, importedAssets, cssModules, base, workerReferences })] },
713
691
  reportDiagnostics: true
714
692
  })
715
693
 
@@ -724,6 +702,7 @@ async function compile(file, sourceFiles, sourceIndex, staticFiles, importedAsse
724
702
  await mkdir(resolve(output, ".."), { recursive: true })
725
703
  await writeFile(output, result.outputText)
726
704
 
705
+ const { nativeHandlers, effectHandlers, reactiveBindings, listExpressions, clientImports } = semantic
727
706
  if (!nativeHandlers.length && !effectHandlers.length && !reactiveBindings.length && !listExpressions.length) return undefined
728
707
  const callbacks = [...nativeHandlers, ...effectHandlers]
729
708
  const moduleSource = printHandlerModule({ callbacks, reactiveBindings, listExpressions, handlerPath })
@@ -819,94 +798,6 @@ function normalizeClsxSyntax(sourceFile, factory, context) {
819
798
  return ts.visitNode(sourceFile, visitor)
820
799
  }
821
800
 
822
- function analyzeZustandStores(sourceFile) {
823
- const createNames = new Set()
824
- for (const statement of sourceFile.statements) {
825
- if (!ts.isImportDeclaration(statement) || statement.importClause?.isTypeOnly || !ts.isStringLiteral(statement.moduleSpecifier) || statement.moduleSpecifier.text !== "zustand") continue
826
- const bindings = statement.importClause?.namedBindings
827
- if (statement.importClause?.name || !bindings || !ts.isNamedImports(bindings)) throw sourceNodeError(statement, sourceFile, "Zustand migration input requires a named create import")
828
- for (const entry of bindings.elements) {
829
- if (entry.isTypeOnly) continue
830
- if ((entry.propertyName ?? entry.name).text !== "create") throw sourceNodeError(entry, sourceFile, "Only Zustand create is supported")
831
- createNames.add(entry.name.text)
832
- }
833
- }
834
- const stores = new Map()
835
- if (!createNames.size) return stores
836
- for (const statement of sourceFile.statements) {
837
- if (!ts.isVariableStatement(statement) || !statement.modifiers?.some(modifier => modifier.kind === ts.SyntaxKind.ExportKeyword)) continue
838
- for (const declaration of statement.declarationList.declarations) {
839
- if (!ts.isIdentifier(declaration.name) || !declaration.initializer || !ts.isCallExpression(declaration.initializer) || !ts.isIdentifier(declaration.initializer.expression) || !createNames.has(declaration.initializer.expression.text)) continue
840
- const callback = declaration.initializer.arguments[0]
841
- if (declaration.initializer.arguments.length !== 1 || !callback || (!ts.isArrowFunction(callback) && !ts.isFunctionExpression(callback)) || callback.parameters.length !== 1 || !ts.isIdentifier(callback.parameters[0].name) || callback.asteriskToken || callback.modifiers?.some(modifier => modifier.kind === ts.SyntaxKind.AsyncKeyword)) throw sourceNodeError(declaration.initializer, sourceFile, "Zustand create() requires one synchronous initializer with one set parameter")
842
- const body = unwrapExpression(callback.body)
843
- if (!ts.isObjectLiteralExpression(body)) throw sourceNodeError(callback.body, sourceFile, "Zustand create() initializer must return one object literal")
844
- const data = []
845
- const actions = new Map()
846
- for (const property of body.properties) {
847
- if (!ts.isPropertyAssignment(property) || !property.name || !(ts.isIdentifier(property.name) || ts.isStringLiteral(property.name))) throw sourceNodeError(property, sourceFile, "Zustand store entries must be ordinary properties")
848
- const name = property.name.text
849
- const value = unwrapExpression(property.initializer)
850
- if (ts.isArrowFunction(value) || ts.isFunctionExpression(value)) actions.set(name, value)
851
- else data.push({ name, value })
852
- }
853
- if (data.length !== 1 || !isSerializableStateLiteral(data[0].value)) throw sourceNodeError(body, sourceFile, "Zustand migration stores require exactly one directly serializable data property")
854
- if (!actions.size) throw sourceNodeError(body, sourceFile, "Zustand migration stores require at least one action")
855
- for (const [name, action] of actions) {
856
- if (action.asteriskToken || action.modifiers?.some(modifier => modifier.kind === ts.SyntaxKind.AsyncKeyword)) throw sourceNodeError(action, sourceFile, `Zustand action ${JSON.stringify(name)} must be synchronous`)
857
- const capture = [...nativeCaptureNames(action, new Map())].find(entry => entry !== callback.parameters[0].name.text)
858
- if (capture) throw sourceNodeError(action, sourceFile, `Zustand action ${JSON.stringify(name)} cannot capture ${JSON.stringify(capture)}`)
859
- const validateAction = node => {
860
- if (ts.isAwaitExpression(node) || ts.isYieldExpression(node) || ts.isNewExpression(node)) throw sourceNodeError(node, sourceFile, `Zustand action ${JSON.stringify(name)} must be synchronous`)
861
- if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression) && ["then", "catch", "finally"].includes(node.expression.name.text)) throw sourceNodeError(node, sourceFile, `Zustand action ${JSON.stringify(name)} cannot schedule asynchronous updates`)
862
- if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === callback.parameters[0].name.text && !isShadowedIdentifier(node.expression, action)) {
863
- if (nearestFunction(node) !== action) throw sourceNodeError(node, sourceFile, `Zustand action ${JSON.stringify(name)} must call set directly`)
864
- if (node.arguments.length !== 1) throw sourceNodeError(node, sourceFile, `Zustand action ${JSON.stringify(name)} set() requires exactly one partial update`)
865
- }
866
- ts.forEachChild(node, validateAction)
867
- }
868
- validateAction(action.body)
869
- }
870
- stores.set(declaration.name.text, { name: declaration.name.text, setName: callback.parameters[0].name.text, field: data[0].name, initialValue: data[0].value, actions, declaration })
871
- }
872
- }
873
- const visit = node => {
874
- const recognized = ts.isIdentifier(node) && ts.isCallExpression(node.parent) && node.parent.expression === node && [...stores.values()].some(store => store.declaration.initializer === node.parent)
875
- if (ts.isIdentifier(node) && createNames.has(node.text) && isReferenceIdentifier(node) && !isShadowedIdentifier(node, sourceFile) && !recognized) throw sourceNodeError(node, sourceFile, "Zustand create must directly initialize an exported const store")
876
- ts.forEachChild(node, visit)
877
- }
878
- visit(sourceFile)
879
- return stores
880
- }
881
-
882
- function normalizeZustandMigrationSyntax(sourceFile, factory, context) {
883
- const stores = analyzeZustandStores(sourceFile)
884
- if (!stores.size) {
885
- const declaration = sourceFile.statements.find(statement => ts.isImportDeclaration(statement) && ts.isStringLiteral(statement.moduleSpecifier) && statement.moduleSpecifier.text === "zustand" && !statement.importClause?.isTypeOnly)
886
- if (declaration) throw sourceNodeError(declaration, sourceFile, "Zustand create must directly initialize an exported const store")
887
- return sourceFile
888
- }
889
- const identity = name => `${relative(sourceDirectory, sourceFile.fileName).replaceAll(sep, "/")}#${name}`
890
- const visitor = node => {
891
- if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && stores.has(node.name.text)) {
892
- const store = stores.get(node.name.text)
893
- return factory.updateVariableDeclaration(node, node.name, node.exclamationToken, node.type, factory.createCallExpression(factory.createIdentifier("__kCreateStore"), undefined, [
894
- factory.createStringLiteral(identity(store.name)),
895
- factory.createStringLiteral(store.field),
896
- store.initialValue,
897
- factory.createArrayLiteralExpression([...store.actions.keys()].map(name => factory.createStringLiteral(name)))
898
- ]))
899
- }
900
- if (ts.isImportDeclaration(node) && ts.isStringLiteral(node.moduleSpecifier) && node.moduleSpecifier.text === "zustand") return undefined
901
- return ts.visitEachChild(node, visitor, context)
902
- }
903
- const normalized = ts.visitNode(sourceFile, visitor)
904
- const declaration = factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports([factory.createImportSpecifier(false, undefined, factory.createIdentifier("__kCreateStore"))])), factory.createStringLiteral("@kudzujs/core"))
905
- const statements = [...normalized.statements]
906
- statements.splice(statements.findLastIndex(statement => ts.isImportDeclaration(statement)) + 1, 0, declaration)
907
- return factory.updateSourceFile(normalized, statements)
908
- }
909
-
910
801
  function normalizeLazyStateInitializers(sourceFile, factory, context, file, sourceFiles, sourceIndex) {
911
802
  const bindings = new Set()
912
803
  for (const statement of sourceFile.statements) {
@@ -986,7 +877,8 @@ function normalizeCompilerSource(sourceFile, { base, context, file, importedColl
986
877
  return { sourceFile, customHookTimerStates }
987
878
  }
988
879
 
989
- function createKudzuTransformer(nativeHandlers, effectHandlers, reactiveBindings, listExpressions, handlerUrl, file, sourceFiles, sourceIndex, staticFiles, importedAssets, cssModules, base, clientImports, workerReferences) {
880
+ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourceIndex, staticFiles, importedAssets, cssModules, base, workerReferences }) {
881
+ const { nativeHandlers, effectHandlers } = semantic
990
882
  return context => sourceFile => {
991
883
  const hasLinkElements = /<link/i.test(sourceFile.text)
992
884
  const importedStaticCollections = importedSerializableCollections(sourceFile, file, sourceFiles, sourceIndex)
@@ -995,6 +887,15 @@ function createKudzuTransformer(nativeHandlers, effectHandlers, reactiveBindings
995
887
  sourceFile = normalized.sourceFile
996
888
  const { customHookTimerStates } = normalized
997
889
  const factory = context.factory
890
+ const descriptors = createDescriptorSession({
891
+ semantic,
892
+ handlerUrl,
893
+ factory,
894
+ context,
895
+ compileEventCommand,
896
+ isPrimitiveLiteral: isPrimitiveDefaultLiteral,
897
+ rejectWorkerConstructions: expression => workerCompiler.rejectConstructions(expression, expression.getSourceFile(), "Relative TypeScript Worker construction is only supported directly inside an inline useEffect() callback")
898
+ })
998
899
  const importBindings = clientImportBindings(sourceFile, file, sourceFiles)
999
900
  const packageBindings = packageImportBindings(sourceFile)
1000
901
  for (const [name] of packageBindings) {
@@ -1408,14 +1309,14 @@ function createKudzuTransformer(nativeHandlers, effectHandlers, reactiveBindings
1408
1309
  const call = unwrapExpression(current.expression)
1409
1310
  if (ts.isIdentifier(call.expression) && importBindings.has(call.expression.text) && importBindings.get(call.expression.text).kind !== "namespace") {
1410
1311
  validateImportedCalculation(call, current.name.text)
1411
- for (const argument of call.arguments) collectionExpression(argument, {}, (target, message) => fail(target, message.replace("Rendered collection", "Reactive imported calculation")), allowedNames)
1312
+ for (const argument of call.arguments) collectionExpression(argument, { fail: (target, message) => fail(target, message.replace("Rendered collection", "Reactive imported calculation")), stateNames: allowedNames })
1412
1313
  return factory.createNumericLiteral(0)
1413
1314
  }
1414
1315
  }
1415
1316
  return ts.visitEachChild(current, validate, context)
1416
1317
  }
1417
1318
  const normalized = ts.visitNode(value, validate)
1418
- collectionExpression(normalized, {}, (node, message) => fail(node, message.replace("Rendered collection", "Reactive JSX local")), allowedNames)
1319
+ collectionExpression(normalized, { fail: (node, message) => fail(node, message.replace("Rendered collection", "Reactive JSX local")), stateNames: allowedNames })
1419
1320
  return
1420
1321
  }
1421
1322
  const intl = constructor.expression.expression
@@ -1425,7 +1326,7 @@ function createKudzuTransformer(nativeHandlers, effectHandlers, reactiveBindings
1425
1326
  const roundAccess = rounded && ts.isCallExpression(rounded) && !rounded.questionDotToken && rounded.arguments.length === 1 && ts.isPropertyAccessExpression(rounded.expression) && !rounded.expression.questionDotToken && rounded.expression.name.text === "round" && ts.isIdentifier(rounded.expression.expression) && rounded.expression.expression.text === "Math" ? rounded.expression : undefined
1426
1327
  if (!roundAccess) fail(value, "Reactive JSX Intl.NumberFormat format() requires exactly Math.round(expression)")
1427
1328
  if (!isUnshadowedGlobal(roundAccess.expression, sourceFile)) fail(roundAccess.expression, "Reactive JSX Intl.NumberFormat requires the unshadowed global Math object")
1428
- collectionExpression(rounded.arguments[0], {}, (node, message) => fail(node, message.replace("Rendered collection", "Reactive JSX local")), allowedNames)
1329
+ collectionExpression(rounded.arguments[0], { fail: (node, message) => fail(node, message.replace("Rendered collection", "Reactive JSX local")), stateNames: allowedNames })
1429
1330
  }
1430
1331
  const resolveReactiveJsxExpression = (expression, owner, setters) => {
1431
1332
  const declarations = jsxLocalDeclarations.get(owner)
@@ -1997,16 +1898,12 @@ function createKudzuTransformer(nativeHandlers, effectHandlers, reactiveBindings
1997
1898
  if (!usedStates.size && !captures.size) return ts.visitEachChild(expression, visitor, context)
1998
1899
  usesBehavior = true
1999
1900
  usesConditional = true
2000
- return compileConditional(
1901
+ return descriptors.compileConditional(
2001
1902
  parts.kind,
2002
1903
  parts.condition,
2003
1904
  compileRenderExpression(parts.truthy, anchor),
2004
1905
  compileRenderExpression(parts.falsy, anchor),
2005
- setters,
2006
- factory,
2007
- context,
2008
- reactiveBindings,
2009
- handlerUrl
1906
+ setters
2010
1907
  )
2011
1908
  }
2012
1909
 
@@ -2106,7 +2003,7 @@ function createKudzuTransformer(nativeHandlers, effectHandlers, reactiveBindings
2106
2003
  const derivedStates = initializer && !directAlias ? referencedStateNames(initializer, setters) : new Set()
2107
2004
  if (derivedStates.size) {
2108
2005
  const usedStates = new Set()
2109
- const expression = collectionExpression(initializer, {}, effectFail, stateNames, usedStates)
2006
+ const expression = collectionExpression(initializer, { fail: effectFail, stateNames, selectorStates: usedStates })
2110
2007
  if (!usedStates.size) effectFail(dependency, `useEffect() derived dependency "${dependency.text}" must read direct primitive state`)
2111
2008
  dependencyExpressions.push(expression)
2112
2009
  for (const name of usedStates) {
@@ -2162,7 +2059,15 @@ function createKudzuTransformer(nativeHandlers, effectHandlers, reactiveBindings
2162
2059
  } else {
2163
2060
  compiledCallback = workerCompiler.rewriteEffect(compiledCallback, callbackFile, callbackSource, sourceFiles, workerReferences, factory, context)
2164
2061
  }
2165
- const descriptor = compileNativeCallback(compiledCallback, setters, reducersForNode(node, reducersByFunction), factory, effectHandlers, specializedEffect?.imports ?? importBindings, clientImports, "effect", dependencyItem, true, returns.cleanup, customHookTimerStates)
2062
+ const descriptor = descriptors.compileEffectCallback(compiledCallback, {
2063
+ setters,
2064
+ reducers: reducersForNode(node, reducersByFunction),
2065
+ importBindings: specializedEffect?.imports ?? importBindings,
2066
+ listItem: dependencyItem,
2067
+ deferValues: true,
2068
+ snapshotNested: returns.cleanup,
2069
+ liveStates: customHookTimerStates
2070
+ })
2166
2071
  for (const reference of workerReferences.slice(workerStart)) Object.assign(reference, { module: handlerUrl, handler: descriptor.exportName })
2167
2072
  usesListItem ||= Boolean(itemDependencies.length && !listEffect)
2168
2073
  usesBehavior = true
@@ -2218,19 +2123,19 @@ function createKudzuTransformer(nativeHandlers, effectHandlers, reactiveBindings
2218
2123
 
2219
2124
  if (ts.isJsxExpression(node) && node.expression && listConditions.has(node.expression)) {
2220
2125
  const entry = listConditions.get(node.expression)
2221
- return factory.updateJsxExpression(node, compileListConditional({
2126
+ return factory.updateJsxExpression(node, descriptors.compileListConditional({
2222
2127
  ...entry,
2223
2128
  truthy: ts.visitNode(entry.truthy, visitor),
2224
2129
  falsy: ts.visitNode(entry.falsy, visitor)
2225
- }, factory, listExpressions, handlerUrl))
2130
+ }))
2226
2131
  }
2227
2132
 
2228
2133
  if (ts.isJsxExpression(node) && node.expression && listValues.has(node.expression)) {
2229
- return factory.updateJsxExpression(node, compileListValue(node.expression, listValues.get(node.expression), factory, context, listExpressions, handlerUrl))
2134
+ return factory.updateJsxExpression(node, descriptors.compileListValue(node.expression, listValues.get(node.expression)))
2230
2135
  }
2231
2136
 
2232
2137
  if (ts.isJsxAttribute(node) && node.initializer && ts.isJsxExpression(node.initializer) && node.initializer.expression && listValues.has(node.initializer.expression)) {
2233
- return factory.updateJsxAttribute(node, node.name, factory.createJsxExpression(undefined, compileListValue(node.initializer.expression, listValues.get(node.initializer.expression), factory, context, listExpressions, handlerUrl)))
2138
+ return factory.updateJsxAttribute(node, node.name, factory.createJsxExpression(undefined, descriptors.compileListValue(node.initializer.expression, listValues.get(node.initializer.expression))))
2234
2139
  }
2235
2140
 
2236
2141
  if (ts.isJsxExpression(node) && node.initializer === undefined && node.expression && (ts.isJsxElement(node.parent) || ts.isJsxFragment(node.parent))) {
@@ -2242,7 +2147,7 @@ function createKudzuTransformer(nativeHandlers, effectHandlers, reactiveBindings
2242
2147
  let listSource = listParts.state
2243
2148
  if (listParts.calculation) {
2244
2149
  usesBinding = true
2245
- listSource = compileReactiveBinding(listParts.calculation, settersForNode(node, settersByFunction), factory, context, reactiveBindings, handlerUrl, importBindings, clientImports)
2150
+ listSource = descriptors.compileReactiveBinding(listParts.calculation, { setters: settersForNode(node, settersByFunction), importBindings })
2246
2151
  }
2247
2152
  const arguments_ = [
2248
2153
  listSource,
@@ -2268,7 +2173,7 @@ function createKudzuTransformer(nativeHandlers, effectHandlers, reactiveBindings
2268
2173
  if ((usedStates.size || captures.size) && !ts.isIdentifier(expression) && !containsJsx(expression)) {
2269
2174
  usesBehavior = true
2270
2175
  usesBinding = true
2271
- return factory.updateJsxExpression(node, compileReactiveBinding(expression, setters, factory, context, reactiveBindings, handlerUrl, importBindings, clientImports))
2176
+ return factory.updateJsxExpression(node, descriptors.compileReactiveBinding(expression, { setters, importBindings }))
2272
2177
  }
2273
2178
  }
2274
2179
 
@@ -2281,14 +2186,20 @@ function createKudzuTransformer(nativeHandlers, effectHandlers, reactiveBindings
2281
2186
  if ((usedStates.size || captures.size) && !ts.isIdentifier(expression)) {
2282
2187
  usesBehavior = true
2283
2188
  usesBinding = true
2284
- const compiled = compileReactiveBinding(expression, setters, factory, context, reactiveBindings, handlerUrl, importBindings, clientImports)
2189
+ const compiled = descriptors.compileReactiveBinding(expression, { setters, importBindings })
2285
2190
  return factory.updateJsxAttribute(node, node.name, factory.createJsxExpression(undefined, compiled))
2286
2191
  }
2287
2192
  }
2288
2193
 
2289
2194
  if (ts.isJsxAttribute(node) && node.initializer && ts.isJsxExpression(node.initializer) && node.initializer.expression && /^on[A-Z]/.test(node.name.text)) {
2290
2195
  const setters = settersForNode(node, settersByFunction)
2291
- const event = compileEvent(node.initializer.expression, setters, reducersForNode(node, reducersByFunction), functionsForNode(node), factory, nativeHandlers, handlerUrl, listEventItems.get(node), new Map([...importBindings, ...packageBindings]), clientImports)
2196
+ const event = descriptors.compileEvent(node.initializer.expression, {
2197
+ setters,
2198
+ reducers: reducersForNode(node, reducersByFunction),
2199
+ functions: functionsForNode(node),
2200
+ listItem: listEventItems.get(node),
2201
+ importBindings: new Map([...importBindings, ...packageBindings])
2202
+ })
2292
2203
  if (event) {
2293
2204
  usesBehavior = true
2294
2205
  return factory.updateJsxAttribute(node, node.name, factory.createJsxExpression(undefined, event))
@@ -2356,7 +2267,9 @@ function keyedListParts(expression, setters, declarations, fail, aliases = new S
2356
2267
  const value = unwrapExpression(expression)
2357
2268
  const directFrom = isArrayFromCall(value) && value.arguments.length === 2 && containsJsx(value.arguments[1])
2358
2269
  if (!directFrom && (!ts.isCallExpression(value) || value.arguments.length !== 1 || !ts.isPropertyAccessExpression(value.expression) || value.expression.name.text !== "map")) return undefined
2359
- let collection = renderedCollectionSource(directFrom ? value.arguments[0] : value.expression.expression, setters, declarations, fail, aliases, importedCollections, new Set(setters.values()), importedCollectionTransforms, factory, context, calculatedCollection, staticCollection)
2270
+ let collection = analyzeCollectionPipeline(directFrom ? value.arguments[0] : value.expression.expression, {
2271
+ setters, declarations, fail, aliases, importedCollections, stateNames: new Set(setters.values()), importedCollectionTransforms, calculatedCollection, staticCollection
2272
+ })
2360
2273
  if (!collection?.state && !collection?.calculation) return undefined
2361
2274
  if (directFrom) collection.selector.push(["from", undefined])
2362
2275
  let callback = directFrom ? value.arguments[1] : value.arguments[0]
@@ -2366,7 +2279,7 @@ function keyedListParts(expression, setters, declarations, fail, aliases = new S
2366
2279
  if (!context || root.statements.length !== 2 || !ts.isVariableStatement(root.statements[0]) || (root.statements[0].declarationList.flags & ts.NodeFlags.Const) === 0 || root.statements[0].declarationList.declarations.length !== 1 || !ts.isReturnStatement(root.statements[1]) || !root.statements[1].expression) fail(root, "Block-bodied keyed list map callbacks require one computed child collection const and a final JSX return")
2367
2280
  const declaration = root.statements[0].declarationList.declarations[0]
2368
2281
  if (!ts.isIdentifier(declaration.name) || !declaration.initializer) fail(declaration, "Computed child collections must initialize one const identifier")
2369
- const computed = renderedCollectionSource(declaration.initializer, new Map(), undefined, fail, new Set(), new Set(), new Set(), importedCollectionTransforms, factory, context)
2282
+ const computed = analyzeCollectionPipeline(declaration.initializer, { fail, importedCollectionTransforms })
2370
2283
  if (!computed?.ownerField || computed.parentItem !== parameters.item) fail(declaration.initializer, `Computed child collections must start from ${parameters.item}.<field>`)
2371
2284
  const returned = root.statements[1].expression
2372
2285
  if (identifierReferenceCount(returned, declaration.name.text) !== 1) fail(declaration.name, `Computed child collection alias "${declaration.name.text}" must be used exactly once`)
@@ -2390,7 +2303,7 @@ function keyedListParts(expression, setters, declarations, fail, aliases = new S
2390
2303
  function nestedKeyedListParts(expression, parentItem, fail) {
2391
2304
  const value = unwrapExpression(expression)
2392
2305
  if (!ts.isCallExpression(value) || value.arguments.length !== 1 || !ts.isPropertyAccessExpression(value.expression) || value.expression.name.text !== "map") return undefined
2393
- let collection = renderedCollectionSource(value.expression.expression, new Map(), undefined, fail, new Set())
2306
+ let collection = analyzeCollectionPipeline(value.expression.expression, { fail })
2394
2307
  if (!collection?.ownerField || collection.parentItem !== parentItem) return undefined
2395
2308
  let callback = value.arguments[0]
2396
2309
  const parameters = collectionParameters(callback, "Nested keyed list map", fail)
@@ -2422,175 +2335,13 @@ function conditionalKeyedMapRoot(callback, root, parameters, collection, fail, s
2422
2335
  }
2423
2336
  if (parameters.index) fail(callback.parameters[1], "Conditional keyed map callbacks cannot use a map index because filtering changes index semantics; use an explicit filter(...).map((item, index) => ...) when a filtered index is intended")
2424
2337
  const selectorStates = new Set(collection.selectorStates)
2425
- const selector = collectionExpression(condition, parameters, fail, stateNames, selectorStates)
2338
+ const selector = collectionExpression(condition, { parameters, fail, stateNames, selectorStates })
2426
2339
  const normalized = factory.updateArrowFunction(callback, callback.modifiers, callback.typeParameters, callback.parameters, callback.type, callback.equalsGreaterThanToken, rendered)
2427
2340
  ts.setParentRecursive(normalized, false)
2428
2341
  normalized.parent = parent
2429
2342
  return { callback: normalized, root: rendered, collection: { ...collection, selector: [...collection.selector, ["filter", selector]], selectorStates } }
2430
2343
  }
2431
2344
 
2432
- function renderedCollectionSource(expression, setters, declarations, fail, aliases, importedCollections = new Set(), stateNames = new Set(), importedCollectionTransforms = new Map(), factory = ts.factory, context, calculatedCollection, staticCollection) {
2433
- const value = unwrapExpression(expression)
2434
- if (ts.isIdentifier(value)) {
2435
- if ([...setters.values()].includes(value.text)) {
2436
- const localStatic = staticCollection?.(value.text)
2437
- return { state: value, static: localStatic, localStatic, selector: [], selectorStates: new Set() }
2438
- }
2439
- if (importedCollections.has(value.text)) return { state: value, static: true, selector: [], selectorStates: new Set() }
2440
- const entries = declarations?.get(value.text)
2441
- if (!entries) return undefined
2442
- if (entries.length !== 1 || aliases.has(value.text) || entries[0].node.parent?.parent?.parent !== nearestFunction(entries[0].node)?.body) fail(value, `Rendered collection alias "${value.text}" must be one top-level immutable local`)
2443
- aliases.add(value.text)
2444
- const source = renderedCollectionSource(entries[0].initializer, setters, declarations, fail, aliases, importedCollections, stateNames, importedCollectionTransforms, factory, context, calculatedCollection, staticCollection)
2445
- aliases.delete(value.text)
2446
- return source && { ...source, aliasDeclarations: [...(source.aliasDeclarations ?? []), entries[0].node], aliasUses: [...(source.aliasUses ?? []), value] }
2447
- }
2448
- if (ts.isPropertyAccessExpression(value) && ts.isIdentifier(value.expression)) {
2449
- const calculation = calculatedCollection?.(value)
2450
- if (calculation) return { calculation, selector: [], selectorStates: new Set() }
2451
- return { state: undefined, ownerField: value.name.text, selector: [], parentItem: value.expression.text }
2452
- }
2453
- if (ts.isCallExpression(value) && ts.isIdentifier(value.expression) && importedCollectionTransforms.has(value.expression.text)) {
2454
- const transform = importedCollectionTransforms.get(value.expression.text)
2455
- const parameter = transform.parameters[0]
2456
- if (value.arguments.length !== 1 || transform.parameters.length !== 1 || transform.asteriskToken || transform.modifiers?.some(modifier => modifier.kind === ts.SyntaxKind.AsyncKeyword) || !parameter || !ts.isIdentifier(parameter.name) || parameter.dotDotDotToken || parameter.initializer || parameter.questionToken) fail(value, `Imported collection transform "${value.expression.text}" must be synchronous with exactly one identifier parameter and one argument`)
2457
- const returned = ts.isBlock(transform.body)
2458
- ? transform.body.statements.length === 1 && ts.isReturnStatement(transform.body.statements[0]) ? transform.body.statements[0].expression : undefined
2459
- : transform.body
2460
- if (!returned) fail(value, `Imported collection transform "${value.expression.text}" must contain only one returned collection expression`)
2461
- const transformSource = renderedCollectionSource(returned, new Map([[parameter.name.text, parameter.name.text]]), undefined, fail, new Set(), new Set(), new Set([parameter.name.text]))
2462
- if (!transformSource?.state || transformSource.state.text !== parameter.name.text || transformSource.selectorStates.size) fail(value, `Imported collection transform "${value.expression.text}" must return a supported pure pipeline rooted only in its parameter`)
2463
- const source = renderedCollectionSource(value.arguments[0], setters, declarations, fail, aliases, importedCollections, stateNames, importedCollectionTransforms, factory, context, calculatedCollection, staticCollection)
2464
- if (!source) fail(value.arguments[0], `Imported collection transform "${value.expression.text}" requires a supported collection argument`)
2465
- return { ...source, selector: [...source.selector, ...transformSource.selector] }
2466
- }
2467
- if (ts.isCallExpression(value) && ts.isPropertyAccessExpression(value.expression)) {
2468
- const method = value.expression.name.text
2469
- if (method === "filter") {
2470
- if (value.arguments.length !== 1) fail(value, "Rendered collection filter() requires one inline predicate")
2471
- const source = renderedCollectionSource(value.expression.expression, setters, declarations, fail, aliases, importedCollections, stateNames, importedCollectionTransforms, factory, context, calculatedCollection, staticCollection)
2472
- if (!source) return undefined
2473
- const parameters = collectionParameters(value.arguments[0], "Rendered collection filter()", fail)
2474
- const selectorStates = new Set(source.selectorStates)
2475
- return { ...source, selector: [...source.selector, ["filter", collectionExpression(unwrapExpression(value.arguments[0].body), parameters, fail, stateNames, selectorStates)]], selectorStates }
2476
- }
2477
- if (method === "flatMap") {
2478
- if (value.arguments.length !== 1) fail(value, "Rendered collection flatMap() requires one inline projector")
2479
- const source = renderedCollectionSource(value.expression.expression, setters, declarations, fail, aliases, importedCollections, stateNames, importedCollectionTransforms, factory, context, calculatedCollection, staticCollection)
2480
- if (!source) return undefined
2481
- const parameters = collectionParameters(value.arguments[0], "Rendered collection flatMap()", fail)
2482
- const field = directProperty(value.arguments[0].body, parameters.item)
2483
- if (!field) fail(value.arguments[0].body, `Rendered collection flatMap() projector must be ${parameters.item}.<field>`)
2484
- if (["__proto__", "constructor", "prototype"].includes(field)) fail(value.arguments[0].body, `Rendered collection property "${field}" is not supported`)
2485
- return { ...source, selector: [...source.selector, ["flatMap", field]] }
2486
- }
2487
- if (method === "slice") {
2488
- if (value.arguments.length < 1 || value.arguments.length > 2) fail(value, "Rendered collection slice() requires a start and optional end")
2489
- const source = renderedCollectionSource(value.expression.expression, setters, declarations, fail, aliases, importedCollections, stateNames, importedCollectionTransforms, factory, context, calculatedCollection, staticCollection)
2490
- if (!source) return undefined
2491
- const selectorStates = new Set(source.selectorStates)
2492
- const start = collectionExpression(value.arguments[0], {}, fail, stateNames, selectorStates)
2493
- const end = value.arguments[1] && collectionExpression(value.arguments[1], {}, fail, stateNames, selectorStates)
2494
- return { ...source, selector: [...source.selector, ["slice", start, end]], selectorStates }
2495
- }
2496
- if (method === "toSorted") {
2497
- if (value.arguments.length !== 1) fail(value, "Rendered collection toSorted() requires one inline comparator")
2498
- const source = renderedCollectionSource(value.expression.expression, setters, declarations, fail, aliases, importedCollections, stateNames, importedCollectionTransforms, factory, context, calculatedCollection, staticCollection)
2499
- if (!source) return undefined
2500
- const comparator = value.arguments[0]
2501
- const parameters = collectionParameters(comparator, "Rendered collection toSorted()", fail)
2502
- if (comparator.parameters.length !== 2 || ts.isBlock(comparator.body)) fail(comparator, "Rendered collection toSorted() comparator must be a synchronous expression arrow with (left, right) identifier parameters")
2503
- const selectorStates = new Set(source.selectorStates)
2504
- const expression = collectionExpression(comparator.body, parameters, fail, stateNames, selectorStates)
2505
- return { ...source, selector: [...source.selector, ["sort", expression]], selectorStates }
2506
- }
2507
- if (method === "sort") fail(value, "Rendered collections cannot use mutating sort(); use toSorted()")
2508
- }
2509
- if (isArrayFromCall(value)) {
2510
- if (value.arguments.length < 1 || value.arguments.length > 2) fail(value, "Rendered Array.from() requires an anchor and optional inline mapper")
2511
- const source = renderedCollectionSource(value.arguments[0], setters, declarations, fail, aliases, importedCollections, stateNames, importedCollectionTransforms, factory, context, calculatedCollection, staticCollection)
2512
- if (!source) return undefined
2513
- let mapper
2514
- if (value.arguments[1]) {
2515
- const parameters = collectionParameters(value.arguments[1], "Rendered Array.from() mapper", fail)
2516
- const selectorStates = new Set(source.selectorStates)
2517
- mapper = collectionExpression(unwrapExpression(value.arguments[1].body), parameters, fail, stateNames, selectorStates)
2518
- source.selectorStates = selectorStates
2519
- }
2520
- return { ...source, selector: [...source.selector, ["from", mapper]] }
2521
- }
2522
- }
2523
-
2524
- function isArrayFromCall(value) {
2525
- return ts.isCallExpression(value) && ts.isPropertyAccessExpression(value.expression) && ts.isIdentifier(value.expression.expression) && value.expression.expression.text === "Array" && value.expression.name.text === "from"
2526
- }
2527
-
2528
- function collectionParameters(callback, label, fail) {
2529
- if (!ts.isArrowFunction(callback) || callback.modifiers?.some(modifier => modifier.kind === ts.SyntaxKind.AsyncKeyword) || callback.parameters.length < 1 || callback.parameters.length > 2 || callback.parameters.some(parameter => !ts.isIdentifier(parameter.name) || parameter.dotDotDotToken || parameter.initializer || parameter.questionToken)) fail(callback, `${label} callback must be a synchronous arrow function with (item) or (item, index) identifier parameters`)
2530
- return { item: callback.parameters[0].name.text, index: callback.parameters[1]?.name.text }
2531
- }
2532
-
2533
- function collectionExpression(expression, parameters, fail, stateNames = new Set(), selectorStates = new Set()) {
2534
- const encode = node => {
2535
- node = unwrapExpression(node)
2536
- if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node) || ts.isNumericLiteral(node)) return ["value", ts.isNumericLiteral(node) ? Number(node.text) : node.text]
2537
- if (node.kind === ts.SyntaxKind.TrueKeyword) return ["value", true]
2538
- if (node.kind === ts.SyntaxKind.FalseKeyword) return ["value", false]
2539
- if (node.kind === ts.SyntaxKind.NullKeyword) return ["value", null]
2540
- if (ts.isIdentifier(node)) {
2541
- if (node.text === parameters.item) return ["item"]
2542
- if (node.text === parameters.index) return ["index"]
2543
- if (node.text === "undefined") return ["undefined"]
2544
- if (stateNames.has(node.text)) {
2545
- selectorStates.add(node.text)
2546
- return ["state", node.text]
2547
- }
2548
- fail(node, `Rendered collection expression identifier "${node.text}" is not allowed`)
2549
- }
2550
- if (ts.isPropertyAccessExpression(node)) {
2551
- if (["__proto__", "constructor", "prototype"].includes(node.name.text)) fail(node, `Rendered collection property "${node.name.text}" is not supported`)
2552
- return ["get", encode(node.expression), node.name.text, Boolean(node.questionDotToken)]
2553
- }
2554
- if (ts.isElementAccessExpression(node)) {
2555
- const key = node.argumentExpression
2556
- if (!ts.isStringLiteral(key) && !ts.isNumericLiteral(key)) fail(node, "Rendered collection computed properties require a direct string or numeric literal key")
2557
- if (ts.isStringLiteral(key) && ["__proto__", "constructor", "prototype"].includes(key.text)) fail(node, `Rendered collection property "${key.text}" is not supported`)
2558
- return ["get", encode(node.expression), ts.isNumericLiteral(key) ? Number(key.text) : key.text, Boolean(node.questionDotToken)]
2559
- }
2560
- if (ts.isPrefixUnaryExpression(node)) {
2561
- const operator = node.operator === ts.SyntaxKind.ExclamationToken ? "!" : node.operator === ts.SyntaxKind.PlusToken ? "+" : node.operator === ts.SyntaxKind.MinusToken ? "-" : undefined
2562
- if (!operator) fail(node, "Rendered collection expression uses an unsupported unary operator")
2563
- return ["unary", operator, encode(node.operand)]
2564
- }
2565
- if (ts.isTypeOfExpression(node)) return ["unary", "typeof", encode(node.expression)]
2566
- if (ts.isBinaryExpression(node)) {
2567
- const operator = node.operatorToken.getText()
2568
- if (!new Set(["&&", "||", "??", "===", "!==", "==", "!=", "<", "<=", ">", ">=", "+", "-", "*", "/", "%"]).has(operator)) fail(node, `Rendered collection expression operator "${operator}" is not supported`)
2569
- return ["binary", operator, encode(node.left), encode(node.right)]
2570
- }
2571
- if (ts.isConditionalExpression(node)) return ["conditional", encode(node.condition), encode(node.whenTrue), encode(node.whenFalse)]
2572
- if (ts.isArrayLiteralExpression(node) && !node.elements.some(ts.isSpreadElement)) return ["array", ...node.elements.map(encode)]
2573
- if (ts.isObjectLiteralExpression(node)) return ["object", ...node.properties.map(property => {
2574
- if (!ts.isPropertyAssignment(property) || !ts.isIdentifier(property.name) && !ts.isStringLiteral(property.name) && !ts.isNumericLiteral(property.name)) fail(property, "Rendered collection mapper objects require direct properties")
2575
- return [property.name.text, encode(property.initializer)]
2576
- })]
2577
- if (ts.isTemplateExpression(node)) return ["template", [node.head.text, ...node.templateSpans.map(span => span.literal.text)], node.templateSpans.map(span => encode(span.expression))]
2578
- if (ts.isCallExpression(node)) {
2579
- if (ts.isIdentifier(node.expression) && ["Boolean", "Number", "String"].includes(node.expression.text)) return ["global", node.expression.text, ...node.arguments.map(encode)]
2580
- if (ts.isPropertyAccessExpression(node.expression)) {
2581
- const method = node.expression.name.text
2582
- if (ts.isIdentifier(node.expression.expression) && node.expression.expression.text === "Math" && pureMathMethods.has(method)) return ["math", method, ...node.arguments.map(encode)]
2583
- if (pureListMethods.has(method)) return ["call", encode(node.expression.expression), method, ...node.arguments.map(encode)]
2584
- if (mutatingListMethods.has(method)) fail(node, `Rendered collection expressions cannot call mutating method "${method}"`)
2585
- }
2586
- fail(node, "Rendered collection expressions cannot call arbitrary functions")
2587
- }
2588
- if (ts.isArrowFunction(node) || ts.isFunctionExpression(node) || ts.isAwaitExpression(node) || ts.isNewExpression(node) || ts.isYieldExpression(node) || ts.isDeleteExpression(node) || ts.isPostfixUnaryExpression(node)) fail(node, "Rendered collection expressions must be pure and synchronous")
2589
- fail(node, "Rendered collection expression is not supported")
2590
- }
2591
- return encode(expression)
2592
- }
2593
-
2594
2345
  function jsonExpression(value, factory) {
2595
2346
  return factory.createCallExpression(factory.createPropertyAccessExpression(factory.createIdentifier("JSON"), "parse"), undefined, [factory.createStringLiteral(JSON.stringify(value))])
2596
2347
  }
@@ -3218,9 +2969,6 @@ function jsxTagUses(root, name) {
3218
2969
  return uses
3219
2970
  }
3220
2971
 
3221
- const pureListMethods = new Set(["at", "charAt", "charCodeAt", "concat", "endsWith", "includes", "indexOf", "join", "lastIndexOf", "localeCompare", "padEnd", "padStart", "repeat", "replace", "replaceAll", "slice", "startsWith", "substring", "toLowerCase", "toUpperCase", "trim", "trimEnd", "trimStart"])
3222
- const mutatingListMethods = new Set(["copyWithin", "fill", "pop", "push", "reverse", "shift", "sort", "splice", "unshift"])
3223
- const pureMathMethods = new Set(["abs", "ceil", "floor", "max", "min", "pow", "round", "sign", "sqrt", "trunc"])
3224
2972
  const pureListGlobals = new Set(["Boolean", "Infinity", "Math", "NaN", "Number", "String", "undefined"])
3225
2973
  const assignmentOperators = new Set([
3226
2974
  ts.SyntaxKind.EqualsToken, ts.SyntaxKind.PlusEqualsToken, ts.SyntaxKind.MinusEqualsToken,
@@ -3270,37 +3018,6 @@ function validateListExpression(expression, item, source, fail, index, states =
3270
3018
  visit(expression)
3271
3019
  }
3272
3020
 
3273
- function compileListExpression(read, expression, item, factory, listExpressions, handlerUrl, index, states = new Set()) {
3274
- const exportName = `listExpression${listExpressions.length}`
3275
- listExpressions.push({ exportName, expression, item, index, states })
3276
- const arguments_ = [read, factory.createStringLiteral(handlerUrl), factory.createStringLiteral(exportName)]
3277
- if (states.size) arguments_.push(factory.createArrayLiteralExpression([...states].map(name => factory.createArrayLiteralExpression([factory.createStringLiteral(name), factory.createIdentifier(name)]))))
3278
- return factory.createCallExpression(factory.createIdentifier("__kListExpression"), undefined, arguments_)
3279
- }
3280
-
3281
- function compileListConditional(entry, factory, listExpressions, handlerUrl) {
3282
- const exportName = `listExpression${listExpressions.length}`
3283
- listExpressions.push({ exportName, expression: entry.condition, item: entry.item, index: entry.index })
3284
- const read = factory.createArrowFunction(undefined, undefined, [], undefined, factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), entry.condition)
3285
- const thunk = branch => factory.createArrowFunction(undefined, undefined, [], undefined, factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), branch)
3286
- return factory.createCallExpression(factory.createIdentifier("__kListConditional"), undefined, [
3287
- factory.createStringLiteral(entry.kind), read, thunk(entry.truthy), thunk(entry.falsy), factory.createStringLiteral(handlerUrl), factory.createStringLiteral(exportName)
3288
- ])
3289
- }
3290
-
3291
- function compileListValue(expression, entry, factory, context, listExpressions, handlerUrl) {
3292
- const rewrite = node => {
3293
- if (ts.isShorthandPropertyAssignment(node) && entry.states?.has(node.name.text)) return factory.createPropertyAssignment(node.name, factory.createPropertyAccessExpression(node.name, "value"))
3294
- if (ts.isIdentifier(node) && entry.states?.has(node.text) && isReferenceIdentifier(node)) return factory.createPropertyAccessExpression(node, "value")
3295
- return ts.visitEachChild(node, rewrite, context)
3296
- }
3297
- const initial = entry.states?.size ? ts.visitNode(expression, rewrite) : expression
3298
- const read = factory.createArrowFunction(undefined, undefined, [], undefined, factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), initial)
3299
- return entry.field
3300
- ? factory.createCallExpression(factory.createIdentifier("__kListField"), undefined, [read, factory.createStringLiteral(entry.field)])
3301
- : compileListExpression(read, expression, entry.item, factory, listExpressions, handlerUrl, entry.index, entry.states)
3302
- }
3303
-
3304
3021
  function directProperty(expression, objectName) {
3305
3022
  const value = unwrapExpression(expression)
3306
3023
  if (!ts.isPropertyAccessExpression(value) || !ts.isIdentifier(value.expression)) return undefined
@@ -3337,69 +3054,6 @@ function isJsxLocalValue(expression, known) {
3337
3054
  return Boolean(parts && (isJsxLocalValue(parts.truthy, known) || isJsxLocalValue(parts.falsy, known)))
3338
3055
  }
3339
3056
 
3340
- function compileReactiveBinding(expression, setters, factory, context, reactiveBindings, handlerUrl, importBindings = new Map(), clientImports = new Set()) {
3341
- const parts = conditionalParts(expression)
3342
- const state = parts && directStateIdentifier(parts.condition, setters)
3343
- if (state && isPrimitiveDefaultLiteral(parts.truthy) && isPrimitiveDefaultLiteral(parts.falsy)) {
3344
- return factory.createCallExpression(factory.createIdentifier("__kSelect"), undefined, [state, parts.truthy, parts.falsy])
3345
- }
3346
- return factory.createCallExpression(factory.createIdentifier("__kBinding"), undefined, compileReactiveExpression(expression, setters, factory, context, reactiveBindings, handlerUrl, importBindings, clientImports))
3347
- }
3348
-
3349
- function compileConditional(kind, expression, truthy, falsy, setters, factory, context, reactiveBindings, handlerUrl) {
3350
- const state = directStateIdentifier(expression, setters)
3351
- const thunk = branch => factory.createArrowFunction(undefined, undefined, [], undefined, factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), branch)
3352
- if (state) return factory.createCallExpression(factory.createIdentifier("__kStateConditional"), undefined, [factory.createStringLiteral(kind), state, thunk(truthy), thunk(falsy)])
3353
- const [initial, ...descriptor] = compileReactiveExpression(expression, setters, factory, context, reactiveBindings, handlerUrl)
3354
- return factory.createCallExpression(factory.createIdentifier("__kConditional"), undefined, [factory.createStringLiteral(kind), initial, thunk(truthy), thunk(falsy), ...descriptor])
3355
- }
3356
-
3357
- function directStateIdentifier(expression, setters) {
3358
- const value = unwrapExpression(expression)
3359
- return ts.isIdentifier(value) && new Set(setters.values()).has(value.text) ? value : undefined
3360
- }
3361
-
3362
- function compileReactiveExpression(expression, setters, factory, context, reactiveBindings, handlerUrl, importBindings = new Map(), clientImports = new Set()) {
3363
- const usedStates = referencedStateNames(expression, setters)
3364
- const importedNames = referencedImportedBindings(expression, importBindings)
3365
- const imports = [...importedNames].map(name => importBindings.get(name))
3366
- for (const entry of imports) if (!entry.package) clientImports.add(entry.target)
3367
- const captures = new Set([...captureNames(expression, expression, setters)].filter(name => !importedNames.has(name)))
3368
- const exportName = `binding${reactiveBindings.length}`
3369
- reactiveBindings.push({ exportName, expression, captures, states: usedStates, imports })
3370
- const states = [...usedStates].map(name => factory.createArrayLiteralExpression([
3371
- factory.createStringLiteral(name),
3372
- factory.createIdentifier(name)
3373
- ]))
3374
- const scope = [...captures].map(name => factory.createArrayLiteralExpression([
3375
- factory.createStringLiteral(name),
3376
- factory.createIdentifier(name)
3377
- ]))
3378
- const stateNames = new Set(usedStates)
3379
- const rewriteInitial = node => {
3380
- if (ts.isShorthandPropertyAssignment(node) && stateNames.has(node.name.text)) {
3381
- return factory.createPropertyAssignment(node.name, factory.createPropertyAccessExpression(node.name, "value"))
3382
- }
3383
- if (ts.isIdentifier(node) && stateNames.has(node.text) && isReferenceIdentifier(node) && !isShadowedByParameter(node, expression)) {
3384
- return factory.createPropertyAccessExpression(node, "value")
3385
- }
3386
- if (ts.isShorthandPropertyAssignment(node) && captures.has(node.name.text)) {
3387
- return factory.createPropertyAssignment(node.name, factory.createCallExpression(factory.createIdentifier("__kBindingValue"), undefined, [node.name]))
3388
- }
3389
- if (ts.isIdentifier(node) && captures.has(node.text) && isReferenceIdentifier(node)) {
3390
- return factory.createCallExpression(factory.createIdentifier("__kBindingValue"), undefined, [node])
3391
- }
3392
- return ts.visitEachChild(node, rewriteInitial, context)
3393
- }
3394
- return [
3395
- ts.visitNode(expression, rewriteInitial),
3396
- factory.createStringLiteral(handlerUrl),
3397
- factory.createStringLiteral(exportName),
3398
- factory.createArrayLiteralExpression(states),
3399
- factory.createArrayLiteralExpression(scope)
3400
- ]
3401
- }
3402
-
3403
3057
  function conditionalParts(expression) {
3404
3058
  const unwrap = node => ts.isParenthesizedExpression(node) ? unwrap(node.expression) : node
3405
3059
  const value = unwrap(expression)
@@ -3416,138 +3070,6 @@ function factoryNull() {
3416
3070
  return ts.factory.createNull()
3417
3071
  }
3418
3072
 
3419
- function compileEvent(expression, setters, reducers, functions, factory, nativeHandlers, handlerUrl, listItem, importBindings, clientImports) {
3420
- if (ts.isIdentifier(expression)) expression = functions.get(expression.text)
3421
- if (!expression || (!ts.isArrowFunction(expression) && !ts.isFunctionExpression(expression) && !ts.isFunctionDeclaration(expression))) return undefined
3422
-
3423
- const optimized = referencedReducerDispatches(expression.body, reducers, expression).size ? undefined : compileOptimizedEvent(expression, setters, factory)
3424
- if (optimized) return optimized
3425
-
3426
- workerCompiler.rejectConstructions(expression, expression.getSourceFile(), "Relative TypeScript Worker construction is only supported directly inside an inline useEffect() callback")
3427
- const descriptor = compileNativeCallback(expression, setters, reducers, factory, nativeHandlers, importBindings, clientImports, "handler", listItem)
3428
- return factory.createCallExpression(factory.createIdentifier("__kNativeBehavior"), undefined, [
3429
- factory.createStringLiteral(handlerUrl),
3430
- factory.createStringLiteral(descriptor.exportName),
3431
- descriptor.states,
3432
- descriptor.scope
3433
- ])
3434
- }
3435
-
3436
- function compileNativeCallback(expression, setters, reducers, factory, entries, importBindings, clientImports, prefix, listItem, deferValues = false, snapshotNested = false, liveStates = new Set()) {
3437
- const allCaptures = nativeCaptureNames(expression, setters)
3438
- const usedReducers = referencedReducerDispatches(expression.body, reducers, expression)
3439
- const imports = [...referencedImportedBindings(expression, importBindings)].map(name => importBindings.get(name))
3440
- imports.push(...[...usedReducers].map(name => reducers.get(name).import).filter(Boolean))
3441
- const captures = new Set([...allCaptures].filter(name => !importBindings.has(name) && !usedReducers.has(name)))
3442
- for (const entry of imports) if (!entry.package) clientImports.add(entry.target)
3443
- const usedStates = nativeStateNames(expression, setters)
3444
- for (const name of usedReducers) {
3445
- const reducer = reducers.get(name)
3446
- if (reducer.contextAction) for (const state of referencedStateNames(reducer.contextAction.body, reducer.states, reducer.contextAction)) usedStates.add(state)
3447
- }
3448
- const exportName = `${prefix}${entries.length}`
3449
- 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 })
3450
- const value = name => deferValues
3451
- ? factory.createArrowFunction(undefined, undefined, [], undefined, factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), factory.createIdentifier(name))
3452
- : factory.createIdentifier(name)
3453
- return {
3454
- exportName,
3455
- states: factory.createArrayLiteralExpression([...usedStates].map(name => factory.createArrayLiteralExpression([
3456
- factory.createStringLiteral(name),
3457
- value(name)
3458
- ]))),
3459
- scope: factory.createArrayLiteralExpression([...captures].map(name => factory.createArrayLiteralExpression([
3460
- factory.createStringLiteral(name),
3461
- name === (typeof listItem === "string" ? listItem : listItem?.item)
3462
- ? factory.createCallExpression(factory.createIdentifier("__kListItem"), undefined, [])
3463
- : name === listItem?.index
3464
- ? factory.createCallExpression(factory.createIdentifier("__kListIndex"), undefined, [])
3465
- : value(name)
3466
- ])))
3467
- }
3468
- }
3469
-
3470
- function referencedReducerDispatches(root, reducers, scopeRoot = root) {
3471
- const used = new Set()
3472
- const visit = node => {
3473
- if (ts.isIdentifier(node) && reducers.has(node.text) && isReferenceIdentifier(node) && !isShadowedIdentifier(node, scopeRoot)) used.add(node.text)
3474
- ts.forEachChild(node, visit)
3475
- }
3476
- visit(root)
3477
- return used
3478
- }
3479
-
3480
- function nativeStateNames(expression, setters) {
3481
- return referencedStateNames(expression.body, setters, expression)
3482
- }
3483
-
3484
- function referencedStateNames(root, setters, scopeRoot = root) {
3485
- const stateNames = new Set(setters.values())
3486
- const used = new Set()
3487
- const visit = node => {
3488
- if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && setters.has(node.expression.text) && !isShadowedIdentifier(node.expression, scopeRoot)) used.add(setters.get(node.expression.text))
3489
- if (ts.isIdentifier(node) && setters.has(node.text) && isReferenceIdentifier(node) && !isShadowedIdentifier(node, scopeRoot)) used.add(setters.get(node.text))
3490
- if (ts.isIdentifier(node) && stateNames.has(node.text) && isReferenceIdentifier(node) && !isShadowedByParameter(node, scopeRoot)) used.add(node.text)
3491
- ts.forEachChild(node, visit)
3492
- }
3493
- visit(root)
3494
- return used
3495
- }
3496
-
3497
- function compileOptimizedEvent(expression, setters, factory) {
3498
- const statements = ts.isBlock(expression.body) ? expression.body.statements : [factory.createExpressionStatement(expression.body)]
3499
- const commands = statements.map(statement => {
3500
- if (!ts.isExpressionStatement(statement)) return undefined
3501
- return compileEventCommand(statement.expression, setters, factory)
3502
- })
3503
- if (!commands.length || commands.some(command => !command)) return undefined
3504
-
3505
- return factory.createCallExpression(factory.createIdentifier("__kBehavior"), undefined, [factory.createArrayLiteralExpression(commands)])
3506
- }
3507
-
3508
- const nativeGlobals = new Set([
3509
- "Array", "ArrayBuffer", "BigInt", "Blob", "Boolean", "Date", "Error", "Event", "FileReader", "FormData", "Infinity", "IntersectionObserver", "Intl", "JSON", "Map", "Math", "NaN", "Number", "Object", "Promise", "Proxy", "RangeError", "ReferenceError", "Reflect", "RegExp", "Set", "String", "Symbol", "TypeError", "URL", "URLSearchParams", "WeakMap", "WeakSet", "WebSocket", "Worker", "alert", "atob", "btoa", "cancelAnimationFrame", "clearInterval", "clearTimeout", "console", "crypto", "document", "fetch", "globalThis", "history", "isFinite", "isNaN", "localStorage", "location", "navigator", "parseFloat", "parseInt", "performance", "queueMicrotask", "requestAnimationFrame", "setInterval", "setTimeout", "structuredClone", "undefined", "window"
3510
- ])
3511
-
3512
- function nativeCaptureNames(expression, setters) {
3513
- return captureNames(expression, expression.body, setters)
3514
- }
3515
-
3516
- function referencedImportedBindings(expression, imports) {
3517
- const names = new Set()
3518
- const visit = node => {
3519
- if (ts.isIdentifier(node) && imports.has(node.text) && isReferenceIdentifier(node)) names.add(node.text)
3520
- ts.forEachChild(node, visit)
3521
- }
3522
- visit(expression.body ?? expression)
3523
- return names
3524
- }
3525
-
3526
- function captureNames(declarationRoot, referenceRoot, setters) {
3527
- const local = new Set()
3528
- if (!isFunctionLike(declarationRoot)) {
3529
- const collectDeclarations = node => {
3530
- if (ts.isVariableDeclaration(node)) for (const name of bindingNames(node.name)) local.add(name)
3531
- if (ts.isParameter(node)) for (const name of bindingNames(node.name)) local.add(name)
3532
- if ((ts.isFunctionDeclaration(node) || ts.isClassDeclaration(node)) && node.name) local.add(node.name.text)
3533
- ts.forEachChild(node, collectDeclarations)
3534
- }
3535
- collectDeclarations(declarationRoot)
3536
- }
3537
- const stateNames = new Set(setters.values())
3538
- const captures = new Set()
3539
- const visit = node => {
3540
- if (ts.isTypeNode(node)) return
3541
- if (ts.isIdentifier(node)) {
3542
- const declared = isFunctionLike(declarationRoot) ? isShadowedIdentifier(node, declarationRoot) : local.has(node.text)
3543
- if (isReferenceIdentifier(node) && !declared && !setters.has(node.text) && !stateNames.has(node.text) && !nativeGlobals.has(node.text)) captures.add(node.text)
3544
- }
3545
- ts.forEachChild(node, visit)
3546
- }
3547
- visit(referenceRoot)
3548
- return captures
3549
- }
3550
-
3551
3073
  function settersForNode(node, settersByFunction) {
3552
3074
  for (let current = node.parent; current; current = current.parent) {
3553
3075
  if (!ts.isFunctionDeclaration(current) && !ts.isFunctionExpression(current) && !ts.isArrowFunction(current)) continue
@@ -4059,42 +3581,6 @@ function relativeModulePath(from, to) {
4059
3581
  return path.startsWith(".") ? path : `./${path}`
4060
3582
  }
4061
3583
 
4062
- function compileEventCommand(expression, setters, factory) {
4063
- if (ts.isCallExpression(expression) && ts.isPropertyAccessExpression(expression.expression) && ts.isIdentifier(expression.expression.expression) && expression.expression.expression.text === "console" && expression.expression.name.text === "log" && expression.arguments.length === 2 && ts.isStringLiteral(expression.arguments[0]) && ts.isIdentifier(expression.arguments[1]) && [...setters.values()].includes(expression.arguments[1].text)) {
4064
- return command(factory, "log", expression.arguments[1], factory.createStringLiteral(expression.arguments[0].text))
4065
- }
4066
-
4067
- if (!ts.isCallExpression(expression) || !ts.isIdentifier(expression.expression) || expression.arguments.length !== 1) return undefined
4068
- const stateName = setters.get(expression.expression.text)
4069
- if (!stateName) return undefined
4070
-
4071
- const state = factory.createIdentifier(stateName)
4072
- const value = expression.arguments[0]
4073
- if (ts.isBinaryExpression(value) && ts.isIdentifier(value.left) && value.left.text === stateName && ts.isNumericLiteral(value.right)) {
4074
- if (value.operatorToken.kind !== ts.SyntaxKind.PlusToken && value.operatorToken.kind !== ts.SyntaxKind.MinusToken) return undefined
4075
- return command(factory, "add", state, numericExpression(factory, Number(value.right.text), value.operatorToken.kind === ts.SyntaxKind.MinusToken))
4076
- }
4077
- if (ts.isArrowFunction(value) && value.parameters.length === 1 && ts.isIdentifier(value.parameters[0].name) && ts.isBinaryExpression(value.body) && ts.isIdentifier(value.body.left) && value.body.left.text === value.parameters[0].name.text && ts.isNumericLiteral(value.body.right)) {
4078
- if (value.body.operatorToken.kind !== ts.SyntaxKind.PlusToken && value.body.operatorToken.kind !== ts.SyntaxKind.MinusToken) return undefined
4079
- return command(factory, "add", state, numericExpression(factory, Number(value.body.right.text), value.body.operatorToken.kind === ts.SyntaxKind.MinusToken))
4080
- }
4081
- if (isPrimitiveLiteral(value)) return command(factory, "set", state, synthesizeSerializableStateLiteral(value, factory))
4082
- return undefined
4083
- }
4084
-
4085
- function command(factory, operation, state, value) {
4086
- return factory.createArrayLiteralExpression([factory.createStringLiteral(operation), state, value])
4087
- }
4088
-
4089
- function isPrimitiveLiteral(node) {
4090
- return isPrimitiveDefaultLiteral(node)
4091
- }
4092
-
4093
- function numericExpression(factory, value, negative) {
4094
- const literal = factory.createNumericLiteral(value)
4095
- return negative ? factory.createPrefixUnaryExpression(ts.SyntaxKind.MinusToken, literal) : literal
4096
- }
4097
-
4098
3584
  function compiledPath(file) {
4099
3585
  return join(workDirectory, relative(sourceDirectory, file)).replace(/\.(?:ts|tsx)$/, ".mjs")
4100
3586
  }
@@ -4262,7 +3748,7 @@ const printHandlerModule = createHandlerCodegen({
4262
3748
  synthesizeTree,
4263
3749
  resolveClientImport: (entry, handlerPath) => entry.package ? entry.target : relativeModulePath(handlerPath, clientModulePath(entry.target))
4264
3750
  })
4265
- const { normalizeReactMigrationSyntax, validateUseIdSyntax } = createReactMigrationPass({ cloneAst, isArrayFromCall, jsxTagName, renderedCollectionSource })
3751
+ const { normalizeReactMigrationSyntax, validateUseIdSyntax } = createReactMigrationPass({ cloneAst, jsxTagName })
4266
3752
  const normalizeReactRouterSyntax = createRouterPass({ withBase })
4267
3753
 
4268
3754
  async function staticPathEntries(module, file) {