@kudzujs/core 0.8.13 → 0.8.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 CHANGED
@@ -14,19 +14,21 @@ 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.13 - Browser capability migration.** Memos scroll spies, Excalidraw progressive sharing, and Cal.com responsive media queries compile through owned browser effects without React or general capability runtimes. Read the [release notes](./RELEASES.md#0813---browser-capability-migration) or open the [release page](https://kudzujs.cloud/releases/0.8.13).
17
+ **Latest release: 0.8.15 - Compiler architecture.** Kudzu's normalization passes, React and Router migration analysis, Worker compiler, effect and handler codegen, and development server now have explicit module boundaries while preserving generated output and runtime behavior. Read the [release notes](./RELEASES.md#0815---compiler-architecture) or open the [release page](https://kudzujs.cloud/releases/0.8.15).
18
18
 
19
19
  - [Documentation](https://kudzujs.cloud/docs)
20
20
  - [Installation guide](https://kudzujs.cloud/docs#install)
21
21
  - [Components and migration support](https://kudzujs.cloud/docs#components)
22
22
  - [Current limits](https://kudzujs.cloud/docs#limits)
23
23
  - [Benchmarks](https://kudzujs.cloud/docs#benchmarks)
24
- - [Raw performance records](./PERFORMANCE.md)
25
- - [React migration roadmap](./MIGRATION_ROADMAP.md)
24
+ - [Raw performance records](https://github.com/kudzujs/kudzu/blob/main/PERFORMANCE.md)
25
+ - [React migration roadmap](https://github.com/kudzujs/kudzu/blob/main/MIGRATION_ROADMAP.md)
26
26
  - [Release history](./RELEASES.md)
27
27
 
28
28
  ## Quick Start
29
29
 
30
+ Kudzu requires Node.js 22 or newer.
31
+
30
32
  ```bash
31
33
  npm create kudzu@latest my-app
32
34
  cd my-app
@@ -38,7 +40,19 @@ The generated project includes reusable components, an interactive state example
38
40
  To add Kudzu to an existing project:
39
41
 
40
42
  ```bash
41
- npm install @kudzujs/core
43
+ npm install @kudzujs/core typescript
44
+ ```
45
+
46
+ Add the Kudzu commands to `package.json`:
47
+
48
+ ```json
49
+ {
50
+ "scripts": {
51
+ "dev": "kudzu dev",
52
+ "build": "kudzu build",
53
+ "check": "tsc --noEmit && kudzu build"
54
+ }
55
+ }
42
56
  ```
43
57
 
44
58
  ```json
@@ -100,6 +114,20 @@ See the [complete guide](https://kudzujs.cloud/docs), [interactive features](htt
100
114
 
101
115
  ## Architecture
102
116
 
117
+ Kudzu treats React-shaped TSX as compiler input rather than as a browser runtime programming model:
118
+
119
+ ```text
120
+ React-shaped TypeScript/TSX
121
+ -> ordered AST normalization passes
122
+ -> state/effect/handler/binding/list analysis
123
+ -> complete HTML + route-specific capability ESM
124
+ ```
125
+
126
+ - `framework/compiler/normalization-pipeline.mjs` owns pass order and repairs AST parent pointers after every transform.
127
+ - Focused passes own React, React Router, browser-signal, animation-frame, timer, render-control, and Worker validation.
128
+ - The main transformer produces descriptors; effect and handler codegen modules turn them into route-specific ESM.
129
+ - Unsupported nearby syntax fails with source-located diagnostics instead of falling back to React or a generic runtime.
130
+
103
131
  Kudzu intentionally does not provide:
104
132
 
105
133
  - React runtime compatibility
@@ -109,7 +137,7 @@ Kudzu intentionally does not provide:
109
137
  - Request-time SSR or server actions
110
138
  - A general client state or effect runtime
111
139
 
112
- Browser code is a compiler-generated capability module, included only when a route uses that capability.
140
+ Browser code is a compiler-generated capability module, included only when a route uses that capability. Kudzu does not eliminate every runtime; it eliminates unused runtime.
113
141
 
114
142
  ## Packages
115
143
 
@@ -123,7 +151,7 @@ npm run check
123
151
  npm test
124
152
  ```
125
153
 
126
- Read `AGENTS.md` and `MIGRATION_ROADMAP.md` before extending migration syntax or browser capabilities.
154
+ Read [AGENTS.md](https://github.com/kudzujs/kudzu/blob/main/AGENTS.md) and the [migration roadmap](https://github.com/kudzujs/kudzu/blob/main/MIGRATION_ROADMAP.md) before extending migration syntax or browser capabilities.
127
155
 
128
156
  ## License
129
157
 
package/RELEASES.md CHANGED
@@ -1,5 +1,67 @@
1
1
  # Kudzu Releases
2
2
 
3
+ ## 0.8.15 - Compiler architecture
4
+
5
+ Kudzu 0.8.15 makes the compiler's actual architecture explicit in source and public documentation: React-shaped TSX is normalized, analyzed into capability descriptors, and emitted as complete HTML plus route-specific ESM.
6
+
7
+ ### Changed in 0.8.15
8
+
9
+ - `build.mjs` now owns build orchestration while focused compiler modules own shared AST scope analysis, ordered normalization, React and React Router migration, browser-signal and resource-lifecycle passes, Worker graphs, and effect/handler code generation.
10
+ - The duplicated page/imported-source normalization sequence is one ordered pipeline that restores TypeScript AST parent pointers after every pass.
11
+ - Private custom-hook timer metadata returns explicitly from its pass instead of relying on a module-global `WeakMap` keyed by transformed AST identity.
12
+ - Worker validation, relative graph checks, content-hashed emission, and placeholder rewriting share one compiler boundary while final rendered-effect gating remains in build orchestration.
13
+ - Effect, native-handler, reactive-binding, and keyed-list evaluator generation consume analyzed descriptors without participating in source analysis.
14
+ - The public homepage, README, and web documentation now describe Kudzu as a compiler that treats supported React-shaped TSX as input rather than as a browser runtime model.
15
+
16
+ ### Fixed in 0.8.15
17
+
18
+ - Existing-project installation now states the Node.js 22 requirement and required package scripts.
19
+ - The generator package lock and generated core dependency now match the published package versions.
20
+ - The missing 0.8.2 release page is restored and release routes are included in the sitemap without replacing their page metadata.
21
+ - Documentation tables, code blocks, callouts, navigation, and long inline code remain bounded at a 390px viewport.
22
+ - Package-import handler guidance, serializable array-state guidance, benchmark provenance, and media-query external-store wording now agree with source and tests.
23
+ - Dedicated AST helper and normalization tests raise the complete suite to 153 tests.
24
+
25
+ ### Boundary
26
+
27
+ This release does not broaden the accepted React-shaped syntax or add browser runtime capabilities. The main transformer still owns tightly coupled component, state, collection, and ownership analysis; future extraction should follow proven input/output seams rather than create directory structure without reducing coupling.
28
+
29
+ ### Upgrade
30
+
31
+ ```bash
32
+ npm install @kudzujs/core@^0.8.15
33
+ ```
34
+
35
+ ## 0.8.14 - Localized blog migration
36
+
37
+ Kudzu 0.8.14 proves a localized MDX blog and continuous imperative canvas can migrate without React, request-time i18n, eval, or a canvas runtime.
38
+
39
+ ### New in 0.8.14
40
+
41
+ - Build-known MDX emits complete static article HTML without `eval()` or `new Function()`.
42
+ - Locale-prefixed `/ko` and `/en` routes build through `getStaticPaths()`, and relative Link replacements automatically prefix native hrefs from the build-known locale.
43
+ - The root route selects a stored locale or reads `navigator.languages`, then preserves query and hash through native `location.replace()`.
44
+ - MDX copy blocks and tabs compile through existing clipboard, state, binding, and conditional capabilities.
45
+ - One `null`-initialized canvas ref may feed an inline effect whose local state survives recursive animation frames, visibility changes, and native input listeners.
46
+ - Bare `IntersectionObserver` and `performance` remain browser globals in route-specific effect ESM.
47
+ - Static sibling routes remain complete zero-JavaScript HTML.
48
+
49
+ ### Fixed in 0.8.14
50
+
51
+ - Effect-owned `IntersectionObserver` instances must disconnect in cleanup.
52
+ - Local animation-frame handles assigned by effects must be cancelled in cleanup.
53
+ - The complete suite passes 151/151 tests.
54
+
55
+ ### Boundary
56
+
57
+ This release does not execute `next-intl`, add request-time `Accept-Language` negotiation, general mutable refs, callbacks shared across effects or handlers, a canvas runtime, or arbitrary MDX component execution. Request-time locale redirects remain host/edge configuration; resource-private drawing state and listeners stay inside one owning effect.
58
+
59
+ ### Upgrade
60
+
61
+ ```bash
62
+ npm install @kudzujs/core@^0.8.14
63
+ ```
64
+
3
65
  ## 0.8.13 - Browser capability migration
4
66
 
5
67
  Kudzu 0.8.13 proves browser-owned migration paths from Memos, Excalidraw, and Cal.com without adding React, general external-store compatibility, or browser capability runtimes.
@@ -2,12 +2,16 @@
2
2
 
3
3
  Kudzu specializes ordinary common React-shaped TSX so migrations need minimal source restructuring. Declarative components, collection pipelines, conditions, hooks, and handlers should be lowered at build time rather than replaced with application-owned imperative DOM code. This principle applies across migrations and is not Stay-specific; it does not imply a React package, VDOM, hydration, or ecosystem runtime.
4
4
 
5
- Migration source may retain conventional `react` imports for supported named or aliased hooks, direct members such as `React.useState`, same-file `memo`, inline `useCallback`, direct-state expression or analyzable collection-pipeline `useMemo`, direct intrinsic `forwardRef`, top-level `const` identifiers initialized by `useId()`, and default, namespace, or named `Fragment`. A named or default zero-argument custom hook imported from a relative TypeScript module may return direct shorthand state/setter pairs and callbacks that capture those states, including a callback that resets multiple states to direct primitive literals; callers use one top-level `const` object destructuring without aliases, defaults, or rest. Kudzu's JSX declarations accept ReactNode-shaped component returns and contextually type common intrinsic DOM events, so strict React component props do not need migration-only `unknown` or explicit event annotations. `forwardRef()` accepts one inline synchronous `(props, ref)` function and requires the object ref exactly once on its direct intrinsic root; the compiler removes `ref` from props/rest and erases the wrapper. `useId()` becomes a deterministic build-time HTML ID and emits no browser capability; keyed rows reject it because cloned row templates cannot safely duplicate HTML IDs. Collection memos may start from local array state or a named relative import of an exported JSON-safe `const` array, including type-only `as const` and `satisfies` wrappers, and may read direct local state declared in their dependency array. `build.mjs` canonicalizes those forms and rewrites module references to `@kudzujs/core` before build-time evaluation. Memo wrappers are erased or inlined into existing bindings and keyed-list selectors because no browser component rerender or memo cache exists. Static routes remain JavaScript-free and emitted modules are checked for surviving React imports.
5
+ Migration source may retain conventional `react` imports for supported named or aliased hooks, direct members such as `React.useState`, same-file `memo`, inline `useCallback`, direct-state expression or analyzable collection-pipeline `useMemo`, direct intrinsic `forwardRef`, top-level `const` identifiers initialized by `useId()`, and default, namespace, or named `Fragment`. A named or default zero-argument custom hook imported from a relative TypeScript module may return direct shorthand state/setter pairs and callbacks that capture those states, including a callback that resets multiple states to direct primitive literals; callers use one top-level `const` object destructuring without aliases, defaults, or rest. Kudzu's JSX declarations accept ReactNode-shaped component returns and contextually type common intrinsic DOM events, so strict React component props do not need migration-only `unknown` or explicit event annotations. `forwardRef()` accepts one inline synchronous `(props, ref)` function and requires the object ref exactly once on its direct intrinsic root; the compiler removes `ref` from props/rest and erases the wrapper. `useId()` becomes a deterministic build-time HTML ID and emits no browser capability; keyed rows reject it because cloned row templates cannot safely duplicate HTML IDs. Collection memos may start from local array state or a named relative import of an exported JSON-safe `const` array, including type-only `as const` and `satisfies` wrappers, and may read direct local state declared in their dependency array. `compiler/react-migration-pass.mjs` canonicalizes those forms and rewrites module references to `@kudzujs/core` before build-time evaluation. Memo wrappers are erased or inlined into existing bindings and keyed-list selectors because no browser component rerender or memo cache exists. Static routes remain JavaScript-free and emitted modules are checked for surviving React imports.
6
6
 
7
7
  Compilation begins from page entries and follows relative runtime imports, re-exports, and validated Worker references; unreachable TypeScript migration files are not transformed. Direct maps over imported immutable JSON-safe arrays fold to literals for zero-JavaScript static rows. Synchronous relative calculation functions may return objects whose direct static fields feed reactive JSX bindings; build rendering uses current signal values and route-specific binding ESM reevaluates the same helper after state commits. One direct array field may instead feed a keyed intrinsic map: its evaluator refreshes a compiler-owned array anchor before the existing list reconciler runs, preserving keyed DOM and SVG identity without a calculation runtime. That field must remain a JSON-safe array after every source-state commit. Package imports have a separate narrow boundary: direct references inside intrinsic JSX event callbacks are erased from build modules and bundled into route handler ESM, while render-time, effect, helper-indirect, and mixed package use fails.
8
8
 
9
9
  Native platform work remains ordinary source. A direct async handler or directly returned relative custom-hook callback may call `navigator.clipboard.writeText()` and update application-owned success/failure state; Kudzu emits only its existing route handler ESM. Debounced synchronization uses a dependency effect that creates `setTimeout()` work and directly returns `clearTimeout()` cleanup, reusing dependency, conditional, keyed, and route ownership. One directly returned relative custom-hook callback may own one `null`-initialized private timeout ref when it directly clears the previous value, assigns a numeric-literal-delay `setTimeout()`, and an empty-dependency effect directly clears the timer on cleanup. Kudzu lowers that ref to compiler-owned state shared by existing handler and effect contexts. An ordinary effect may instead exclusively own `useRef(0)` as one animation-frame handle when a local callback resets it, one scheduler assigns `requestAnimationFrame()`, and cleanup cancels the pending frame; Kudzu lowers it to the existing effect scope without an animation runtime. Multiple timers, dynamic delays, intervals, ref aliases, cross-effect frame handles, unowned delayed writes, and arbitrary timed callback graphs remain unsupported.
10
10
 
11
+ Imperative canvas migrations use the same effect ownership rather than a component or canvas runtime. One `null`-initialized canvas DOM ref may feed an inline effect whose local variables persist across a recursive animation-frame callback, an `IntersectionObserver`, and native canvas/window listeners; the returned cleanup must cancel the latest frame, disconnect the observer, and remove every listener. Bare `IntersectionObserver` and `performance` identifiers remain browser globals in emitted effect ESM. Component-level mutable value refs and callbacks shared across effects or JSX handlers remain unsupported; move resource-private state and listeners into the owning effect.
12
+
13
+ Localized static migrations do not need a package router. `getStaticPaths()` emits each build-known locale, a small relative Link replacement prefixes native hrefs from the locale prop, and an optional `/` mount effect may choose a stored locale or inspect `navigator.languages` before calling `location.replace()` with the original query and hash. This preserves automatic browser-language entry and locale-prefixed navigation; request-time `Accept-Language` negotiation still requires host or edge configuration. Build-known MDX becomes static `dangerouslySetInnerHTML`, while copy controls, tabs, and other supported interactive components remain ordinary state and handler capabilities beside that HTML.
14
+
11
15
  A top-level component local shaped exactly as `const supported = "literal" in navigator` may control one direct `supported && <Branch />` JSX site. Kudzu emits the branch as false static fallback, evaluates the browser capability once through an existing mount effect, and uses existing state-conditional ownership to mount its DOM and handlers only when supported. The value cannot escape or feed aliases, composed expressions, ternaries, dynamic properties, `canShare()`, or other browser globals.
12
16
 
13
17
  A direct top-level `useSyncExternalStore()` may represent one static `window.matchMedia()` boolean when its three inline callbacks add/remove the same `change` listener, read the same literal query, and return `false` for the server snapshot. Kudzu emits false static fallback state and lowers the subscription to an existing owned effect. This is not general external-store support: imported/parameterized media hooks, dynamic queries, legacy listeners, and arbitrary snapshots remain diagnosed.
@@ -26,7 +30,19 @@ Repeated ordinary same-file and relative-imported child components execute indep
26
30
 
27
31
  Reduced Zustand migration stores lower to one ordinary layout-lifetime state slot. The compiler accepts one exported `create(set => ({ data, ...actions }))` store with one serializable data property, direct property selectors, and synchronous capture-free actions using one-argument merge-form `set`; selected actions reuse the reducer-style functional update compiler, so same-turn calls observe current logical state and DOM writes still batch. The shared layout must initialize the store before route consumers, outside keyed rows. No Zustand import, store subscription runtime, React hook, or generic external-store capability is emitted.
28
32
 
29
- - `build.mjs`: TSX compilation, static, `getStaticPaths`, and runtime-fallback routes, base paths, CSS collection, post-build hooks, behavior extraction, static HTML output, and the development server.
33
+ - `build.mjs`: TSX compilation, static, `getStaticPaths`, and runtime-fallback routes, base paths, CSS collection, post-build hooks, behavior extraction, and static HTML output.
34
+ - `compiler/ast-helpers.mjs`: shared TypeScript AST traversal, scope, binding, effect-return, and source-location helpers used by compiler passes.
35
+ - `compiler/animation-frame-pass.mjs`: effect-owned animation-frame ref validation and plain mutable effect-scope lowering.
36
+ - `compiler/browser-signal-passes.mjs`: static media-query external-store and navigator capability-condition normalization.
37
+ - `compiler/normalization-pipeline.mjs`: ordered source normalization with parent-pointer repair after every compiler pass.
38
+ - `compiler/react-migration-pass.mjs`: React import, memo, callback, `forwardRef`, and `useId` migration validation and lowering.
39
+ - `compiler/custom-hook-timer-pass.mjs`: private custom-hook timeout-ref validation and compiler-owned timer-state lowering.
40
+ - `compiler/effect-codegen.mjs`: route-specific ordinary, owned, and navigable effect entry generation.
41
+ - `compiler/handler-codegen.mjs`: route-specific native handler, reactive binding, keyed-list evaluator, and client-import module source generation.
42
+ - `compiler/render-control-pass.mjs`: render-function early-return and exhaustive adjacent-assignment normalization.
43
+ - `compiler/router-pass.mjs`: React Router import validation and native Link, pathname parameter, search parameter, and imperative navigation lowering.
44
+ - `compiler/worker-compiler.mjs`: relative TypeScript Worker candidate validation, effect rewriting, graph validation, and content-hashed ESM emission.
45
+ - `dev-server.mjs`: development HTTP server, source watching, rebuild serialization, SSE reloads, runtime-route fallback serving, and response-only state restoration injection.
30
46
  - `core.mjs`: server-side JSX rendering, state slots, context providers, behavior metadata, and serializable capture validation.
31
47
  - `jsx-runtime.mjs`: automatic JSX runtime used by TypeScript.
32
48
  - `runtime.js`: command-only runtime for direct state-to-text patches.
@@ -41,6 +57,10 @@ Reduced Zustand migration stores lower to one ordinary layout-lifetime state slo
41
57
  - `dev-state.js`: dev-only, short-lived logical-state snapshot validation and restoration.
42
58
  - `*.d.ts`: public TypeScript and JSX declarations.
43
59
 
60
+ Compiler ownership follows four explicit stages. `build.mjs` owns project discovery, route planning, capability selection, and artifact emission. `compiler/normalization-pipeline.mjs` owns pass order and repairs parent pointers after every source transform; migration passes keep per-file state local and return additional metadata explicitly. The main transformer analyzes normalized source into handler, effect, binding, and list descriptors. The codegen modules turn those descriptors into route-specific ESM without participating in source analysis.
61
+
62
+ New syntax support belongs in an existing pass or a focused new pass only when a reduced migration fixture proves it. Passes must preserve source-located diagnostics, avoid module-global analysis state, and expose metadata through return values rather than AST-identity side channels. Build orchestration stays in `build.mjs`; feature-specific graph validation or code generation moves under `compiler/` when it has a stable input/output boundary.
63
+
44
64
  Static routes receive no browser runtime. Command routes receive `runtime.js`; dependency effects use route-specific `kudzu-deps.js` unless that route already requires shared commit hooks; runtime bracket pages using `useParams()` add one route-specific pathname matcher; reactive attributes and conditions add `binding-runtime.js`; keyed lists add `list-runtime.js`; native handlers add `native-runtime.js`; effects add `effect-runtime.js` and one route-specific entry. Generated module scripts live in the document head, so cold downloads overlap HTML transfer while standard module deferral preserves execution after parsing. A single effect with one dependency compiles to a direct runner; generic maps, sets, and ordering are reserved for larger effect graphs. Dependency commits coalesce in a microtask; affected cleanups are awaited in declaration order before replacement setups run. Document cleanup integrates with shared unmount hooks when present and otherwise disposes directly on non-persisted `pagehide`. List builds remove unused text-range, attribute, event, expression, condition, seed, and mount branches. Effect builds omit capture deserialization entirely when every effect scope is empty. Capability runtimes share state and lifecycle hooks through `shared-runtime.js`. Generated evaluators and their bundled relative TypeScript helpers live under `dist/assets/handlers/`; shared helper chunks are emitted only when multiple handler entries need them. Runtime fallback rewrites are ordered by specificity in `.kudzu/kudzu-plan.json` and passed to `afterBuild()`; exact static files take precedence in development. The dev server derives stable state identities from route-unique state variable names in each route plan; every state sharing a duplicate name is omitted. It then injects its SSE reload, short-lived full-URL-scoped logical-state snapshot, and build-error client into responses only, never into `dist/`. Snapshots are consumed even when the next page is static or broken. Reload restoration covers compatible framework state, not uncontrolled DOM state, focus, selection, or imperative mutations.
45
65
 
46
66
  Exact relative `.worker.ts` constructors in inline effects are validated and bundled in a separate content-hashed ESM graph under `dist/assets/workers/`. Those files are referenced only by rendered effect handlers and never become document capability scripts, preloads, or window imports; unreachable source effects do not emit their Worker roots. Worker graphs allow relative TypeScript ESM runtime imports only and reject JSX, package runtime imports, import-equals declarations, dynamic imports, `require()`, and paths outside `src`. Ordinary runtime imports or re-exports of `.worker.ts` and Worker construction in imported keyed-row effects are rejected.