@kudzujs/core 0.8.36 → 0.8.38
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/MIGRATION_ROADMAP.md +1 -1
- package/PERFORMANCE.md +50 -0
- package/README.md +1 -1
- package/RELEASES.md +64 -0
- package/docs/next-architecture/README.md +1 -1
- package/docs/next-architecture/compiler-current-architecture.md +14 -12
- package/docs/next-architecture/large-application-ai-native-roadmap.md +7 -1
- package/docs/next-architecture/versioning.md +3 -1
- package/framework/README.md +1 -1
- package/framework/build.mjs +48 -43
- package/framework/compiler/analysis/binding-index.mjs +15 -1
- package/framework/compiler/analysis/component-analysis.mjs +8 -2
- package/framework/compiler/descriptor-session.mjs +72 -28
- package/framework/compiler/ir/module-ir.mjs +141 -30
- package/framework/compiler/route-build-record.mjs +81 -0
- package/framework/compiler/route-capability-planner.mjs +6 -3
- package/framework/compiler/source-compiler.mjs +108 -44
- package/framework/core.d.ts +1 -1
- package/framework/core.mjs +28 -11
- package/package.json +1 -1
|
@@ -257,6 +257,8 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
|
|
|
257
257
|
const { customHookTimerStates } = normalized
|
|
258
258
|
const bindingIndex = createBindingIndex(sourceFile)
|
|
259
259
|
const factory = context.factory
|
|
260
|
+
let activeStateOwners
|
|
261
|
+
let activeKeyedBlock
|
|
260
262
|
const sourceName = source => relative(root, source.fileName).replaceAll(sep, "/")
|
|
261
263
|
const componentAnalysis = createComponentAnalysisSession(semantic.componentAnalysis)
|
|
262
264
|
const descriptors = createDescriptorSession({
|
|
@@ -268,6 +270,27 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
|
|
|
268
270
|
compileEventCommand,
|
|
269
271
|
handlerLowering,
|
|
270
272
|
isPrimitiveLiteral: isPrimitiveDefaultLiteral,
|
|
273
|
+
stateReferences: node => new Map([...stateOwnersForNode(node), ...(activeStateOwners ?? [])]),
|
|
274
|
+
symbolReference: (name, node, aliases) => {
|
|
275
|
+
const names = new Set([name, ...aliases])
|
|
276
|
+
let binding
|
|
277
|
+
const visitName = current => {
|
|
278
|
+
if (binding) return
|
|
279
|
+
if (ts.isIdentifier(current)) {
|
|
280
|
+
if (names.has(current.text)) binding = current
|
|
281
|
+
return
|
|
282
|
+
}
|
|
283
|
+
for (const element of current.elements) if (ts.isBindingElement(element)) visitName(element.name)
|
|
284
|
+
}
|
|
285
|
+
for (let current = node; current && !binding; current = current.parent) if (isFunctionLike(current)) {
|
|
286
|
+
for (const parameter of current.parameters) visitName(parameter.name)
|
|
287
|
+
if (ts.isBlock(current.body)) for (const statement of current.body.statements) {
|
|
288
|
+
if (ts.isVariableStatement(statement)) for (const declaration of statement.declarationList.declarations) visitName(declaration.name)
|
|
289
|
+
if (ts.isFunctionDeclaration(statement) && statement.name) visitName(statement.name)
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
return binding ? { kind: "module-symbol", symbol: modules.symbol(binding.getSourceFile().fileName, binding, name) } : undefined
|
|
293
|
+
},
|
|
271
294
|
sourceName,
|
|
272
295
|
rejectWorkerConstructions: expression => workerCompiler.rejectConstructions(expression, expression.getSourceFile(), "Relative TypeScript Worker construction is only supported directly inside an inline useEffect() callback")
|
|
273
296
|
})
|
|
@@ -341,24 +364,33 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
|
|
|
341
364
|
const ensureOwner = (owner, kind = "component") => componentAnalysis.registerOwner(owner, { kind, name: ownerName(owner), props: analyzedProps(owner), site: analysisSite(owner, "owner"), source: analysisSource(owner) })
|
|
342
365
|
const registerState = (owner, state, setter, kind, node, externalOwner) => {
|
|
343
366
|
const ownerRecord = ensureOwner(owner)
|
|
344
|
-
const
|
|
367
|
+
const stateRecord = componentAnalysis.registerState(owner, { name: state, setter, kind, ...(externalOwner ? { owner: externalOwner.owner } : {}), site: analysisSite(node, "hook"), source: analysisSource(node) })
|
|
368
|
+
const stateOwner = externalOwner?.state
|
|
369
|
+
? { kind: "module-symbol", symbol: externalOwner.state }
|
|
370
|
+
: { kind: "state", owner: { kind: "component", slot: ownerRecord.slot }, slot: stateRecord.slot }
|
|
345
371
|
const stateOwners = stateOwnersByFunction.get(owner) ?? new Map()
|
|
346
372
|
stateOwners.set(state, stateOwner)
|
|
347
373
|
stateOwnersByFunction.set(owner, stateOwners)
|
|
348
|
-
return
|
|
374
|
+
return stateRecord
|
|
349
375
|
}
|
|
350
376
|
const stateOwnersForNode = node => {
|
|
351
377
|
for (let current = node.parent; current; current = current.parent) {
|
|
352
|
-
if (isFunctionLike(current)
|
|
378
|
+
if (isFunctionLike(current)) {
|
|
379
|
+
const stateOwners = stateOwnersByFunction.get(current) ?? stateOwnersByFunction.get(ts.getOriginalNode(current))
|
|
380
|
+
if (stateOwners) return stateOwners
|
|
381
|
+
const site = analysisSite(current, "owner")
|
|
382
|
+
const owner = site && semantic.componentAnalysis.owners.find(entry => entry.site === site)
|
|
383
|
+
if (owner) return new Map(owner.states.map(state => [state.name, { kind: "state", owner: { kind: "component", slot: owner.slot }, slot: state.slot }]))
|
|
384
|
+
}
|
|
353
385
|
}
|
|
354
386
|
return new Map()
|
|
355
387
|
}
|
|
356
388
|
const fallbackOwner = node => {
|
|
357
389
|
for (let current = node.parent; current; current = current.parent) {
|
|
358
390
|
const owner = isFunctionLike(current) ? componentAnalysis.owner(current) : undefined
|
|
359
|
-
if (owner) return
|
|
391
|
+
if (owner) return { kind: "component", slot: owner.slot }
|
|
360
392
|
}
|
|
361
|
-
return "module"
|
|
393
|
+
return { kind: "module" }
|
|
362
394
|
}
|
|
363
395
|
let usesBehavior = false
|
|
364
396
|
let usesBinding = false
|
|
@@ -405,16 +437,20 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
|
|
|
405
437
|
if (!value || !ts.isObjectLiteralExpression(value)) throw sourceNodeError(provider, providerSource, "Context Provider value must be one direct object literal")
|
|
406
438
|
const owner = nearestFunction(provider)
|
|
407
439
|
if (!owner) throw sourceNodeError(provider, providerSource, "Context Provider value must be returned by a component")
|
|
408
|
-
const stateOwner =
|
|
440
|
+
const stateOwner = { kind: "module-symbol", symbol: modules.symbol(providerSource.fileName, owner, ownerName(owner)) }
|
|
409
441
|
|
|
410
442
|
const states = new Map()
|
|
443
|
+
const stateSymbols = new Map()
|
|
411
444
|
const callbacks = new Map()
|
|
412
445
|
const hasUseState = hasFrameworkImport(providerSource, "useState")
|
|
413
446
|
const collectProviderBindings = node => {
|
|
414
447
|
if (ts.isVariableDeclaration(node) && nearestFunction(node) === owner) {
|
|
415
448
|
if (hasUseState && ts.isArrayBindingPattern(node.name) && node.initializer && ts.isCallExpression(node.initializer) && ts.isIdentifier(node.initializer.expression) && node.initializer.expression.text === "useState") {
|
|
416
449
|
const [state, setter] = node.name.elements
|
|
417
|
-
if (node.name.elements.length === 2 && state && setter && ts.isBindingElement(state) && ts.isBindingElement(setter) && ts.isIdentifier(state.name) && ts.isIdentifier(setter.name))
|
|
450
|
+
if (node.name.elements.length === 2 && state && setter && ts.isBindingElement(state) && ts.isBindingElement(setter) && ts.isIdentifier(state.name) && ts.isIdentifier(setter.name)) {
|
|
451
|
+
states.set(setter.name.text, state.name.text)
|
|
452
|
+
stateSymbols.set(state.name.text, modules.symbol(providerSource.fileName, state.name, state.name.text))
|
|
453
|
+
}
|
|
418
454
|
}
|
|
419
455
|
if (ts.isIdentifier(node.name) && node.initializer && (ts.isArrowFunction(node.initializer) || ts.isFunctionExpression(node.initializer))) callbacks.set(node.name.text, node.initializer)
|
|
420
456
|
}
|
|
@@ -443,7 +479,7 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
|
|
|
443
479
|
if (!setter || !fields.has(state) || !fields.has(setter)) throw sourceNodeError(callback, providerSource, `Context action ${JSON.stringify(name)} requires exposed state and setter fields for ${JSON.stringify(state)}`)
|
|
444
480
|
}
|
|
445
481
|
}
|
|
446
|
-
return { callbacks: new Map([...callbacks].filter(([name]) => fields.has(name))), context: true, fields, privateStates: new Set(), stateOwner, states }
|
|
482
|
+
return { callbacks: new Map([...callbacks].filter(([name]) => fields.has(name))), context: true, fields, privateStates: new Set(), stateOwner, stateSymbols, states }
|
|
447
483
|
}
|
|
448
484
|
|
|
449
485
|
const resolveCustomHook = (binding, call) => {
|
|
@@ -518,7 +554,7 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
|
|
|
518
554
|
if (!names.has(state) && !requiredContextStates.has(state)) continue
|
|
519
555
|
const localSetter = names.has(setter) || requiredContextStates.has(state) ? setter : `__kContextState_${state}`
|
|
520
556
|
setters.set(localSetter, state)
|
|
521
|
-
registerState(owner, state, localSetter, "context", node, hook.stateOwner)
|
|
557
|
+
registerState(owner, state, localSetter, "context", node, { owner: hook.stateOwner, state: hook.stateSymbols.get(state) })
|
|
522
558
|
if (requiredContextStates.has(state)) {
|
|
523
559
|
const fields = customHookPrivateFields.get(node) ?? []
|
|
524
560
|
for (const field of [state, setter]) {
|
|
@@ -813,6 +849,7 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
|
|
|
813
849
|
return expanded
|
|
814
850
|
}
|
|
815
851
|
const componentSpecializations = new WeakMap()
|
|
852
|
+
const specializedEffectStateOwners = new WeakMap()
|
|
816
853
|
const setterHookHelpers = new WeakMap()
|
|
817
854
|
const expandedRowSpecializations = new WeakMap()
|
|
818
855
|
const nestedRowSpecializations = new Map()
|
|
@@ -833,11 +870,11 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
|
|
|
833
870
|
if (ts.isIdentifier(expression) && setters.has(expression.text)) signals.add(setters.get(expression.text))
|
|
834
871
|
const callback = ts.isIdentifier(expression) ? callbacks.get(expression.text) : undefined
|
|
835
872
|
for (const state of referencedStateNames((callback ?? expression).body ?? callback ?? expression, setters, callback ?? expression, bindingIndex)) signals.add(state)
|
|
836
|
-
return [...signals].map(name => (
|
|
873
|
+
return [...signals].map(name => descriptors.signal(name, expression, stateOwners))
|
|
837
874
|
}
|
|
838
875
|
result.analysis = componentAnalysis.registerSpecialization({
|
|
839
876
|
kind: label,
|
|
840
|
-
...(owner ? { owner: ensureOwner(owner).slot } : {}),
|
|
877
|
+
...(owner ? { owner: { kind: "component", slot: ensureOwner(owner).slot } } : {}),
|
|
841
878
|
...(analysisSite(call, "component-call") ? { site: analysisSite(call, "component-call") } : {}),
|
|
842
879
|
...(analysisSource(call) ? { source: analysisSource(call) } : {}),
|
|
843
880
|
props: result.props.map(prop => {
|
|
@@ -852,8 +889,18 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
|
|
|
852
889
|
refs: [...result.rowRefs.map(({ name, source }) => ({ name, kind: "row", ...(analysisSite(source, "hook") ? { site: analysisSite(source, "hook") } : {}), ...(analysisSource(source) ? { source: analysisSource(source) } : {}) })), ...result.ordinaryRefs.map(({ name, source }) => ({ name, kind: "component", ...(analysisSite(source, "hook") ? { site: analysisSite(source, "hook") } : {}), ...(analysisSource(source) ? { source: analysisSource(source) } : {}) }))],
|
|
853
890
|
ids: result.ordinaryIds.map(({ name, source }) => ({ name, ...(analysisSite(source, "hook") ? { site: analysisSite(source, "hook") } : {}), ...(analysisSource(source) ? { source: analysisSource(source) } : {}) }))
|
|
854
891
|
})
|
|
855
|
-
|
|
856
|
-
|
|
892
|
+
result.propStateOwners = new Map(result.props.flatMap(prop => {
|
|
893
|
+
const expression = result.propExpressions.get(prop.name)
|
|
894
|
+
const value = expression && unwrapExpression(expression)
|
|
895
|
+
const reference = value && ts.isIdentifier(value) ? stateOwners.get(value.text) : undefined
|
|
896
|
+
return reference ? [[prop.local, reference]] : []
|
|
897
|
+
}))
|
|
898
|
+
for (const effect of result.effects) {
|
|
899
|
+
effect.stateOwners = result.propStateOwners
|
|
900
|
+
effect.analysisOwner = { kind: "specialization", slot: result.analysis.slot }
|
|
901
|
+
}
|
|
902
|
+
for (const [slot, state] of [...result.rowStates, ...result.ordinaryStates].entries()) state.analysisReference = { kind: "state", owner: { kind: "specialization", slot: result.analysis.slot }, slot }
|
|
903
|
+
for (const [slot, ref] of [...result.rowRefs, ...result.ordinaryRefs].entries()) ref.analysisReference = { specialization: result.analysis.slot, ref: slot }
|
|
857
904
|
return result
|
|
858
905
|
}
|
|
859
906
|
const registerRowHooks = (call, specialization) => {
|
|
@@ -870,7 +917,7 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
|
|
|
870
917
|
const stateOwners = new Map(stateOwnersByFunction.get(owner))
|
|
871
918
|
for (const state of specialization.rowStates) {
|
|
872
919
|
setters.set(state.setter, state.state)
|
|
873
|
-
stateOwners.set(state.state, state.
|
|
920
|
+
stateOwners.set(state.state, state.analysisReference)
|
|
874
921
|
}
|
|
875
922
|
settersByFunction.set(owner, setters)
|
|
876
923
|
stateOwnersByFunction.set(owner, stateOwners)
|
|
@@ -911,7 +958,7 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
|
|
|
911
958
|
merged.parent = root.parent
|
|
912
959
|
return merged
|
|
913
960
|
}
|
|
914
|
-
const expandReducerCallbacks = (root, componentSource, call) => {
|
|
961
|
+
const expandReducerCallbacks = (root, componentSource, call, ownership) => {
|
|
915
962
|
const componentImports = clientImportBindings(componentSource, componentSource.fileName, sourceFiles)
|
|
916
963
|
const replacements = new WeakMap()
|
|
917
964
|
let count = 0
|
|
@@ -927,7 +974,7 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
|
|
|
927
974
|
fail(nestedCalls[0], "Reducer callback props require a component imported from a relative TypeScript module")
|
|
928
975
|
}
|
|
929
976
|
for (const nestedCall of nestedCalls) {
|
|
930
|
-
const nested = specialize(nestedCall, nestedComponent, "Reducer-callback")
|
|
977
|
+
const nested = specialize(nestedCall, nestedComponent, "Reducer-callback", false, false, new Set(), ownership)
|
|
931
978
|
if (nested.effects.length) fail(nestedCall, "Reducer-callback components cannot declare effects")
|
|
932
979
|
nested.root = mergeSpecializedImports(nested.root, nestedComponent.getSourceFile(), nestedCall, nested.effects)
|
|
933
980
|
synthesizeTree(nested.root)
|
|
@@ -1001,7 +1048,7 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
|
|
|
1001
1048
|
const setters = new Map(parentSetters)
|
|
1002
1049
|
for (const state of aggregate.ordinaryStates) setters.set(state.setter, state.state)
|
|
1003
1050
|
const stateOwners = new Map(parentStateOwners)
|
|
1004
|
-
for (const state of aggregate.ordinaryStates) stateOwners.set(state.state, state.
|
|
1051
|
+
for (const state of aggregate.ordinaryStates) stateOwners.set(state.state, state.analysisReference)
|
|
1005
1052
|
if (jsxSetterCallbackProps(node, setters, functionsForNode(node), reducersForNode(node, reducersByFunction)).length) fail(node, "Setter callbacks cannot cross a second component boundary")
|
|
1006
1053
|
const nested = specialize(node, component, "Nested setter-callback", true, true, new Set(setters.values()), { setters, stateOwners })
|
|
1007
1054
|
if (dynamic && (nested.hookDeclarations.length || nested.effects.length)) fail(node, "Hookful nested setter-callback components require an unconditional or statically truthy render path")
|
|
@@ -1099,6 +1146,7 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
|
|
|
1099
1146
|
const effectCall = factory.updateCallExpression(entry.call, factory.createIdentifier("__kComponentUseEffect"), entry.call.typeArguments, entry.call.arguments)
|
|
1100
1147
|
synthesizeTree(effectCall)
|
|
1101
1148
|
ts.setOriginalNode(effectCall, entry.source)
|
|
1149
|
+
specializedEffectStateOwners.set(effectCall, { owner: { kind: "specialization", slot: specialization.analysis.slot }, references: specialization.propStateOwners })
|
|
1102
1150
|
return factory.createExpressionStatement(effectCall)
|
|
1103
1151
|
})
|
|
1104
1152
|
const helper = factory.createFunctionDeclaration(
|
|
@@ -1118,8 +1166,8 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
|
|
|
1118
1166
|
const setters = new Map(settersForNode(call, settersByFunction))
|
|
1119
1167
|
for (const state of specialization.ordinaryStates) setters.set(state.setter, state.state)
|
|
1120
1168
|
settersByFunction.set(helper, setters)
|
|
1121
|
-
const stateOwners = new Map(stateOwnersForNode(call))
|
|
1122
|
-
for (const state of specialization.ordinaryStates) stateOwners.set(state.state, state.
|
|
1169
|
+
const stateOwners = new Map([...stateOwnersForNode(call), ...specialization.propStateOwners])
|
|
1170
|
+
for (const state of specialization.ordinaryStates) stateOwners.set(state.state, state.analysisReference)
|
|
1123
1171
|
stateOwnersByFunction.set(helper, stateOwners)
|
|
1124
1172
|
usesComponentState ||= specialization.ordinaryStates.length > 0
|
|
1125
1173
|
usesComponentId ||= specialization.usesComponentId
|
|
@@ -1162,7 +1210,10 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
|
|
|
1162
1210
|
if (componentSpecializations.has(call)) fail(call, "Reducer dispatch props cannot be combined with another component specialization")
|
|
1163
1211
|
const specialization = specialize(call, component.function, "Reducer-dispatch")
|
|
1164
1212
|
registerRowHooks(call, specialization)
|
|
1165
|
-
specialization.root = expandReducerCallbacks(specialization.root, component.function.getSourceFile(), call
|
|
1213
|
+
specialization.root = expandReducerCallbacks(specialization.root, component.function.getSourceFile(), call, {
|
|
1214
|
+
setters: new Map([...settersForNode(call, settersByFunction), ...[...specialization.rowStates, ...specialization.ordinaryStates].map(state => [state.setter, state.state])]),
|
|
1215
|
+
stateOwners: new Map([...stateOwnersForNode(call), ...[...specialization.rowStates, ...specialization.ordinaryStates].map(state => [state.state, state.analysisReference])])
|
|
1216
|
+
})
|
|
1166
1217
|
componentSpecializations.set(call, specialization)
|
|
1167
1218
|
reducerComponentCalls.add(call)
|
|
1168
1219
|
}
|
|
@@ -1185,7 +1236,10 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
|
|
|
1185
1236
|
if (componentSpecializations.has(call)) fail(call, "Reducer dispatch props cannot be combined with another component specialization")
|
|
1186
1237
|
const specialization = specialize(call, component, "Reducer-dispatch")
|
|
1187
1238
|
registerRowHooks(call, specialization)
|
|
1188
|
-
specialization.root = expandReducerCallbacks(specialization.root, componentSource, call
|
|
1239
|
+
specialization.root = expandReducerCallbacks(specialization.root, componentSource, call, {
|
|
1240
|
+
setters: new Map([...settersForNode(call, settersByFunction), ...[...specialization.rowStates, ...specialization.ordinaryStates].map(state => [state.setter, state.state])]),
|
|
1241
|
+
stateOwners: new Map([...stateOwnersForNode(call), ...[...specialization.rowStates, ...specialization.ordinaryStates].map(state => [state.state, state.analysisReference])])
|
|
1242
|
+
})
|
|
1189
1243
|
specialization.root = mergeSpecializedImports(specialization.root, componentSource, call, specialization.effects)
|
|
1190
1244
|
synthesizeTree(specialization.root)
|
|
1191
1245
|
componentSpecializations.set(call, specialization)
|
|
@@ -1337,6 +1391,7 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
|
|
|
1337
1391
|
const call = factory.updateCallExpression(entry.call, factory.createIdentifier("__kListUseEffect"), entry.call.typeArguments, entry.call.arguments)
|
|
1338
1392
|
synthesizeTree(call)
|
|
1339
1393
|
ts.setOriginalNode(call, entry.source)
|
|
1394
|
+
specializedEffectStateOwners.set(call, { owner: entry.analysisOwner, references: entry.stateOwners ?? specialization.propStateOwners })
|
|
1340
1395
|
return factory.createExpressionStatement(call)
|
|
1341
1396
|
}))
|
|
1342
1397
|
}
|
|
@@ -1375,7 +1430,7 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
|
|
|
1375
1430
|
specializations: [specialization.analysis?.slot, ...(specialization.specializations ?? [])].filter(slot => slot !== undefined),
|
|
1376
1431
|
rowStates: [...specialization.rowStates, ...specialization.ordinaryStates],
|
|
1377
1432
|
rowRefs: specialization.rowRefs,
|
|
1378
|
-
analysisStateOwners: new Map([...stateOwnersForNode(originalParts.root), ...[...specialization.rowStates, ...specialization.ordinaryStates].map(state => [state.state, state.
|
|
1433
|
+
analysisStateOwners: new Map([...stateOwnersForNode(originalParts.root), ...(specialization.propStateOwners ?? []), ...[...specialization.rowStates, ...specialization.ordinaryStates].map(state => [state.state, state.analysisReference])])
|
|
1379
1434
|
}
|
|
1380
1435
|
for (const calculation of specialization.calculations) {
|
|
1381
1436
|
ts.setParentRecursive(calculation, false)
|
|
@@ -1404,8 +1459,6 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
|
|
|
1404
1459
|
)
|
|
1405
1460
|
}
|
|
1406
1461
|
|
|
1407
|
-
let activeStateOwners
|
|
1408
|
-
let activeKeyedBlock
|
|
1409
1462
|
const visitWithStateOwners = (node, stateOwners) => {
|
|
1410
1463
|
const previous = activeStateOwners
|
|
1411
1464
|
activeStateOwners = new Map([...(previous ?? []), ...stateOwners])
|
|
@@ -1419,18 +1472,23 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
|
|
|
1419
1472
|
usesList = true
|
|
1420
1473
|
const blockSlot = moduleIR.keyedBlocks.length
|
|
1421
1474
|
let listSource = listParts.state
|
|
1422
|
-
|
|
1475
|
+
const collectionName = listParts.state?.text
|
|
1476
|
+
const collectionReference = collectionName && new Map([...stateOwnersForNode(node), ...(activeStateOwners ?? [])]).get(collectionName)
|
|
1477
|
+
const collectionSymbol = listParts.state && bindingIndex.resolveReference(listParts.state, node)?.slot
|
|
1478
|
+
let collection = collectionReference
|
|
1479
|
+
? { kind: "signal", signal: descriptors.signal(collectionName, node, new Map([[collectionName, collectionReference]])) }
|
|
1480
|
+
: collectionSymbol !== undefined ? { kind: "symbol", symbol: collectionSymbol } : { kind: "static" }
|
|
1423
1481
|
if (listParts.calculation) {
|
|
1424
1482
|
usesBinding = true
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
collection = { kind: "binding",
|
|
1483
|
+
const compiled = descriptors.compileReactiveBinding(listParts.calculation, { setters: settersForNode(node, settersByFunction), importBindings, keyedBlock: blockSlot })
|
|
1484
|
+
listSource = compiled.node
|
|
1485
|
+
collection = { kind: "binding", binding: compiled.binding }
|
|
1428
1486
|
}
|
|
1429
1487
|
const derived = listParts.selector?.length ? descriptors.registerDerived("selector", listParts.selector, listParts.selectorStates, node) : undefined
|
|
1430
1488
|
const parent = activeKeyedBlock?.block
|
|
1431
|
-
const rowStates = (listParts.rowStates ?? []).map(state => ({ name: state.state, setter: state.setter,
|
|
1432
|
-
const rowRefs = (listParts.rowRefs ?? []).map(ref => ({ name: ref.name,
|
|
1433
|
-
const specializations = [...new Set([...(listParts.specializations ?? []), ...rowStates.map(state => state.owner), ...rowRefs.map(ref => ref.
|
|
1489
|
+
const rowStates = (listParts.rowStates ?? []).map(state => ({ name: state.state, setter: state.setter, signal: descriptors.signal(state.state, node, new Map([[state.state, state.analysisReference]])), ...(analysisSource(state.source) ? { source: analysisSource(state.source) } : {}) }))
|
|
1490
|
+
const rowRefs = (listParts.rowRefs ?? []).map(ref => ({ name: ref.name, ...ref.analysisReference, ...(analysisSource(ref.source) ? { source: analysisSource(ref.source) } : {}) }))
|
|
1491
|
+
const specializations = [...new Set([...(listParts.specializations ?? []), ...rowStates.map(state => moduleIR.signals[state.signal].reference.owner?.slot), ...rowRefs.map(ref => ref.specialization)].filter(value => value !== undefined))]
|
|
1434
1492
|
const block = descriptors.registerKeyedBlock({
|
|
1435
1493
|
...(analysisSite(node, "keyed-list") ? { site: analysisSite(node, "keyed-list") } : {}),
|
|
1436
1494
|
...(analysisSource(node) ? { source: analysisSource(node) } : {}),
|
|
@@ -1444,7 +1502,7 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
|
|
|
1444
1502
|
indexed: listParts.indexed,
|
|
1445
1503
|
static: Boolean(listParts.static),
|
|
1446
1504
|
...(derived ? { selector: derived.slot } : {}),
|
|
1447
|
-
|
|
1505
|
+
selectorSignals: [...(listParts.selectorStates ?? [])].map(name => descriptors.signal(name, node)),
|
|
1448
1506
|
specializations,
|
|
1449
1507
|
rowStates,
|
|
1450
1508
|
rowRefs
|
|
@@ -1462,7 +1520,7 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
|
|
|
1462
1520
|
jsonExpression(derived?.selector ?? listParts.selector ?? [], factory),
|
|
1463
1521
|
block.indexed ? factory.createTrue() : factory.createFalse()
|
|
1464
1522
|
]
|
|
1465
|
-
if (block.
|
|
1523
|
+
if (block.selectorSignals.length || block.static) arguments_.push(factory.createArrayLiteralExpression([...(listParts.selectorStates ?? [])].map(name => factory.createArrayLiteralExpression([factory.createStringLiteral(name), factory.createIdentifier(name)]))))
|
|
1466
1524
|
if (block.static) arguments_.push(factory.createTrue())
|
|
1467
1525
|
return factory.updateJsxExpression(node, factory.createCallExpression(factory.createIdentifier("__kList"), undefined, arguments_))
|
|
1468
1526
|
}
|
|
@@ -1480,7 +1538,7 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
|
|
|
1480
1538
|
if (specializedDeclarations.has(node)) return node
|
|
1481
1539
|
if (componentSpecializations.has(node)) {
|
|
1482
1540
|
const specialization = componentSpecializations.get(node)
|
|
1483
|
-
const stateOwners = new Map([...stateOwnersForNode(node), ...[...specialization.rowStates, ...specialization.ordinaryStates].map(state => [state.state, state.
|
|
1541
|
+
const stateOwners = new Map([...stateOwnersForNode(node), ...(specialization.propStateOwners ?? []), ...[...specialization.rowStates, ...specialization.ordinaryStates].map(state => [state.state, state.analysisReference])])
|
|
1484
1542
|
return visitWithStateOwners(specialization.root, stateOwners)
|
|
1485
1543
|
}
|
|
1486
1544
|
|
|
@@ -1595,6 +1653,7 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
|
|
|
1595
1653
|
importBindings: specializedEffect?.imports ?? importBindings,
|
|
1596
1654
|
listItem: dependencyItem,
|
|
1597
1655
|
keyedBlock: activeKeyedBlock?.block.slot,
|
|
1656
|
+
stateOwners: new Map([...stateOwnersForNode(callbackArgument), ...stateOwnersForNode(node), ...(specializedEffectStateOwners.get(node)?.references ?? []), ...(activeStateOwners ?? [])]),
|
|
1598
1657
|
deferValues: true,
|
|
1599
1658
|
snapshotNested: returns.cleanup,
|
|
1600
1659
|
liveStates: customHookTimerStates
|
|
@@ -1604,14 +1663,19 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
|
|
|
1604
1663
|
const derivedDependencies = hasDerivedDependency ? dependencyEntries.map(entry => entry.kind === "derived" ? descriptors.registerDerived("expression", entry.expression, entry.states, entry.source) : undefined) : []
|
|
1605
1664
|
const effectSource = specializedEffect?.source ?? node
|
|
1606
1665
|
const lexicalOwner = nearestFunction(effectSource)
|
|
1666
|
+
const effectStateOwners = new Map([...stateOwnersForNode(callbackArgument), ...stateOwnersForNode(node), ...(specializedEffectStateOwners.get(node)?.references ?? []), ...(activeStateOwners ?? [])])
|
|
1667
|
+
const signalFor = name => descriptors.signal(name, node, effectStateOwners)
|
|
1668
|
+
const subscriptionNames = (hasDerivedDependency ? subscriptionDependencies : ordinaryDependencies).map(dependency => dependency.text)
|
|
1669
|
+
const dependencyStateNames = [...dependencyStates.keys()]
|
|
1607
1670
|
const effect = descriptors.registerEffect(descriptor, {
|
|
1608
1671
|
cleanup: returns.cleanup,
|
|
1609
|
-
dependencies: hasDerivedDependency ? dependencyEntries.map((entry, index) => entry.kind === "derived" ? { kind: "derived", derived: derivedDependencies[index].slot, sources: [...entry.states] } : { kind: "signal",
|
|
1610
|
-
subscriptions:
|
|
1611
|
-
|
|
1672
|
+
dependencies: hasDerivedDependency ? dependencyEntries.map((entry, index) => entry.kind === "derived" ? { kind: "derived", derived: derivedDependencies[index].slot, sources: [...entry.states].map(signalFor) } : { kind: "signal", signal: signalFor(entry.name) }) : ordinaryDependencies.map(dependency => ({ kind: "signal", signal: signalFor(dependency.text) })),
|
|
1673
|
+
subscriptions: subscriptionNames.map(signalFor),
|
|
1674
|
+
dependencySignals: dependencyStateNames.map(signalFor),
|
|
1612
1675
|
itemDependencies,
|
|
1613
1676
|
ownership: {
|
|
1614
1677
|
kind: activeKeyedBlock ? "keyed" : "component",
|
|
1678
|
+
owner: specializedEffectStateOwners.get(node)?.owner ?? fallbackOwner(effectSource),
|
|
1615
1679
|
...(activeKeyedBlock ? { keyedBlock: activeKeyedBlock.block.slot } : {}),
|
|
1616
1680
|
...(lexicalOwner ? { component: { name: ownerName(lexicalOwner), ...(analysisSite(lexicalOwner, "owner") ? { site: analysisSite(lexicalOwner, "owner") } : {}), ...(analysisSource(lexicalOwner) ? { source: analysisSource(lexicalOwner) } : {}) } } : {})
|
|
1617
1681
|
},
|
|
@@ -1619,10 +1683,10 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
|
|
|
1619
1683
|
...(analysisSite(effectSource, "hook") ? { site: analysisSite(effectSource, "hook") } : {}),
|
|
1620
1684
|
...(analysisSource(effectSource) ? { source: analysisSource(effectSource) } : {})
|
|
1621
1685
|
})
|
|
1622
|
-
const dependencyExpressions = effect.dependencies.map(dependency => dependency.kind === "derived" ? moduleIR.derived[dependency.derived].expression : ["state",
|
|
1686
|
+
const dependencyExpressions = effect.dependencies.map((dependency, index) => dependency.kind === "derived" ? moduleIR.derived[dependency.derived].expression : ["state", dependencyEntries[index]?.name ?? ordinaryDependencies[index].text])
|
|
1623
1687
|
return factory.updateCallExpression(node, node.expression, node.typeArguments, [
|
|
1624
1688
|
callback,
|
|
1625
|
-
factory.createArrayLiteralExpression(
|
|
1689
|
+
factory.createArrayLiteralExpression(subscriptionNames.map(name => factory.createIdentifier(name))),
|
|
1626
1690
|
factory.createStringLiteral(handlerUrl),
|
|
1627
1691
|
factory.createStringLiteral(effect.setup.exportName),
|
|
1628
1692
|
descriptor.states,
|
|
@@ -1631,7 +1695,7 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
|
|
|
1631
1695
|
effect.cleanup ? factory.createTrue() : factory.createFalse(),
|
|
1632
1696
|
factory.createArrayLiteralExpression(effect.itemDependencies.map(field => factory.createStringLiteral(field))),
|
|
1633
1697
|
hasDerivedDependency ? jsonExpression(dependencyExpressions, factory) : factory.createArrayLiteralExpression(),
|
|
1634
|
-
factory.createArrayLiteralExpression(
|
|
1698
|
+
factory.createArrayLiteralExpression(dependencyStateNames.map(name => factory.createArrayLiteralExpression([factory.createStringLiteral(name), factory.createIdentifier(name)])))
|
|
1635
1699
|
])
|
|
1636
1700
|
}
|
|
1637
1701
|
|
|
@@ -1707,7 +1771,7 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
|
|
|
1707
1771
|
if ((usedStates.size || captures.size) && !ts.isIdentifier(expression) && !containsJsx(expression)) {
|
|
1708
1772
|
usesBehavior = true
|
|
1709
1773
|
usesBinding = true
|
|
1710
|
-
return factory.updateJsxExpression(node, descriptors.compileReactiveBinding(expression, { setters, importBindings }))
|
|
1774
|
+
return factory.updateJsxExpression(node, descriptors.compileReactiveBinding(expression, { setters, importBindings }).node)
|
|
1711
1775
|
}
|
|
1712
1776
|
}
|
|
1713
1777
|
|
|
@@ -1721,7 +1785,7 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
|
|
|
1721
1785
|
usesBehavior = true
|
|
1722
1786
|
usesBinding = true
|
|
1723
1787
|
const compiled = descriptors.compileReactiveBinding(expression, { setters, importBindings })
|
|
1724
|
-
return factory.updateJsxAttribute(node, node.name, factory.createJsxExpression(undefined, compiled))
|
|
1788
|
+
return factory.updateJsxAttribute(node, node.name, factory.createJsxExpression(undefined, compiled.node))
|
|
1725
1789
|
}
|
|
1726
1790
|
}
|
|
1727
1791
|
|
|
@@ -1729,7 +1793,7 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
|
|
|
1729
1793
|
const setters = settersForNode(node, settersByFunction)
|
|
1730
1794
|
const event = descriptors.compileEvent(node.initializer.expression, {
|
|
1731
1795
|
owner: fallbackOwner(node),
|
|
1732
|
-
stateOwners:
|
|
1796
|
+
stateOwners: new Map([...stateOwnersForNode(node), ...(activeStateOwners ?? [])]),
|
|
1733
1797
|
setters,
|
|
1734
1798
|
reducers: reducersForNode(node, reducersByFunction),
|
|
1735
1799
|
functions: functionsForNode(node),
|
|
@@ -2023,7 +2087,7 @@ function validateKeyedList(parts, sourceFile, setters, rowStates, componentSpeci
|
|
|
2023
2087
|
specializations: [specialization?.analysis?.slot, ...(specialization?.specializations ?? [])].filter(slot => slot !== undefined),
|
|
2024
2088
|
rowStates: specializedStates,
|
|
2025
2089
|
rowRefs: specialization?.rowRefs ?? [],
|
|
2026
|
-
analysisStateOwners: new Map([...(parts.analysisStateOwners ?? []), ...specializedStates.map(state => [state.state, state.
|
|
2090
|
+
analysisStateOwners: new Map([...(parts.analysisStateOwners ?? []), ...(specialization?.propStateOwners ?? []), ...specializedStates.map(state => [state.state, state.analysisReference])])
|
|
2027
2091
|
}
|
|
2028
2092
|
for (const calculation of specialization?.calculations ?? []) {
|
|
2029
2093
|
ts.setParentRecursive(calculation, false)
|
package/framework/core.d.ts
CHANGED
package/framework/core.mjs
CHANGED
|
@@ -207,8 +207,10 @@ export function useEffect(callback, dependencies, module, handler, states, scope
|
|
|
207
207
|
owner = nextRenderId("e")
|
|
208
208
|
owners.push(owner)
|
|
209
209
|
}
|
|
210
|
-
if (!renderContext.listDepth || list)
|
|
211
|
-
|
|
210
|
+
if (!renderContext.listDepth || list) {
|
|
211
|
+
renderContext.effects.push({ module, handler, states, scope, source, renderScope: renderContext.renderScope, ...(dependencyIds.length ? { dependencies: dependencyIds } : {}), ...(dependencyExpressions.length ? { dependencyExpressions, dependencyStates: dependencyStateIds } : {}), ...(itemDependencies.length ? { itemDependencies, listState: renderContext.listRoot.state } : {}), ...(cleanup ? { cleanup: true } : {}), ...(owner ? { owner } : {}), ...(list ? { list: true } : {}) })
|
|
212
|
+
retainHandlerReference(module, handler)
|
|
213
|
+
}
|
|
212
214
|
renderContext.hasBehaviors = true
|
|
213
215
|
renderContext.hasEffects = true
|
|
214
216
|
}
|
|
@@ -254,7 +256,6 @@ export function behavior(commands) {
|
|
|
254
256
|
}
|
|
255
257
|
|
|
256
258
|
export function nativeBehavior(module, handler, states, scope) {
|
|
257
|
-
renderContext?.handlerModules.add(module)
|
|
258
259
|
return {
|
|
259
260
|
[nativeBehaviorMarker]: true,
|
|
260
261
|
module,
|
|
@@ -325,7 +326,6 @@ export function listField(read, field) {
|
|
|
325
326
|
}
|
|
326
327
|
|
|
327
328
|
export function listExpression(read, module, handler, states = []) {
|
|
328
|
-
renderContext?.handlerModules.add(module)
|
|
329
329
|
const stateMap = Object.fromEntries(states.map(([name, state]) => {
|
|
330
330
|
if (!state?.[signalMarker] || !validEffectDependency(state.value)) throw new Error(`Derived keyed list item expression state ${JSON.stringify(name)} must be primitive Kudzu state`)
|
|
331
331
|
return [name, state.id]
|
|
@@ -346,7 +346,6 @@ export function listIndex() {
|
|
|
346
346
|
}
|
|
347
347
|
|
|
348
348
|
export function listConditional(kind, read, truthy, falsy, module, handler) {
|
|
349
|
-
renderContext?.handlerModules.add(module)
|
|
350
349
|
return { [listConditionalMarker]: true, kind, value: renderContext?.listTemplate ? undefined : read(), truthy, falsy, module, handler }
|
|
351
350
|
}
|
|
352
351
|
|
|
@@ -380,7 +379,6 @@ function assertListValue(value, seen) {
|
|
|
380
379
|
}
|
|
381
380
|
|
|
382
381
|
function reactiveDescriptor(module, handler, states, scope) {
|
|
383
|
-
renderContext?.handlerModules.add(module)
|
|
384
382
|
const scopeStates = {}
|
|
385
383
|
const serializedScope = {}
|
|
386
384
|
const scopeBindings = {}
|
|
@@ -402,6 +400,16 @@ function reactiveDescriptor(module, handler, states, scope) {
|
|
|
402
400
|
}
|
|
403
401
|
}
|
|
404
402
|
|
|
403
|
+
function retainHandlerReference(module, handler) {
|
|
404
|
+
const reference = { module, handler }
|
|
405
|
+
renderContext?.handlerReferences.set(JSON.stringify([module, handler]), reference)
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
function retainDescriptorHandlers(descriptor) {
|
|
409
|
+
if (descriptor?.module && descriptor.handler) retainHandlerReference(descriptor.module, descriptor.handler)
|
|
410
|
+
for (const nested of Object.values(descriptor?.scopeBindings ?? {})) retainDescriptorHandlers(nested)
|
|
411
|
+
}
|
|
412
|
+
|
|
405
413
|
export function bindingValue(value) {
|
|
406
414
|
return value?.[signalMarker] || value?.[bindingMarker] ? value.value : value
|
|
407
415
|
}
|
|
@@ -449,7 +457,7 @@ function serializeCapture(name, value, seen) {
|
|
|
449
457
|
}
|
|
450
458
|
|
|
451
459
|
export async function renderPage(component, metadata = {}, props = {}, layout) {
|
|
452
|
-
renderContext = { scoped: Boolean(layout), renderScope: layout ? "layout" : "route", counters: { layout: { s: 0, r: 0, c: 0, l: 0, e: 0, p: 0, i: 0 }, route: { s: 0, r: 0, c: 0, l: 0, e: 0, p: 0, i: 0 } }, nextState: 0, nextRef: 0, nextCondition: 0, nextList: 0, nextEffect: 0, nextParam: 0, nextId: 0, conditionDepth: 0, listDepth: 0, listRoot: undefined, listRowRoot: undefined, listTemplate: false, listInitialMarkers: false, listConditionalBranch: false, listFields: undefined, listEffectOwners: [], listRowStates: [], listRowRefs: [], listRowConditions: [], listRowLists: [], effectOwners: [], contexts: [], stores: new Map(), states: {}, textStates: new Set(), conditionStates: new Set(), conditionOwnedStates: new Set(), events: [], effects: [], bindings: [], textBindings: [], conditions: [], lists: [],
|
|
460
|
+
renderContext = { scoped: Boolean(layout), renderScope: layout ? "layout" : "route", counters: { layout: { s: 0, r: 0, c: 0, l: 0, e: 0, p: 0, i: 0 }, route: { s: 0, r: 0, c: 0, l: 0, e: 0, p: 0, i: 0 } }, nextState: 0, nextRef: 0, nextCondition: 0, nextList: 0, nextEffect: 0, nextParam: 0, nextId: 0, conditionDepth: 0, listDepth: 0, listRoot: undefined, listRowRoot: undefined, listTemplate: false, listInitialMarkers: false, listConditionalBranch: false, listFields: undefined, listEffectOwners: [], listRowStates: [], listRowRefs: [], listRowConditions: [], listRowLists: [], effectOwners: [], contexts: [], stores: new Map(), states: {}, textStates: new Set(), conditionStates: new Set(), conditionOwnedStates: new Set(), events: [], effects: [], bindings: [], textBindings: [], conditions: [], lists: [], handlerReferences: new Map(), runtimeParamNames: metadata.runtimeParams, paramEntries: [], params: undefined, searchParams: new Map(), searchParamEntries: [], searchParamsWritable: false, hasBehaviors: false, hasNativeBehaviors: false, hasEffects: false, hasParams: false, hasBindings: false, hasLists: false, hasListStyles: false }
|
|
453
461
|
|
|
454
462
|
try {
|
|
455
463
|
const page = { [routeScopeMarker]: true, component, props }
|
|
@@ -525,7 +533,7 @@ export async function renderPage(component, metadata = {}, props = {}, layout) {
|
|
|
525
533
|
hasLists: renderContext.hasLists,
|
|
526
534
|
hasListStyles: renderContext.hasListStyles,
|
|
527
535
|
hasStateSeed: initialState.length > 0,
|
|
528
|
-
|
|
536
|
+
handlerReferences: [...renderContext.handlerReferences.values()],
|
|
529
537
|
plan: {
|
|
530
538
|
version: 1,
|
|
531
539
|
states: Object.entries(renderContext.states).map(([id, state], slot) => ({ slot, id, ...state })),
|
|
@@ -648,6 +656,7 @@ async function renderNode(node, namespace, selectValue = noSelectValue) {
|
|
|
648
656
|
const metadata = { id, kind: node.kind, initial: node.value, ...descriptor, ...(namespace === "svg" ? { svg: true } : {}), ...(owned ? { owned } : {}), ...(mount ? { mount: true } : {}) }
|
|
649
657
|
for (const stateId of stateIds) renderContext.conditionStates.add(stateId)
|
|
650
658
|
renderContext.conditions.push(metadata)
|
|
659
|
+
retainDescriptorHandlers(metadata)
|
|
651
660
|
renderContext.hasBehaviors = true
|
|
652
661
|
renderContext.hasBindings = true
|
|
653
662
|
const encoded = escapeJsonAttribute(metadata)
|
|
@@ -667,7 +676,9 @@ async function renderNode(node, namespace, selectValue = noSelectValue) {
|
|
|
667
676
|
}
|
|
668
677
|
if (node?.[listExpressionMarker]) {
|
|
669
678
|
const descriptor = { module: node.module, handler: node.handler, ...(Object.keys(node.states).length ? { states: node.states } : {}) }
|
|
670
|
-
const
|
|
679
|
+
const retained = renderContext.listTemplate || renderContext.listInitialMarkers || renderContext.listConditionalBranch
|
|
680
|
+
if (retained) retainDescriptorHandlers(descriptor)
|
|
681
|
+
const marker = retained ? ` data-k-list-expression='${escapeJsonAttribute(descriptor)}'` : ""
|
|
671
682
|
return `<template${marker}></template>${escapeHtml(node.value ?? "")}<template data-k-list-expression-end></template>`
|
|
672
683
|
}
|
|
673
684
|
if (node?.[bindingMarker]) {
|
|
@@ -675,6 +686,7 @@ async function renderNode(node, namespace, selectValue = noSelectValue) {
|
|
|
675
686
|
const reactive = reactiveStateIds(descriptor).size > 0
|
|
676
687
|
if (!reactive) return renderNode(node.value, namespace, selectValue)
|
|
677
688
|
renderContext.bindings.push({ target: "text", ...descriptor })
|
|
689
|
+
retainDescriptorHandlers(descriptor)
|
|
678
690
|
if (renderContext.conditionDepth || renderContext.listDepth) for (const stateId of reactiveStateIds(descriptor)) renderContext.conditionStates.add(stateId)
|
|
679
691
|
renderContext.hasBehaviors = true
|
|
680
692
|
renderContext.hasBindings = true
|
|
@@ -685,6 +697,7 @@ async function renderNode(node, namespace, selectValue = noSelectValue) {
|
|
|
685
697
|
if (node?.[listConditionalMarker]) {
|
|
686
698
|
if (namespace === "svg") throw new Error("Keyed row conditions are not supported inside svg")
|
|
687
699
|
const descriptor = { kind: node.kind, module: node.module, handler: node.handler }
|
|
700
|
+
retainDescriptorHandlers(descriptor)
|
|
688
701
|
const owner = renderContext.listRoot ?? renderContext.listRowRoot
|
|
689
702
|
if (owner) owner.conditions = true
|
|
690
703
|
const previousBranch = renderContext.listConditionalBranch
|
|
@@ -800,6 +813,7 @@ async function renderNode(node, namespace, selectValue = noSelectValue) {
|
|
|
800
813
|
const native = template
|
|
801
814
|
attributes += ` data-k-native-${event}='${escapeJsonAttribute(native)}'`
|
|
802
815
|
renderContext.events.push({ event, native })
|
|
816
|
+
retainDescriptorHandlers(native)
|
|
803
817
|
if (renderContext.listDepth && Object.values(template.scope).some(entry => entry?.type === "list-item" || entry?.type === "list-index")) listEvents.push([event, template])
|
|
804
818
|
renderContext.hasNativeBehaviors = true
|
|
805
819
|
} else {
|
|
@@ -817,9 +831,10 @@ async function renderNode(node, namespace, selectValue = noSelectValue) {
|
|
|
817
831
|
if (name === "style") renderContext.hasListStyles = true
|
|
818
832
|
continue
|
|
819
833
|
}
|
|
820
|
-
|
|
834
|
+
if (value?.[listExpressionMarker]) {
|
|
821
835
|
attributes += renderAttribute(name, value.value)
|
|
822
|
-
|
|
836
|
+
listExpressionAttributes.push([name, value.module, value.handler, ...(Object.keys(value.states).length ? [value.states] : [])])
|
|
837
|
+
if (renderContext.listTemplate || renderContext.listInitialMarkers || renderContext.listConditionalBranch) retainHandlerReference(value.module, value.handler)
|
|
823
838
|
if (name === "style") renderContext.hasListStyles = true
|
|
824
839
|
continue
|
|
825
840
|
}
|
|
@@ -837,6 +852,7 @@ async function renderNode(node, namespace, selectValue = noSelectValue) {
|
|
|
837
852
|
if (propertyTarget) attributes += ` data-k-bind-${name}='${escapeJsonAttribute(descriptor)}'`
|
|
838
853
|
else attributeBindings.push({ target: name, ...descriptor })
|
|
839
854
|
renderContext.bindings.push({ target: name, ...descriptor })
|
|
855
|
+
retainDescriptorHandlers(descriptor)
|
|
840
856
|
if (renderContext.conditionDepth || renderContext.listDepth) for (const stateId of reactiveStateIds(descriptor)) renderContext.conditionStates.add(stateId)
|
|
841
857
|
renderContext.hasBehaviors = true
|
|
842
858
|
renderContext.hasBindings = true
|
|
@@ -944,6 +960,7 @@ async function renderList(node, namespace, selectValue) {
|
|
|
944
960
|
}
|
|
945
961
|
if (!node.ownerField || ownerTemplate && !rowList.planned) {
|
|
946
962
|
renderContext.lists.push(descriptor)
|
|
963
|
+
retainDescriptorHandlers(descriptor.source)
|
|
947
964
|
if (rowList) rowList.planned = true
|
|
948
965
|
}
|
|
949
966
|
renderContext.hasBehaviors = true
|