@kudzujs/core 0.6.14 → 0.6.15
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/README.md +8 -0
- package/framework/README.md +1 -1
- package/framework/build.mjs +10 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -201,6 +201,14 @@ return <Input onSubmit={add} />
|
|
|
201
201
|
|
|
202
202
|
Kudzu substitutes the callback into the child's compiled event handler at build time. This is not general function-prop serialization: only one nested specialized callback boundary is supported, and `useCallback`, further forwarding, effects, component roots, and callback use outside event handlers are rejected.
|
|
203
203
|
|
|
204
|
+
Reducer dispatch and callback components may use destructured string, finite-number, boolean, or `null` defaults. A missing prop is replaced during specialization; object, array, computed, and function-call defaults remain unsupported:
|
|
205
|
+
|
|
206
|
+
```tsx
|
|
207
|
+
function Input({ onSubmit, editing = false }) {
|
|
208
|
+
// ...
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
204
212
|
## Reactive Attributes
|
|
205
213
|
|
|
206
214
|
`className`, `disabled`, controlled `value`, and controlled `checked` accept normal state-dependent TSX expressions. The same `value` binding works for inputs and selects:
|
package/framework/README.md
CHANGED
|
@@ -25,7 +25,7 @@ Inline SVG rendering normalizes an explicit set of common React presentation ali
|
|
|
25
25
|
|
|
26
26
|
Same-file and relative-imported components receiving a direct local-state array prop are specialized to intrinsic JSX before keyed-list analysis, so their component function is not retained in the browser. Handler modules are emitted only when a rendered descriptor references them, preventing specialized imported components from adding dead browser assets. Direct JSON-safe primitive keyed-item dependencies subscribe each row record to its owning list commit and compare selected fields after `list-runtime.js` synchronously refreshes the row marker. Only changed rows rerun with the complete latest item; reorder compares equal, unrelated fields do nothing, and key changes remain remove plus mount. Builds without item dependencies emit no item reader or list-state subscription code.
|
|
27
27
|
|
|
28
|
-
The reduced `useReducer` form reuses ordinary state slots. A direct dispatch in a compiled handler becomes a functional `set` whose reducer is bundled from a relative TypeScript module into that handler graph. One direct dispatch prop into a same-file or relative-imported synchronous component is specialized to intrinsic JSX at the call site, so its handler retains the parent reducer scope and no dispatch capture or child handler asset is emitted. Relative TypeScript imports referenced inside that child handler receive collision-free call-site aliases and join the parent handler graph. One nested relative-imported intrinsic child may receive an inline or simple `const` callback containing dispatch; the compiler recursively substitutes that callback once and omits the nested child handler asset. Reducer-free routes and shared runtimes are unchanged; no reducer runtime or browser component instance exists.
|
|
28
|
+
The reduced `useReducer` form reuses ordinary state slots. A direct dispatch in a compiled handler becomes a functional `set` whose reducer is bundled from a relative TypeScript module into that handler graph. One direct dispatch prop into a same-file or relative-imported synchronous component is specialized to intrinsic JSX at the call site, so its handler retains the parent reducer scope and no dispatch capture or child handler asset is emitted. Relative TypeScript imports referenced inside that child handler receive collision-free call-site aliases and join the parent handler graph. One nested relative-imported intrinsic child may receive an inline or simple `const` callback containing dispatch; the compiler recursively substitutes that callback once and omits the nested child handler asset. Missing primitive literal defaults in these reducer specializations are substituted at the same call site. Reducer-free routes and shared runtimes are unchanged; no reducer runtime or browser component instance exists.
|
|
29
29
|
|
|
30
30
|
`kudzu.config` may opt one emitted shared-layout group into same-document navigation with legacy `navigation: { routes: ["/product", "/items/[id]"] }`, or multiple groups with `navigation: { groups: [{ routes: [...] }, { routes: [...] }] }`. The forms are mutually exclusive. Identities are globally unique emitted exact paths or `runtimeParams` patterns; each group uses one page-exported layout function identity. Runtime records securely match concrete pathnames under `base`, and their cache-safe parameter initializer runs before route DOM/effects mount on every transition. Each group receives a deterministic route-hashed asset specialized to only its records, pattern decoder, and effect/parameter lifecycle needs. Cross-group and ungrouped anchors remain native and are not prefetched; overlapping path domains across groups fail the build. Route effect entries export cache-safe layout and route mount functions: layout effects, including conditional/keyed DOM-owned effects, persist for the group session; route effects receive a fresh owner registry after each route insertion; and non-persisted page disposal cleans route before layout. Direct primitive state, runtime parameter, and keyed-item property dependencies and cleanup are supported. Fragment payloads and coordinated View Transitions are not implemented.
|
|
31
31
|
|
package/framework/build.mjs
CHANGED
|
@@ -2473,10 +2473,11 @@ function specializeComponentCall(call, component, sourceFile, factory, context,
|
|
|
2473
2473
|
const substitutions = new Map()
|
|
2474
2474
|
const acceptedProps = new Set()
|
|
2475
2475
|
for (const element of component.parameters[0].name.elements) {
|
|
2476
|
-
if (element.dotDotDotToken ||
|
|
2477
|
-
|
|
2476
|
+
if (element.dotDotDotToken || !ts.isIdentifier(element.name) || (element.initializer && label === "Keyed list")) fail(element, `${label} component props cannot use rest, defaults, or nested destructuring`)
|
|
2477
|
+
if (element.initializer && !isPrimitiveDefaultLiteral(element.initializer)) fail(element.initializer, `${label} component prop defaults must be primitive literals`)
|
|
2478
|
+
const prop = (element.propertyName ?? element.name).text
|
|
2478
2479
|
acceptedProps.add(prop)
|
|
2479
|
-
substitutions.set(element.name.text, props.get(prop) ?? factory.createIdentifier("undefined"))
|
|
2480
|
+
substitutions.set(element.name.text, props.has(prop) ? props.get(prop) : element.initializer ?? factory.createIdentifier("undefined"))
|
|
2480
2481
|
}
|
|
2481
2482
|
for (const prop of props.keys()) if (!acceptedProps.has(prop)) fail(call, `Unknown ${label.toLowerCase()} component prop "${prop}"`)
|
|
2482
2483
|
|
|
@@ -2516,6 +2517,12 @@ function specializeComponentCall(call, component, sourceFile, factory, context,
|
|
|
2516
2517
|
return { root, calculations, effects }
|
|
2517
2518
|
}
|
|
2518
2519
|
|
|
2520
|
+
function isPrimitiveDefaultLiteral(node) {
|
|
2521
|
+
return ts.isStringLiteral(node) || ts.isNumericLiteral(node) ||
|
|
2522
|
+
(ts.isPrefixUnaryExpression(node) && (node.operator === ts.SyntaxKind.PlusToken || node.operator === ts.SyntaxKind.MinusToken) && ts.isNumericLiteral(node.operand)) ||
|
|
2523
|
+
node.kind === ts.SyntaxKind.TrueKeyword || node.kind === ts.SyntaxKind.FalseKeyword || node.kind === ts.SyntaxKind.NullKeyword
|
|
2524
|
+
}
|
|
2525
|
+
|
|
2519
2526
|
function substituteClone(root, substitutions, factory, context) {
|
|
2520
2527
|
const visit = (node, shadowed = new Set()) => {
|
|
2521
2528
|
if (ts.isTypeNode(node)) return cloneAst(node, factory, context)
|