Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
162 commits
Select commit Hold shift + click to select a range
fe30d8b
test: add native compiler migration evaluation harness
Sheraff Sep 26, 2026
7870d28
test: record final compiler migration measurements
Sheraff Sep 26, 2026
22be2cc
feat: migrate Router and Start compilation to Yuku
Sheraff Sep 26, 2026
20ef7aa
test: verify JSX bindings across Yuku compiler outputs
Sheraff Sep 27, 2026
8a1b9e5
feat: upgrade native compiler to Yuku 0.12 and validate migration
Sheraff Sep 27, 2026
edd6c4f
ci: allow Yuku 0.12 native packages through release age policy
Sheraff Sep 27, 2026
d99f54f
test: cover native compiler import boundaries and syntax diagnostics
Sheraff Sep 27, 2026
0c33be3
fix: preserve compiler import boundaries and bundler contracts
Sheraff Sep 27, 2026
859aaed
docs: record Yuku CI fixes and final validation
Sheraff Sep 27, 2026
34a1947
test: preserve compiler contracts across stack rebase
Sheraff Sep 27, 2026
e1da04f
feat: upgrade Yuku to 0.17 and use its WebAssembly core in WebContainers
Sheraff Oct 6, 2026
1ece20a
chore: remove Yuku evaluation benchmarks and results
Sheraff Oct 6, 2026
f2f4a15
fix: ignore signature parameters when indexing declarations
Sheraff Oct 6, 2026
4a79cb6
Merge remote-tracking branch 'origin/main' into codex/yuku-evaluation
Sheraff Oct 6, 2026
2f83502
chore: require Node.js 22.12 for the compiler packages
Sheraff Oct 6, 2026
655a9d5
fix(router-utils): resolve the WASM core from import.meta first
Sheraff Oct 6, 2026
6d3bb1d
chore(router-utils): silence no-unnecessary-condition on the CJS impo…
Sheraff Oct 6, 2026
fdbcfb7
fix(router-utils): keep side effects that only reference themselves
Sheraff Oct 6, 2026
d3f8a0d
test(router-plugin): cover self-referencing side effects in split routes
Sheraff Oct 6, 2026
bfda3fb
fix(router-plugin): emit valid split chunks for re-exports and partia…
Sheraff Oct 6, 2026
474e36f
fix(router-plugin): only treat module-scope factory calls as route de…
Sheraff Oct 6, 2026
9c72a5e
fix(router-plugin): hoist React Refresh components before a renamed e…
Sheraff Oct 6, 2026
479a3cd
fix(start-plugin-core): restore Hydrate cleanup, directives, and JSX …
Sheraff Oct 6, 2026
494d87c
fix(start-plugin-core): only extract module-level server functions
Sheraff Oct 6, 2026
91bdae0
fix(start-plugin-core): accept direct-call kinds that were not text-d…
Sheraff Oct 6, 2026
70188c0
test(start-plugin-core): fail the build for server functions not assi…
Sheraff Oct 6, 2026
c9c01ec
fix(router-plugin): do not share route-option dependencies through `R…
Sheraff Oct 6, 2026
7517bed
fix(router-utils, router-plugin): keep block-scoped `var` out of shar…
Sheraff Oct 6, 2026
cd845b4
fix(router-plugin): declare exports alongside private state a split c…
Sheraff Oct 6, 2026
966ba53
fix(router-plugin): export only the last of duplicate split keys
Sheraff Oct 6, 2026
15d685c
fix(start-plugin-core): keep nested returns and self-closing Hydrate …
Sheraff Oct 6, 2026
30ec838
fix(router-generator): read route calls through parentheses
Sheraff Oct 6, 2026
c025def
fix(start-plugin-core): count every JS line terminator in compiler di…
Sheraff Oct 6, 2026
16dac79
test(start-plugin-core): cover server-fn cases that main miscompiles
Sheraff Oct 6, 2026
16f8b04
test(router-plugin): cover code-splitting cases that main miscompiles
Sheraff Oct 6, 2026
c3d52dc
test: cover Hydrate and code-splitting cases that main miscompiles
Sheraff Oct 6, 2026
271eb00
test: cover React Compiler fixture shapes that main miscompiles
Sheraff Oct 6, 2026
7d3a928
fix(router-utils): parse JavaScript files without TypeScript syntax
Sheraff Oct 6, 2026
8ca9d18
fix(router-utils): remove only declarations whose runtime uses were e…
Sheraff Oct 6, 2026
e5d1591
fix(router-utils): drop the destructuring elements of erased bindings
Sheraff Oct 6, 2026
2da7d5c
fix(router-plugin): always import exported variables into split chunks
Sheraff Oct 6, 2026
1fbf818
fix(router-plugin): never route ambient declarations through the shar…
Sheraff Oct 6, 2026
93c4997
fix(router-plugin): keep file-level pragmas at the top of every split…
Sheraff Oct 6, 2026
4cab52d
fix(start-plugin-core): keep file-level JSX pragmas leading compiler …
Sheraff Oct 6, 2026
cd70189
test(start-plugin-core): cover JSX pragmas below inserted imports tha…
Sheraff Oct 6, 2026
36ed126
refactor(router-utils): share one file-pragma rule between the compilers
Sheraff Oct 6, 2026
6ecbfdd
test(start-plugin-core): cover directive-compiler cases that main mis…
Sheraff Oct 6, 2026
967638b
fix(router-utils): keep solid-refresh pragmas leading compiler output
Sheraff Oct 6, 2026
39abc15
test: cover dead-code and RSC transform cases that main miscompiles
Sheraff Oct 6, 2026
09fe852
fix(router-utils): remove unused `var` declarators nested in statements
Sheraff Oct 6, 2026
4aede32
Merge branch 'main' into codex/yuku-evaluation
Sheraff Oct 7, 2026
97da9a8
Merge remote-tracking branch 'origin/main' into codex/yuku-evaluation
Sheraff Oct 8, 2026
d896129
chore: upgrade Yuku to 0.18.0
Sheraff Oct 8, 2026
e595df0
test(router-plugin): reject duplicate type and value re-exports like …
Sheraff Oct 8, 2026
6350300
test(router-plugin): drop the duplicate type and value re-export case
Sheraff Oct 8, 2026
e0b2f89
test(start-plugin-core): share compile-error and directive prologue h…
Sheraff Oct 8, 2026
6880c83
fix(start-plugin-core): reject createServerFn declared below module l…
Sheraff Oct 8, 2026
8b52391
fix(start-plugin-core): compile a createServerOnlyFn passed to a serv…
Sheraff Oct 8, 2026
6e6769a
test(start-plugin-core): fold ported Start compiler suites into topic…
Sheraff Oct 8, 2026
3039dc0
test(start-plugin-core): fold ported Hydrate suites into topic suites
Sheraff Oct 8, 2026
4e5c74b
test(start-plugin-core): fold the JSX pragma suites into one
Sheraff Oct 8, 2026
8c805d1
test(start-plugin-core): move Hydrate pipeline cases into hydrate-spl…
Sheraff Oct 8, 2026
66b2dc6
test(start-plugin-core): format the folded suites and drop PR-history…
Sheraff Oct 8, 2026
a7119f1
test(router-plugin): cover shared enums, namespaces, ambient consts a…
Sheraff Oct 8, 2026
d08b75d
fix(router-plugin): split the handler of object-form loaders
Sheraff Oct 8, 2026
cd827d1
fix(router-utils): drop imports only types reference from modules wit…
Sheraff Oct 8, 2026
f10df31
test(router-plugin): drop code-splitter fixtures that snapshot duplic…
Sheraff Oct 8, 2026
a6851a5
test(router-plugin): replace redundant compiler fixtures with behavio…
Sheraff Oct 8, 2026
85f3a16
test(router-plugin): fold the ported compiler scenarios into the topi…
Sheraff Oct 8, 2026
7cef17c
test(router-plugin): assert route analysis reuse through the emitted …
Sheraff Oct 8, 2026
542321a
test(router-utils): assert declaration ownership through cleaned-up o…
Sheraff Oct 8, 2026
3931c0c
test(router-generator): reach the single route call guard with valid …
Sheraff Oct 8, 2026
6d95078
test(router-plugin): satisfy lint in the regression helpers and snaps…
Sheraff Oct 8, 2026
4e3045b
docs(router-plugin): describe split-module export imports without PR …
Sheraff Oct 8, 2026
97cd7ca
fix(router-utils): keep the classic JSX factories named by @jsx and @…
Sheraff Oct 8, 2026
1d36f4e
fix(router-plugin): add route HMR for the exported Route, not the fir…
Sheraff Oct 8, 2026
6e13246
fix(start-plugin-core): Hydrate chunks import the parent module's Rou…
Sheraff Oct 8, 2026
f02fdd9
fix(router-utils): parse a .ts module as TypeScript when a directory …
Sheraff Oct 8, 2026
271ccbc
fix(start-plugin-core): accept a server fn assigned through an option…
Sheraff Oct 8, 2026
dd5bbb3
fix(router-utils): fall back to Yuku's WebAssembly core when the nati…
Sheraff Oct 8, 2026
95a5794
fix(router-utils): keep leading legal comments and the annotations of…
Sheraff Oct 8, 2026
16e7a89
chore(router): keep the Node.js >=20.19 floor of the router compiler …
Sheraff Oct 8, 2026
ca9f8dc
chore: satisfy lint and type checks of the compiler regression fixes
Sheraff Oct 8, 2026
c74bede
feat(router-plugin): warn when a route file creates its route inside …
Sheraff Oct 8, 2026
37111c7
docs(changeset): state the createServerFn placement build error and t…
Sheraff Oct 8, 2026
1cc8197
refactor: standardize Yuku compiler helpers and terminology
Sheraff Oct 9, 2026
00720d6
chore: upgrade Yuku to 0.18.1
Sheraff Oct 10, 2026
ea0e20d
Merge remote-tracking branch 'origin/main' into codex/yuku-evaluation
Sheraff Oct 10, 2026
00e4caa
Merge remote-tracking branch 'origin/main' into codex/yuku-evaluation
Sheraff Oct 10, 2026
5f30c2a
test: regenerate compiler snapshots with formatted Yuku output
Sheraff Oct 10, 2026
88628b4
docs(router-plugin): describe source maps through generateModule
Sheraff Oct 10, 2026
eff0de0
chore: pin Yuku through the workspace catalog and match the Node.js r…
Sheraff Oct 10, 2026
aa3aff1
fix: locate the unassigned createServerFn error and document behaviou…
Sheraff Oct 10, 2026
75407d5
Merge remote-tracking branch 'origin/main' into codex/yuku-evaluation
Sheraff Oct 10, 2026
1e64b4a
Merge remote-tracking branch 'origin/main' into codex/yuku-evaluation
Sheraff Oct 10, 2026
3e65ade
fix(router-plugin): keep module-level writes to an enum or namespace …
Sheraff Oct 10, 2026
5171626
docs(changeset): limit the object-form loader split to inline objects
Sheraff Oct 10, 2026
04dfa2d
test(router-utils): cover the WebAssembly fallback when the native bi…
Sheraff Oct 10, 2026
41a5290
Merge remote-tracking branch 'origin/main' into codex/yuku-evaluation
Sheraff Oct 10, 2026
59d5642
fix(router-plugin): keep the tsrShared import the route analysis cach…
Sheraff Oct 10, 2026
f5db120
fix(router-utils): never hand an empty source to Yuku
Sheraff Oct 10, 2026
7db30d0
chore: upgrade Yuku to 0.19.0
Sheraff Oct 11, 2026
3149f2d
refactor: use Yuku's isWrapper, nameOf and literal guards
Sheraff Oct 11, 2026
7441c74
refactor(router-generator): find the Route export with Yuku's export …
Sheraff Oct 11, 2026
0702466
refactor(start-plugin-core): read import protection exports from Yuku…
Sheraff Oct 11, 2026
ea2aaaa
refactor: walk ancestors with Yuku instead of manual parent loops
Sheraff Oct 11, 2026
6937adc
refactor(start-plugin-core): drop expression guards on callees and me…
Sheraff Oct 11, 2026
23a292e
Merge remote-tracking branch 'origin/main' into codex/yuku-evaluation
Sheraff Oct 11, 2026
93829b9
perf(router-plugin,start-plugin-core): avoid eager ancestor arrays in…
Sheraff Oct 11, 2026
f8964c9
perf(router-utils): memoise the declaration graph per module
Sheraff Oct 11, 2026
19b0bd7
refactor(router-utils): remove the unused roots option of removeUnuse…
Sheraff Oct 11, 2026
8413e04
refactor(router-utils): simplify the binding liveness helpers
Sheraff Oct 11, 2026
75174fa
refactor(router-utils): drop dead source map reshaping in generateModule
Sheraff Oct 11, 2026
b40f14b
refactor(router-utils): stop exporting helpers no package uses
Sheraff Oct 11, 2026
e0e46e1
refactor(router-utils): unwrap any node in unwrapExpression
Sheraff Oct 11, 2026
65659ef
refactor(start-plugin-core): climb wrapper parents directly in import…
Sheraff Oct 11, 2026
ec6b657
refactor(router-plugin): check codeSplitGroupings items with is.Strin…
Sheraff Oct 11, 2026
6473109
refactor(start-plugin-core): read the Hydrate import from the module …
Sheraff Oct 11, 2026
16f55a9
refactor(start-plugin-core): build the Hydrate boundary id literal wi…
Sheraff Oct 11, 2026
e41f7fd
refactor(react-start-rsc): build the RSC CSS fragment with builders
Sheraff Oct 11, 2026
a4803cd
refactor(start-plugin-core): use the module info ingestModule returns
Sheraff Oct 11, 2026
9404f58
refactor(start-plugin-core): inline generateResultFromAst
Sheraff Oct 11, 2026
5276339
refactor(start-plugin-core): keep the extracted bindings map instead …
Sheraff Oct 11, 2026
6c43513
perf(start-plugin-core): check for a module-level initializer only wh…
Sheraff Oct 11, 2026
37fa52c
perf(start-plugin-core): index only the parents of the Start output tree
Sheraff Oct 11, 2026
4f9dca9
perf(router-plugin): read each output program once for unique identif…
Sheraff Oct 11, 2026
072f6c4
revert: memoising the declaration graph per module
Sheraff Oct 11, 2026
d63690c
refactor(router-plugin): analyze each route transform once instead of…
Sheraff Oct 11, 2026
547a65a
refactor(start-plugin-core): retain Hydrate source code, not its anal…
Sheraff Oct 11, 2026
33d9c58
refactor(router-utils): compute JSX factories and initially unused bi…
Sheraff Oct 11, 2026
6d102ce
refactor(router-plugin): reserve unique names in a set each output owns
Sheraff Oct 11, 2026
28d72dd
refactor(router-plugin): give reference compiler plugins the route's …
Sheraff Oct 11, 2026
7a079cf
docs(changeset): note the type-only Route export and import protectio…
Sheraff Oct 11, 2026
47bbc0e
refactor(router-utils): collect referenced names in the removal walk
Sheraff Oct 11, 2026
e7faaba
Merge branch 'main' into codex/yuku-evaluation
Sheraff Oct 14, 2026
8f02f6a
chore: drop the Yuku 0.19.0 release-age exclusion
Sheraff Oct 14, 2026
c1107b9
test(router-plugin): add failing tests for the Yuku migration regress…
Sheraff Oct 15, 2026
e13a932
test(start-plugin-core): add failing tests for the Yuku migration reg…
Sheraff Oct 15, 2026
c37a7d2
test(router-plugin,router-generator): cover compiler fixes of the Yuk…
Sheraff Oct 15, 2026
63e2335
test(start-plugin-core): cover compiler fixes of the Yuku migration
Sheraff Oct 15, 2026
305b475
test(start-plugin-core): a Hydrate children prop moves into the chunk…
Sheraff Oct 15, 2026
222d3be
test(router-plugin,start-plugin-core): cover more compiler fixes of t…
Sheraff Oct 15, 2026
6a5abf0
test(router-plugin,start-plugin-core): add failing tests for more Yuk…
Sheraff Oct 15, 2026
4d0e9e0
fix(router-plugin): move exported functions and classes split options…
Sheraff Oct 15, 2026
b0fb3b2
docs(changeset): correct the createServerFn and route factory notes, …
Sheraff Oct 15, 2026
a370684
test(router-plugin): add failing tests for shared and split modules c…
Sheraff Oct 15, 2026
c8cbba5
test(start-plugin-core): cover wrapped Start factory chains and callees
Sheraff Oct 15, 2026
904d09f
fix(router-utils): keep declarations that only unused code inside the…
Sheraff Oct 15, 2026
2cdd392
fix(router-plugin): leave an unused function's reference to a moved s…
Sheraff Oct 15, 2026
2b968f7
fix(router-plugin): do not copy into a chunk a declaration it only re…
Sheraff Oct 15, 2026
bd5ab5a
fix(router-plugin): do not copy an unused namespace into split chunks
Sheraff Oct 15, 2026
0f7e46c
fix(router-plugin): never share an ambient var merged with an interface
Sheraff Oct 15, 2026
24fe5c4
fix(router-plugin): split a route declared with var in a module-level…
Sheraff Oct 15, 2026
f098dfd
fix(start-plugin-core): skip wrapped inner links of a Start factory c…
Sheraff Oct 15, 2026
5bd4733
fix(start-plugin-core): name the route file as the source of Hydrate …
Sheraff Oct 15, 2026
2e0bb53
fix(router-plugin): compute shared bindings from the code each transf…
Sheraff Oct 15, 2026
8e7a390
fix(router-plugin): always tell the bundler whether a shared module h…
Sheraff Oct 15, 2026
8c53b97
test(router-plugin): Solid and Vue route modules are Vite HMR boundaries
Sheraff Oct 15, 2026
d02f87e
test: React route files emit shared components apart from shared values
Sheraff Oct 15, 2026
59ee408
fix(router-plugin): accept Vite route HMR updates with a literal impo…
Sheraff Oct 15, 2026
5dba595
fix(router-plugin): emit a React route file's shared components apart…
Sheraff Oct 15, 2026
cd22d48
docs(changeset): note the React shared components module and the Soli…
Sheraff Oct 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
9 changes: 9 additions & 0 deletions .changeset/mean-birds-bow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'@tanstack/router-utils': patch
'@tanstack/router-generator': patch
'@tanstack/router-plugin': patch
'@tanstack/start-plugin-core': patch
'@tanstack/react-start-rsc': patch
---

Standardize Yuku AST guards and compiler terminology, and share directive-preserving statement insertion and source-position helpers across compilation transforms.
42 changes: 42 additions & 0 deletions .changeset/smooth-parrots-argue.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
---
'@tanstack/router-utils': minor
'@tanstack/router-plugin': minor
'@tanstack/start-plugin-core': minor
'@tanstack/router-generator': minor
'@tanstack/router-cli': minor
'@tanstack/router-vite-plugin': minor
'@tanstack/react-start-rsc': patch
---

Replace Babel-based route and Start compilation with Yuku's parser, semantic analysis, AST utilities, and code generator. Parsing and analysis run on Yuku's native core, or on its WebAssembly core in WebContainers such as StackBlitz and where the native binding cannot load. The WebAssembly core does not run on big-endian hosts (such as s390x) or under `node --jitless`.

Compiler extension hooks and AST helpers now expose Yuku nodes and semantic context. Babel nodes, traversal paths, and helper APIs are removed; extensions must migrate to the native interfaces. Routing and server-function runtime contracts remain unchanged.

A `createServerFn` that is not assigned to a module-level variable now fails the build in every environment: one declared inside a function or block, a default export, an object property, an assignment, or a function that returns one, including such wrappers shipped by libraries, since the compiler also processes dependencies. None of these placements worked before: depending on the other Start APIs in the file, the build already failed with "must be assigned to a variable", or the server function was left untransformed, so its handler and server-only imports shipped to the client and its calls returned `undefined`, or the output failed later in the bundler or at runtime. A function that returns an unfinished builder (`defineAction(schema).handler(fn)`) is still not detected.

A route created inside a function or IIFE (a route factory) is no longer code-split, and the router plugin reports a bundler warning for it in development and builds. Edits to it may need a full page reload. Previously such routes were split, which broke when a split option used the function's locals: a `ReferenceError` at render, a build error, or the wrong component.

`@tanstack/router-utils`, `@tanstack/router-generator`, `@tanstack/router-plugin`, `@tanstack/router-cli` and `@tanstack/router-vite-plugin` now require Node.js `^20.19.0 || >=22.12.0`: their CommonJS builds load Yuku's ES modules with `require()`, which Node.js 22.0 to 22.11 does not support without a flag.

Other behaviour changes:

- An exported function or class that a split route option uses (an exported page component the split component renders, an exported store class or helper) now moves into the route's shared module, which the route module re-exports it from, so the split chunk and every other importer use one instance: `instanceof` checks and module state agree across modules. When nothing else imports it, the bundler can leave it, and the imports only it uses, out of the route module's chunk, so the top-level side effects of those imports run when the split chunk loads. Previously the split chunk declared its own copy. An exported function that reads `Route` is still copied into the chunk when it holds no module state. In React route files, shared components go to a module of their own, apart from the other shared bindings, so that React Refresh can hot-update them.
- A class, enum, namespace or reassigned `let` that only a split route option uses now moves into that option's chunk, like other declarations already did, so its top-level side effects run when the chunk loads instead of when the route module loads.
- A split route option that refers to an exported binding through a TypeScript cast (`errorComponent: ErrorComponent as any`) is no longer split, like the same option without the cast, and reports the existing "will not be code-split" warning. Previously the component was duplicated into the route module and the chunk.
- A `<Hydrate>` whose content is passed as a `children` prop is now split like JSX children, so hooks or functions there require `split={false}`, as they do for JSX children.
- `.js` and `.jsx` files are parsed as JavaScript with JSX, so TypeScript syntax in them is a syntax error.
- Exporting a type and a value of the same name from two separate `export` statements (`export type { Post } from './types'` and `export { Post } from './post'`) is now a syntax error, as it is in TypeScript.
- The route generator no longer treats a type-only `Route` export (`export type { X as Route }`, `export { type X as Route }`, `export declare const Route`) as the route export: it exports nothing at runtime, so the file is reported as having no `Route` export.
- Import protection mocks now include the names that `export * as ns`, `export enum` and `export namespace` export, no longer expose names exported inside a namespace, and cover `ns.x` reads that come before `import * as ns`.

Fixes:

- Route HMR in Vite now hot-updates Solid routes with automatic code splitting and Vue routes instead of reloading the page.
- Route options wrapped in `as any`, `satisfies` or `!`, and a route factory call wrapped in a cast, are now code-split. Previously the file was left untransformed, so a server route written this way also kept its `server` handlers in the client.
- Split route components written as a call containing JSX (`React.memo(() => <p />)`, a Vue `defineComponent` whose `setup` returns JSX) now compile.
- A component used by both a split and an unsplit route option no longer throws "is not defined", and a component declared with `function Page` and exported with `export { Page }` no longer breaks the build.
- Split chunks now import the classic JSX factory (`/** @jsx h */`) the route module exports, declare an enum or namespace that the loader and the component read once, keep the body of a dotted namespace (`namespace A.B {}`), and no longer import values that only types read. A split-only component named like a type no longer also stays in the route module.
- The route generator records route options written as methods or accessors.
- `createServerOnlyFn` and `createIsomorphicFn` passed to `.handler()`, and `.server()` or `.validator()` chain links wrapped in a cast, now compile correctly.
- `<Hydrate>` children that read `Route` import it, also when it is exported under a string name; children passed through a `children` prop or a spread render; and `split={false}` works inside a cast.
- Import protection reports usages inside JSX and skips type positions and member properties, and its mocks no longer reuse a name the module declares.
5 changes: 5 additions & 0 deletions .changeset/split-object-form-loader.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@tanstack/router-plugin': patch
---

Split the `handler` of a loader written inline in object form (`loader: { handler, staleReloadMode }`) into its chunk, keeping the other loader options in the route module, so that a split object-form loader loads its data. An object-form loader passed by reference (`const loader = { handler }`, then `loader`) is not supported yet.
22 changes: 22 additions & 0 deletions e2e/react-start/hmr/src/routeTree.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { Route as rootRouteImport } from './routes/__root'
import { Route as IndexRouteImport } from './routes/index'
import { Route as ChildRouteImport } from './routes/child'
import { Route as ComponentHmrRouteImport } from './routes/component-hmr'
import { Route as ComponentHmrExportedSplitRouteImport } from './routes/component-hmr-exported-split'
import { Route as ComponentHmrInlineErrorSplitRouteImport } from './routes/component-hmr-inline-error-split'
import { Route as ComponentHmrInlineNosplitRouteImport } from './routes/component-hmr-inline-nosplit'
import { Route as ComponentHmrInlineSplitRouteImport } from './routes/component-hmr-inline-split'
Expand Down Expand Up @@ -39,6 +40,12 @@ const ComponentHmrRoute = ComponentHmrRouteImport.update({
path: '/component-hmr',
getParentRoute: () => rootRouteImport,
} as any)
const ComponentHmrExportedSplitRoute =
ComponentHmrExportedSplitRouteImport.update({
id: '/component-hmr-exported-split',
path: '/component-hmr-exported-split',
getParentRoute: () => rootRouteImport,
} as any)
const ComponentHmrInlineErrorSplitRoute =
ComponentHmrInlineErrorSplitRouteImport.update({
id: '/component-hmr-inline-error-split',
Expand Down Expand Up @@ -105,6 +112,7 @@ export interface FileRoutesByFullPath {
'/': typeof IndexRoute
'/child': typeof ChildRoute
'/component-hmr': typeof ComponentHmrRoute
'/component-hmr-exported-split': typeof ComponentHmrExportedSplitRoute
'/component-hmr-inline-error-split': typeof ComponentHmrInlineErrorSplitRoute
'/component-hmr-inline-nosplit': typeof ComponentHmrInlineNosplitRoute
'/component-hmr-inline-split': typeof ComponentHmrInlineSplitRoute
Expand All @@ -121,6 +129,7 @@ export interface FileRoutesByTo {
'/': typeof IndexRoute
'/child': typeof ChildRoute
'/component-hmr': typeof ComponentHmrRoute
'/component-hmr-exported-split': typeof ComponentHmrExportedSplitRoute
'/component-hmr-inline-error-split': typeof ComponentHmrInlineErrorSplitRoute
'/component-hmr-inline-nosplit': typeof ComponentHmrInlineNosplitRoute
'/component-hmr-inline-split': typeof ComponentHmrInlineSplitRoute
Expand All @@ -138,6 +147,7 @@ export interface FileRoutesById {
'/': typeof IndexRoute
'/child': typeof ChildRoute
'/component-hmr': typeof ComponentHmrRoute
'/component-hmr-exported-split': typeof ComponentHmrExportedSplitRoute
'/component-hmr-inline-error-split': typeof ComponentHmrInlineErrorSplitRoute
'/component-hmr-inline-nosplit': typeof ComponentHmrInlineNosplitRoute
'/component-hmr-inline-split': typeof ComponentHmrInlineSplitRoute
Expand All @@ -156,6 +166,7 @@ export interface FileRouteTypes {
| '/'
| '/child'
| '/component-hmr'
| '/component-hmr-exported-split'
| '/component-hmr-inline-error-split'
| '/component-hmr-inline-nosplit'
| '/component-hmr-inline-split'
Expand All @@ -172,6 +183,7 @@ export interface FileRouteTypes {
| '/'
| '/child'
| '/component-hmr'
| '/component-hmr-exported-split'
| '/component-hmr-inline-error-split'
| '/component-hmr-inline-nosplit'
| '/component-hmr-inline-split'
Expand All @@ -188,6 +200,7 @@ export interface FileRouteTypes {
| '/'
| '/child'
| '/component-hmr'
| '/component-hmr-exported-split'
| '/component-hmr-inline-error-split'
| '/component-hmr-inline-nosplit'
| '/component-hmr-inline-split'
Expand All @@ -205,6 +218,7 @@ export interface RootRouteChildren {
IndexRoute: typeof IndexRoute
ChildRoute: typeof ChildRoute
ComponentHmrRoute: typeof ComponentHmrRoute
ComponentHmrExportedSplitRoute: typeof ComponentHmrExportedSplitRoute
ComponentHmrInlineErrorSplitRoute: typeof ComponentHmrInlineErrorSplitRoute
ComponentHmrInlineNosplitRoute: typeof ComponentHmrInlineNosplitRoute
ComponentHmrInlineSplitRoute: typeof ComponentHmrInlineSplitRoute
Expand Down Expand Up @@ -240,6 +254,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof ComponentHmrRouteImport
parentRoute: typeof rootRouteImport
}
'/component-hmr-exported-split': {
id: '/component-hmr-exported-split'
path: '/component-hmr-exported-split'
fullPath: '/component-hmr-exported-split'
preLoaderRoute: typeof ComponentHmrExportedSplitRouteImport
parentRoute: typeof rootRouteImport
}
'/component-hmr-inline-error-split': {
id: '/component-hmr-inline-error-split'
path: '/component-hmr-inline-error-split'
Expand Down Expand Up @@ -337,6 +358,7 @@ const rootRouteChildren: RootRouteChildren = {
IndexRoute: IndexRoute,
ChildRoute: ChildRoute,
ComponentHmrRoute: ComponentHmrRoute,
ComponentHmrExportedSplitRoute: ComponentHmrExportedSplitRoute,
ComponentHmrInlineErrorSplitRoute: ComponentHmrInlineErrorSplitRoute,
ComponentHmrInlineNosplitRoute: ComponentHmrInlineNosplitRoute,
ComponentHmrInlineSplitRoute: ComponentHmrInlineSplitRoute,
Expand Down
40 changes: 40 additions & 0 deletions e2e/react-start/hmr/src/routes/component-hmr-exported-split.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { createFileRoute } from '@tanstack/react-router'
import { useState } from 'react'

export const Route = createFileRoute('/component-hmr-exported-split')({
component: ComponentHmrPage,
})

// Private values the exported component reads: they are shared with the
// split component, next to the component
const markerClassNames = { marker: 'hmr-marker' }

function markerClassName() {
return markerClassNames.marker
}

export function ComponentHmrMarker() {
return (
<p className={markerClassName()} data-testid="component-hmr-marker">
component-hmr-exported-split-baseline
</p>
)
}

function ComponentHmrPage() {
const [count, setCount] = useState(0)

return (
<main className="hmr-card flex flex-col gap-5">
<ComponentHmrMarker />
<p data-testid="component-hmr-count">Count: {count}</p>
<button
data-testid="component-hmr-increment"
onClick={() => setCount((value) => value + 1)}
>
Increment
</button>
<input data-testid="component-hmr-message" defaultValue="" />
</main>
)
}
32 changes: 32 additions & 0 deletions e2e/react-start/hmr/tests/app.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const routeFilePaths = {
componentHmrNamedSplit: 'routes/component-hmr-named-split.tsx',
componentHmrLowercaseNamedSplit:
'routes/component-hmr-lowercase-named-split.tsx',
componentHmrExportedSplit: 'routes/component-hmr-exported-split.tsx',
componentHmrNamedNosplit: 'routes/component-hmr-named-nosplit.tsx',
componentHmrInlineErrorSplit: 'routes/component-hmr-inline-error-split.tsx',
componentHmrNamedErrorSplit: 'routes/component-hmr-named-error-split.tsx',
Expand Down Expand Up @@ -122,6 +123,11 @@ const routeFileRestoreChecks: Partial<
testId: 'component-hmr-marker',
text: 'component-hmr-lowercase-named-split-baseline',
},
componentHmrExportedSplit: {
url: '/component-hmr-exported-split',
testId: 'component-hmr-marker',
text: 'component-hmr-exported-split-baseline',
},
componentHmrNamedNosplit: {
url: '/component-hmr-named-nosplit',
testId: 'component-hmr-marker',
Expand Down Expand Up @@ -279,6 +285,10 @@ function normalizeRouteSource(routeFileKey: RouteFileKey, source: string) {
'component-hmr-lowercase-named-split-updated',
'component-hmr-lowercase-named-split-baseline',
],
componentHmrExportedSplit: [
'component-hmr-exported-split-updated',
'component-hmr-exported-split-baseline',
],
componentHmrNamedNosplit: [
'component-hmr-named-nosplit-updated',
'component-hmr-named-nosplit-baseline',
Expand Down Expand Up @@ -1442,6 +1452,28 @@ test.describe('react-start hmr', () => {
await expectComponentHmrStatePreserved(page)
})

// The split component renders an exported component that reads private
// values: all of them go to the route file's shared modules
test('updates an exported component that a code-split route component renders', async ({
page,
}) => {
await seedComponentHmrState(page, '/component-hmr-exported-split')

await replaceRouteTextAndWait(
page,
'componentHmrExportedSplit',
'component-hmr-exported-split-baseline',
'component-hmr-exported-split-updated',
async () => {
await hmrExpect(page.getByTestId('component-hmr-marker')).toHaveText(
'component-hmr-exported-split-updated',
)
},
)

await expectComponentHmrStatePreserved(page)
})

test('updates non-root route component during HMR when component is not defined inline and codeSplitGroupings disables code splitting', async ({
page,
}) => {
Expand Down
8 changes: 4 additions & 4 deletions e2e/react-start/rsbuild/tests/error-overlay.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -376,9 +376,9 @@ test('shows a module syntax error through the error overlay', async ({
[
{
"title": "Build failed",
"description": "SyntaxError: Unexpected token (16:22)",
"description": "SyntaxError: <fixture-root>/src/routes/index.tsx: Unexpected token ';' (16:22)",
"runtime": "client",
"sourceFrame": "File: ./src/routes/index.tsx:1:1\\nx Module build failed (from <rsbuild-transform-loader>):\\n-> x SyntaxError: Unexpected token (16:22)",
"sourceFrame": "File: ./src/routes/index.tsx:1:1\\nx Module build failed (from <rsbuild-transform-loader>):\\n-> x SyntaxError: <fixture-root>/src/routes/index.tsx: Unexpected token ';' (16:22)",
"importTrace": [
"../../../packages/react-start/dist/plugin/default-entry/client.tsx",
"../../../packages/react-start/dist/esm/client.js",
Expand All @@ -389,9 +389,9 @@ test('shows a module syntax error through the error overlay', async ({
},
{
"title": "Build failed",
"description": "SyntaxError: Unexpected token (16:22)",
"description": "SyntaxError: <fixture-root>/src/routes/index.tsx: Unexpected token ';' (16:22)",
"runtime": "server",
"sourceFrame": "File: ./src/routes/index.tsx:1:1\\nx Module build failed (from <rsbuild-transform-loader>):\\n-> x SyntaxError: Unexpected token (16:22)",
"sourceFrame": "File: ./src/routes/index.tsx:1:1\\nx Module build failed (from <rsbuild-transform-loader>):\\n-> x SyntaxError: <fixture-root>/src/routes/index.tsx: Unexpected token ';' (16:22)",
"importTrace": [
"../../../packages/react-start/dist/plugin/default-entry/server.ts",
"../../../packages/react-start/dist/esm/server.js",
Expand Down
3 changes: 2 additions & 1 deletion packages/react-start-rsc/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@
"@tanstack/start-fn-stubs": "workspace:*",
"@tanstack/start-plugin-core": "workspace:*",
"@tanstack/start-storage-context": "workspace:*",
"pathe": "catalog:"
"pathe": "catalog:",
"yuku-ast": "catalog:"
},
"devDependencies": {
"@rspack/core": "2.2.7",
Expand Down
Loading
Loading