From b5c7b7c3d8a08179df23919fbc4177145ee738f2 Mon Sep 17 00:00:00 2001 From: shkumbinhasani <34962865+shkumbinhasani@users.noreply.github.com> Date: Sun, 18 Oct 2026 14:26:58 +0200 Subject: [PATCH 1/2] fix(router-plugin): split object-form loaders MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With `loader` in a split grouping, a loader written as `{ handler, staleReloadMode }` made the loader chunk throw `Unexpected splitNode type ☝️: ObjectExpression`, and the route file replaced the whole object with `lazyFn(...)`. Split only `handler` (an arrow or function, a `handler() {}` method, or an identifier) and keep the other options in the route file. A binding read by the handler and by another loader option goes to the shared module. The loader stays in the route file when a spread or computed key after `handler` may replace it, when there is no plain `handler`, or when a method handler uses `super`. Co-Authored-By: Claude Opus 5.5 --- .changeset/split-object-form-loader.md | 5 + .../src/core/code-splitter/compilers.ts | 160 +++++++++++++++--- packages/router-plugin/src/core/utils.ts | 2 +- .../tests/code-splitter-route-options.test.ts | 114 +++++++++++++ .../react/1-default/loader-object-arrow.tsx | 12 ++ .../loader-object-arrow@component.tsx | 6 + .../loader-object-arrow@errorComponent.tsx | 0 .../loader-object-arrow@notFoundComponent.tsx | 0 .../1-default/loader-object-arrow@shared.tsx | 0 .../1-default/loader-object-imported.tsx | 9 + .../loader-object-imported@component.tsx | 2 + .../loader-object-imported@errorComponent.tsx | 0 ...ader-object-imported@notFoundComponent.tsx | 0 .../loader-object-imported@shared.tsx | 0 .../react/1-default/loader-object-method.tsx | 16 ++ .../loader-object-method@component.tsx | 2 + .../loader-object-method@errorComponent.tsx | 0 ...loader-object-method@notFoundComponent.tsx | 0 .../1-default/loader-object-method@shared.tsx | 0 .../loader-object-arrow.tsx | 14 ++ ...--notFoundComponent---pendingComponent.tsx | 6 + .../loader-object-arrow@loader.tsx | 3 + .../loader-object-arrow@shared.tsx | 0 .../loader-object-imported.tsx | 14 ++ ...--notFoundComponent---pendingComponent.tsx | 2 + .../loader-object-imported@loader.tsx | 3 + .../loader-object-imported@shared.tsx | 0 .../loader-object-method.tsx | 15 ++ ...--notFoundComponent---pendingComponent.tsx | 2 + .../loader-object-method@loader.tsx | 7 + .../loader-object-method@shared.tsx | 2 + .../loader-object-arrow.tsx | 14 ++ ...--notFoundComponent---pendingComponent.tsx | 9 + .../loader-object-arrow@errorComponent.tsx | 0 .../loader-object-arrow@shared.tsx | 0 .../loader-object-imported.tsx | 14 ++ ...--notFoundComponent---pendingComponent.tsx | 5 + .../loader-object-imported@errorComponent.tsx | 0 .../loader-object-imported@shared.tsx | 0 .../loader-object-method.tsx | 15 ++ ...--notFoundComponent---pendingComponent.tsx | 9 + .../loader-object-method@errorComponent.tsx | 0 .../loader-object-method@shared.tsx | 2 + .../test-files/react/loader-object-arrow.tsx | 15 ++ .../react/loader-object-imported.tsx | 7 + .../test-files/react/loader-object-method.tsx | 15 ++ .../router-plugin/tests/regression-helpers.ts | 9 +- 47 files changed, 482 insertions(+), 28 deletions(-) create mode 100644 .changeset/split-object-form-loader.md create mode 100644 packages/router-plugin/tests/code-splitter/snapshots/react/1-default/loader-object-arrow.tsx create mode 100644 packages/router-plugin/tests/code-splitter/snapshots/react/1-default/loader-object-arrow@component.tsx create mode 100644 packages/router-plugin/tests/code-splitter/snapshots/react/1-default/loader-object-arrow@errorComponent.tsx create mode 100644 packages/router-plugin/tests/code-splitter/snapshots/react/1-default/loader-object-arrow@notFoundComponent.tsx create mode 100644 packages/router-plugin/tests/code-splitter/snapshots/react/1-default/loader-object-arrow@shared.tsx create mode 100644 packages/router-plugin/tests/code-splitter/snapshots/react/1-default/loader-object-imported.tsx create mode 100644 packages/router-plugin/tests/code-splitter/snapshots/react/1-default/loader-object-imported@component.tsx create mode 100644 packages/router-plugin/tests/code-splitter/snapshots/react/1-default/loader-object-imported@errorComponent.tsx create mode 100644 packages/router-plugin/tests/code-splitter/snapshots/react/1-default/loader-object-imported@notFoundComponent.tsx create mode 100644 packages/router-plugin/tests/code-splitter/snapshots/react/1-default/loader-object-imported@shared.tsx create mode 100644 packages/router-plugin/tests/code-splitter/snapshots/react/1-default/loader-object-method.tsx create mode 100644 packages/router-plugin/tests/code-splitter/snapshots/react/1-default/loader-object-method@component.tsx create mode 100644 packages/router-plugin/tests/code-splitter/snapshots/react/1-default/loader-object-method@errorComponent.tsx create mode 100644 packages/router-plugin/tests/code-splitter/snapshots/react/1-default/loader-object-method@notFoundComponent.tsx create mode 100644 packages/router-plugin/tests/code-splitter/snapshots/react/1-default/loader-object-method@shared.tsx create mode 100644 packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/loader-object-arrow.tsx create mode 100644 packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/loader-object-arrow@component---errorComponent---notFoundComponent---pendingComponent.tsx create mode 100644 packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/loader-object-arrow@loader.tsx create mode 100644 packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/loader-object-arrow@shared.tsx create mode 100644 packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/loader-object-imported.tsx create mode 100644 packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/loader-object-imported@component---errorComponent---notFoundComponent---pendingComponent.tsx create mode 100644 packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/loader-object-imported@loader.tsx create mode 100644 packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/loader-object-imported@shared.tsx create mode 100644 packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/loader-object-method.tsx create mode 100644 packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/loader-object-method@component---errorComponent---notFoundComponent---pendingComponent.tsx create mode 100644 packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/loader-object-method@loader.tsx create mode 100644 packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/loader-object-method@shared.tsx create mode 100644 packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/loader-object-arrow.tsx create mode 100644 packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/loader-object-arrow@component---loader---notFoundComponent---pendingComponent.tsx create mode 100644 packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/loader-object-arrow@errorComponent.tsx create mode 100644 packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/loader-object-arrow@shared.tsx create mode 100644 packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/loader-object-imported.tsx create mode 100644 packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/loader-object-imported@component---loader---notFoundComponent---pendingComponent.tsx create mode 100644 packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/loader-object-imported@errorComponent.tsx create mode 100644 packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/loader-object-imported@shared.tsx create mode 100644 packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/loader-object-method.tsx create mode 100644 packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/loader-object-method@component---loader---notFoundComponent---pendingComponent.tsx create mode 100644 packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/loader-object-method@errorComponent.tsx create mode 100644 packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/loader-object-method@shared.tsx create mode 100644 packages/router-plugin/tests/code-splitter/test-files/react/loader-object-arrow.tsx create mode 100644 packages/router-plugin/tests/code-splitter/test-files/react/loader-object-imported.tsx create mode 100644 packages/router-plugin/tests/code-splitter/test-files/react/loader-object-method.tsx diff --git a/.changeset/split-object-form-loader.md b/.changeset/split-object-form-loader.md new file mode 100644 index 00000000000..69793cca0fe --- /dev/null +++ b/.changeset/split-object-form-loader.md @@ -0,0 +1,5 @@ +--- +'@tanstack/router-plugin': patch +--- + +Split only the `handler` of a loader in object form (`loader: { handler, staleReloadMode }`) when `loader` is code-split, and keep its other options on the route. Before, the split chunk failed with `Unexpected splitNode type ☝️: ObjectExpression`. diff --git a/packages/router-plugin/src/core/code-splitter/compilers.ts b/packages/router-plugin/src/core/code-splitter/compilers.ts index 89cdae41add..172c44657b8 100644 --- a/packages/router-plugin/src/core/code-splitter/compilers.ts +++ b/packages/router-plugin/src/core/code-splitter/compilers.ts @@ -139,6 +139,88 @@ const allCreateRouteFns = [ ...unsplittableCreateRouteFns, ] +/** + * A loader can be an object, `{ handler, staleReloadMode }`. The router reads + * its other keys from the route options, so only `handler` is split. Returns + * the property to split, or `undefined` to keep the whole loader in the route + * file. A `handler() {}` method is replaced by a property holding the same + * function, so it splits like any other value. + */ +function getLoaderHandler( + loader: t.ObjectExpression, +): t.ObjectProperty | undefined { + // The last `handler` wins, unless a spread or computed key after it may + // replace it + for (let index = loader.properties.length - 1; index >= 0; index--) { + const prop = loader.properties[index]! + if (t.isSpreadElement(prop) || prop.computed) { + return undefined + } + if (getObjectPropertyKeyName(prop) !== 'handler') { + continue + } + if (t.isObjectProperty(prop)) { + const value = prop.value + const isFallback = + t.isBooleanLiteral(value) || + t.isNullLiteral(value) || + (t.isIdentifier(value) && value.name === 'undefined') + return isFallback ? undefined : prop + } + // `super` is only valid in a method + if (prop.kind !== 'method' || containsSuper(prop.body)) { + return undefined + } + const fn = t.functionExpression( + null, + prop.params, + prop.body, + prop.generator, + prop.async, + ) + fn.returnType = prop.returnType + fn.typeParameters = prop.typeParameters + const handler = t.objectProperty(t.identifier('handler'), fn) + loader.properties[index] = handler + return handler + } + return undefined +} + +function containsSuper(node: t.Node) { + let found = false + t.traverseFast(node, (child) => { + if (t.isSuper(child)) found = true + }) + return found +} + +/** + * The parts of a route option and the group each one belongs to (-1 for the + * route file): the whole value, or, for a split loader in object form, its + * handler and the options that stay in the route file. + */ +function getRouteOptionParts( + key: string, + value: t.Node, + groupIndex: number, +): Array<[node: t.Node, groupIndex: number]> { + if (key !== 'loader' || groupIndex === -1 || !t.isObjectExpression(value)) { + return [[value, groupIndex]] + } + const handler = getLoaderHandler(value) + if (!handler) { + return [[value, -1]] + } + const options = value.properties.filter((prop) => prop !== handler) + return options.length > 0 + ? [ + [handler.value, groupIndex], + [t.objectExpression(options), -1], + ] + : [[handler.value, groupIndex]] +} + /** * Computes module-level bindings that are shared between split and non-split * route properties. These bindings need to be extracted into a shared virtual @@ -211,11 +293,17 @@ export function computeSharedBindings(opts: { if (!key) continue if (key === 'codeSplitGroupings') continue if (t.isIdentifier(prop.value) && prop.value.name === 'undefined') continue - const groupIndex = findIndexForSplitNode(key) // -1 if non-split - if (groupIndex === -1) { - hasNonSplit = true - } else { - splitGroupsPresent.add(groupIndex) + const parts = getRouteOptionParts( + key, + prop.value, + findIndexForSplitNode(key), // -1 if non-split + ) + for (const [, groupIndex] of parts) { + if (groupIndex === -1) { + hasNonSplit = true + } else { + splitGroupsPresent.add(groupIndex) + } } } @@ -250,25 +338,31 @@ export function computeSharedBindings(opts: { if (key === 'codeSplitGroupings') continue - const groupIndex = findIndexForSplitNode(key) // -1 if non-split - - const directRefs = collectModuleLevelRefsFromNode( + const parts = getRouteOptionParts( + key, prop.value, - localModuleLevelBindings, + findIndexForSplitNode(key), // -1 if non-split ) - // Expand transitively: if component references SharedComp which references - // `shared`, then `shared` is also attributed to component's group. - const allRefs = new Set(directRefs) - expandTransitively(allRefs, depGraph) + for (const [node, groupIndex] of parts) { + const directRefs = collectModuleLevelRefsFromNode( + node, + localModuleLevelBindings, + ) + + // Expand transitively: if component references SharedComp which references + // `shared`, then `shared` is also attributed to component's group. + const allRefs = new Set(directRefs) + expandTransitively(allRefs, depGraph) - for (const ref of allRefs) { - let groups = refsByGroup.get(ref) - if (!groups) { - groups = new Set() - refsByGroup.set(ref, groups) + for (const ref of allRefs) { + let groups = refsByGroup.get(ref) + if (!groups) { + groups = new Set() + refsByGroup.set(ref, groups) + } + groups.add(groupIndex) } - groups.add(groupIndex) } } @@ -667,7 +761,15 @@ export function compileCodeSplitReferenceRoute( addRouteHmr(insertionPath, routeOptions) } else { // if (splitNodeMeta.splitStrategy === 'lazyFn') { - const value = prop.value + // A loader in object form splits only its handler + const target = + key === 'loader' && t.isObjectExpression(prop.value) + ? getLoaderHandler(prop.value) + : prop + if (!target) { + return + } + const value = target.value let shouldSplit = true @@ -725,7 +827,7 @@ export function compileCodeSplitReferenceRoute( } // Add the lazyFn call with the dynamic import to the prop value - prop.value = template.expression( + target.value = template.expression( `${LAZY_FN_IDENT}(${splitNodeMeta.localImporterIdent}, '${splitNodeMeta.exporterIdent}')`, )() } @@ -932,7 +1034,19 @@ export function compileCodeSplitVirtualRoute( return } - const value = prop.value + let value = prop.value + + // A loader in object form splits only its handler + if ( + splitType === 'loader' && + t.isObjectExpression(value) + ) { + const handler = getLoaderHandler(value) + if (!handler) { + return + } + value = handler.value + } // If the value for the `key` is `undefined`, then we don't need to include it // in the split file, so we can just return, since it will kept in-place in the @@ -957,7 +1071,7 @@ export function compileCodeSplitVirtualRoute( } else { const meta = SPLIT_NODES_CONFIG.get(splitType)! trackedNodesToSplitByType[splitType] = { - node: prop.value, + node: value, meta, } } diff --git a/packages/router-plugin/src/core/utils.ts b/packages/router-plugin/src/core/utils.ts index 61ab1ca278f..3e520a4a7ad 100644 --- a/packages/router-plugin/src/core/utils.ts +++ b/packages/router-plugin/src/core/utils.ts @@ -23,7 +23,7 @@ export const routeFactoryCallCodeFilter = [ ] export function getObjectPropertyKeyName( - prop: t.ObjectProperty, + prop: t.ObjectProperty | t.ObjectMethod, ): string | undefined { if (prop.computed) { return undefined diff --git a/packages/router-plugin/tests/code-splitter-route-options.test.ts b/packages/router-plugin/tests/code-splitter-route-options.test.ts index 84d24392d15..a1d782a12e9 100644 --- a/packages/router-plugin/tests/code-splitter-route-options.test.ts +++ b/packages/router-plugin/tests/code-splitter-route-options.test.ts @@ -1,12 +1,15 @@ import { describe, expect, it } from 'vitest' import { + buildAndRun, compileRouteModules, declarationOf, expectValidModules, + exportedNames, head, importSources, importedNames, } from './regression-helpers' +import type { CodeSplitGroupings } from '../src/core/constants' describe('route options', () => { it('are split when declared in a variable', async () => { @@ -114,3 +117,114 @@ export const Route = createFileRoute('/')({ await expectValidModules(modules) }) }) + +describe('a loader in object form', () => { + const groupings: CodeSplitGroupings = [['loader'], ['component']] + + /** Loads `/` with a real router and returns each match's loader data. */ + const routerEntry = `import { createMemoryHistory, createRouter } from '@tanstack/react-router' +import { routeTree } from './routeTree.gen' +export { Route } from './routes/index' +export async function load() { + const router = createRouter({ + routeTree, + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + await router.load() + return router.state.matches.map((match) => match.loaderData ?? null) +}` + + it.each([ + { name: 'an arrow handler', handler: `handler: async () => 'loaded'` }, + { + name: 'a method handler', + handler: `async handler() { + return 'loaded' + }`, + }, + { + name: 'an imported handler', + imports: `import { load } from '../load'\n`, + handler: `handler: load`, + }, + ])( + 'loads route data and keeps its options with $name split', + async ({ imports = '', handler }) => { + const result = await buildAndRun( + `${head}${imports}export const Route = createFileRoute('/')({ + loader: { + staleReloadMode: 'blocking', + ${handler}, + }, + component: () =>

index

, +})`, + `return [await entry.load(), entry.Route.options.loader.staleReloadMode]`, + { + files: { + 'entry.ts': routerEntry, + 'load.ts': `export const load = async () => 'loaded'`, + }, + groupings, + }, + ) + expect(result).toEqual([[null, 'loaded'], 'blocking']) + }, + 30_000, + ) + + it.each([ + { + name: 'a spread after the handler', + loader: `{ handler: () => 1, ...defaults }`, + }, + { + name: 'a computed key after the handler', + loader: `{ handler: () => 1, [key]: () => 2 }`, + }, + { name: 'no handler', loader: `{ ...defaults }` }, + { + name: 'a getter handler', + loader: `{ get handler() { return () => 1 } }`, + }, + { + name: 'a method handler that uses super', + loader: `{ handler() { return super.valueOf() } }`, + }, + ])('stays in the route file with $name', async ({ loader }) => { + const { modules } = compileRouteModules( + `${head}import { defaults, key } from './defaults' +export const Route = createFileRoute('/')({ + loader: ${loader}, + component: () =>

index

, +}) +`, + { groupings }, + ) + expect(modules.reference).not.toContain('tsr-split=loader') + expect(exportedNames(modules['virtual loader']!)).toEqual([]) + await expectValidModules(modules) + }) + + it('shares what its other options and a split option in the same chunk read', async () => { + const { modules, sharedBindings } = compileRouteModules( + `${head}const settings = { mode: 'blocking' as const } +export const Route = createFileRoute('/')({ + loader: { + staleReloadMode: settings.mode, + handler: () => settings, + }, + component: () =>

index

, +}) +`, + { groupings: [['loader', 'component']] }, + ) + // The handler moves to the chunk while `staleReloadMode` stays in the + // route file, so both read one `settings` object from the shared module. + expect(sharedBindings).toEqual(['settings']) + expect(modules.reference).not.toMatch(declarationOf('settings')) + expect(modules['virtual loader-component']).not.toMatch( + declarationOf('settings'), + ) + await expectValidModules(modules) + }) +}) diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/loader-object-arrow.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/loader-object-arrow.tsx new file mode 100644 index 00000000000..0fc05a913c5 --- /dev/null +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/loader-object-arrow.tsx @@ -0,0 +1,12 @@ +const $$splitComponentImporter = () => + import('loader-object-arrow.tsx?tsr-split=component') +import { lazyRouteComponent } from '@tanstack/react-router' +import { createFileRoute } from '@tanstack/react-router' +import { fetchPost } from '../posts' +export const Route = createFileRoute('/posts/$postId')({ + loader: { + staleReloadMode: 'blocking', + handler: async ({ params }) => fetchPost(params.postId), + }, + component: lazyRouteComponent($$splitComponentImporter, 'component'), +}) diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/loader-object-arrow@component.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/loader-object-arrow@component.tsx new file mode 100644 index 00000000000..3dfd8f4ebb6 --- /dev/null +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/loader-object-arrow@component.tsx @@ -0,0 +1,6 @@ +import { Route } from 'loader-object-arrow.tsx' +function PostComponent() { + const post = Route.useLoaderData() + return
{post.title}
+} +export { PostComponent as component } diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/loader-object-arrow@errorComponent.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/loader-object-arrow@errorComponent.tsx new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/loader-object-arrow@notFoundComponent.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/loader-object-arrow@notFoundComponent.tsx new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/loader-object-arrow@shared.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/loader-object-arrow@shared.tsx new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/loader-object-imported.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/loader-object-imported.tsx new file mode 100644 index 00000000000..06ed30141e8 --- /dev/null +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/loader-object-imported.tsx @@ -0,0 +1,9 @@ +const $$splitComponentImporter = () => + import('loader-object-imported.tsx?tsr-split=component') +import { lazyRouteComponent } from '@tanstack/react-router' +import { createFileRoute } from '@tanstack/react-router' +import { loadPosts } from '../posts' +export const Route = createFileRoute('/posts')({ + loader: { handler: loadPosts, staleReloadMode: 'background' }, + component: lazyRouteComponent($$splitComponentImporter, 'component'), +}) diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/loader-object-imported@component.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/loader-object-imported@component.tsx new file mode 100644 index 00000000000..671675707b4 --- /dev/null +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/loader-object-imported@component.tsx @@ -0,0 +1,2 @@ +const SplitComponent = () =>
posts
+export { SplitComponent as component } diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/loader-object-imported@errorComponent.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/loader-object-imported@errorComponent.tsx new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/loader-object-imported@notFoundComponent.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/loader-object-imported@notFoundComponent.tsx new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/loader-object-imported@shared.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/loader-object-imported@shared.tsx new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/loader-object-method.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/loader-object-method.tsx new file mode 100644 index 00000000000..a7219ece62c --- /dev/null +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/loader-object-method.tsx @@ -0,0 +1,16 @@ +const $$splitComponentImporter = () => + import('loader-object-method.tsx?tsr-split=component') +import { lazyRouteComponent } from '@tanstack/react-router' +import { createFileRoute } from '@tanstack/react-router' +import { fetchPosts } from '../posts' +const reloadMode = 'blocking' +export const Route = createFileRoute('/posts')({ + loader: { + staleReloadMode: reloadMode, + async handler({ abortController }) { + const posts = await fetchPosts({ signal: abortController.signal }) + return { posts, reloadMode } + }, + }, + component: lazyRouteComponent($$splitComponentImporter, 'component'), +}) diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/loader-object-method@component.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/loader-object-method@component.tsx new file mode 100644 index 00000000000..671675707b4 --- /dev/null +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/loader-object-method@component.tsx @@ -0,0 +1,2 @@ +const SplitComponent = () =>
posts
+export { SplitComponent as component } diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/loader-object-method@errorComponent.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/loader-object-method@errorComponent.tsx new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/loader-object-method@notFoundComponent.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/loader-object-method@notFoundComponent.tsx new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/loader-object-method@shared.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/loader-object-method@shared.tsx new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/loader-object-arrow.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/loader-object-arrow.tsx new file mode 100644 index 00000000000..9759abc7e2d --- /dev/null +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/loader-object-arrow.tsx @@ -0,0 +1,14 @@ +const $$splitComponentImporter = () => + import('loader-object-arrow.tsx?tsr-split=component---errorComponent---notFoundComponent---pendingComponent') +import { lazyRouteComponent } from '@tanstack/react-router' +const $$splitLoaderImporter = () => + import('loader-object-arrow.tsx?tsr-split=loader') +import { lazyFn } from '@tanstack/react-router' +import { createFileRoute } from '@tanstack/react-router' +export const Route = createFileRoute('/posts/$postId')({ + loader: { + staleReloadMode: 'blocking', + handler: lazyFn($$splitLoaderImporter, 'loader'), + }, + component: lazyRouteComponent($$splitComponentImporter, 'component'), +}) diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/loader-object-arrow@component---errorComponent---notFoundComponent---pendingComponent.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/loader-object-arrow@component---errorComponent---notFoundComponent---pendingComponent.tsx new file mode 100644 index 00000000000..3dfd8f4ebb6 --- /dev/null +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/loader-object-arrow@component---errorComponent---notFoundComponent---pendingComponent.tsx @@ -0,0 +1,6 @@ +import { Route } from 'loader-object-arrow.tsx' +function PostComponent() { + const post = Route.useLoaderData() + return
{post.title}
+} +export { PostComponent as component } diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/loader-object-arrow@loader.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/loader-object-arrow@loader.tsx new file mode 100644 index 00000000000..e39398f8b0e --- /dev/null +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/loader-object-arrow@loader.tsx @@ -0,0 +1,3 @@ +import { fetchPost } from '../posts' +const SplitLoader = async ({ params }) => fetchPost(params.postId) +export { SplitLoader as loader } diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/loader-object-arrow@shared.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/loader-object-arrow@shared.tsx new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/loader-object-imported.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/loader-object-imported.tsx new file mode 100644 index 00000000000..91de54e6684 --- /dev/null +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/loader-object-imported.tsx @@ -0,0 +1,14 @@ +const $$splitComponentImporter = () => + import('loader-object-imported.tsx?tsr-split=component---errorComponent---notFoundComponent---pendingComponent') +import { lazyRouteComponent } from '@tanstack/react-router' +const $$splitLoaderImporter = () => + import('loader-object-imported.tsx?tsr-split=loader') +import { lazyFn } from '@tanstack/react-router' +import { createFileRoute } from '@tanstack/react-router' +export const Route = createFileRoute('/posts')({ + loader: { + handler: lazyFn($$splitLoaderImporter, 'loader'), + staleReloadMode: 'background', + }, + component: lazyRouteComponent($$splitComponentImporter, 'component'), +}) diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/loader-object-imported@component---errorComponent---notFoundComponent---pendingComponent.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/loader-object-imported@component---errorComponent---notFoundComponent---pendingComponent.tsx new file mode 100644 index 00000000000..671675707b4 --- /dev/null +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/loader-object-imported@component---errorComponent---notFoundComponent---pendingComponent.tsx @@ -0,0 +1,2 @@ +const SplitComponent = () =>
posts
+export { SplitComponent as component } diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/loader-object-imported@loader.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/loader-object-imported@loader.tsx new file mode 100644 index 00000000000..7d2bcc4aa00 --- /dev/null +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/loader-object-imported@loader.tsx @@ -0,0 +1,3 @@ +import { loadPosts } from '../posts' +const SplitLoader = loadPosts +export { SplitLoader as loader } diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/loader-object-imported@shared.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/loader-object-imported@shared.tsx new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/loader-object-method.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/loader-object-method.tsx new file mode 100644 index 00000000000..da5a0e034df --- /dev/null +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/loader-object-method.tsx @@ -0,0 +1,15 @@ +import { reloadMode } from 'loader-object-method.tsx?tsr-shared=1' +const $$splitComponentImporter = () => + import('loader-object-method.tsx?tsr-split=component---errorComponent---notFoundComponent---pendingComponent') +import { lazyRouteComponent } from '@tanstack/react-router' +const $$splitLoaderImporter = () => + import('loader-object-method.tsx?tsr-split=loader') +import { lazyFn } from '@tanstack/react-router' +import { createFileRoute } from '@tanstack/react-router' +export const Route = createFileRoute('/posts')({ + loader: { + staleReloadMode: reloadMode, + handler: lazyFn($$splitLoaderImporter, 'loader'), + }, + component: lazyRouteComponent($$splitComponentImporter, 'component'), +}) diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/loader-object-method@component---errorComponent---notFoundComponent---pendingComponent.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/loader-object-method@component---errorComponent---notFoundComponent---pendingComponent.tsx new file mode 100644 index 00000000000..671675707b4 --- /dev/null +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/loader-object-method@component---errorComponent---notFoundComponent---pendingComponent.tsx @@ -0,0 +1,2 @@ +const SplitComponent = () =>
posts
+export { SplitComponent as component } diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/loader-object-method@loader.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/loader-object-method@loader.tsx new file mode 100644 index 00000000000..1dcdae71930 --- /dev/null +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/loader-object-method@loader.tsx @@ -0,0 +1,7 @@ +import { reloadMode } from 'loader-object-method.tsx?tsr-shared=1' +import { fetchPosts } from '../posts' +const SplitLoader = async function ({ abortController }) { + const posts = await fetchPosts({ signal: abortController.signal }) + return { posts, reloadMode } +} +export { SplitLoader as loader } diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/loader-object-method@shared.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/loader-object-method@shared.tsx new file mode 100644 index 00000000000..f9988bbf09a --- /dev/null +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/loader-object-method@shared.tsx @@ -0,0 +1,2 @@ +const reloadMode = 'blocking' +export { reloadMode } diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/loader-object-arrow.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/loader-object-arrow.tsx new file mode 100644 index 00000000000..8a4a2423613 --- /dev/null +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/loader-object-arrow.tsx @@ -0,0 +1,14 @@ +const $$splitComponentImporter = () => + import('loader-object-arrow.tsx?tsr-split=component---loader---notFoundComponent---pendingComponent') +import { lazyRouteComponent } from '@tanstack/react-router' +const $$splitLoaderImporter = () => + import('loader-object-arrow.tsx?tsr-split=component---loader---notFoundComponent---pendingComponent') +import { lazyFn } from '@tanstack/react-router' +import { createFileRoute } from '@tanstack/react-router' +export const Route = createFileRoute('/posts/$postId')({ + loader: { + staleReloadMode: 'blocking', + handler: lazyFn($$splitLoaderImporter, 'loader'), + }, + component: lazyRouteComponent($$splitComponentImporter, 'component'), +}) diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/loader-object-arrow@component---loader---notFoundComponent---pendingComponent.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/loader-object-arrow@component---loader---notFoundComponent---pendingComponent.tsx new file mode 100644 index 00000000000..16c1f9dfa86 --- /dev/null +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/loader-object-arrow@component---loader---notFoundComponent---pendingComponent.tsx @@ -0,0 +1,9 @@ +import { fetchPost } from '../posts' +import { Route } from 'loader-object-arrow.tsx' +function PostComponent() { + const post = Route.useLoaderData() + return
{post.title}
+} +const SplitLoader = async ({ params }) => fetchPost(params.postId) +export { SplitLoader as loader } +export { PostComponent as component } diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/loader-object-arrow@errorComponent.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/loader-object-arrow@errorComponent.tsx new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/loader-object-arrow@shared.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/loader-object-arrow@shared.tsx new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/loader-object-imported.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/loader-object-imported.tsx new file mode 100644 index 00000000000..be42a03d382 --- /dev/null +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/loader-object-imported.tsx @@ -0,0 +1,14 @@ +const $$splitComponentImporter = () => + import('loader-object-imported.tsx?tsr-split=component---loader---notFoundComponent---pendingComponent') +import { lazyRouteComponent } from '@tanstack/react-router' +const $$splitLoaderImporter = () => + import('loader-object-imported.tsx?tsr-split=component---loader---notFoundComponent---pendingComponent') +import { lazyFn } from '@tanstack/react-router' +import { createFileRoute } from '@tanstack/react-router' +export const Route = createFileRoute('/posts')({ + loader: { + handler: lazyFn($$splitLoaderImporter, 'loader'), + staleReloadMode: 'background', + }, + component: lazyRouteComponent($$splitComponentImporter, 'component'), +}) diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/loader-object-imported@component---loader---notFoundComponent---pendingComponent.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/loader-object-imported@component---loader---notFoundComponent---pendingComponent.tsx new file mode 100644 index 00000000000..f1380d7e759 --- /dev/null +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/loader-object-imported@component---loader---notFoundComponent---pendingComponent.tsx @@ -0,0 +1,5 @@ +import { loadPosts } from '../posts' +const SplitLoader = loadPosts +export { SplitLoader as loader } +const SplitComponent = () =>
posts
+export { SplitComponent as component } diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/loader-object-imported@errorComponent.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/loader-object-imported@errorComponent.tsx new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/loader-object-imported@shared.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/loader-object-imported@shared.tsx new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/loader-object-method.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/loader-object-method.tsx new file mode 100644 index 00000000000..433c5ca006f --- /dev/null +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/loader-object-method.tsx @@ -0,0 +1,15 @@ +import { reloadMode } from 'loader-object-method.tsx?tsr-shared=1' +const $$splitComponentImporter = () => + import('loader-object-method.tsx?tsr-split=component---loader---notFoundComponent---pendingComponent') +import { lazyRouteComponent } from '@tanstack/react-router' +const $$splitLoaderImporter = () => + import('loader-object-method.tsx?tsr-split=component---loader---notFoundComponent---pendingComponent') +import { lazyFn } from '@tanstack/react-router' +import { createFileRoute } from '@tanstack/react-router' +export const Route = createFileRoute('/posts')({ + loader: { + staleReloadMode: reloadMode, + handler: lazyFn($$splitLoaderImporter, 'loader'), + }, + component: lazyRouteComponent($$splitComponentImporter, 'component'), +}) diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/loader-object-method@component---loader---notFoundComponent---pendingComponent.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/loader-object-method@component---loader---notFoundComponent---pendingComponent.tsx new file mode 100644 index 00000000000..df9d32640c3 --- /dev/null +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/loader-object-method@component---loader---notFoundComponent---pendingComponent.tsx @@ -0,0 +1,9 @@ +import { reloadMode } from 'loader-object-method.tsx?tsr-shared=1' +import { fetchPosts } from '../posts' +const SplitLoader = async function ({ abortController }) { + const posts = await fetchPosts({ signal: abortController.signal }) + return { posts, reloadMode } +} +export { SplitLoader as loader } +const SplitComponent = () =>
posts
+export { SplitComponent as component } diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/loader-object-method@errorComponent.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/loader-object-method@errorComponent.tsx new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/loader-object-method@shared.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/loader-object-method@shared.tsx new file mode 100644 index 00000000000..f9988bbf09a --- /dev/null +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/loader-object-method@shared.tsx @@ -0,0 +1,2 @@ +const reloadMode = 'blocking' +export { reloadMode } diff --git a/packages/router-plugin/tests/code-splitter/test-files/react/loader-object-arrow.tsx b/packages/router-plugin/tests/code-splitter/test-files/react/loader-object-arrow.tsx new file mode 100644 index 00000000000..893d390d067 --- /dev/null +++ b/packages/router-plugin/tests/code-splitter/test-files/react/loader-object-arrow.tsx @@ -0,0 +1,15 @@ +import { createFileRoute } from '@tanstack/react-router' +import { fetchPost } from '../posts' + +export const Route = createFileRoute('/posts/$postId')({ + loader: { + staleReloadMode: 'blocking', + handler: async ({ params }) => fetchPost(params.postId), + }, + component: PostComponent, +}) + +function PostComponent() { + const post = Route.useLoaderData() + return
{post.title}
+} diff --git a/packages/router-plugin/tests/code-splitter/test-files/react/loader-object-imported.tsx b/packages/router-plugin/tests/code-splitter/test-files/react/loader-object-imported.tsx new file mode 100644 index 00000000000..7099264d944 --- /dev/null +++ b/packages/router-plugin/tests/code-splitter/test-files/react/loader-object-imported.tsx @@ -0,0 +1,7 @@ +import { createFileRoute } from '@tanstack/react-router' +import { loadPosts } from '../posts' + +export const Route = createFileRoute('/posts')({ + loader: { handler: loadPosts, staleReloadMode: 'background' }, + component: () =>
posts
, +}) diff --git a/packages/router-plugin/tests/code-splitter/test-files/react/loader-object-method.tsx b/packages/router-plugin/tests/code-splitter/test-files/react/loader-object-method.tsx new file mode 100644 index 00000000000..a185368dab3 --- /dev/null +++ b/packages/router-plugin/tests/code-splitter/test-files/react/loader-object-method.tsx @@ -0,0 +1,15 @@ +import { createFileRoute } from '@tanstack/react-router' +import { fetchPosts } from '../posts' + +const reloadMode = 'blocking' + +export const Route = createFileRoute('/posts')({ + loader: { + staleReloadMode: reloadMode, + async handler({ abortController }) { + const posts = await fetchPosts({ signal: abortController.signal }) + return { posts, reloadMode } + }, + }, + component: () =>
posts
, +}) diff --git a/packages/router-plugin/tests/regression-helpers.ts b/packages/router-plugin/tests/regression-helpers.ts index 3cca5f6cff7..10c217e2c0a 100644 --- a/packages/router-plugin/tests/regression-helpers.ts +++ b/packages/router-plugin/tests/regression-helpers.ts @@ -33,9 +33,9 @@ export { declarationOf } from './validate-module' export const head = `import { createFileRoute } from '@tanstack/react-router'\n` /** - * Compiles a route file with the default groupings into every module the code - * splitter emits for it: `reference`, one `virtual ` chunk per grouping - * and, when bindings are shared, `shared`. + * Compiles a route file (with the default groupings unless `groupings` is + * given) into every module the code splitter emits for it: `reference`, one + * `virtual ` chunk per grouping and, when bindings are shared, `shared`. */ export function compileRouteModules( code: string, @@ -44,14 +44,15 @@ export function compileRouteModules( targetFramework?: 'react' | 'solid' /** Compile with route HMR and the framework's HMR compiler plugins. */ hmr?: boolean + groupings?: CodeSplitGroupings } = {}, ) { const { filename = 'route.tsx', targetFramework = 'react', hmr = false, + groupings = defaultCodeSplitGroupings, } = options - const groupings = defaultCodeSplitGroupings const compilerPlugins = hmr ? getFrameworkHmrCompilerPlugins({ targetFramework }) : undefined From 4d25bdc6d5d88da39e9c09f9d9e95e0e08dcdfdd Mon Sep 17 00:00:00 2001 From: shkumbinhasani <34962865+shkumbinhasani@users.noreply.github.com> Date: Sun, 18 Oct 2026 14:30:28 +0200 Subject: [PATCH 2/2] docs(router-plugin): describe how an object-form loader is split Co-Authored-By: Claude Opus 5.5 --- packages/router-plugin/ARCHITECTURE-CODE-SPLITTING.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/router-plugin/ARCHITECTURE-CODE-SPLITTING.md b/packages/router-plugin/ARCHITECTURE-CODE-SPLITTING.md index b3016930784..30f7e1c5659 100644 --- a/packages/router-plugin/ARCHITECTURE-CODE-SPLITTING.md +++ b/packages/router-plugin/ARCHITECTURE-CODE-SPLITTING.md @@ -870,6 +870,12 @@ Components use `lazyRouteComponent` (framework-provided). In React this integrates with Suspense-compatible lazy loading. Loaders use `lazyFn` which wraps the async function for deferred loading. +A loader in object form (`{ handler, staleReloadMode }`) splits only its +`handler`: the router reads the other keys from the route options, so they +stay in the reference file. The loader stays whole in the reference file when +a spread or computed key after `handler` may replace it, when there is no +plain `handler`, or when a `handler() {}` method uses `super`. + ### 11. Source Maps Are Preserved All three compilers pass `sourceMaps: true` and `sourceFileName` to