Skip to content

Commit 638a902

Browse files
fix(router-plugin): add support for boolean and null literals in code splitter (#4915)
Co-authored-by: SeanCassiere <[email protected]>
1 parent 38abdc2 commit 638a902

29 files changed

+195
-7
lines changed

e2e/react-router/basic-file-based/src/routeTree.gen.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { Route as Char45824Char54620Char48124Char44397RouteImport } from './rout
1515
import { Route as PostsRouteImport } from './routes/posts'
1616
import { Route as EditingBRouteImport } from './routes/editing-b'
1717
import { Route as EditingARouteImport } from './routes/editing-a'
18+
import { Route as ComponentTypesTestRouteImport } from './routes/component-types-test'
1819
import { Route as AnchorRouteImport } from './routes/anchor'
1920
import { Route as LayoutRouteImport } from './routes/_layout'
2021
import { Route as SearchParamsRouteRouteImport } from './routes/search-params/route'
@@ -80,6 +81,11 @@ const EditingARoute = EditingARouteImport.update({
8081
path: '/editing-a',
8182
getParentRoute: () => rootRouteImport,
8283
} as any)
84+
const ComponentTypesTestRoute = ComponentTypesTestRouteImport.update({
85+
id: '/component-types-test',
86+
path: '/component-types-test',
87+
getParentRoute: () => rootRouteImport,
88+
} as any)
8389
const AnchorRoute = AnchorRouteImport.update({
8490
id: '/anchor',
8591
path: '/anchor',
@@ -276,6 +282,7 @@ export interface FileRoutesByFullPath {
276282
'/': typeof groupLayoutRouteWithChildren
277283
'/search-params': typeof SearchParamsRouteRouteWithChildren
278284
'/anchor': typeof AnchorRoute
285+
'/component-types-test': typeof ComponentTypesTestRoute
279286
'/editing-a': typeof EditingARoute
280287
'/editing-b': typeof EditingBRoute
281288
'/posts': typeof PostsRouteWithChildren
@@ -315,6 +322,7 @@ export interface FileRoutesByFullPath {
315322
export interface FileRoutesByTo {
316323
'/': typeof groupLayoutRouteWithChildren
317324
'/anchor': typeof AnchorRoute
325+
'/component-types-test': typeof ComponentTypesTestRoute
318326
'/editing-a': typeof EditingARoute
319327
'/editing-b': typeof EditingBRoute
320328
'/대한민국': typeof Char45824Char54620Char48124Char44397Route
@@ -355,6 +363,7 @@ export interface FileRoutesById {
355363
'/search-params': typeof SearchParamsRouteRouteWithChildren
356364
'/_layout': typeof LayoutRouteWithChildren
357365
'/anchor': typeof AnchorRoute
366+
'/component-types-test': typeof ComponentTypesTestRoute
358367
'/editing-a': typeof EditingARoute
359368
'/editing-b': typeof EditingBRoute
360369
'/posts': typeof PostsRouteWithChildren
@@ -400,6 +409,7 @@ export interface FileRouteTypes {
400409
| '/'
401410
| '/search-params'
402411
| '/anchor'
412+
| '/component-types-test'
403413
| '/editing-a'
404414
| '/editing-b'
405415
| '/posts'
@@ -439,6 +449,7 @@ export interface FileRouteTypes {
439449
to:
440450
| '/'
441451
| '/anchor'
452+
| '/component-types-test'
442453
| '/editing-a'
443454
| '/editing-b'
444455
| '/대한민국'
@@ -478,6 +489,7 @@ export interface FileRouteTypes {
478489
| '/search-params'
479490
| '/_layout'
480491
| '/anchor'
492+
| '/component-types-test'
481493
| '/editing-a'
482494
| '/editing-b'
483495
| '/posts'
@@ -523,6 +535,7 @@ export interface RootRouteChildren {
523535
SearchParamsRouteRoute: typeof SearchParamsRouteRouteWithChildren
524536
LayoutRoute: typeof LayoutRouteWithChildren
525537
AnchorRoute: typeof AnchorRoute
538+
ComponentTypesTestRoute: typeof ComponentTypesTestRoute
526539
EditingARoute: typeof EditingARoute
527540
EditingBRoute: typeof EditingBRoute
528541
PostsRoute: typeof PostsRouteWithChildren
@@ -585,6 +598,13 @@ declare module '@tanstack/react-router' {
585598
preLoaderRoute: typeof EditingARouteImport
586599
parentRoute: typeof rootRouteImport
587600
}
601+
'/component-types-test': {
602+
id: '/component-types-test'
603+
path: '/component-types-test'
604+
fullPath: '/component-types-test'
605+
preLoaderRoute: typeof ComponentTypesTestRouteImport
606+
parentRoute: typeof rootRouteImport
607+
}
588608
'/anchor': {
589609
id: '/anchor'
590610
path: '/anchor'
@@ -946,6 +966,7 @@ const rootRouteChildren: RootRouteChildren = {
946966
SearchParamsRouteRoute: SearchParamsRouteRouteWithChildren,
947967
LayoutRoute: LayoutRouteWithChildren,
948968
AnchorRoute: AnchorRoute,
969+
ComponentTypesTestRoute: ComponentTypesTestRoute,
949970
EditingARoute: EditingARoute,
950971
EditingBRoute: EditingBRoute,
951972
PostsRoute: PostsRouteWithChildren,
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { createFileRoute } from '@tanstack/react-router'
2+
3+
// Test route for type safety of different component types
4+
// This test should
5+
// - check that `false` can be set on the `errorComponent`
6+
// ...without causing build-time TypeScript errors
7+
export const Route = createFileRoute('/component-types-test')({
8+
component: () => <div>Testing test types</div>,
9+
errorComponent: false, // Should not cause TypeScript error
10+
pendingComponent: undefined, // Should not cause TypeScript error
11+
notFoundComponent: undefined, // Should not cause TypeScript error
12+
})

e2e/solid-router/basic-file-based/src/routeTree.gen.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { Route as rootRouteImport } from './routes/__root'
1414
import { Route as PostsRouteImport } from './routes/posts'
1515
import { Route as EditingBRouteImport } from './routes/editing-b'
1616
import { Route as EditingARouteImport } from './routes/editing-a'
17+
import { Route as ComponentTypesTestRouteImport } from './routes/component-types-test'
1718
import { Route as AnchorRouteImport } from './routes/anchor'
1819
import { Route as LayoutRouteImport } from './routes/_layout'
1920
import { Route as SearchParamsRouteRouteImport } from './routes/search-params/route'
@@ -63,6 +64,11 @@ const EditingARoute = EditingARouteImport.update({
6364
path: '/editing-a',
6465
getParentRoute: () => rootRouteImport,
6566
} as any)
67+
const ComponentTypesTestRoute = ComponentTypesTestRouteImport.update({
68+
id: '/component-types-test',
69+
path: '/component-types-test',
70+
getParentRoute: () => rootRouteImport,
71+
} as any)
6672
const AnchorRoute = AnchorRouteImport.update({
6773
id: '/anchor',
6874
path: '/anchor',
@@ -204,6 +210,7 @@ export interface FileRoutesByFullPath {
204210
'/': typeof groupLayoutRouteWithChildren
205211
'/search-params': typeof SearchParamsRouteRouteWithChildren
206212
'/anchor': typeof AnchorRoute
213+
'/component-types-test': typeof ComponentTypesTestRoute
207214
'/editing-a': typeof EditingARoute
208215
'/editing-b': typeof EditingBRoute
209216
'/posts': typeof PostsRouteWithChildren
@@ -232,6 +239,7 @@ export interface FileRoutesByFullPath {
232239
export interface FileRoutesByTo {
233240
'/': typeof groupLayoutRouteWithChildren
234241
'/anchor': typeof AnchorRoute
242+
'/component-types-test': typeof ComponentTypesTestRoute
235243
'/editing-a': typeof EditingARoute
236244
'/editing-b': typeof EditingBRoute
237245
'/onlyrouteinside': typeof anotherGroupOnlyrouteinsideRoute
@@ -261,6 +269,7 @@ export interface FileRoutesById {
261269
'/search-params': typeof SearchParamsRouteRouteWithChildren
262270
'/_layout': typeof LayoutRouteWithChildren
263271
'/anchor': typeof AnchorRoute
272+
'/component-types-test': typeof ComponentTypesTestRoute
264273
'/editing-a': typeof EditingARoute
265274
'/editing-b': typeof EditingBRoute
266275
'/posts': typeof PostsRouteWithChildren
@@ -295,6 +304,7 @@ export interface FileRouteTypes {
295304
| '/'
296305
| '/search-params'
297306
| '/anchor'
307+
| '/component-types-test'
298308
| '/editing-a'
299309
| '/editing-b'
300310
| '/posts'
@@ -323,6 +333,7 @@ export interface FileRouteTypes {
323333
to:
324334
| '/'
325335
| '/anchor'
336+
| '/component-types-test'
326337
| '/editing-a'
327338
| '/editing-b'
328339
| '/onlyrouteinside'
@@ -351,6 +362,7 @@ export interface FileRouteTypes {
351362
| '/search-params'
352363
| '/_layout'
353364
| '/anchor'
365+
| '/component-types-test'
354366
| '/editing-a'
355367
| '/editing-b'
356368
| '/posts'
@@ -385,6 +397,7 @@ export interface RootRouteChildren {
385397
SearchParamsRouteRoute: typeof SearchParamsRouteRouteWithChildren
386398
LayoutRoute: typeof LayoutRouteWithChildren
387399
AnchorRoute: typeof AnchorRoute
400+
ComponentTypesTestRoute: typeof ComponentTypesTestRoute
388401
EditingARoute: typeof EditingARoute
389402
EditingBRoute: typeof EditingBRoute
390403
PostsRoute: typeof PostsRouteWithChildren
@@ -429,6 +442,13 @@ declare module '@tanstack/solid-router' {
429442
preLoaderRoute: typeof EditingARouteImport
430443
parentRoute: typeof rootRouteImport
431444
}
445+
'/component-types-test': {
446+
id: '/component-types-test'
447+
path: '/component-types-test'
448+
fullPath: '/component-types-test'
449+
preLoaderRoute: typeof ComponentTypesTestRouteImport
450+
parentRoute: typeof rootRouteImport
451+
}
432452
'/anchor': {
433453
id: '/anchor'
434454
path: '/anchor'
@@ -720,6 +740,7 @@ const rootRouteChildren: RootRouteChildren = {
720740
SearchParamsRouteRoute: SearchParamsRouteRouteWithChildren,
721741
LayoutRoute: LayoutRouteWithChildren,
722742
AnchorRoute: AnchorRoute,
743+
ComponentTypesTestRoute: ComponentTypesTestRoute,
723744
EditingARoute: EditingARoute,
724745
EditingBRoute: EditingBRoute,
725746
PostsRoute: PostsRouteWithChildren,
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { createFileRoute } from '@tanstack/solid-router'
2+
3+
// Test route for type safety of different component types
4+
// This test should
5+
// - check that `false` can be set on the `errorComponent`
6+
// ...without causing build-time TypeScript errors
7+
export const Route = createFileRoute('/component-types-test')({
8+
component: () => <div>Testing test types</div>,
9+
errorComponent: false, // Should not cause TypeScript error
10+
pendingComponent: undefined, // Should not cause TypeScript error
11+
notFoundComponent: undefined, // Should not cause TypeScript error
12+
})

packages/react-router/src/route.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ import type { LinkComponentRoute } from './link'
4949
declare module '@tanstack/router-core' {
5050
export interface UpdatableRouteOptionsExtensions {
5151
component?: RouteComponent
52-
errorComponent?: false | null | ErrorRouteComponent
52+
errorComponent?: false | null | undefined | ErrorRouteComponent
5353
notFoundComponent?: NotFoundRouteComponent
5454
pendingComponent?: RouteComponent
5555
}

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

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -209,12 +209,14 @@ export function compileCodeSplitReferenceRoute(
209209
return
210210
}
211211

212-
// Exit early if the value is undefined
213-
// Since we don't need to run an import just to get the value of `undefined`
214-
// This is useful for cases like: `createFileRoute('/')({ component: undefined })`
212+
// Exit early if the value is a boolean, null, or undefined.
213+
// These values mean "don't use this component, fallback to parent"
214+
// No code splitting needed to preserve fallback behavior
215215
if (
216-
t.isIdentifier(prop.value) &&
217-
prop.value.name === 'undefined'
216+
t.isBooleanLiteral(prop.value) ||
217+
t.isNullLiteral(prop.value) ||
218+
(t.isIdentifier(prop.value) &&
219+
prop.value.name === 'undefined')
218220
) {
219221
return
220222
}
@@ -652,6 +654,14 @@ export function compileCodeSplitVirtualRoute(
652654
),
653655
]),
654656
)
657+
} else if (t.isBooleanLiteral(splitNode)) {
658+
// Handle boolean literals
659+
// This exits early here, since this value will be kept in the reference file
660+
return
661+
} else if (t.isNullLiteral(splitNode)) {
662+
// Handle null literals
663+
// This exits early here, since this value will be kept in the reference file
664+
return
655665
} else {
656666
console.info('Unexpected splitNode type:', splitNode)
657667
throw new Error(`Unexpected splitNode type ☝️: ${splitNode.type}`)
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
const $$splitComponentImporter = () => import('boolean-null-literals.tsx?tsr-split=component');
2+
import { lazyRouteComponent } from '@tanstack/react-router';
3+
import { createFileRoute } from '@tanstack/router';
4+
5+
// Test errorComponent with false literal
6+
export const Route = createFileRoute('/test')({
7+
component: lazyRouteComponent($$splitComponentImporter, 'component'),
8+
errorComponent: false,
9+
pendingComponent: null,
10+
loader: async () => ({
11+
data: 'test'
12+
})
13+
});
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// Test errorComponent with false literal
2+
import { Route } from "boolean-null-literals.tsx";
3+
const SplitComponent = () => <div>Test Component</div>;
4+
export { SplitComponent as component };
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// Test errorComponent with false literal
2+
import { Route } from "boolean-null-literals.tsx";
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// Test errorComponent with false literal
2+
import { Route } from "boolean-null-literals.tsx";

0 commit comments

Comments
 (0)