@bleedingdev/modern-js-plugin-tanstack 3.2.0-ultramodern.12 → 3.2.0-ultramodern.120

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.
Files changed (87) hide show
  1. package/dist/cjs/cli/index.js +47 -9
  2. package/dist/cjs/cli/routeSplitting.js +87 -0
  3. package/dist/cjs/cli/tanstackTypes.js +230 -63
  4. package/dist/cjs/cli.js +12 -8
  5. package/dist/cjs/runtime/DefaultNotFound.js +9 -5
  6. package/dist/cjs/runtime/basepathRewrite.js +12 -8
  7. package/dist/cjs/runtime/dataMutation.js +9 -5
  8. package/dist/cjs/runtime/hooks.js +9 -5
  9. package/dist/cjs/runtime/hydrationBoundary.js +48 -0
  10. package/dist/cjs/runtime/index.js +330 -74
  11. package/dist/cjs/runtime/lifecycle.js +15 -11
  12. package/dist/cjs/runtime/outlet.js +58 -0
  13. package/dist/cjs/runtime/plugin.js +203 -98
  14. package/dist/cjs/runtime/plugin.node.js +38 -16
  15. package/dist/cjs/runtime/plugin.worker.js +53 -0
  16. package/dist/cjs/runtime/prefetchLink.js +10 -6
  17. package/dist/cjs/runtime/routeTree.js +81 -17
  18. package/dist/cjs/runtime/rsc/ClientSlot.js +9 -5
  19. package/dist/cjs/runtime/rsc/CompositeComponent.js +9 -5
  20. package/dist/cjs/runtime/rsc/ReplayableStream.js +14 -9
  21. package/dist/cjs/runtime/rsc/RscNodeRenderer.js +9 -5
  22. package/dist/cjs/runtime/rsc/SlotContext.js +9 -5
  23. package/dist/cjs/runtime/rsc/client.js +9 -5
  24. package/dist/cjs/runtime/rsc/createRscProxy.js +9 -5
  25. package/dist/cjs/runtime/rsc/index.js +9 -5
  26. package/dist/cjs/runtime/rsc/payloadRouter.js +9 -5
  27. package/dist/cjs/runtime/rsc/server.js +9 -5
  28. package/dist/cjs/runtime/rsc/slotUsageSanitizer.js +9 -5
  29. package/dist/cjs/runtime/rsc/symbols.js +20 -15
  30. package/dist/cjs/runtime/types.js +31 -1
  31. package/dist/cjs/runtime/utils.js +9 -5
  32. package/dist/cjs/runtime.js +9 -5
  33. package/dist/esm/cli/index.mjs +28 -6
  34. package/dist/esm/cli/routeSplitting.mjs +43 -0
  35. package/dist/esm/cli/tanstackTypes.mjs +219 -59
  36. package/dist/esm/runtime/hydrationBoundary.mjs +10 -0
  37. package/dist/esm/runtime/index.mjs +3 -2
  38. package/dist/esm/runtime/outlet.mjs +17 -0
  39. package/dist/esm/runtime/plugin.mjs +197 -96
  40. package/dist/esm/runtime/plugin.node.mjs +30 -12
  41. package/dist/esm/runtime/plugin.worker.mjs +1 -0
  42. package/dist/esm/runtime/prefetchLink.mjs +1 -1
  43. package/dist/esm/runtime/routeTree.mjs +73 -13
  44. package/dist/esm/runtime/types.mjs +7 -0
  45. package/dist/esm-node/cli/index.mjs +28 -6
  46. package/dist/esm-node/cli/routeSplitting.mjs +44 -0
  47. package/dist/esm-node/cli/tanstackTypes.mjs +219 -59
  48. package/dist/esm-node/runtime/hydrationBoundary.mjs +11 -0
  49. package/dist/esm-node/runtime/index.mjs +3 -2
  50. package/dist/esm-node/runtime/outlet.mjs +18 -0
  51. package/dist/esm-node/runtime/plugin.mjs +197 -96
  52. package/dist/esm-node/runtime/plugin.node.mjs +30 -12
  53. package/dist/esm-node/runtime/plugin.worker.mjs +2 -0
  54. package/dist/esm-node/runtime/prefetchLink.mjs +1 -1
  55. package/dist/esm-node/runtime/routeTree.mjs +73 -13
  56. package/dist/esm-node/runtime/types.mjs +7 -0
  57. package/dist/types/cli/index.d.ts +7 -1
  58. package/dist/types/cli/routeSplitting.d.ts +29 -0
  59. package/dist/types/cli/tanstackTypes.d.ts +9 -0
  60. package/dist/types/runtime/hooks.d.ts +9 -24
  61. package/dist/types/runtime/hydrationBoundary.d.ts +2 -0
  62. package/dist/types/runtime/index.d.ts +5 -2
  63. package/dist/types/runtime/outlet.d.ts +2 -0
  64. package/dist/types/runtime/plugin.d.ts +1 -1
  65. package/dist/types/runtime/plugin.node.d.ts +1 -1
  66. package/dist/types/runtime/plugin.worker.d.ts +1 -0
  67. package/dist/types/runtime/types.d.ts +7 -0
  68. package/package.json +20 -20
  69. package/src/cli/index.ts +59 -2
  70. package/src/cli/routeSplitting.ts +81 -0
  71. package/src/cli/tanstackTypes.ts +347 -67
  72. package/src/runtime/hydrationBoundary.tsx +12 -0
  73. package/src/runtime/index.tsx +107 -2
  74. package/src/runtime/outlet.tsx +48 -0
  75. package/src/runtime/plugin.node.tsx +58 -8
  76. package/src/runtime/plugin.tsx +372 -157
  77. package/src/runtime/plugin.worker.tsx +4 -0
  78. package/src/runtime/prefetchLink.tsx +1 -1
  79. package/src/runtime/routeTree.ts +194 -23
  80. package/src/runtime/ssr-shim.d.ts +1 -3
  81. package/src/runtime/types.ts +13 -0
  82. package/tests/router/cli.test.ts +315 -0
  83. package/tests/router/fastDefaults.test.ts +25 -0
  84. package/tests/router/hydrationBoundary.test.tsx +23 -0
  85. package/tests/router/prefetchLink.test.tsx +43 -7
  86. package/tests/router/routeTree.test.ts +416 -1
  87. package/tests/router/tanstackTypes.test.ts +415 -1
@@ -1,6 +1,8 @@
1
1
  import "node:module";
2
- import { createRootRoute, createRoute, notFound, redirect } from "@tanstack/react-router";
2
+ import { createRootRoute, createRoute, notFound, redirect, rootRouteId } from "@tanstack/react-router";
3
+ import { createElement } from "react";
3
4
  import { DefaultNotFound } from "./DefaultNotFound.mjs";
5
+ import { withModernRouteMatchContext } from "./outlet.mjs";
4
6
  import { isTanstackRscPayloadNavigationEnabled, loadTanstackRscRouteData } from "./rsc/payloadRouter.mjs";
5
7
  function createTanstackRoute(options) {
6
8
  return createRoute(options);
@@ -8,6 +10,10 @@ function createTanstackRoute(options) {
8
10
  function createTanstackRootRoute(options) {
9
11
  return createRootRoute(options);
10
12
  }
13
+ function wrapRouteComponentWithModernContext(route, component, routeId) {
14
+ const routeMatchId = routeId || route.id;
15
+ if (component && routeMatchId) route.options.component = withModernRouteMatchContext(component, routeMatchId);
16
+ }
11
17
  function toTanstackPath(pathname) {
12
18
  return pathname.split('/').map((segment)=>{
13
19
  if (!segment) return segment;
@@ -55,6 +61,40 @@ function normalizeModernLoaderResponse(result) {
55
61
  }
56
62
  return normalizeModernLoaderResult(result);
57
63
  }
64
+ function pickRouteModuleComponent(routeModule, seen = new Set()) {
65
+ if ('function' == typeof routeModule || routeModule && 'object' == typeof routeModule && '$$typeof' in routeModule) return routeModule;
66
+ if (!routeModule || 'object' != typeof routeModule) return;
67
+ if (seen.has(routeModule)) return;
68
+ seen.add(routeModule);
69
+ const module = routeModule;
70
+ for (const candidate of [
71
+ module.default,
72
+ module.Component
73
+ ]){
74
+ const component = pickRouteModuleComponent(candidate, seen);
75
+ if (component) return component;
76
+ }
77
+ }
78
+ function createServerLazyImportComponent(lazyImport, fallbackComponent) {
79
+ if ("u" > typeof document) return fallbackComponent;
80
+ let resolvedComponent;
81
+ let pendingLoad;
82
+ const load = async ()=>{
83
+ if (resolvedComponent) return resolvedComponent;
84
+ const routeModule = await lazyImport();
85
+ const component = pickRouteModuleComponent(routeModule);
86
+ if (component) resolvedComponent = component;
87
+ return resolvedComponent;
88
+ };
89
+ const Component = (props)=>{
90
+ if (resolvedComponent) return createElement(resolvedComponent, props);
91
+ pendingLoad ||= load();
92
+ throw pendingLoad;
93
+ };
94
+ Component.load = load;
95
+ Component.preload = load;
96
+ return Component;
97
+ }
58
98
  function isAbsoluteUrl(value) {
59
99
  try {
60
100
  new URL(value);
@@ -127,7 +167,7 @@ function wrapModernLoader(modernRoute, modernLoader, revalidationState, options
127
167
  const signal = ctx?.abortController?.signal || ctx?.signal || new AbortController().signal;
128
168
  const baseRequest = ctx?.context?.request instanceof Request ? ctx.context.request : void 0;
129
169
  const href = 'string' == typeof ctx?.location ? ctx.location : ctx?.location?.publicHref || ctx?.location?.href || ctx?.location?.url?.href || '';
130
- const request = baseRequest ? new Request(baseRequest, {
170
+ const request = void 0 !== baseRequest ? new Request(baseRequest, {
131
171
  signal
132
172
  }) : createModernRequest(href, signal);
133
173
  const params = mapParamsForModernLoader({
@@ -192,7 +232,7 @@ function wrapRouteObjectLoader(route, revalidationState, options = {}) {
192
232
  const signal = ctx?.abortController?.signal || ctx?.signal || new AbortController().signal;
193
233
  const baseRequest = ctx?.context?.request instanceof Request ? ctx.context.request : void 0;
194
234
  const href = 'string' == typeof ctx?.location ? ctx.location : ctx?.location?.publicHref || ctx?.location?.href || ctx?.location?.url?.href || '';
195
- const request = baseRequest ? new Request(baseRequest, {
235
+ const request = void 0 !== baseRequest ? new Request(baseRequest, {
196
236
  signal
197
237
  }) : createModernRequest(href, signal);
198
238
  const params = mapParamsForRouteObjectLoader({
@@ -229,10 +269,18 @@ function wrapRouteObjectLoader(route, revalidationState, options = {}) {
229
269
  }
230
270
  function toRouteComponent(routeObject) {
231
271
  const route = routeObject;
272
+ const lazyImport = 'function' == typeof route.lazyImport ? route.lazyImport : void 0;
273
+ const fallbackComponent = route.Component ? route.Component : route.element ? ()=>route.element : void 0;
274
+ if (lazyImport && fallbackComponent) return createServerLazyImportComponent(lazyImport, fallbackComponent);
232
275
  if (route.Component) return route.Component;
233
276
  const element = route.element;
234
277
  if (element) return ()=>element;
235
278
  }
279
+ function toModernRouteComponent(route) {
280
+ const component = route.component || void 0;
281
+ if ('function' == typeof route.lazyImport && component) return createServerLazyImportComponent(route.lazyImport, component);
282
+ return component;
283
+ }
236
284
  function toErrorComponent(routeObject) {
237
285
  const route = routeObject;
238
286
  if (route.ErrorBoundary) return route.ErrorBoundary;
@@ -270,12 +318,14 @@ function createRouteFromRouteObject(opts) {
270
318
  const shouldRevalidate = modernRouteObject.shouldRevalidate;
271
319
  const shouldReload = createModernShouldReload(shouldRevalidate, revalidationState);
272
320
  const stableFallbackId = routeObject.id || modernRouteObject.file || routeObject.path || 'pathless';
321
+ const component = toRouteComponent(routeObject);
273
322
  const base = {
274
323
  getParentRoute: ()=>parent,
275
- component: toRouteComponent(routeObject),
324
+ component,
276
325
  pendingComponent: toPendingComponent(routeObject),
277
326
  errorComponent: toErrorComponent(routeObject),
278
- wrapInSuspense: true,
327
+ validateSearch: modernRouteObject.validateSearch,
328
+ loaderDeps: modernRouteObject.loaderDeps,
279
329
  staticData: createRouteStaticData({
280
330
  modernRouteId: routeObject.id,
281
331
  modernRouteAction: modernRouteObject.action,
@@ -294,6 +344,7 @@ function createRouteFromRouteObject(opts) {
294
344
  if (isRouteObjectPathlessLayout(routeObject)) base.id = stableFallbackId;
295
345
  else base.path = routeObject.index ? '/' : toTanstackPath(routeObject.path || '');
296
346
  const route = createTanstackRoute(base);
347
+ wrapRouteComponentWithModernContext(route, component, routeObject.id);
297
348
  const children = routeObject.children;
298
349
  if (children && children.length > 0) {
299
350
  const childRoutes = children.map((child)=>createRouteFromRouteObject({
@@ -313,7 +364,7 @@ function createRouteFromModernRoute(opts) {
313
364
  const stableFallbackId = modernId || route._component || route.filename || route.data || ('function' == typeof route.loader ? route.id : void 0);
314
365
  const pendingComponent = route.loading || route.pendingComponent;
315
366
  const errorComponent = route.error || route.errorComponent;
316
- const component = route.component;
367
+ const component = toModernRouteComponent(route);
317
368
  const modernLoader = route.loader;
318
369
  const modernAction = route.action;
319
370
  const modernShouldRevalidate = route.shouldRevalidate;
@@ -325,7 +376,8 @@ function createRouteFromModernRoute(opts) {
325
376
  component: component || void 0,
326
377
  pendingComponent: pendingComponent || void 0,
327
378
  errorComponent: errorComponent || void 0,
328
- wrapInSuspense: true,
379
+ validateSearch: route.validateSearch,
380
+ loaderDeps: route.loaderDeps,
329
381
  staticData: createRouteStaticData({
330
382
  modernRouteId: modernId,
331
383
  modernRouteAction: modernAction,
@@ -347,6 +399,7 @@ function createRouteFromModernRoute(opts) {
347
399
  base.path = isIndexRoute ? '/' : toTanstackPath(rawPath || '');
348
400
  }
349
401
  const tanstackRoute = createTanstackRoute(base);
402
+ wrapRouteComponentWithModernContext(tanstackRoute, component, modernId);
350
403
  const children = route.children;
351
404
  if (children && children.length > 0) {
352
405
  const childRoutes = children.map((child)=>createRouteFromModernRoute({
@@ -360,7 +413,7 @@ function createRouteFromModernRoute(opts) {
360
413
  }
361
414
  function createRouteTreeFromModernRoutes(routes, options = {}) {
362
415
  const rootModern = routes.find((r)=>r && 'nested' === r.type && r.isRoot);
363
- const rootComponent = rootModern?.component;
416
+ const rootComponent = rootModern ? toModernRouteComponent(rootModern) : void 0;
364
417
  const pendingComponent = rootModern?.loading;
365
418
  const errorComponent = rootModern?.error;
366
419
  const rootLoader = rootModern?.loader;
@@ -373,7 +426,8 @@ function createRouteTreeFromModernRoutes(routes, options = {}) {
373
426
  component: rootComponent || void 0,
374
427
  pendingComponent: pendingComponent || void 0,
375
428
  errorComponent: errorComponent || void 0,
376
- wrapInSuspense: true,
429
+ validateSearch: rootModern?.validateSearch,
430
+ loaderDeps: rootModern?.loaderDeps,
377
431
  notFoundComponent: DefaultNotFound,
378
432
  staticData: createRouteStaticData({
379
433
  modernRouteId: rootModernId,
@@ -391,6 +445,7 @@ function createRouteTreeFromModernRoutes(routes, options = {}) {
391
445
  if (rootShouldReload) rootRouteOptions.shouldReload = rootShouldReload;
392
446
  if (rootModern?.inValidSSRRoute) rootRouteOptions.ssr = false;
393
447
  const rootRoute = createTanstackRootRoute(rootRouteOptions);
448
+ if (rootComponent) rootRoute.options.component = withModernRouteMatchContext(rootComponent, rootRouteId);
394
449
  const topLevel = rootModern ? rootModern.children || [] : routes;
395
450
  const childRoutes = topLevel.map((child)=>createRouteFromModernRoute({
396
451
  options,
@@ -408,11 +463,13 @@ function createRouteTreeFromRouteObjects(routes, options = {}) {
408
463
  const rootRevalidationState = {};
409
464
  const rootShouldRevalidate = rootLikeRoute?.shouldRevalidate;
410
465
  const rootShouldReload = createModernShouldReload(rootShouldRevalidate, rootRevalidationState);
466
+ const rootComponent = rootLikeRoute ? toRouteComponent(rootLikeRoute) : void 0;
411
467
  const rootRouteOptions = {
412
- component: rootLikeRoute ? toRouteComponent(rootLikeRoute) : void 0,
468
+ component: rootComponent,
413
469
  pendingComponent: rootLikeRoute ? toPendingComponent(rootLikeRoute) : void 0,
414
470
  errorComponent: rootLikeRoute ? toErrorComponent(rootLikeRoute) : void 0,
415
- wrapInSuspense: true,
471
+ validateSearch: rootLikeRoute?.validateSearch,
472
+ loaderDeps: rootLikeRoute?.loaderDeps,
416
473
  notFoundComponent: DefaultNotFound,
417
474
  staticData: createRouteStaticData({
418
475
  modernRouteId: rootLikeRoute?.id,
@@ -430,6 +487,7 @@ function createRouteTreeFromRouteObjects(routes, options = {}) {
430
487
  if (rootShouldReload) rootRouteOptions.shouldReload = rootShouldReload;
431
488
  if (rootLikeRoute?.inValidSSRRoute) rootRouteOptions.ssr = false;
432
489
  const rootRoute = createTanstackRootRoute(rootRouteOptions);
490
+ if (rootComponent) rootRoute.options.component = withModernRouteMatchContext(rootComponent, rootRouteId);
433
491
  const topLevel = rootLikeRoute ? [
434
492
  ...rootLikeRoute.children || [],
435
493
  ...routes.filter((route)=>route !== rootLikeRoute)
@@ -444,9 +502,11 @@ function createRouteTreeFromRouteObjects(routes, options = {}) {
444
502
  }
445
503
  function getModernRouteIdsFromMatches(router) {
446
504
  const matches = router.state.matches || [];
505
+ const routesById = router.routesById;
447
506
  const ids = matches.map((match)=>{
448
- const route = match.route;
449
- return route?.options?.staticData?.modernRouteId;
507
+ const normalizedMatch = match;
508
+ const routeId = 'string' == typeof normalizedMatch.routeId ? normalizedMatch.routeId : void 0;
509
+ return normalizedMatch.route?.options?.staticData?.modernRouteId ?? (routeId ? routesById?.[routeId]?.options?.staticData?.modernRouteId : void 0);
450
510
  }).filter((id)=>'string' == typeof id);
451
511
  return Array.from(new Set(ids));
452
512
  }
@@ -1 +1,8 @@
1
1
  import "node:module";
2
+ const modernTanstackRouterFastDefaults = {
3
+ defaultStructuralSharing: true
4
+ };
5
+ const getModernTanstackRouterFastDefaults = (config = {})=>({
6
+ defaultStructuralSharing: config.defaultStructuralSharing ?? modernTanstackRouterFastDefaults.defaultStructuralSharing
7
+ });
8
+ export { getModernTanstackRouterFastDefaults, modernTanstackRouterFastDefaults };
@@ -1,15 +1,21 @@
1
1
  import type { AppTools, AppToolsContext, CliPlugin } from '@modern-js/app-tools';
2
2
  import type { NestedRouteForCli, PageRoute } from '@modern-js/types';
3
- export { generateTanstackRouterTypesSourceForEntry, isTanstackRouterFrameworkEnabled, } from './tanstackTypes';
3
+ import { type TanstackRouteCodeSplittingOption } from './routeSplitting';
4
+ export type { TanstackRouteCodeSplittingOption, TanstackRsbuildRouteSplittingProfile, } from './routeSplitting';
5
+ export { createTanstackRsbuildRouteSplittingProfile, isTanstackStartRouteModuleSource, resolveTanstackRouteCodeSplittingEnabled, } from './routeSplitting';
6
+ export { collectCanonicalRoutesForEntry, generateTanstackRouterTypesSourceForEntry, isTanstackRouterFrameworkEnabled, } from './tanstackTypes';
4
7
  export type TanstackRouterPluginOptions = {
5
8
  routesDir?: string;
6
9
  generatedDirName?: string;
10
+ routeCodeSplitting?: TanstackRouteCodeSplittingOption;
7
11
  };
8
12
  export declare function writeTanstackRegisterFile(opts: {
9
13
  entries: string[];
10
14
  generatedDirName?: string;
11
15
  runtimeModule?: string;
12
16
  srcDirectory: string;
17
+ canonicalRoutes?: Record<string, string> | null;
18
+ i18nRuntimeModule?: string;
13
19
  }): Promise<void>;
14
20
  export declare function writeTanstackRouterTypesForEntries(opts: {
15
21
  appContext: AppToolsContext;
@@ -0,0 +1,29 @@
1
+ export type TanstackRouteCodeSplittingOption = boolean | {
2
+ enabled?: boolean;
3
+ };
4
+ export type TanstackRsbuildRouteSplittingProfile = {
5
+ defaultConfig: {
6
+ output: {
7
+ splitRouteChunks: boolean;
8
+ };
9
+ };
10
+ modernRouteChunks: {
11
+ enabled: boolean;
12
+ owner: 'modern';
13
+ };
14
+ builderChunkSplit: {
15
+ owner: 'modern-rsbuild';
16
+ preserved: true;
17
+ };
18
+ tanstackStartRspackSplitter: {
19
+ compatible: boolean;
20
+ reason: string;
21
+ clientDeleteNodes: string[];
22
+ routeFactoryCalls: string[];
23
+ };
24
+ };
25
+ export declare function isTanstackStartRouteModuleSource(source: string): boolean;
26
+ export declare function resolveTanstackRouteCodeSplittingEnabled(option?: TanstackRouteCodeSplittingOption): boolean;
27
+ export declare function createTanstackRsbuildRouteSplittingProfile(opts: {
28
+ routeCodeSplitting?: TanstackRouteCodeSplittingOption;
29
+ }): TanstackRsbuildRouteSplittingProfile;
@@ -1,5 +1,14 @@
1
1
  import type { AppToolsContext } from '@modern-js/app-tools';
2
2
  import type { NestedRouteForCli, PageRoute } from '@modern-js/types';
3
+ /**
4
+ * Derive the canonical (language-agnostic) route map for an entry: the
5
+ * leading locale param is stripped and localized physical variants (routes
6
+ * carrying `modernCanonicalPath` metadata from `@modern-js/plugin-i18n`)
7
+ * collapse to their canonical pattern. Returns `null` when the entry has no
8
+ * i18n routing surface (no locale param and no localized variants), so plain
9
+ * TanStack apps never get a `@modern-js/plugin-i18n` module augmentation.
10
+ */
11
+ export declare function collectCanonicalRoutesForEntry(routes: (NestedRouteForCli | PageRoute)[]): Record<string, string> | null;
3
12
  export declare function isTanstackRouterFrameworkEnabled(appContext: AppToolsContext): Promise<boolean>;
4
13
  export declare function generateTanstackRouterTypesSourceForEntry(opts: {
5
14
  appContext: AppToolsContext;
@@ -1,27 +1,12 @@
1
- declare const modifyRoutes: {
2
- tap: (cb: any) => void;
3
- call: (...params: any[]) => any;
4
- };
5
- declare const onBeforeCreateRoutes: {
6
- tap: (cb: any) => void;
7
- call: (...params: any[]) => any;
8
- };
9
- declare const onBeforeCreateRouter: {
10
- tap: (cb: any) => void;
11
- call: (...params: any[]) => any;
12
- };
13
- declare const onAfterCreateRouter: {
14
- tap: (cb: any) => void;
15
- call: (...params: any[]) => any;
16
- };
17
- declare const onBeforeHydrateRouter: {
18
- tap: (cb: any) => void;
19
- call: (...params: any[]) => any;
20
- };
21
- declare const onAfterHydrateRouter: {
22
- tap: (cb: any) => void;
23
- call: (...params: any[]) => any;
24
- };
1
+ import type { TRuntimeContext } from '@modern-js/runtime/context';
2
+ import type { RouteObject } from '@modern-js/runtime-utils/router';
3
+ import type { RouterLifecycleContext } from './lifecycle';
4
+ declare const modifyRoutes: import("@modern-js/plugin").SyncHook<(routes: RouteObject[]) => RouteObject[]>;
5
+ declare const onBeforeCreateRoutes: import("@modern-js/plugin").SyncHook<(context: TRuntimeContext) => void>;
6
+ declare const onBeforeCreateRouter: import("@modern-js/plugin").SyncHook<(context: RouterLifecycleContext) => void>;
7
+ declare const onAfterCreateRouter: import("@modern-js/plugin").SyncHook<(context: RouterLifecycleContext) => void>;
8
+ declare const onBeforeHydrateRouter: import("@modern-js/plugin").SyncHook<(context: RouterLifecycleContext) => void>;
9
+ declare const onAfterHydrateRouter: import("@modern-js/plugin").SyncHook<(context: RouterLifecycleContext) => void>;
25
10
  export { modifyRoutes, onAfterCreateRouter, onAfterHydrateRouter, onBeforeCreateRouter, onBeforeCreateRoutes, onBeforeHydrateRouter, };
26
11
  export type RouterExtendsHooks = {
27
12
  modifyRoutes: typeof modifyRoutes;
@@ -0,0 +1,2 @@
1
+ import { type ReactElement } from 'react';
2
+ export declare function wrapTanstackSsrHydrationBoundary(routerContent: ReactElement, shouldWrap: boolean): import("react").JSX.Element;
@@ -1,9 +1,12 @@
1
- export * from '@tanstack/react-router';
2
- export { useMatch } from '@tanstack/react-router';
1
+ export type * from '@tanstack/react-router';
2
+ export { Asset, Await, Block, CatchBoundary, CatchNotFound, ClientOnly, cleanPath, composeRewrites, createBrowserHistory, createControlledPromise, createFileRoute, createHashHistory, createHistory, createLazyFileRoute, createLazyRoute, createLink, createMemoryHistory, createRootRoute, createRootRouteWithContext, createRoute, createRouteMask, createRouter, createRouterConfig, createSerializationAdapter, DEFAULT_PROTOCOL_ALLOWLIST, DefaultGlobalNotFound, deepEqual, defaultParseSearch, defaultStringifySearch, defer, ErrorComponent, FileRoute, FileRouteLoader, functionalUpdate, getRouteApi, HeadContent, interpolatePath, isMatch, isNotFound, isPlainArray, isPlainObject, isRedirect, joinPaths, LazyRoute, lazyFn, lazyRouteComponent, linkOptions, Match, Matches, MatchRoute, Navigate, NotFoundRoute, notFound, parseSearchWith, RootRoute, Route, RouteApi, Router, RouterContextProvider, RouterProvider, reactUse, redirect, replaceEqualDeep, resolvePath, retainSearchParams, rootRouteId, rootRouteWithContext, ScriptOnce, Scripts, ScrollRestoration, SearchParamError, stringifySearchWith, stripSearchParams, trimPath, trimPathLeft, trimPathRight, useAwaited, useBlocker, useCanGoBack, useChildMatches, useElementScrollRestoration, useHydrated, useLayoutEffect, useLinkProps, useLoaderData, useLoaderDeps, useLocation, useMatch, useMatches, useMatchRoute, useNavigate, useParams, useParentMatches, useRouteContext, useRouter, useRouterState, useSearch, useTags, } from '@tanstack/react-router';
3
3
  export type { Fetcher, FetcherState, FetcherSubmitOptions, FormProps, SubmitOptions, } from './dataMutation';
4
4
  export { Form, RouteActionResponseError, useFetcher, } from './dataMutation';
5
+ export { Outlet } from './outlet';
5
6
  export { tanstackRouterPlugin, tanstackRouterPlugin as default, } from './plugin';
6
7
  export type { LinkProps, NavLinkProps, PrefetchBehavior, } from './prefetchLink';
7
8
  export { Link, NavLink } from './prefetchLink';
8
9
  export type { AnyCompositeComponent, AnyRenderableServerComponent, CompositeComponentProps, } from './rsc/client';
9
10
  export { CompositeComponent } from './rsc/client';
11
+ export type { RouterConfig } from './types';
12
+ export { getModernTanstackRouterFastDefaults, modernTanstackRouterFastDefaults, } from './types';
@@ -0,0 +1,2 @@
1
+ export declare const Outlet: import("react").MemoExoticComponent<() => import("react").JSX.Element>;
2
+ export declare function withModernRouteMatchContext(component: unknown, _routeId: string): unknown;
@@ -2,7 +2,7 @@ import type { Plugin, RuntimePluginExtends } from '@modern-js/plugin';
2
2
  import type { RuntimePluginAPI } from '@modern-js/plugin/runtime';
3
3
  import { type TInternalRuntimeContext } from '@modern-js/runtime/context';
4
4
  import { type RouterExtendsHooks } from './hooks';
5
- import type { RouterConfig } from './types';
5
+ import { type RouterConfig } from './types';
6
6
  type TanstackRouterRuntimeConfig = {
7
7
  plugins?: TanstackRouterRuntimePlugin[];
8
8
  router?: Partial<RouterConfig>;
@@ -2,7 +2,7 @@ import type { Plugin, RuntimePluginExtends } from '@modern-js/plugin';
2
2
  import type { RuntimePluginAPI } from '@modern-js/plugin/runtime';
3
3
  import { type TInternalRuntimeContext } from '@modern-js/runtime/context';
4
4
  import { type RouterExtendsHooks } from './hooks';
5
- import type { RouterConfig } from './types';
5
+ import { type RouterConfig } from './types';
6
6
  type TanstackRouterRuntimeConfig = {
7
7
  plugins?: TanstackRouterRuntimePlugin[];
8
8
  router?: Partial<RouterConfig>;
@@ -0,0 +1 @@
1
+ export { default, tanstackRouterPlugin, } from './plugin.node';
@@ -18,8 +18,15 @@ export type RouterConfig = {
18
18
  future?: Partial<{
19
19
  v7_startTransition: boolean;
20
20
  }>;
21
+ defaultStructuralSharing?: boolean;
21
22
  unstable_reloadOnURLMismatch?: boolean;
22
23
  };
24
+ export declare const modernTanstackRouterFastDefaults: {
25
+ readonly defaultStructuralSharing: true;
26
+ };
27
+ export declare const getModernTanstackRouterFastDefaults: (config?: Partial<Pick<RouterConfig, 'defaultStructuralSharing'>>) => {
28
+ defaultStructuralSharing: boolean;
29
+ };
23
30
  export interface RouterRouteMatchSnapshot {
24
31
  routeId: string;
25
32
  assetRouteId?: string;
package/package.json CHANGED
@@ -18,7 +18,7 @@
18
18
  "modern.js",
19
19
  "tanstack-router"
20
20
  ],
21
- "version": "3.2.0-ultramodern.12",
21
+ "version": "3.2.0-ultramodern.120",
22
22
  "engines": {
23
23
  "node": ">=20"
24
24
  },
@@ -85,33 +85,33 @@
85
85
  }
86
86
  },
87
87
  "dependencies": {
88
- "@swc/helpers": "^0.5.21",
89
- "@tanstack/react-router": "1.170.1",
90
- "@tanstack/router-core": "1.170.1",
91
- "@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.2.0-ultramodern.12",
92
- "@modern-js/types": "npm:@bleedingdev/modern-js-types@3.2.0-ultramodern.12",
93
- "@modern-js/runtime-utils": "npm:@bleedingdev/modern-js-runtime-utils@3.2.0-ultramodern.12",
94
- "@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.2.0-ultramodern.12"
88
+ "@swc/helpers": "^0.5.23",
89
+ "@tanstack/react-router": "1.170.15",
90
+ "@tanstack/router-core": "1.171.13",
91
+ "@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.2.0-ultramodern.120",
92
+ "@modern-js/runtime-utils": "npm:@bleedingdev/modern-js-runtime-utils@3.2.0-ultramodern.120",
93
+ "@modern-js/types": "npm:@bleedingdev/modern-js-types@3.2.0-ultramodern.120",
94
+ "@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.2.0-ultramodern.120"
95
95
  },
96
96
  "peerDependencies": {
97
- "@modern-js/runtime": "3.2.0-ultramodern.12",
98
- "react": "^19.2.6",
99
- "react-dom": "^19.2.6"
97
+ "@modern-js/runtime": "3.2.0-ultramodern.120",
98
+ "react": "^19.2.7",
99
+ "react-dom": "^19.2.7"
100
100
  },
101
101
  "devDependencies": {
102
- "@rslib/core": "0.21.5",
102
+ "@rslib/core": "0.22.0",
103
103
  "@tanstack/history": "1.162.0",
104
104
  "@testing-library/dom": "^10.4.1",
105
105
  "@testing-library/react": "^16.3.2",
106
- "@types/node": "^25.8.0",
107
- "@types/react": "^19.2.14",
106
+ "@types/node": "^25.9.3",
107
+ "@types/react": "^19.2.17",
108
108
  "@types/react-dom": "^19.2.3",
109
- "@typescript/native-preview": "7.0.0-dev.20260516.1",
110
- "react": "^19.2.6",
111
- "react-dom": "^19.2.6",
112
- "@modern-js/app-tools": "npm:@bleedingdev/modern-js-app-tools@3.2.0-ultramodern.12",
113
- "@scripts/rstest-config": "2.66.0",
114
- "@modern-js/runtime": "npm:@bleedingdev/modern-js-runtime@3.2.0-ultramodern.12"
109
+ "@typescript/native-preview": "7.0.0-dev.20260610.1",
110
+ "react": "^19.2.7",
111
+ "react-dom": "^19.2.7",
112
+ "@modern-js/app-tools": "npm:@bleedingdev/modern-js-app-tools@3.2.0-ultramodern.120",
113
+ "@modern-js/runtime": "npm:@bleedingdev/modern-js-runtime@3.2.0-ultramodern.120",
114
+ "@scripts/rstest-config": "2.66.0"
115
115
  },
116
116
  "sideEffects": false,
117
117
  "publishConfig": {
package/src/cli/index.ts CHANGED
@@ -19,11 +19,26 @@ import {
19
19
  NESTED_ROUTE_SPEC_FILE,
20
20
  } from '@modern-js/utils';
21
21
  import {
22
+ createTanstackRsbuildRouteSplittingProfile,
23
+ type TanstackRouteCodeSplittingOption,
24
+ } from './routeSplitting';
25
+ import {
26
+ collectCanonicalRoutesForEntry,
22
27
  generateTanstackRouterTypesSourceForEntry,
23
28
  isTanstackRouterFrameworkEnabled,
24
29
  } from './tanstackTypes';
25
30
 
31
+ export type {
32
+ TanstackRouteCodeSplittingOption,
33
+ TanstackRsbuildRouteSplittingProfile,
34
+ } from './routeSplitting';
35
+ export {
36
+ createTanstackRsbuildRouteSplittingProfile,
37
+ isTanstackStartRouteModuleSource,
38
+ resolveTanstackRouteCodeSplittingEnabled,
39
+ } from './routeSplitting';
26
40
  export {
41
+ collectCanonicalRoutesForEntry,
27
42
  generateTanstackRouterTypesSourceForEntry,
28
43
  isTanstackRouterFrameworkEnabled,
29
44
  } from './tanstackTypes';
@@ -35,6 +50,7 @@ const ENTRYPOINTS_KEY = '@modern-js/plugin-tanstack';
35
50
  export type TanstackRouterPluginOptions = {
36
51
  routesDir?: string;
37
52
  generatedDirName?: string;
53
+ routeCodeSplitting?: TanstackRouteCodeSplittingOption;
38
54
  };
39
55
 
40
56
  type RuntimeRouterCliHelpers = {
@@ -110,6 +126,8 @@ async function writeFileIfChanged(filePath: string, content: string) {
110
126
  function createRegisterDtsContent(opts: {
111
127
  entries: string[];
112
128
  runtimeModule: string;
129
+ canonicalRoutes?: Record<string, string> | null;
130
+ i18nRuntimeModule?: string;
113
131
  }) {
114
132
  const importStatements = opts.entries
115
133
  .map(
@@ -121,6 +139,20 @@ function createRegisterDtsContent(opts: {
121
139
  .map((_, index) => `typeof router${index}`)
122
140
  .join(' | ');
123
141
 
142
+ const canonicalEntries = Object.entries(opts.canonicalRoutes ?? {});
143
+ const canonicalRoutesAugmentation =
144
+ canonicalEntries.length > 0
145
+ ? `
146
+ declare module '${opts.i18nRuntimeModule || '@modern-js/plugin-i18n/runtime'}' {
147
+ interface UltramodernCanonicalRoutes {
148
+ ${canonicalEntries
149
+ .map(([routePath, paramsType]) => ` '${routePath}': ${paramsType};`)
150
+ .join('\n')}
151
+ }
152
+ }
153
+ `
154
+ : '';
155
+
124
156
  return `// This file is auto-generated by Modern.js. Do not edit manually.
125
157
 
126
158
  ${importStatements}
@@ -130,7 +162,7 @@ declare module '${opts.runtimeModule}' {
130
162
  router: ${routerUnionType};
131
163
  }
132
164
  }
133
- `;
165
+ ${canonicalRoutesAugmentation}`;
134
166
  }
135
167
 
136
168
  export async function writeTanstackRegisterFile(opts: {
@@ -138,12 +170,16 @@ export async function writeTanstackRegisterFile(opts: {
138
170
  generatedDirName?: string;
139
171
  runtimeModule?: string;
140
172
  srcDirectory: string;
173
+ canonicalRoutes?: Record<string, string> | null;
174
+ i18nRuntimeModule?: string;
141
175
  }) {
142
176
  const {
143
177
  entries,
144
178
  generatedDirName = DEFAULT_GENERATED_DIR_NAME,
145
179
  runtimeModule = '@modern-js/plugin-tanstack/runtime',
146
180
  srcDirectory,
181
+ canonicalRoutes,
182
+ i18nRuntimeModule,
147
183
  } = opts;
148
184
 
149
185
  if (entries.length === 0) {
@@ -158,7 +194,12 @@ export async function writeTanstackRegisterFile(opts: {
158
194
 
159
195
  await writeFileIfChanged(
160
196
  registerDtsPath,
161
- createRegisterDtsContent({ entries, runtimeModule }),
197
+ createRegisterDtsContent({
198
+ entries,
199
+ runtimeModule,
200
+ canonicalRoutes,
201
+ i18nRuntimeModule,
202
+ }),
162
203
  );
163
204
  }
164
205
 
@@ -211,10 +252,23 @@ export async function writeTanstackRouterTypesForEntries(opts: {
211
252
  return a.localeCompare(b);
212
253
  });
213
254
 
255
+ // Merge the canonical (language-agnostic) route maps of every entry so the
256
+ // typed i18n Link covers all routes the app can navigate to.
257
+ let canonicalRoutes: Record<string, string> | null = null;
258
+ for (const entryName of registerEntries) {
259
+ const entryCanonicalRoutes = collectCanonicalRoutesForEntry(
260
+ routesByEntry[entryName],
261
+ );
262
+ if (entryCanonicalRoutes) {
263
+ canonicalRoutes = { ...entryCanonicalRoutes, ...(canonicalRoutes ?? {}) };
264
+ }
265
+ }
266
+
214
267
  await writeTanstackRegisterFile({
215
268
  entries: registerEntries,
216
269
  generatedDirName,
217
270
  srcDirectory: appContext.srcDirectory,
271
+ canonicalRoutes,
218
272
  });
219
273
  }
220
274
 
@@ -224,6 +278,8 @@ export function tanstackRouterPlugin(
224
278
  const routesDir = options.routesDir || DEFAULT_ROUTES_DIR;
225
279
  const generatedDirName =
226
280
  options.generatedDirName || DEFAULT_GENERATED_DIR_NAME;
281
+ const routeSplittingProfile =
282
+ createTanstackRsbuildRouteSplittingProfile(options);
227
283
 
228
284
  return {
229
285
  name: '@modern-js/plugin-tanstack',
@@ -265,6 +321,7 @@ export function tanstackRouterPlugin(
265
321
  }));
266
322
 
267
323
  api.config(() => ({
324
+ ...routeSplittingProfile.defaultConfig,
268
325
  source: {
269
326
  include: [
270
327
  /[\\/]node_modules[\\/]@tanstack[\\/]react-router[\\/]/,