Skip to content

Commit 96bde26

Browse files
authored
fix(router-plugin): show warning for non-splittable items (#5145)
1 parent 9a728b6 commit 96bde26

File tree

71 files changed

+97
-72
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

71 files changed

+97
-72
lines changed

docs/router/framework/react/guide/automatic-code-splitting.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,38 @@ This means that it creates three separate lazy-loaded chunks for each route. Res
7474
- One for the error component
7575
- And one for the not-found component.
7676

77+
### Rules of Splitting
78+
79+
For automatic code splitting to work, there are some rules in-place to make sure that this process can reliably and predictably happen.
80+
81+
#### Do not export route properties
82+
83+
Route properties like `component`, `loader`, etc., should not be exported from the route file. Exporting these properties results in them being bundled into the main application bundle, which means that they will not be code-split.
84+
85+
```tsx
86+
import { createRoute } from '@tanstack/react-router'
87+
88+
export const Route = createRoute('/posts')({
89+
// ...
90+
notFoundComponent: PostsNotFoundComponent,
91+
})
92+
93+
// ❌ Do NOT do this!
94+
// Exporting the notFoundComponent will prevent it from being code-split
95+
// and will be included in the main bundle.
96+
export function PostsNotFoundComponent() {
97+
//
98+
// ...
99+
}
100+
101+
function PostsNotFoundComponent() {
102+
//
103+
// ...
104+
}
105+
```
106+
107+
**That's it!** There are no other restrictions. You can use any other JavaScript or TypeScript features in your route files as you normally would. If you run into any issues, please [open an issue](https://github.com/tanstack/router/issues) on GitHub.
108+
77109
## Granular control
78110

79111
For most applications, the default behavior of using `autoCodeSplitting: true` is sufficient. However, TanStack Router provides several options to customize how your routes are split into chunks, allowing you to optimize for specific use cases or performance needs.

packages/router-plugin/src/core/code-splitter/compilers.ts

Lines changed: 45 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,8 @@ function addSplitSearchParamToFilename(
8484
const params = new URLSearchParams()
8585
params.append(tsrSplit, createIdentifier(grouping))
8686

87-
return `${bareFilename}?${params.toString()}`
87+
const result = `${bareFilename}?${params.toString()}`
88+
return result
8889
}
8990

9091
function removeSplitSearchParamFromFilename(filename: string) {
@@ -116,6 +117,8 @@ export function compileCodeSplitReferenceRoute(
116117

117118
const refIdents = findReferencedIdentifiers(ast)
118119

120+
const knownExportedIdents = new Set<string>()
121+
119122
function findIndexForSplitNode(str: string) {
120123
return opts.codeSplitGroupings.findIndex((group) =>
121124
group.includes(str as any),
@@ -251,6 +254,9 @@ export function compileCodeSplitReferenceRoute(
251254
// since they are already being imported
252255
// and need to be retained in the compiled file
253256
const isExported = hasExport(ast, value)
257+
if (isExported) {
258+
knownExportedIdents.add(value.name)
259+
}
254260
shouldSplit = !isExported
255261

256262
if (shouldSplit) {
@@ -320,6 +326,9 @@ export function compileCodeSplitReferenceRoute(
320326
// since they are already being imported
321327
// and need to be retained in the compiled file
322328
const isExported = hasExport(ast, value)
329+
if (isExported) {
330+
knownExportedIdents.add(value.name)
331+
}
323332
shouldSplit = !isExported
324333

325334
if (shouldSplit) {
@@ -410,6 +419,26 @@ export function compileCodeSplitReferenceRoute(
410419

411420
deadCodeElimination(ast, refIdents)
412421

422+
// if there are exported identifiers, then we need to add a warning
423+
// to the file to let the user know that the exported identifiers
424+
// will not in the split file but in the original file, therefore
425+
// increasing the bundle size
426+
if (knownExportedIdents.size > 0) {
427+
const warningMessage = createNotExportableMessage(
428+
opts.filename,
429+
knownExportedIdents,
430+
)
431+
console.warn(warningMessage)
432+
433+
// append this warning to the file using a template
434+
if (process.env.NODE_ENV !== 'production') {
435+
const warningTemplate = template.statement(
436+
`console.warn(${JSON.stringify(warningMessage)})`,
437+
)()
438+
ast.program.body.unshift(warningTemplate)
439+
}
440+
}
441+
413442
return generateFromAst(ast, {
414443
sourceMaps: true,
415444
sourceFileName: opts.filename,
@@ -712,28 +741,6 @@ export function compileCodeSplitVirtualRoute(
712741

713742
deadCodeElimination(ast, refIdents)
714743

715-
// if there are exported identifiers, then we need to add a warning
716-
// to the file to let the user know that the exported identifiers
717-
// will not in the split file but in the original file, therefore
718-
// increasing the bundle size
719-
if (knownExportedIdents.size > 0) {
720-
const list = Array.from(knownExportedIdents).reduce((str, ident) => {
721-
str += `\n- ${ident}`
722-
return str
723-
}, '')
724-
725-
const warningMessage = `These exports from "${opts.filename}" are not being code-split and will increase your bundle size: ${list}\nThese should either have their export statements removed or be imported from another file that is not a route.`
726-
console.warn(warningMessage)
727-
728-
// append this warning to the file using a template
729-
if (process.env.NODE_ENV !== 'production') {
730-
const warningTemplate = template.statement(
731-
`console.warn(${JSON.stringify(warningMessage)})`,
732-
)()
733-
ast.program.body.unshift(warningTemplate)
734-
}
735-
}
736-
737744
return generateFromAst(ast, {
738745
sourceMaps: true,
739746
sourceFileName: opts.filename,
@@ -837,6 +844,21 @@ export function detectCodeSplitGroupingsFromRoute(opts: ParseAstOptions): {
837844
return { groupings: codeSplitGroupings }
838845
}
839846

847+
function createNotExportableMessage(
848+
filename: string,
849+
idents: Set<string>,
850+
): string {
851+
const list = Array.from(idents).map((d) => `- ${d}`)
852+
853+
const message = [
854+
`[tanstack-router] These exports from "${filename}" will not be code-split and will increase your bundle size:`,
855+
...list,
856+
'For the best optimization, these items should either have their export statements removed, or be imported from another location that is not a route file.',
857+
].join('\n')
858+
859+
return message
860+
}
861+
840862
function getImportSpecifierAndPathFromLocalName(
841863
programPath: babel.NodePath<t.Program>,
842864
name: string,

packages/router-plugin/tests/code-splitter/snapshots/react/1-default/export-default-component-and-normal-notFound.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
console.warn("[tanstack-router] These exports from \"export-default-component-and-normal-notFound.tsx\" will not be code-split and will increase your bundle size:\n- Home\nFor the best optimization, these items should either have their export statements removed, or be imported from another location that is not a route file.");
12
const $$splitNotFoundComponentImporter = () => import('export-default-component-and-normal-notFound.tsx?tsr-split=notFoundComponent');
23
import { lazyRouteComponent } from '@tanstack/react-router';
34
import React, { useState } from 'react';
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
1-
console.warn("These exports from \"export-default-component-and-normal-notFound.tsx?component\" are not being code-split and will increase your bundle size: \n- Home\nThese should either have their export statements removed or be imported from another file that is not a route.");
21
import React from 'react';
32
import { Route } from "export-default-component-and-normal-notFound.tsx";
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
1-
console.warn("These exports from \"export-default-component-and-normal-notFound.tsx?errorComponent\" are not being code-split and will increase your bundle size: \n- Home\nThese should either have their export statements removed or be imported from another file that is not a route.");
21
import React from 'react';
32
import { Route } from "export-default-component-and-normal-notFound.tsx";

packages/router-plugin/tests/code-splitter/snapshots/react/1-default/export-default-component-and-normal-notFound@notFoundComponent.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
console.warn("These exports from \"export-default-component-and-normal-notFound.tsx?notFoundComponent\" are not being code-split and will increase your bundle size: \n- Home\nThese should either have their export statements removed or be imported from another file that is not a route.");
21
import React from 'react';
32
import { Route } from "export-default-component-and-normal-notFound.tsx";
43
function NotFoundComponent() {

packages/router-plugin/tests/code-splitter/snapshots/react/1-default/export-default-component.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
console.warn("[tanstack-router] These exports from \"export-default-component.tsx\" will not be code-split and will increase your bundle size:\n- Home\nFor the best optimization, these items should either have their export statements removed, or be imported from another location that is not a route file.");
12
import React, { useState } from 'react';
23
import { createFileRoute } from '@tanstack/react-router';
34
export const Route = createFileRoute('/home')({
Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
1-
console.warn("These exports from \"export-default-component.tsx?component\" are not being code-split and will increase your bundle size: \n- Home\nThese should either have their export statements removed or be imported from another file that is not a route.");
21
import React from 'react';
32
import { Route } from "export-default-component.tsx";
Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
1-
console.warn("These exports from \"export-default-component.tsx?errorComponent\" are not being code-split and will increase your bundle size: \n- Home\nThese should either have their export statements removed or be imported from another file that is not a route.");
21
import React from 'react';
32
import { Route } from "export-default-component.tsx";
Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
1-
console.warn("These exports from \"export-default-component.tsx?notFoundComponent\" are not being code-split and will increase your bundle size: \n- Home\nThese should either have their export statements removed or be imported from another file that is not a route.");
21
import React from 'react';
32
import { Route } from "export-default-component.tsx";

0 commit comments

Comments
 (0)