@bleedingdev/modern-js-plugin-tanstack 3.2.0-ultramodern.9 → 3.2.0-ultramodern.90
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/cli/index.js +12 -0
- package/dist/cjs/cli/routeSplitting.js +83 -0
- package/dist/cjs/cli/tanstackTypes.js +146 -58
- package/dist/cjs/runtime/index.js +38 -6
- package/dist/cjs/runtime/plugin.js +6 -5
- package/dist/cjs/runtime/plugin.node.js +27 -10
- package/dist/cjs/runtime/plugin.worker.js +49 -0
- package/dist/cjs/runtime/routeTree.js +55 -4
- package/dist/cjs/runtime/types.js +27 -1
- package/dist/esm/cli/index.mjs +4 -1
- package/dist/esm/cli/routeSplitting.mjs +43 -0
- package/dist/esm/cli/tanstackTypes.mjs +146 -58
- package/dist/esm/runtime/index.mjs +2 -1
- package/dist/esm/runtime/plugin.mjs +10 -9
- package/dist/esm/runtime/plugin.node.mjs +28 -11
- package/dist/esm/runtime/plugin.worker.mjs +1 -0
- package/dist/esm/runtime/routeTree.mjs +55 -4
- package/dist/esm/runtime/types.mjs +7 -0
- package/dist/esm-node/cli/index.mjs +4 -1
- package/dist/esm-node/cli/routeSplitting.mjs +44 -0
- package/dist/esm-node/cli/tanstackTypes.mjs +146 -58
- package/dist/esm-node/runtime/index.mjs +2 -1
- package/dist/esm-node/runtime/plugin.mjs +10 -9
- package/dist/esm-node/runtime/plugin.node.mjs +28 -11
- package/dist/esm-node/runtime/plugin.worker.mjs +2 -0
- package/dist/esm-node/runtime/routeTree.mjs +55 -4
- package/dist/esm-node/runtime/types.mjs +7 -0
- package/dist/types/cli/index.d.ts +4 -0
- package/dist/types/cli/routeSplitting.d.ts +29 -0
- package/dist/types/runtime/index.d.ts +3 -1
- package/dist/types/runtime/plugin.d.ts +1 -1
- package/dist/types/runtime/plugin.node.d.ts +1 -1
- package/dist/types/runtime/plugin.worker.d.ts +1 -0
- package/dist/types/runtime/types.d.ts +7 -0
- package/package.json +14 -14
- package/src/cli/index.ts +17 -0
- package/src/cli/routeSplitting.ts +81 -0
- package/src/cli/tanstackTypes.ts +216 -67
- package/src/runtime/index.tsx +13 -1
- package/src/runtime/plugin.node.tsx +54 -7
- package/src/runtime/plugin.tsx +8 -5
- package/src/runtime/plugin.worker.tsx +4 -0
- package/src/runtime/routeTree.ts +125 -8
- package/src/runtime/types.ts +13 -0
- package/tests/router/cli.test.ts +239 -0
- package/tests/router/fastDefaults.test.ts +25 -0
- package/tests/router/routeTree.test.ts +193 -1
- package/tests/router/tanstackTypes.test.ts +184 -0
package/src/runtime/routeTree.ts
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
notFound,
|
|
13
13
|
redirect,
|
|
14
14
|
} from '@tanstack/react-router';
|
|
15
|
+
import { createElement, type ElementType } from 'react';
|
|
15
16
|
import { DefaultNotFound } from './DefaultNotFound';
|
|
16
17
|
import {
|
|
17
18
|
isTanstackRscPayloadNavigationEnabled,
|
|
@@ -75,8 +76,10 @@ type ModernRouteObject = RouteObject & {
|
|
|
75
76
|
isClientComponent?: boolean;
|
|
76
77
|
lazyImport?: () => unknown;
|
|
77
78
|
loader?: ModernLoader;
|
|
79
|
+
loaderDeps?: unknown;
|
|
78
80
|
pendingComponent?: unknown;
|
|
79
81
|
shouldRevalidate?: ModernShouldRevalidate;
|
|
82
|
+
validateSearch?: unknown;
|
|
80
83
|
};
|
|
81
84
|
|
|
82
85
|
type ModernGeneratedRoute = (NestedRoute | PageRoute) & {
|
|
@@ -101,10 +104,12 @@ type ModernGeneratedRoute = (NestedRoute | PageRoute) & {
|
|
|
101
104
|
isRoot?: boolean;
|
|
102
105
|
lazyImport?: () => unknown;
|
|
103
106
|
loader?: ModernLoader;
|
|
107
|
+
loaderDeps?: unknown;
|
|
104
108
|
loading?: unknown;
|
|
105
109
|
pendingComponent?: unknown;
|
|
106
110
|
path?: string;
|
|
107
111
|
shouldRevalidate?: ModernShouldRevalidate;
|
|
112
|
+
validateSearch?: unknown;
|
|
108
113
|
};
|
|
109
114
|
|
|
110
115
|
type MutableTanstackRoute = AnyRoute & {
|
|
@@ -118,6 +123,15 @@ type ModernDeferredDataLike = {
|
|
|
118
123
|
__modern_deferred?: unknown;
|
|
119
124
|
data?: unknown;
|
|
120
125
|
};
|
|
126
|
+
type ModernRouteModule = {
|
|
127
|
+
Component?: unknown;
|
|
128
|
+
default?: unknown;
|
|
129
|
+
};
|
|
130
|
+
type PreloadableComponent = {
|
|
131
|
+
(props: Record<string, unknown>): ReturnType<typeof createElement>;
|
|
132
|
+
load?: () => Promise<unknown>;
|
|
133
|
+
preload?: () => Promise<unknown>;
|
|
134
|
+
};
|
|
121
135
|
type RouteTreeOptions = {
|
|
122
136
|
rscPayloadRouter?: boolean;
|
|
123
137
|
};
|
|
@@ -219,6 +233,76 @@ function normalizeModernLoaderResponse(result: unknown): unknown {
|
|
|
219
233
|
return normalizeModernLoaderResult(result);
|
|
220
234
|
}
|
|
221
235
|
|
|
236
|
+
function pickRouteModuleComponent(
|
|
237
|
+
routeModule: unknown,
|
|
238
|
+
seen: Set<unknown> = new Set(),
|
|
239
|
+
): ElementType<Record<string, unknown>> | undefined {
|
|
240
|
+
if (
|
|
241
|
+
typeof routeModule === 'function' ||
|
|
242
|
+
(routeModule &&
|
|
243
|
+
typeof routeModule === 'object' &&
|
|
244
|
+
'$$typeof' in routeModule)
|
|
245
|
+
) {
|
|
246
|
+
return routeModule as ElementType<Record<string, unknown>>;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (!routeModule || typeof routeModule !== 'object') {
|
|
250
|
+
return undefined;
|
|
251
|
+
}
|
|
252
|
+
if (seen.has(routeModule)) {
|
|
253
|
+
return undefined;
|
|
254
|
+
}
|
|
255
|
+
seen.add(routeModule);
|
|
256
|
+
|
|
257
|
+
const module = routeModule as ModernRouteModule;
|
|
258
|
+
for (const candidate of [module.default, module.Component]) {
|
|
259
|
+
const component = pickRouteModuleComponent(candidate, seen);
|
|
260
|
+
if (component) {
|
|
261
|
+
return component;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return undefined;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function createServerLazyImportComponent(
|
|
269
|
+
lazyImport: () => unknown,
|
|
270
|
+
fallbackComponent?: unknown,
|
|
271
|
+
): PreloadableComponent | unknown {
|
|
272
|
+
if (typeof document !== 'undefined') {
|
|
273
|
+
return fallbackComponent;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
let resolvedComponent: ElementType<Record<string, unknown>> | undefined;
|
|
277
|
+
let pendingLoad: Promise<unknown> | undefined;
|
|
278
|
+
|
|
279
|
+
const load = async () => {
|
|
280
|
+
if (resolvedComponent) {
|
|
281
|
+
return resolvedComponent;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const routeModule = await lazyImport();
|
|
285
|
+
const component = pickRouteModuleComponent(routeModule);
|
|
286
|
+
if (component) {
|
|
287
|
+
resolvedComponent = component;
|
|
288
|
+
}
|
|
289
|
+
return resolvedComponent;
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
const Component: PreloadableComponent = props => {
|
|
293
|
+
if (resolvedComponent) {
|
|
294
|
+
return createElement(resolvedComponent, props);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
pendingLoad ||= load();
|
|
298
|
+
throw pendingLoad;
|
|
299
|
+
};
|
|
300
|
+
Component.load = load;
|
|
301
|
+
Component.preload = load;
|
|
302
|
+
|
|
303
|
+
return Component;
|
|
304
|
+
}
|
|
305
|
+
|
|
222
306
|
function isAbsoluteUrl(value: string) {
|
|
223
307
|
try {
|
|
224
308
|
void new URL(value);
|
|
@@ -352,9 +436,10 @@ function wrapModernLoader(
|
|
|
352
436
|
ctx?.location?.url?.href ||
|
|
353
437
|
'';
|
|
354
438
|
|
|
355
|
-
const request =
|
|
356
|
-
|
|
357
|
-
|
|
439
|
+
const request =
|
|
440
|
+
baseRequest !== undefined
|
|
441
|
+
? new Request(baseRequest, { signal })
|
|
442
|
+
: createModernRequest(href, signal);
|
|
358
443
|
const params = mapParamsForModernLoader({
|
|
359
444
|
modernRoute,
|
|
360
445
|
params: ctx.params || {},
|
|
@@ -468,9 +553,10 @@ function wrapRouteObjectLoader(
|
|
|
468
553
|
ctx?.location?.url?.href ||
|
|
469
554
|
'';
|
|
470
555
|
|
|
471
|
-
const request =
|
|
472
|
-
|
|
473
|
-
|
|
556
|
+
const request =
|
|
557
|
+
baseRequest !== undefined
|
|
558
|
+
? new Request(baseRequest, { signal })
|
|
559
|
+
: createModernRequest(href, signal);
|
|
474
560
|
|
|
475
561
|
const params = mapParamsForRouteObjectLoader({
|
|
476
562
|
route,
|
|
@@ -519,6 +605,18 @@ function wrapRouteObjectLoader(
|
|
|
519
605
|
|
|
520
606
|
function toRouteComponent(routeObject: RouteObject): unknown {
|
|
521
607
|
const route = routeObject as ModernRouteObject;
|
|
608
|
+
const lazyImport =
|
|
609
|
+
typeof route.lazyImport === 'function' ? route.lazyImport : undefined;
|
|
610
|
+
const fallbackComponent = route.Component
|
|
611
|
+
? route.Component
|
|
612
|
+
: route.element
|
|
613
|
+
? () => route.element
|
|
614
|
+
: undefined;
|
|
615
|
+
|
|
616
|
+
if (lazyImport && fallbackComponent) {
|
|
617
|
+
return createServerLazyImportComponent(lazyImport, fallbackComponent);
|
|
618
|
+
}
|
|
619
|
+
|
|
522
620
|
if (route.Component) {
|
|
523
621
|
return route.Component;
|
|
524
622
|
}
|
|
@@ -529,6 +627,15 @@ function toRouteComponent(routeObject: RouteObject): unknown {
|
|
|
529
627
|
return undefined;
|
|
530
628
|
}
|
|
531
629
|
|
|
630
|
+
function toModernRouteComponent(route: ModernGeneratedRoute): unknown {
|
|
631
|
+
const component = route.component || undefined;
|
|
632
|
+
if (typeof route.lazyImport === 'function' && component) {
|
|
633
|
+
return createServerLazyImportComponent(route.lazyImport, component);
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
return component;
|
|
637
|
+
}
|
|
638
|
+
|
|
532
639
|
function toErrorComponent(routeObject: RouteObject): unknown {
|
|
533
640
|
const route = routeObject as ModernRouteObject;
|
|
534
641
|
if (route.ErrorBoundary) {
|
|
@@ -632,6 +739,8 @@ function createRouteFromRouteObject(opts: {
|
|
|
632
739
|
component: toRouteComponent(routeObject),
|
|
633
740
|
pendingComponent: toPendingComponent(routeObject),
|
|
634
741
|
errorComponent: toErrorComponent(routeObject),
|
|
742
|
+
validateSearch: modernRouteObject.validateSearch,
|
|
743
|
+
loaderDeps: modernRouteObject.loaderDeps,
|
|
635
744
|
wrapInSuspense: true,
|
|
636
745
|
staticData: createRouteStaticData({
|
|
637
746
|
modernRouteId: routeObject.id,
|
|
@@ -702,7 +811,7 @@ function createRouteFromModernRoute(opts: {
|
|
|
702
811
|
|
|
703
812
|
const pendingComponent = route.loading || route.pendingComponent;
|
|
704
813
|
const errorComponent = route.error || route.errorComponent;
|
|
705
|
-
const component = route
|
|
814
|
+
const component = toModernRouteComponent(route);
|
|
706
815
|
const modernLoader = route.loader;
|
|
707
816
|
const modernAction = route.action;
|
|
708
817
|
const modernShouldRevalidate = route.shouldRevalidate;
|
|
@@ -724,6 +833,8 @@ function createRouteFromModernRoute(opts: {
|
|
|
724
833
|
component: component || undefined,
|
|
725
834
|
pendingComponent: pendingComponent || undefined,
|
|
726
835
|
errorComponent: errorComponent || undefined,
|
|
836
|
+
validateSearch: route.validateSearch,
|
|
837
|
+
loaderDeps: route.loaderDeps,
|
|
727
838
|
wrapInSuspense: true,
|
|
728
839
|
staticData: createRouteStaticData({
|
|
729
840
|
modernRouteId: modernId,
|
|
@@ -788,7 +899,9 @@ export function createRouteTreeFromModernRoutes(
|
|
|
788
899
|
(r as ModernGeneratedRoute).isRoot,
|
|
789
900
|
) as ModernGeneratedRoute | undefined;
|
|
790
901
|
|
|
791
|
-
const rootComponent = rootModern
|
|
902
|
+
const rootComponent = rootModern
|
|
903
|
+
? toModernRouteComponent(rootModern)
|
|
904
|
+
: undefined;
|
|
792
905
|
const pendingComponent = rootModern?.loading;
|
|
793
906
|
const errorComponent = rootModern?.error;
|
|
794
907
|
const rootLoader = rootModern?.loader;
|
|
@@ -805,6 +918,8 @@ export function createRouteTreeFromModernRoutes(
|
|
|
805
918
|
component: rootComponent || undefined,
|
|
806
919
|
pendingComponent: pendingComponent || undefined,
|
|
807
920
|
errorComponent: errorComponent || undefined,
|
|
921
|
+
validateSearch: rootModern?.validateSearch,
|
|
922
|
+
loaderDeps: rootModern?.loaderDeps,
|
|
808
923
|
wrapInSuspense: true,
|
|
809
924
|
notFoundComponent: DefaultNotFound,
|
|
810
925
|
staticData: createRouteStaticData({
|
|
@@ -876,6 +991,8 @@ export function createRouteTreeFromRouteObjects(
|
|
|
876
991
|
? toPendingComponent(rootLikeRoute)
|
|
877
992
|
: undefined,
|
|
878
993
|
errorComponent: rootLikeRoute ? toErrorComponent(rootLikeRoute) : undefined,
|
|
994
|
+
validateSearch: rootLikeRoute?.validateSearch,
|
|
995
|
+
loaderDeps: rootLikeRoute?.loaderDeps,
|
|
879
996
|
wrapInSuspense: true,
|
|
880
997
|
notFoundComponent: DefaultNotFound,
|
|
881
998
|
staticData: createRouteStaticData({
|
package/src/runtime/types.ts
CHANGED
|
@@ -20,9 +20,22 @@ export type RouterConfig = {
|
|
|
20
20
|
future?: Partial<{
|
|
21
21
|
v7_startTransition: boolean;
|
|
22
22
|
}>;
|
|
23
|
+
defaultStructuralSharing?: boolean;
|
|
23
24
|
unstable_reloadOnURLMismatch?: boolean;
|
|
24
25
|
};
|
|
25
26
|
|
|
27
|
+
export const modernTanstackRouterFastDefaults = {
|
|
28
|
+
defaultStructuralSharing: true,
|
|
29
|
+
} as const;
|
|
30
|
+
|
|
31
|
+
export const getModernTanstackRouterFastDefaults = (
|
|
32
|
+
config: Partial<Pick<RouterConfig, 'defaultStructuralSharing'>> = {},
|
|
33
|
+
) => ({
|
|
34
|
+
defaultStructuralSharing:
|
|
35
|
+
config.defaultStructuralSharing ??
|
|
36
|
+
modernTanstackRouterFastDefaults.defaultStructuralSharing,
|
|
37
|
+
});
|
|
38
|
+
|
|
26
39
|
export interface RouterRouteMatchSnapshot {
|
|
27
40
|
routeId: string;
|
|
28
41
|
assetRouteId?: string;
|
package/tests/router/cli.test.ts
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { mkdir, mkdtemp, readFile, rm } from 'node:fs/promises';
|
|
2
2
|
import { tmpdir } from 'node:os';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
+
import { mergeConfig } from '@modern-js/plugin/cli';
|
|
4
5
|
import type { Entrypoint } from '@modern-js/types';
|
|
5
6
|
import { fs, NESTED_ROUTE_SPEC_FILE } from '@modern-js/utils';
|
|
6
7
|
import {
|
|
8
|
+
createTanstackRsbuildRouteSplittingProfile,
|
|
9
|
+
isTanstackStartRouteModuleSource,
|
|
7
10
|
tanstackRouterPlugin,
|
|
8
11
|
writeTanstackRegisterFile,
|
|
9
12
|
writeTanstackRouterTypesForEntries,
|
|
@@ -193,6 +196,12 @@ describe('tanstack router cli plugin', () => {
|
|
|
193
196
|
},
|
|
194
197
|
]);
|
|
195
198
|
|
|
199
|
+
expect(taps.config()).toMatchObject({
|
|
200
|
+
output: {
|
|
201
|
+
splitRouteChunks: true,
|
|
202
|
+
},
|
|
203
|
+
});
|
|
204
|
+
|
|
196
205
|
const specPath = path.join(distDirectory, NESTED_ROUTE_SPEC_FILE);
|
|
197
206
|
await fs.outputJSON(specPath, {
|
|
198
207
|
existing: [{ id: 'keep-me' }],
|
|
@@ -383,4 +392,234 @@ describe('tanstack router cli plugin', () => {
|
|
|
383
392
|
}),
|
|
384
393
|
);
|
|
385
394
|
});
|
|
395
|
+
|
|
396
|
+
test('can opt out of Modern-owned route code splitting', async () => {
|
|
397
|
+
const taps: Record<string, any> = {};
|
|
398
|
+
const api = {
|
|
399
|
+
getAppContext: () => ({
|
|
400
|
+
srcDirectory: '/tmp/app/src',
|
|
401
|
+
serverRoutes: [],
|
|
402
|
+
}),
|
|
403
|
+
_internalRuntimePlugins: () => {},
|
|
404
|
+
checkEntryPoint: () => {},
|
|
405
|
+
config: (tap: any) => {
|
|
406
|
+
taps.config = tap;
|
|
407
|
+
},
|
|
408
|
+
modifyEntrypoints: () => {},
|
|
409
|
+
generateEntryCode: () => {},
|
|
410
|
+
onFileChanged: () => {},
|
|
411
|
+
modifyFileSystemRoutes: () => {},
|
|
412
|
+
onBeforeGenerateRoutes: () => {},
|
|
413
|
+
};
|
|
414
|
+
|
|
415
|
+
tanstackRouterPlugin({ routeCodeSplitting: false }).setup!(api as any);
|
|
416
|
+
|
|
417
|
+
expect(taps.config()).toMatchObject({
|
|
418
|
+
output: {
|
|
419
|
+
splitRouteChunks: false,
|
|
420
|
+
},
|
|
421
|
+
});
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
test('documents why TanStack Start Rspack splitter is not registered for Modern routes', () => {
|
|
425
|
+
const profile = createTanstackRsbuildRouteSplittingProfile({});
|
|
426
|
+
|
|
427
|
+
expect(profile).toMatchObject({
|
|
428
|
+
defaultConfig: {
|
|
429
|
+
output: {
|
|
430
|
+
splitRouteChunks: true,
|
|
431
|
+
},
|
|
432
|
+
},
|
|
433
|
+
modernRouteChunks: {
|
|
434
|
+
enabled: true,
|
|
435
|
+
owner: 'modern',
|
|
436
|
+
},
|
|
437
|
+
builderChunkSplit: {
|
|
438
|
+
owner: 'modern-rsbuild',
|
|
439
|
+
preserved: true,
|
|
440
|
+
},
|
|
441
|
+
tanstackStartRspackSplitter: {
|
|
442
|
+
compatible: false,
|
|
443
|
+
clientDeleteNodes: ['ssr', 'server', 'headers'],
|
|
444
|
+
},
|
|
445
|
+
});
|
|
446
|
+
expect(
|
|
447
|
+
isTanstackStartRouteModuleSource(
|
|
448
|
+
"export const Route = createFileRoute('/dashboard')({ component })",
|
|
449
|
+
),
|
|
450
|
+
).toBe(true);
|
|
451
|
+
expect(
|
|
452
|
+
isTanstackStartRouteModuleSource(
|
|
453
|
+
'export const route = createRoute({ getParentRoute, path })',
|
|
454
|
+
),
|
|
455
|
+
).toBe(false);
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
test('preserves user-selected route and builder chunk splitting modes', () => {
|
|
459
|
+
const pluginDefaults = createTanstackRsbuildRouteSplittingProfile(
|
|
460
|
+
{},
|
|
461
|
+
).defaultConfig;
|
|
462
|
+
const chunkSplits = [
|
|
463
|
+
{ strategy: 'split-by-module' },
|
|
464
|
+
{ strategy: 'split-by-experience' },
|
|
465
|
+
{ strategy: 'all-in-one' },
|
|
466
|
+
{ strategy: 'single-vendor' },
|
|
467
|
+
{ strategy: 'split-by-size', minSize: 10_000, maxSize: 60_000 },
|
|
468
|
+
{
|
|
469
|
+
strategy: 'custom',
|
|
470
|
+
splitChunks: {
|
|
471
|
+
chunks: 'all',
|
|
472
|
+
cacheGroups: {
|
|
473
|
+
tractors: {
|
|
474
|
+
name: 'tractors',
|
|
475
|
+
test: /tractors/u,
|
|
476
|
+
},
|
|
477
|
+
},
|
|
478
|
+
},
|
|
479
|
+
},
|
|
480
|
+
];
|
|
481
|
+
|
|
482
|
+
for (const chunkSplit of chunkSplits) {
|
|
483
|
+
expect(
|
|
484
|
+
mergeConfig([
|
|
485
|
+
pluginDefaults,
|
|
486
|
+
{
|
|
487
|
+
output: {
|
|
488
|
+
splitRouteChunks: false,
|
|
489
|
+
},
|
|
490
|
+
performance: {
|
|
491
|
+
chunkSplit,
|
|
492
|
+
},
|
|
493
|
+
splitChunks: false,
|
|
494
|
+
},
|
|
495
|
+
]),
|
|
496
|
+
).toMatchObject({
|
|
497
|
+
output: {
|
|
498
|
+
splitRouteChunks: false,
|
|
499
|
+
},
|
|
500
|
+
performance: {
|
|
501
|
+
chunkSplit,
|
|
502
|
+
},
|
|
503
|
+
splitChunks: false,
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
const pageSplitWithManualAsyncChunks = mergeConfig([
|
|
508
|
+
pluginDefaults,
|
|
509
|
+
{
|
|
510
|
+
performance: {
|
|
511
|
+
chunkSplit: {
|
|
512
|
+
strategy: 'custom',
|
|
513
|
+
splitChunks: {
|
|
514
|
+
chunks: 'async',
|
|
515
|
+
},
|
|
516
|
+
},
|
|
517
|
+
},
|
|
518
|
+
},
|
|
519
|
+
]);
|
|
520
|
+
|
|
521
|
+
expect(pageSplitWithManualAsyncChunks).toMatchObject({
|
|
522
|
+
output: {
|
|
523
|
+
splitRouteChunks: true,
|
|
524
|
+
},
|
|
525
|
+
performance: {
|
|
526
|
+
chunkSplit: {
|
|
527
|
+
strategy: 'custom',
|
|
528
|
+
splitChunks: {
|
|
529
|
+
chunks: 'async',
|
|
530
|
+
},
|
|
531
|
+
},
|
|
532
|
+
},
|
|
533
|
+
});
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
test('keeps custom cache group details intact', () => {
|
|
537
|
+
const pluginDefaults = createTanstackRsbuildRouteSplittingProfile(
|
|
538
|
+
{},
|
|
539
|
+
).defaultConfig;
|
|
540
|
+
|
|
541
|
+
const mergedConfig = mergeConfig([
|
|
542
|
+
pluginDefaults,
|
|
543
|
+
{
|
|
544
|
+
performance: {
|
|
545
|
+
chunkSplit: {
|
|
546
|
+
strategy: 'custom',
|
|
547
|
+
splitChunks: {
|
|
548
|
+
chunks: 'all',
|
|
549
|
+
cacheGroups: {
|
|
550
|
+
tractors: {
|
|
551
|
+
name: 'tractors',
|
|
552
|
+
test: /tractors/u,
|
|
553
|
+
},
|
|
554
|
+
},
|
|
555
|
+
},
|
|
556
|
+
},
|
|
557
|
+
},
|
|
558
|
+
},
|
|
559
|
+
]);
|
|
560
|
+
|
|
561
|
+
expect(
|
|
562
|
+
(
|
|
563
|
+
mergedConfig as {
|
|
564
|
+
performance?: {
|
|
565
|
+
chunkSplit?: {
|
|
566
|
+
splitChunks?: {
|
|
567
|
+
cacheGroups?: {
|
|
568
|
+
tractors?: {
|
|
569
|
+
test?: RegExp;
|
|
570
|
+
};
|
|
571
|
+
};
|
|
572
|
+
};
|
|
573
|
+
};
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
).performance?.chunkSplit?.splitChunks?.cacheGroups?.tractors?.test,
|
|
577
|
+
).toEqual(/tractors/u);
|
|
578
|
+
expect(mergedConfig).toMatchObject({
|
|
579
|
+
output: {
|
|
580
|
+
splitRouteChunks: true,
|
|
581
|
+
},
|
|
582
|
+
performance: {
|
|
583
|
+
chunkSplit: {
|
|
584
|
+
strategy: 'custom',
|
|
585
|
+
splitChunks: {
|
|
586
|
+
chunks: 'all',
|
|
587
|
+
cacheGroups: {
|
|
588
|
+
tractors: {
|
|
589
|
+
name: 'tractors',
|
|
590
|
+
},
|
|
591
|
+
},
|
|
592
|
+
},
|
|
593
|
+
},
|
|
594
|
+
},
|
|
595
|
+
});
|
|
596
|
+
});
|
|
597
|
+
|
|
598
|
+
test('plugin opt-out can still combine with manual builder chunking', () => {
|
|
599
|
+
const pluginDefaults = createTanstackRsbuildRouteSplittingProfile({
|
|
600
|
+
routeCodeSplitting: false,
|
|
601
|
+
}).defaultConfig;
|
|
602
|
+
|
|
603
|
+
expect(
|
|
604
|
+
mergeConfig([
|
|
605
|
+
pluginDefaults,
|
|
606
|
+
{
|
|
607
|
+
performance: {
|
|
608
|
+
chunkSplit: {
|
|
609
|
+
strategy: 'single-vendor',
|
|
610
|
+
},
|
|
611
|
+
},
|
|
612
|
+
},
|
|
613
|
+
]),
|
|
614
|
+
).toMatchObject({
|
|
615
|
+
output: {
|
|
616
|
+
splitRouteChunks: false,
|
|
617
|
+
},
|
|
618
|
+
performance: {
|
|
619
|
+
chunkSplit: {
|
|
620
|
+
strategy: 'single-vendor',
|
|
621
|
+
},
|
|
622
|
+
},
|
|
623
|
+
});
|
|
624
|
+
});
|
|
386
625
|
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getModernTanstackRouterFastDefaults,
|
|
3
|
+
modernTanstackRouterFastDefaults,
|
|
4
|
+
} from '../../src/runtime/types';
|
|
5
|
+
|
|
6
|
+
describe('tanstack router fast defaults', () => {
|
|
7
|
+
test('enables structural sharing by default', () => {
|
|
8
|
+
expect(modernTanstackRouterFastDefaults).toEqual({
|
|
9
|
+
defaultStructuralSharing: true,
|
|
10
|
+
});
|
|
11
|
+
expect(getModernTanstackRouterFastDefaults()).toEqual({
|
|
12
|
+
defaultStructuralSharing: true,
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test('allows explicit structural sharing override', () => {
|
|
17
|
+
expect(
|
|
18
|
+
getModernTanstackRouterFastDefaults({
|
|
19
|
+
defaultStructuralSharing: false,
|
|
20
|
+
}),
|
|
21
|
+
).toEqual({
|
|
22
|
+
defaultStructuralSharing: false,
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
});
|