@kudzujs/core 0.16.6 → 0.16.7
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 +2 -2
- package/PERFORMANCE.md +15 -0
- package/README.md +1 -1
- package/RELEASES.md +42 -0
- package/docs/next-architecture/README.md +1 -1
- package/docs/next-architecture/application-capability-release-plan.md +32 -1
- package/docs/next-architecture/compiler-current-architecture.md +1 -1
- package/docs/next-architecture/large-application-ai-native-roadmap.md +3 -3
- package/docs/next-architecture/versioning.md +2 -2
- package/framework/compiler/effect-private-ref-pass.mjs +15 -4
- package/package.json +1 -1
package/MIGRATION_ROADMAP.md
CHANGED
|
@@ -51,7 +51,7 @@ Syntax compatibility does not mean reproducing React wholesale. Kudzu accepts th
|
|
|
51
51
|
- stronger JavaScript failure resilience;
|
|
52
52
|
- no remaining material large-route build-scaling weakness.
|
|
53
53
|
|
|
54
|
-
The completed 0.9 plan defines the cross-framework comparison and release gate. The active application packet is `0.18.
|
|
54
|
+
The completed 0.9 plan defines the cross-framework comparison and release gate. The active application packet is `0.18.3` in the capability release plan below. Benchmark-only feature omission, unmatched accessibility, weighted scores that hide losses, and unrecorded environment differences do not count as proof.
|
|
55
55
|
|
|
56
56
|
### 0.10.0 Through 0.21.x: Application Capability Release Train
|
|
57
57
|
|
|
@@ -128,7 +128,7 @@ The following are available building blocks, not future vertical roadmaps:
|
|
|
128
128
|
- `useState`, independent repeated non-keyed child state with conditional mount ownership, reduced relative-imported `useReducer`, direct dispatch specialization, and reduced Zustand-shaped shared state lowered through package-neutral shared-state/action IR and proven by migration fixtures.
|
|
129
129
|
- Mount and dependency effects with cleanup, route/layout lifetimes, conditional/keyed ownership, stale-write isolation, and relative TypeScript Workers.
|
|
130
130
|
- Direct browser-only package references in inline effect setup/cleanup callbacks bundle into route-owned effect ESM without entering build-time component execution or static sibling output.
|
|
131
|
-
- A CodeMirror-class editor may retain one package instance across one mount effect and later dependency update effects through a null-initialized ref with one direct setup assignment and null-reset cleanup. The ref lowers to existing owner-scoped object state; package DOM, bidirectional updates, errors, accessibility, conditional disposal, and fresh remount require no widget runtime or React island.
|
|
131
|
+
- A CodeMirror-class editor may retain one package instance across one mount or guarded lazy activation effect and later dependency update effects through a null-initialized ref with one direct setup assignment and null-reset cleanup. The lazy form assigns only inside the literal package import's direct `.then()` callback. The ref lowers to existing owner-scoped object state; package DOM, bidirectional updates, errors, accessibility, conditional disposal, and fresh remount require no loader, widget runtime, or React island.
|
|
132
132
|
- A Chart.js-class canvas instance uses the same retained-handle contract. One mount effect owns construction, resize listeners, and destroy/reset cleanup; later data effects read the instance without replacing it. Enhanced route disposal and fresh remount require no chart runtime or package-specific IR.
|
|
133
133
|
- A SortableJS-class drag engine may temporarily move keyed DOM during a gesture, but Kudzu state remains the durable order owner. The package callback restores authored DOM order before one immutable state reorder; existing keyed reconciliation then preserves row identity. Keyboard controls use the same state operation, package disposal is effect-owned, and static siblings exclude the package.
|
|
134
134
|
- Keyed local-state and imported static collections, pure selectors, nested direct-property lists, recursive and directly exported row specialization, static/keyed component reuse, analyzable specialized prop spreads, serializable literal defaults, direct intrinsic rest forwarding, forwarded JSX children, latest-item handlers, row state/effects/refs, and stable keyed identity.
|
package/PERFORMANCE.md
CHANGED
|
@@ -2,6 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
Reproducibility classes: `npm run benchmark`, `npm run benchmark:keyed`, `npm run benchmark:native`, `npm run benchmark:module-cache`, `npm run benchmark:project-navigation`, `npm run benchmark:project-state`, and `npm run benchmark:source-scale` are maintained in this repository; `npm run benchmark:commerce` is a maintained paired runner over the public external storefront; older excluded-workspace sections are historical provenance only and are not current framework rankings.
|
|
4
4
|
|
|
5
|
+
## 0.16.7 Lazy Retained Editor Lifecycle
|
|
6
|
+
|
|
7
|
+
Measured 2026-08-30 on macOS arm64 with Node 24.14.0 and Chrome. The existing
|
|
8
|
+
eager real CodeMirror editor owns 214,968 raw / 71,607 aggregate gzip B. The
|
|
9
|
+
lazy retained journey initially owns 15,896 raw / 7,230 gzip B and defers
|
|
10
|
+
250,086 raw / 80,890 gzip B, reducing initial gzip by 64,377 B (89.9%). Its
|
|
11
|
+
static sibling remains complete HTML with zero JavaScript.
|
|
12
|
+
|
|
13
|
+
Required Chrome proves zero initial deferred requests, one activation request,
|
|
14
|
+
bidirectional updates, accessible update-error recovery, retained editor DOM,
|
|
15
|
+
conditional cleanup, cached fresh remount, and document disposal. The maintained
|
|
16
|
+
Worker benchmark remains 907 raw / 477 gzip B for the Worker graph and 14,456
|
|
17
|
+
raw / 6,160 gzip B for the window graph. Seven clean builds record a 230.5 ms
|
|
18
|
+
median. No browser runtime file or concept was added.
|
|
19
|
+
|
|
5
20
|
## 0.16.6 Owner-Triggered Capability Imports
|
|
6
21
|
|
|
7
22
|
Measured 2026-08-29 on macOS arm64 with Node 24.14.0 and Chrome. The existing
|
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.16.x`: the compiler API and supported TSX surface may change.
|
|
16
16
|
|
|
17
|
-
**Latest release: 0.16.
|
|
17
|
+
**Latest release: 0.16.7 - Lazy retained editor lifecycle.** A real CodeMirror editor now loads on first activation while preserving bidirectional updates, accessible error recovery, retained identity, and exact disposal. Read the [release notes](./RELEASES.md#0167---lazy-retained-editor-lifecycle), open the [release page](https://github.com/kudzujs/kudzu/releases/tag/v0.16.7), 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,47 @@
|
|
|
1
1
|
# Kudzu Releases
|
|
2
2
|
|
|
3
|
+
## 0.16.7 - Lazy Retained Editor Lifecycle
|
|
4
|
+
|
|
5
|
+
Kudzu 0.16.7 applies owner-triggered package loading to the real retained
|
|
6
|
+
CodeMirror editor journey without losing update, error, identity, or cleanup
|
|
7
|
+
behavior.
|
|
8
|
+
|
|
9
|
+
### Changed in 0.16.7
|
|
10
|
+
|
|
11
|
+
- Lets one retained `useRef(null)` package handle receive its single setup
|
|
12
|
+
assignment inside the direct `.then()` callback of a guarded literal package
|
|
13
|
+
import.
|
|
14
|
+
- Reuses existing owner-scoped retained state across the lazy activation effect
|
|
15
|
+
and later read-only update effects.
|
|
16
|
+
- Keeps indirect promise callbacks, multiple assignments, update-effect writes,
|
|
17
|
+
attached refs, missing null-reset cleanup, and broader dynamic imports
|
|
18
|
+
unsupported.
|
|
19
|
+
- Preserves application-to-editor and editor-to-application updates, accessible
|
|
20
|
+
package errors and recovery, retained DOM identity, conditional disposal, and
|
|
21
|
+
fresh state on remount.
|
|
22
|
+
- Uses the native browser module map for cached remount and adds no loader,
|
|
23
|
+
resource registry, widget runtime, React, VDOM, hydration, or retained browser
|
|
24
|
+
component tree.
|
|
25
|
+
- Updates `create-kudzu@0.1.132` to generate projects on
|
|
26
|
+
`@kudzujs/core@^0.16.7`.
|
|
27
|
+
|
|
28
|
+
### Output And Browser Evidence
|
|
29
|
+
|
|
30
|
+
- The eager real editor owns 214,968 raw / 71,607 gzip bytes. The lazy journey
|
|
31
|
+
initially owns 15,896 raw / 7,230 gzip bytes and defers 250,086 raw / 80,890
|
|
32
|
+
gzip bytes, reducing initial gzip by 64,377 bytes (89.9%).
|
|
33
|
+
- Required Chrome proves zero initial chunk requests, one on-demand request,
|
|
34
|
+
bidirectional updates, accessible failure recovery, retained identity, exact
|
|
35
|
+
conditional cleanup, cached fresh remount, and document cleanup.
|
|
36
|
+
- The static sibling emits zero JavaScript. The maintained Worker and window
|
|
37
|
+
graphs remain 907 raw / 477 gzip bytes and 14,456 raw / 6,160 gzip bytes.
|
|
38
|
+
|
|
39
|
+
### Upgrade
|
|
40
|
+
|
|
41
|
+
```sh
|
|
42
|
+
npm install @kudzujs/core@^0.16.7
|
|
43
|
+
```
|
|
44
|
+
|
|
3
45
|
## 0.16.6 - Owner-Triggered Capability Imports
|
|
4
46
|
|
|
5
47
|
Kudzu 0.16.6 lets one guarded effect load a literal package dependency only
|
|
@@ -15,7 +15,7 @@ The completed `0.9.0` milestone is recorded in [`0.9-semantic-compression.md`](.
|
|
|
15
15
|
|
|
16
16
|
[`1.0-large-application-compatibility-audit.md`](./1.0-large-application-compatibility-audit.md) records the first post-0.9 probes against Memos, Apache Answer, and Actual Budget. The audit finds that reduced slices build but whole-application source retention and behavior parity do not yet pass.
|
|
17
17
|
|
|
18
|
-
[`application-capability-release-plan.md`](./application-capability-release-plan.md) is the authoritative post-0.9 execution queue, currently at `0.18.
|
|
18
|
+
[`application-capability-release-plan.md`](./application-capability-release-plan.md) is the authoritative post-0.9 execution queue, currently at `0.18.3`. It assigns one application-capability section to each minor release and one independently accepted evidence packet to each patch release from `0.10.0` through the `1.0.0` gate. It supersedes the provisional 0.10/0.11/0.12 tool-first ordering in the completed 0.9 handoff without rewriting that historical record.
|
|
19
19
|
|
|
20
20
|
## Required Invariants
|
|
21
21
|
|
|
@@ -1531,6 +1531,36 @@ as the actual `0.16.6` patch to retain the public `0.16.x` version line.
|
|
|
1531
1531
|
- **Done condition:** initial route bytes fall by the exact deferred package
|
|
1532
1532
|
graph without losing behavior.
|
|
1533
1533
|
|
|
1534
|
+
**Completion evidence:** a real retained CodeMirror editor now begins closed and
|
|
1535
|
+
uses the `0.18.1` guarded literal package edge for its mount. The existing
|
|
1536
|
+
effect-private-ref normalization recognizes its one direct `.then()` assignment
|
|
1537
|
+
as the retained setup write, lowers the handle to owner-scoped state, and leaves
|
|
1538
|
+
later value effects read-only. Indirect callbacks, repeated writes, attached
|
|
1539
|
+
refs, update-effect writes, missing null-reset cleanup, and broader dynamic
|
|
1540
|
+
imports retain diagnostics. Package evaluation remains entirely in browser
|
|
1541
|
+
effect ESM.
|
|
1542
|
+
|
|
1543
|
+
Required Chrome records zero chunk requests before activation and one afterward.
|
|
1544
|
+
It proves initial content and accessible name, application-to-editor and
|
|
1545
|
+
editor-to-application updates, retained DOM identity, authored update failure
|
|
1546
|
+
and recovery, exact conditional disposal, fresh `Alpha` state on remount, no
|
|
1547
|
+
second package request, and final document disposal. Native ESM evaluation
|
|
1548
|
+
failure retry remains outside this packet as recorded in `0.18.1`.
|
|
1549
|
+
|
|
1550
|
+
The eager editor owns 214,968 raw / 71,607 gzip bytes. The lazy retained editor
|
|
1551
|
+
initially owns 15,896 raw / 7,230 gzip bytes and defers 250,086 raw / 80,890 gzip
|
|
1552
|
+
bytes, reducing initial gzip by 64,377 bytes (89.9%). Its static sibling remains
|
|
1553
|
+
0 B JavaScript. The artifact collector covers nine fixtures and 29 routes with
|
|
1554
|
+
no ownerless JavaScript or preload mismatch.
|
|
1555
|
+
|
|
1556
|
+
The packet adds no semantic primitive, IR kind, compiler pass, public API,
|
|
1557
|
+
browser runtime concept/file, loader, resource registry, or widget runtime. One
|
|
1558
|
+
existing normalization pass changes by 15 added and four removed production
|
|
1559
|
+
lines. `npm run check`, 285 required-Chrome tests, package smoke, and the
|
|
1560
|
+
maintained benchmark pass. Worker and window graphs remain 907 raw / 477 gzip
|
|
1561
|
+
and 14,456 raw / 6,160 gzip bytes; the seven-build median is 230.5 ms. This
|
|
1562
|
+
packet ships as the actual `0.16.7` patch to retain the public `0.16.x` line.
|
|
1563
|
+
|
|
1534
1564
|
### `0.18.3`: Shared Chunks And Prefetch Policy
|
|
1535
1565
|
|
|
1536
1566
|
- **Purpose:** avoid duplicate large feature graphs and define optional preload.
|
|
@@ -1837,4 +1867,5 @@ release transaction where possible or document and publish a forward-fix patch.
|
|
|
1837
1867
|
| `0.17.2` | Released as `0.16.5` | Preserve stale-prefetch rejection and bounded document, state, resource, listener, DOM, and external-instance ownership. | Patch release retained the `0.16.x` public version line |
|
|
1838
1868
|
| `0.18.0` | Closed by existing artifact reporting | Preserve exact route/source ownership, bytes, hashes, preload equality, zero-JavaScript controls, and bounded prefetch policy. | No production change; no release consumed |
|
|
1839
1869
|
| `0.18.1` | Released as `0.16.6` | Preserve one guarded literal package edge, native module-map deduplication, exact owner cleanup, deferred artifact ownership, and static exclusion. | Patch release retained the `0.16.x` public version line |
|
|
1840
|
-
| `0.18.2` |
|
|
1870
|
+
| `0.18.2` | Released as `0.16.7` | Preserve on-demand retained editor mount, bidirectional updates, failure recovery, identity, exact cleanup, cached fresh remount, and static exclusion. | Patch release retained the `0.16.x` public version line |
|
|
1871
|
+
| `0.18.3` | Active | Measure shared deferred graphs and choose the smallest evidence-backed prefetch policy. | None |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Current Compiler Architecture
|
|
2
2
|
|
|
3
|
-
This maps the current `0.16.
|
|
3
|
+
This maps the current `0.16.7` architecture, built on the completed `0.9.0` semantic-compression release and `0.8.23` Goal A compiler foundation. The active application packet is `0.18.3`; file and function names are the stable references, while line numbers are intentionally omitted because later work may still move code.
|
|
4
4
|
|
|
5
5
|
## Responsibility Map
|
|
6
6
|
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
## Status
|
|
4
4
|
|
|
5
|
-
Completed compiler-foundation record and longer-term plan after `0.8.35`. The [`0.9-semantic-compression.md`](./0.9-semantic-compression.md) execution queue is complete. The active queue for current work is [`application-capability-release-plan.md`](./application-capability-release-plan.md) at packet `0.18.
|
|
5
|
+
Completed compiler-foundation record and longer-term plan after `0.8.35`. The [`0.9-semantic-compression.md`](./0.9-semantic-compression.md) execution queue is complete. The active queue for current work is [`application-capability-release-plan.md`](./application-capability-release-plan.md) at packet `0.18.3`. This document does not mark any remaining planned capability as supported and does not authorize a React runtime, VDOM, hydration, retained browser component tree, generic rerenderer, public store/query/resource API, SPA router, or islands.
|
|
6
6
|
|
|
7
|
-
[`MIGRATION_ROADMAP.md`](../../MIGRATION_ROADMAP.md) remains the product authority for product invariants and fixture-driven feature selection. [`application-capability-release-plan.md`](./application-capability-release-plan.md) is authoritative for current work order and evidence at packet `0.18.
|
|
7
|
+
[`MIGRATION_ROADMAP.md`](../../MIGRATION_ROADMAP.md) remains the product authority for product invariants and fixture-driven feature selection. [`application-capability-release-plan.md`](./application-capability-release-plan.md) is authoritative for current work order and evidence at packet `0.18.3`; this plan retains the completed foundation, deferred program, and long-term production gates. If implementation evidence changes either boundary, update the relevant document before broadening a patch.
|
|
8
8
|
|
|
9
9
|
## Product Outcome
|
|
10
10
|
|
|
@@ -483,4 +483,4 @@ The first comparison is Kudzu versus React + Vite using the same agent, model, t
|
|
|
483
483
|
|
|
484
484
|
## Immediate Decision
|
|
485
485
|
|
|
486
|
-
All listed foundation and `0.9` slices are complete. Current work continues at `0.18.
|
|
486
|
+
All listed foundation and `0.9` slices are complete. Current work continues at `0.18.3` Shared Chunks And Prefetch Policy under the [`application-capability-release-plan.md`](./application-capability-release-plan.md); keep ResourceIR limited to qualifying independent fixtures, and do not add range ownership, virtualization, optimistic transactions, a public adapter/store API, or a router before evidence justifies them.
|
|
@@ -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.62` are completed scopes represented by package/release records. The `0.10.0` through `1.0.0` minor/patch sequence is maintained in [`application-capability-release-plan.md`](./application-capability-release-plan.md), currently at `0.18.
|
|
3
|
+
This is an execution sequence, not release history. `0.8.16` through `0.8.62` are completed scopes represented by package/release records. The `0.10.0` through `1.0.0` minor/patch sequence is maintained in [`application-capability-release-plan.md`](./application-capability-release-plan.md), currently at `0.18.3`; that plan supersedes the provisional tool-first 0.10/0.11/0.12 ordering in the completed 0.9 handoff.
|
|
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
|
|
|
@@ -66,7 +66,7 @@ Keep each patch behavior-preserving and independently reviewable. If a boundary
|
|
|
66
66
|
|
|
67
67
|
## Generator Versions
|
|
68
68
|
|
|
69
|
-
`create-kudzu@0.1.
|
|
69
|
+
`create-kudzu@0.1.132` retains the explicit install instructions and generates projects with `@kudzujs/core@^0.16.7`.
|
|
70
70
|
|
|
71
71
|
## Release Boundary
|
|
72
72
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import ts from "typescript"
|
|
2
2
|
import { effectReturns, importDeclarationNames, isNodeWithin, isShadowedIdentifier, nearestFunction, referenceIdentifiers, referencesIdentifier, sourceNodeError, statementDeclaresName, unwrapExpression } from "./ast-helpers.mjs"
|
|
3
|
+
import { ownedLazyPackageImport } from "./source-graph.mjs"
|
|
3
4
|
|
|
4
5
|
export function normalizeEffectPrivateRefs(sourceFile, factory, context) {
|
|
5
6
|
const frameCall = (node, name) => ts.isCallExpression(node) && (
|
|
@@ -22,6 +23,16 @@ export function normalizeEffectPrivateRefs(sourceFile, factory, context) {
|
|
|
22
23
|
if (isNegated) condition = unwrapExpression(condition.operand)
|
|
23
24
|
return isNegated === negated && currentAccess(condition, name)
|
|
24
25
|
}
|
|
26
|
+
const containsLazyImport = node => {
|
|
27
|
+
if (ownedLazyPackageImport(node)) return true
|
|
28
|
+
let found = false
|
|
29
|
+
ts.forEachChild(node, child => { if (!found) found = containsLazyImport(child) })
|
|
30
|
+
return found
|
|
31
|
+
}
|
|
32
|
+
const directLazyImportCallback = (callback, effectCallback) => {
|
|
33
|
+
const call = callback?.parent
|
|
34
|
+
return (ts.isArrowFunction(callback) || ts.isFunctionExpression(callback)) && ts.isCallExpression(call) && call.arguments[0] === callback && ts.isPropertyAccessExpression(call.expression) && call.expression.name.text === "then" && ownedLazyPackageImport(unwrapExpression(call.expression.expression)) && isNodeWithin(callback, effectCallback)
|
|
35
|
+
}
|
|
25
36
|
const hasUseRefImport = sourceFile.statements.some(statement => ts.isImportDeclaration(statement) && !statement.importClause?.isTypeOnly && ts.isStringLiteral(statement.moduleSpecifier) && ["react", "@kudzujs/core"].includes(statement.moduleSpecifier.text) && statement.importClause?.namedBindings && ts.isNamedImports(statement.importClause.namedBindings) && statement.importClause.namedBindings.elements.some(entry => !entry.propertyName && entry.name.text === "useRef"))
|
|
26
37
|
const hasUseEffectImport = sourceFile.statements.some(statement => ts.isImportDeclaration(statement) && !statement.importClause?.isTypeOnly && ts.isStringLiteral(statement.moduleSpecifier) && ["react", "@kudzujs/core"].includes(statement.moduleSpecifier.text) && statement.importClause?.namedBindings && ts.isNamedImports(statement.importClause.namedBindings) && statement.importClause.namedBindings.elements.some(entry => !entry.propertyName && entry.name.text === "useEffect"))
|
|
27
38
|
const hasUseStateImport = sourceFile.statements.some(statement => ts.isImportDeclaration(statement) && !statement.importClause?.isTypeOnly && ts.isStringLiteral(statement.moduleSpecifier) && ["react", "@kudzujs/core"].includes(statement.moduleSpecifier.text) && statement.importClause?.namedBindings && ts.isNamedImports(statement.importClause.namedBindings) && statement.importClause.namedBindings.elements.some(entry => !entry.propertyName && entry.name.text === "useState"))
|
|
@@ -73,8 +84,8 @@ export function normalizeEffectPrivateRefs(sourceFile, factory, context) {
|
|
|
73
84
|
const callbacks = owners.map(effect => effect.arguments[0])
|
|
74
85
|
const directCallbacks = callbacks.every(callback => (ts.isArrowFunction(callback) || ts.isFunctionExpression(callback)) && ts.isBlock(callback.body))
|
|
75
86
|
const everyAccessOwned = accesses.every(access => callbacks.filter(callback => isNodeWithin(access, callback)).length === 1)
|
|
76
|
-
const mountEffects = owners.filter(effect => ts.isArrayLiteralExpression(effect.arguments[1]) && !effect.arguments[1].elements.length)
|
|
77
|
-
const updateEffects = owners.filter(effect =>
|
|
87
|
+
const mountEffects = owners.filter(effect => ts.isArrayLiteralExpression(effect.arguments[1]) && (!effect.arguments[1].elements.length || containsLazyImport(effect.arguments[0])))
|
|
88
|
+
const updateEffects = owners.filter(effect => !mountEffects.includes(effect))
|
|
78
89
|
if (owners.length >= 2 && directCallbacks && everyAccessOwned && mountEffects.length === 1 && updateEffects.length === owners.length - 1 && owners[0] === mountEffects[0]) {
|
|
79
90
|
const mount = mountEffects[0]
|
|
80
91
|
const callback = mount.arguments[0]
|
|
@@ -85,7 +96,7 @@ export function normalizeEffectPrivateRefs(sourceFile, factory, context) {
|
|
|
85
96
|
const cleanupWrites = cleanup ? writes.filter(access => isNodeWithin(access, cleanup)) : []
|
|
86
97
|
const setupStatement = setupWrites[0]?.parent.parent
|
|
87
98
|
const cleanupStatement = cleanupWrites[0]?.parent.parent
|
|
88
|
-
const directSetup = setupWrites.length === 1 && ts.isExpressionStatement(setupStatement) && setupStatement.parent === callback.body && unwrapExpression(setupWrites[0].parent.right).kind !== ts.SyntaxKind.NullKeyword
|
|
99
|
+
const directSetup = setupWrites.length === 1 && ts.isExpressionStatement(setupStatement) && (setupStatement.parent === callback.body || directLazyImportCallback(nearestFunction(setupWrites[0]), callback)) && unwrapExpression(setupWrites[0].parent.right).kind !== ts.SyntaxKind.NullKeyword
|
|
89
100
|
const directCleanup = cleanup && ts.isBlock(cleanup.body) && cleanupWrites.length === 1 && ts.isExpressionStatement(cleanupStatement) && cleanupStatement.parent === cleanup.body && unwrapExpression(cleanupWrites[0].parent.right).kind === ts.SyntaxKind.NullKeyword
|
|
90
101
|
const updateWrites = mutations.some(access => updateEffects.some(effect => isNodeWithin(access, effect.arguments[0])))
|
|
91
102
|
if (directSetup && directCleanup && !updateWrites) {
|
|
@@ -96,7 +107,7 @@ export function normalizeEffectPrivateRefs(sourceFile, factory, context) {
|
|
|
96
107
|
return
|
|
97
108
|
}
|
|
98
109
|
}
|
|
99
|
-
if (owners.length >= 2) throw sourceNodeError(node, sourceFile, "Retained instance refs require one empty-dependency mount effect with one direct assignment and null-reset cleanup followed by read-only dependency effects")
|
|
110
|
+
if (owners.length >= 2) throw sourceNodeError(node, sourceFile, "Retained instance refs require one empty-dependency mount effect or guarded lazy activation effect with one direct assignment and null-reset cleanup followed by read-only dependency effects")
|
|
100
111
|
}
|
|
101
112
|
if (!frameAssignments.length) {
|
|
102
113
|
const callback = effects.length === 1 ? effects[0].arguments[0] : undefined
|