@kudzujs/core 0.8.45 → 0.8.47
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 +3 -1
- package/PERFORMANCE.md +9 -1
- package/README.md +1 -1
- package/RELEASES.md +58 -0
- package/docs/next-architecture/README.md +1 -1
- package/docs/next-architecture/compiler-current-architecture.md +2 -2
- package/docs/next-architecture/large-application-ai-native-roadmap.md +2 -2
- package/docs/next-architecture/versioning.md +3 -1
- package/framework/compiler/source-compiler.mjs +58 -6
- package/package.json +1 -1
package/MIGRATION_ROADMAP.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
This document is the source of truth for Kudzu's product direction, architecture invariants, and future development order. Read it before extending React-shaped syntax or browser capabilities.
|
|
4
4
|
|
|
5
|
-
The executable post-`0.8.
|
|
5
|
+
The executable post-`0.8.47` compiler and large-application sequence is maintained in [`docs/next-architecture/large-application-ai-native-roadmap.md`](./docs/next-architecture/large-application-ai-native-roadmap.md). Follow its PR dependencies for implementation work; this document remains authoritative when selecting or accepting a migration capability.
|
|
6
6
|
|
|
7
7
|
[`GOAL_A.md`](./GOAL_A.md) and [`GOAL_B.md`](./GOAL_B.md) are completed capability-validation records. Their commerce and realtime dashboard fixtures prove general lifecycle, navigation, async-workflow, and Worker capabilities; they are not separate product verticals or future priority lists.
|
|
8
8
|
|
|
@@ -143,6 +143,8 @@ This queue orders the next investigations by general migration value. Start only
|
|
|
143
143
|
|
|
144
144
|
- A React Notes migration preserves its Provider, relative `useNotes()` alias, consumer components, and parameterized CRUD actions. A direct custom-hook `useContext(Context)` return resolves one local or named relative Context module whose Provider value exposes direct state/setter pairs and synchronous actions.
|
|
145
145
|
- Context actions inline into existing route handler ESM and compile to concrete state operations. Private captures, dynamic Provider objects, and multiple Provider implementations remain diagnosed; no browser Context tree, callback registry, or shared runtime was added.
|
|
146
|
+
- In `0.8.46`, the same Notes migration may omit action-only setters from its public Context type and authored Provider value when the corresponding state remains exposed. Kudzu restores those setters only in build scratch so existing concrete state operations, RouteIR signals, CRUD behavior, and zero-Context runtime output remain unchanged. Fully hidden action state and exposed setters without state remain diagnosed.
|
|
147
|
+
- In `0.8.47`, a specialized setter-callback child may initialize local state directly from one primitive parent state prop. Build scratch reads the parent signal's build value while retaining the structural prop signal for handlers/effects; direct object/array props, aliases, and composed initializer expressions remain diagnosed.
|
|
146
148
|
|
|
147
149
|
### Completed In 0.8.10
|
|
148
150
|
|
package/PERFORMANCE.md
CHANGED
|
@@ -6,7 +6,15 @@ Reproducibility classes: `npm run benchmark`, `npm run benchmark:keyed`, `npm ru
|
|
|
6
6
|
|
|
7
7
|
The maintained 2026-08-13 comparison used Node 24.14.0 and an Intel Core i5-9500 Linux x64 host, one warm-up, and seven alternating fresh-process samples against clean `v0.8.44`. A narrow fast path skips Kudzu semantic transformation for 450 plain `.ts` modules whose runtime edges are exclusively resolvable relative TypeScript imports or exports; all other modules retain the existing transformer. Compile median fell from 2,323.9 ms to 1,413.2 ms (39.2%) and clean-build median from 3,325.3 ms to 2,382.4 ms (28.4%); every paired sample improved. Compile peak-RSS median fell from 571.2 MiB to 552.6 MiB, while build peak RSS was 570.9 MiB versus 568.8 MiB. Compiler scratch fell from 7,328,390 to 1,971,061 bytes. Both targets emitted the same 50 static HTML files, 10,980 bytes, and deploy SHA-256 `e107d78a7f55bc8a1af0ea6e53efeffa19b3d44d21c892484d103fa346e7ba7b`. This is a source-scale compiler comparison, not a cross-framework result.
|
|
8
8
|
|
|
9
|
-
## Current 0.8.
|
|
9
|
+
## Current 0.8.47 Release Snapshot
|
|
10
|
+
|
|
11
|
+
Kudzu 0.8.47 changes compiler-only specialized child initialization and retains existing parent/child state ownership, handler capabilities, and browser runtimes. No new performance claim is made.
|
|
12
|
+
|
|
13
|
+
## Maintained 0.8.46 Release Snapshot
|
|
14
|
+
|
|
15
|
+
Kudzu 0.8.46 changes compiler-only Context Provider scratch and retains the same concrete Notes state operations, RouteIR signals, runtime modules, and static sibling output. No new performance claim is made.
|
|
16
|
+
|
|
17
|
+
## Maintained 0.8.45 Optimization Snapshot
|
|
10
18
|
|
|
11
19
|
Kudzu 0.8.45 adds the plain TypeScript fast path and maintained paired source-scale runner described above. It changes compiler scratch only: the measured deploy graph is byte-identical to `v0.8.44`, and no browser runtime or public API changed.
|
|
12
20
|
|
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ Kudzu compiles ordinary React-shaped TypeScript and TSX into complete static HTM
|
|
|
14
14
|
|
|
15
15
|
> Experimental `0.8.x`: the compiler API and supported TSX surface may change.
|
|
16
16
|
|
|
17
|
-
**Latest release: 0.8.
|
|
17
|
+
**Latest release: 0.8.47 - Direct primitive prop state.** Specialized setter-callback children may initialize local state directly from one primitive parent state prop. Kudzu reads the build-known primitive value while retaining independent child ownership and the structural parent signal. Read the [release notes](./RELEASES.md#0847---direct-primitive-prop-state), open the [release page](https://github.com/kudzujs/kudzu/releases/tag/v0.8.47), or follow the [architecture packet](./docs/next-architecture/README.md).
|
|
18
18
|
|
|
19
19
|
- [Documentation](https://kudzujs.cloud/docs)
|
|
20
20
|
- [Installation guide](https://kudzujs.cloud/docs#install)
|
package/RELEASES.md
CHANGED
|
@@ -1,5 +1,63 @@
|
|
|
1
1
|
# Kudzu Releases
|
|
2
2
|
|
|
3
|
+
## 0.8.47 - Direct primitive prop state
|
|
4
|
+
|
|
5
|
+
Kudzu 0.8.47 lets a compiler-specialized setter-callback child initialize local state directly from one primitive parent state prop without a migration-only wrapper expression.
|
|
6
|
+
|
|
7
|
+
### Changed in 0.8.47
|
|
8
|
+
|
|
9
|
+
- `useState(value)` is accepted when `value` is a direct specialized prop linked to parent state with a proven primitive literal initializer.
|
|
10
|
+
- Build scratch reads the parent signal's initial primitive value while preserving the structural prop signal for handlers and effects.
|
|
11
|
+
- Existing direct `value.toString()` initialization remains supported for string input buffers.
|
|
12
|
+
- Composed expressions, aliases, and object/array state props remain source-diagnosed.
|
|
13
|
+
|
|
14
|
+
### Output and performance
|
|
15
|
+
|
|
16
|
+
- Parent and child retain independent state IDs and JSON-safe initial values.
|
|
17
|
+
- Handler specialization continues to target the same parent and child signals without a component runtime.
|
|
18
|
+
- No browser runtime module, public Kudzu API, or new performance claim is added.
|
|
19
|
+
|
|
20
|
+
### Validation
|
|
21
|
+
|
|
22
|
+
- `npm run check`, `npm test`, and `npm run test:package` pass with all 212 tests and 162 generated pages.
|
|
23
|
+
- A direct string-state prop emits complete initial input HTML and two matching RouteIR state seeds.
|
|
24
|
+
- Focused negative fixtures retain diagnostics for composed initializer expressions and object-state props.
|
|
25
|
+
|
|
26
|
+
### Upgrade
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npm install @kudzujs/core@^0.8.47
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## 0.8.46 - Private Context action setters
|
|
33
|
+
|
|
34
|
+
Kudzu 0.8.46 lets relative Context-hook actions use Provider-owned setters without forcing those implementation-only setters into the authored Provider value or public Context type.
|
|
35
|
+
|
|
36
|
+
### Changed in 0.8.46
|
|
37
|
+
|
|
38
|
+
- An action-required setter may be omitted when its corresponding Provider state value remains exposed.
|
|
39
|
+
- Kudzu adds only the required setter fields to compiler build scratch so existing setter identity and HandlerIR lowering remain unchanged.
|
|
40
|
+
- Consumer scratch receives collision-free private bindings for action lowering without changing authored hook destructuring.
|
|
41
|
+
- Fully hidden action state and Provider setters exposed without their state remain source-diagnosed.
|
|
42
|
+
|
|
43
|
+
### Output and performance
|
|
44
|
+
|
|
45
|
+
- The Notes migration retains the same two RouteIR signals and concrete `notes` and `activeId` state operations.
|
|
46
|
+
- Context objects, Provider trees, action functions, and a setter registry remain absent from browser output; the static sibling remains zero JavaScript.
|
|
47
|
+
- No browser runtime module, public Kudzu API, or new performance claim is added.
|
|
48
|
+
|
|
49
|
+
### Validation
|
|
50
|
+
|
|
51
|
+
- `npm run check`, `npm test`, and `npm run test:package` pass with all 210 tests and 161 generated pages.
|
|
52
|
+
- The maintained Chrome Notes journey passes create, rename, select, delete, list identity, and active-note updates with authored setters omitted.
|
|
53
|
+
- Focused negative fixtures retain diagnostics for fully hidden action state, setter-only exposure, private captures, and indirect action references.
|
|
54
|
+
|
|
55
|
+
### Upgrade
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
npm install @kudzujs/core@^0.8.46
|
|
59
|
+
```
|
|
60
|
+
|
|
3
61
|
## 0.8.45 - Plain TypeScript fast path
|
|
4
62
|
|
|
5
63
|
Kudzu 0.8.45 reduces large-project compiler work by sending proven plain `.ts` modules through TypeScript transpilation and relative ESM path rewriting without running Kudzu's TSX semantic transformer.
|
|
@@ -11,7 +11,7 @@ The top-level [`GOAL_A.md`](../../GOAL_A.md) and [`GOAL_B.md`](../../GOAL_B.md)
|
|
|
11
11
|
| C: state/resource model | Research only | Reduced fixtures expose a limitation |
|
|
12
12
|
| D: routing compatibility | Current behavior preserved | Revisit only with migration evidence and invariant review |
|
|
13
13
|
|
|
14
|
-
The active post-`0.8.
|
|
14
|
+
The active post-`0.8.47` implementation sequence is [`large-application-ai-native-roadmap.md`](./large-application-ai-native-roadmap.md). P0.12 validation, property-level object-state dependencies, direct three-boundary callback/ref ownership, direct primitive prop state initialization, collision-free and private Context action setters, measured route-entry output optimization, and the plain TypeScript source-scale fast path are complete; broader prop, callback, ref, and Context dataflow remains migration-backed work. The plan orders compiler semantic generalization, large-application foundations, compatibility boundaries, AI tooling, and production validation without changing the invariants below.
|
|
15
15
|
|
|
16
16
|
## Required Invariants
|
|
17
17
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Current Compiler Architecture
|
|
2
2
|
|
|
3
|
-
This maps the current `0.8.
|
|
3
|
+
This maps the current `0.8.47` architecture, built on the completed `0.8.23` Goal A compiler foundation. File and function names are the stable references; line numbers are intentionally omitted because later work may still move code.
|
|
4
4
|
|
|
5
5
|
## Responsibility Map
|
|
6
6
|
|
|
@@ -17,7 +17,7 @@ This maps the current `0.8.45` architecture, built on the completed `0.8.23` Goa
|
|
|
17
17
|
| Source-local binding index | [`framework/compiler/analysis/binding-index.mjs`](../../framework/compiler/analysis/binding-index.mjs) | After normalization, assigns deterministic lexical slots and classifies local, parameter, import, capture, global, and unresolved references. Native handler, effect, binding, list evaluator, optimized-command, and effect-resource consumers use complete index-owned AST; synthesized expressions retain the existing fallback. |
|
|
18
18
|
| Pure collection language | [`framework/compiler/collection-analysis.mjs`](../../framework/compiler/collection-analysis.mjs) | Analyzes collection roots/selectors and serializes the allowed pure expression language used by lists and derived dependencies. |
|
|
19
19
|
| Main semantic analysis | [`framework/compiler/source-compiler.mjs`](../../framework/compiler/source-compiler.mjs), `createKudzuTransformer()` | Produces transformed source plus explicit component, handler, binding, derived, keyed, and effect ownership results. |
|
|
20
|
-
| Component ownership analysis | [`framework/compiler/analysis/component-analysis.mjs`](../../framework/compiler/analysis/component-analysis.mjs) | Produces ComponentAnalysis v2 with ordered JSON-safe owner and specialization slots for state, setters, props, refs, IDs, direct SignalIR links, structural OwnerRefs, source-local SiteIds, and source provenance. Three direct callback/ref component boundaries specialize into the same parent signal and intrinsic ownership; Context action-private
|
|
20
|
+
| Component ownership analysis | [`framework/compiler/analysis/component-analysis.mjs`](../../framework/compiler/analysis/component-analysis.mjs) | Produces ComponentAnalysis v2 with ordered JSON-safe owner and specialization slots for state, setters, props, refs, IDs, direct SignalIR links, structural OwnerRefs, source-local SiteIds, and source provenance. Three direct callback/ref component boundaries specialize into the same parent signal and intrinsic ownership; Context action-private setters may remain compiler-only and receive collision-free consumer-local aliases; AST identity remains private to its source-local session. |
|
|
21
21
|
| Per-source descriptor registration | [`framework/compiler/descriptor-session.mjs`](../../framework/compiler/descriptor-session.mjs), `createSemanticArtifact()`, `createDescriptorSession()` | Keeps AST descriptors private during analysis, then finalizes ModuleIR v2 with deterministic SymbolRef, SignalIR, HandlerIR, BindingIR, DerivedIR, EffectIR, KeyedBlockIR, and ImportIR slots. One fail-closed boundary validates every source-local and component ownership edge before build-module generation. |
|
|
22
22
|
| Route artifact graph | [`framework/compiler/route-build-record.mjs`](../../framework/compiler/route-build-record.mjs), `createRouteBuildRecord()`, `planRouteArtifacts()` | Validates each rendered route's RouteIR, capabilities, entry paths, styles, and exact handler/effect references. Handler modules, Workers, package-client modules, and bundle entry/chunk closure derive from structural edges without serialized output searches or formatted composite keys. |
|
|
23
23
|
| Route contract validation | [`framework/compiler/route-ir.mjs`](../../framework/compiler/route-ir.mjs), `assertRouteIR()` | Fails before artifact selection for invalid state/parameter identity, commands, native/effect captures and dependencies, reactive descriptors, conditions, keyed-list identity/ownership, marker fields, or JSON safety. Immutable in-memory contracts validate once by identity. |
|
|
@@ -379,7 +379,7 @@ function increment(value) { setCount(value + 1) }; increment(count)
|
|
|
379
379
|
After the relevant P0 foundations, investigate capabilities in this order:
|
|
380
380
|
|
|
381
381
|
1. Property-level derived dependencies over ordinary object state. **Completed in `0.8.40`:** direct property paths and top-level immutable primitive locals over object state reuse tagged DerivedIR, subscribe to the source signal, and compare selected values with `Object.is`; whole-object and dynamic dependencies remain rejected.
|
|
382
|
-
2. Multi-boundary component/prop/callback/ref/context dataflow. **Three-boundary callback/ref ownership completed in `0.8.43`; collision-free Context action-private state completed in `0.8.44`:** forwarding preserves parent SignalIR
|
|
382
|
+
2. Multi-boundary component/prop/callback/ref/context dataflow. **Three-boundary callback/ref ownership completed in `0.8.43`; collision-free Context action-private state completed in `0.8.44`; action-only Provider setter exposure removed in `0.8.46`; direct primitive prop state initialization completed in `0.8.47`:** forwarding preserves parent SignalIR, Context action lowering uses compiler-owned aliases when consumer locals reuse Provider state/setter names, action-required setters may remain compiler-only when their state is publicly exposed, and specialized children may seed local state from a direct primitive parent signal. A fourth callback boundary, fully hidden Context state, and object/array prop initializers remain fail-closed. Broader prop, callback, ref, and Context graphs remain migration-led work.
|
|
383
383
|
3. Package-neutral shared state/actions and migration of current Zustand internals.
|
|
384
384
|
4. Browser-only package imports in owned effect/resource modules.
|
|
385
385
|
5. ResourceIR from at least two independent WebSocket/SSE/SDK fixtures with the same semantics.
|
|
@@ -481,4 +481,4 @@ The first comparison is Kudzu versus React + Vite using the same agent, model, t
|
|
|
481
481
|
|
|
482
482
|
## Immediate Decision
|
|
483
483
|
|
|
484
|
-
PR 1 through PR 12, the `0.8.40` property-dependency slice, the `0.8.41` and `0.8.43` direct multi-boundary callback/ref slices, the `0.8.44` Context alias slice, and the `0.8.42` measured route-output optimization are complete. Continue broader **component/prop/callback/ref/context dataflow** only from the next real migration blocker. Do not skip directly to a store, resource, router, virtualization, or ecosystem package feature.
|
|
484
|
+
PR 1 through PR 12, the `0.8.40` property-dependency slice, the `0.8.41` and `0.8.43` direct multi-boundary callback/ref slices, the `0.8.44` Context alias slice, the `0.8.46` action-only Provider setter slice, the `0.8.47` direct primitive prop initializer slice, and the `0.8.42` measured route-output optimization are complete. Continue broader **component/prop/callback/ref/context dataflow** only from the next real migration blocker. Do not skip directly to a store, resource, router, virtualization, or ecosystem package feature.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Planned Version Sequence
|
|
2
2
|
|
|
3
|
-
This is an execution sequence, not release history. `0.8.16` through `0.8.
|
|
3
|
+
This is an execution sequence, not release history. `0.8.16` through `0.8.47` are completed scopes represented by package/release records.
|
|
4
4
|
|
|
5
5
|
Keep each patch behavior-preserving and independently reviewable. If a boundary proves inseparable, revise this plan before combining releases; do not silently broaden a patch.
|
|
6
6
|
|
|
@@ -36,6 +36,8 @@ Keep each patch behavior-preserving and independently reviewable. If a boundary
|
|
|
36
36
|
| `0.8.43` | Preserve callback, setter, and ref ownership through a third direct component boundary. | Parent SignalIR, child state/effects/IDs, object refs, conditional cleanup, fresh remount, and static zero-JavaScript output pass in Chrome; a fourth callback boundary fails closed. |
|
|
37
37
|
| `0.8.44` | Remove consumer-local name collisions from specialized Context actions. | Action-private Provider state/setter fields receive deterministic collision-free aliases; CRUD browser behavior, direct state operations, diagnostics, and zero Context runtime output pass. |
|
|
38
38
|
| `0.8.45` | Add a source-scale benchmark and skip Kudzu semantic transformation for proven plain TypeScript modules. | Seven alternating samples improve compile and clean-build medians with identical deploy output; uncertain modules retain the existing transformer. |
|
|
39
|
+
| `0.8.46` | Remove action-only setter fields from authored Context Provider APIs. | Compiler scratch restores required setters while Notes CRUD, RouteIR signals, concrete state operations, diagnostics, and zero Context runtime output pass. |
|
|
40
|
+
| `0.8.47` | Initialize specialized child state from one direct primitive parent state prop. | Build-known primitive seeds, independent state ownership, existing handler specialization, and negative object/expression diagnostics pass without a runtime change. |
|
|
39
41
|
|
|
40
42
|
## Sequence Rules
|
|
41
43
|
|
|
@@ -359,6 +359,7 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
|
|
|
359
359
|
const functions = new Map()
|
|
360
360
|
const customHookFunctionsByOwner = new Map()
|
|
361
361
|
const customHookPrivateFields = new WeakMap()
|
|
362
|
+
const contextProviderPrivateSetters = new WeakMap()
|
|
362
363
|
const components = new Map()
|
|
363
364
|
const contexts = new Set()
|
|
364
365
|
const customHooks = new Map()
|
|
@@ -490,7 +491,7 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
|
|
|
490
491
|
fields.add(name)
|
|
491
492
|
}
|
|
492
493
|
for (const [setter, state] of states) {
|
|
493
|
-
if (fields.has(setter)
|
|
494
|
+
if (fields.has(setter) && !fields.has(state)) throw sourceNodeError(value, providerSource, `Context Provider setter ${JSON.stringify(setter)} requires exposed state ${JSON.stringify(state)}`)
|
|
494
495
|
}
|
|
495
496
|
for (const [name, callback] of callbacks) {
|
|
496
497
|
if (!fields.has(name)) continue
|
|
@@ -499,7 +500,7 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
|
|
|
499
500
|
if (capture) throw sourceNodeError(callback, providerSource, `Context action ${JSON.stringify(name)} cannot capture private binding ${JSON.stringify(capture)}`)
|
|
500
501
|
for (const state of referencedStateNames(callback.body, states, callback)) {
|
|
501
502
|
const setter = [...states].find(([, candidate]) => candidate === state)?.[0]
|
|
502
|
-
if (!setter || !fields.has(state)
|
|
503
|
+
if (!setter || !fields.has(state)) throw sourceNodeError(callback, providerSource, `Context action ${JSON.stringify(name)} requires exposed state field ${JSON.stringify(state)}`)
|
|
503
504
|
}
|
|
504
505
|
}
|
|
505
506
|
return { callbacks: new Map([...callbacks].filter(([name]) => fields.has(name))), context: true, fields, privateStates: new Set(), stateOwner, stateSymbols, states }
|
|
@@ -739,6 +740,32 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
|
|
|
739
740
|
ts.forEachChild(node, collect)
|
|
740
741
|
}
|
|
741
742
|
collect(sourceFile)
|
|
743
|
+
const collectContextProviderPrivateSetters = node => {
|
|
744
|
+
if (ts.isJsxAttribute(node) && isContextProviderValue(node, contexts)) {
|
|
745
|
+
const value = node.initializer && ts.isJsxExpression(node.initializer) && node.initializer.expression ? unwrapExpression(node.initializer.expression) : undefined
|
|
746
|
+
const owner = nearestFunction(node)
|
|
747
|
+
if (value && ts.isObjectLiteralExpression(value) && owner) {
|
|
748
|
+
const states = new Map()
|
|
749
|
+
const callbacks = new Map()
|
|
750
|
+
const visitOwner = current => {
|
|
751
|
+
if (ts.isVariableDeclaration(current) && nearestFunction(current) === owner) {
|
|
752
|
+
if (ts.isArrayBindingPattern(current.name) && current.initializer && ts.isCallExpression(current.initializer) && ts.isIdentifier(current.initializer.expression) && current.initializer.expression.text === "useState") {
|
|
753
|
+
const [state, setter] = current.name.elements
|
|
754
|
+
if (current.name.elements.length === 2 && state && setter && ts.isBindingElement(state) && ts.isBindingElement(setter) && ts.isIdentifier(state.name) && ts.isIdentifier(setter.name)) states.set(setter.name.text, state.name.text)
|
|
755
|
+
}
|
|
756
|
+
if (ts.isIdentifier(current.name) && current.initializer && (ts.isArrowFunction(current.initializer) || ts.isFunctionExpression(current.initializer))) callbacks.set(current.name.text, current.initializer)
|
|
757
|
+
}
|
|
758
|
+
ts.forEachChild(current, visitOwner)
|
|
759
|
+
}
|
|
760
|
+
visitOwner(owner.body)
|
|
761
|
+
const fields = new Set(value.properties.filter(ts.isShorthandPropertyAssignment).map(property => property.name.text))
|
|
762
|
+
const missing = [...states].filter(([setter, state]) => fields.has(state) && !fields.has(setter) && [...callbacks].some(([name, callback]) => fields.has(name) && referencedStateNames(callback.body, states, callback).has(state))).map(([setter]) => setter)
|
|
763
|
+
if (missing.length) contextProviderPrivateSetters.set(node, missing)
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
ts.forEachChild(node, collectContextProviderPrivateSetters)
|
|
767
|
+
}
|
|
768
|
+
collectContextProviderPrivateSetters(sourceFile)
|
|
742
769
|
const functionsForNode = node => {
|
|
743
770
|
const callbacks = customHookFunctionsByOwner.get(nearestFunction(node))
|
|
744
771
|
return callbacks ? new Map([...functions, ...callbacks]) : functions
|
|
@@ -1581,6 +1608,11 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
|
|
|
1581
1608
|
return factory.updateJsxExpression(node, factory.createCallExpression(factory.createIdentifier("__kList"), undefined, arguments_))
|
|
1582
1609
|
}
|
|
1583
1610
|
const visitor = node => {
|
|
1611
|
+
if (ts.isJsxAttribute(node) && contextProviderPrivateSetters.has(node)) {
|
|
1612
|
+
const expression = unwrapExpression(node.initializer.expression)
|
|
1613
|
+
const value = factory.updateObjectLiteralExpression(expression, [...expression.properties, ...contextProviderPrivateSetters.get(node).map(name => factory.createShorthandPropertyAssignment(name))])
|
|
1614
|
+
return factory.updateJsxAttribute(node, node.name, factory.updateJsxExpression(node.initializer, value))
|
|
1615
|
+
}
|
|
1584
1616
|
if (ts.isVariableDeclaration(node) && ts.isObjectBindingPattern(node.name) && customHookPrivateFields.has(node)) {
|
|
1585
1617
|
const privateFields = customHookPrivateFields.get(node)
|
|
1586
1618
|
return factory.updateVariableDeclaration(node, factory.updateObjectBindingPattern(node.name, [
|
|
@@ -2408,10 +2440,12 @@ function specializeComponentCall(call, component, sourceFile, factory, context,
|
|
|
2408
2440
|
if (declaration.initializer && ts.isCallExpression(declaration.initializer) && ts.isIdentifier(declaration.initializer.expression) && declaration.initializer.expression.text === "useState") {
|
|
2409
2441
|
const hookLabel = ordinaryHooks ? "Setter-callback component" : "Keyed row"
|
|
2410
2442
|
const initialArgument = declaration.initializer.arguments[0]
|
|
2411
|
-
const propReceiver = ordinaryHooks && initialArgument && ts.isCallExpression(initialArgument) && initialArgument.arguments.length === 0 && !initialArgument.questionDotToken && ts.isPropertyAccessExpression(initialArgument.expression) && !initialArgument.expression.questionDotToken && initialArgument.expression.name.text === "toString" && ts.isIdentifier(initialArgument.expression.expression) ? initialArgument.expression.expression : undefined
|
|
2443
|
+
const propReceiver = ordinaryHooks && initialArgument && (ts.isIdentifier(initialArgument) ? initialArgument : ts.isCallExpression(initialArgument) && initialArgument.arguments.length === 0 && !initialArgument.questionDotToken && ts.isPropertyAccessExpression(initialArgument.expression) && !initialArgument.expression.questionDotToken && initialArgument.expression.name.text === "toString" && ts.isIdentifier(initialArgument.expression.expression) ? initialArgument.expression.expression : undefined)
|
|
2412
2444
|
const substitutedProp = propReceiver ? substitutions.get(propReceiver.text) : undefined
|
|
2413
|
-
const
|
|
2414
|
-
|
|
2445
|
+
const substitutedState = substitutedProp && ts.isIdentifier(unwrapExpression(substitutedProp)) ? unwrapExpression(substitutedProp).text : undefined
|
|
2446
|
+
const directProp = ts.isIdentifier(initialArgument)
|
|
2447
|
+
const propInitializer = substitutedState && ordinaryStateNames.has(substitutedState) && (!directProp || hasPrimitiveStateInitializer(call, substitutedState))
|
|
2448
|
+
if (declaration.initializer.arguments.length !== 1 || !isSerializableStateLiteral(initialArgument) && !propInitializer) throw sourceNodeError(declaration.initializer, component.getSourceFile(), `${hookLabel} useState() must use one directly serializable primitive, plain object, or array initial value${ordinaryHooks ? " or direct primitive state prop, optionally followed by .toString()" : ""}; other dynamic initializers are not supported`)
|
|
2415
2449
|
if (!ts.isArrayBindingPattern(declaration.name) || declaration.name.elements.length !== 2 || declaration.name.elements.some(element => !element || !ts.isBindingElement(element) || !ts.isIdentifier(element.name) || element.initializer || element.dotDotDotToken)) throw sourceNodeError(declaration.name, component.getSourceFile(), `${hookLabel} useState() must use [state, setter] identifier destructuring`)
|
|
2416
2450
|
const suffix = `${Math.max(0, call.pos)}_${ordinaryHooks ? ordinaryStates.length : rowStates.length}`
|
|
2417
2451
|
const state = ordinaryHooks ? `__kComponentState${suffix}` : `__kRowState${suffix}`
|
|
@@ -2422,7 +2456,11 @@ function specializeComponentCall(call, component, sourceFile, factory, context,
|
|
|
2422
2456
|
factory.createBindingElement(undefined, undefined, factory.createIdentifier(state)),
|
|
2423
2457
|
factory.createBindingElement(undefined, undefined, factory.createIdentifier(setter))
|
|
2424
2458
|
])
|
|
2425
|
-
const initialValue =
|
|
2459
|
+
const initialValue = propInitializer
|
|
2460
|
+
? ts.isIdentifier(initialArgument)
|
|
2461
|
+
? factory.createPropertyAccessExpression(cloneAst(substitutedProp, factory, context), "value")
|
|
2462
|
+
: substituteClone(initialArgument, substitutions, factory, context)
|
|
2463
|
+
: cloneAst(initialArgument, factory, context)
|
|
2426
2464
|
synthesizeTree(initialValue)
|
|
2427
2465
|
const initializer = factory.createCallExpression(factory.createIdentifier(ordinaryHooks ? "__kComponentUseState" : "__kRowUseState"), undefined, [initialValue])
|
|
2428
2466
|
hookDeclarations.push(factory.createVariableStatement(undefined, factory.createVariableDeclarationList([factory.createVariableDeclaration(binding, undefined, undefined, initializer)], ts.NodeFlags.Const)))
|
|
@@ -2505,6 +2543,20 @@ function isSerializableStateLiteral(node) {
|
|
|
2505
2543
|
return value.properties.every(property => ts.isPropertyAssignment(property) && !ts.isComputedPropertyName(property.name) && property.name.text !== "__proto__" && isSerializableStateLiteral(property.initializer))
|
|
2506
2544
|
}
|
|
2507
2545
|
|
|
2546
|
+
function hasPrimitiveStateInitializer(call, name) {
|
|
2547
|
+
const owner = nearestFunction(call)
|
|
2548
|
+
if (!owner || !ts.isBlock(owner.body)) return false
|
|
2549
|
+
for (const statement of owner.body.statements) {
|
|
2550
|
+
if (!ts.isVariableStatement(statement)) continue
|
|
2551
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
2552
|
+
if (!ts.isArrayBindingPattern(declaration.name) || !declaration.initializer || !ts.isCallExpression(declaration.initializer) || !ts.isIdentifier(declaration.initializer.expression) || declaration.initializer.expression.text !== "useState") continue
|
|
2553
|
+
const state = declaration.name.elements[0]
|
|
2554
|
+
if (state && ts.isBindingElement(state) && ts.isIdentifier(state.name) && state.name.text === name) return declaration.initializer.arguments.length === 1 && isPrimitiveDefaultLiteral(unwrapExpression(declaration.initializer.arguments[0]))
|
|
2555
|
+
}
|
|
2556
|
+
}
|
|
2557
|
+
return false
|
|
2558
|
+
}
|
|
2559
|
+
|
|
2508
2560
|
function synthesizeSerializableStateLiteral(node, factory) {
|
|
2509
2561
|
node = unwrapExpression(node)
|
|
2510
2562
|
if (ts.isStringLiteral(node)) return factory.createStringLiteral(node.text)
|