@bleedingdev/modern-js-plugin-tanstack 3.9.0-ultramodern.3 → 3.9.0-ultramodern.5

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 (80) hide show
  1. package/dist/cjs/cli/index.js +9 -0
  2. package/dist/cjs/cli/tanstackTypes/source.js +3 -1
  3. package/dist/cjs/runtime/basepathRewrite.js +13 -6
  4. package/dist/cjs/runtime/lifecycle.js +7 -7
  5. package/dist/cjs/runtime/plugin.js +1 -1
  6. package/dist/cjs/runtime/plugin.node.js +5 -1
  7. package/dist/cjs/runtime/prefetchLink.js +2 -7
  8. package/dist/cjs/runtime/register.js +2 -2
  9. package/dist/cjs/runtime/routeTree/construction.js +2 -0
  10. package/dist/cjs/runtime/router.js +25 -9
  11. package/dist/cjs/runtime/state.js +2 -2
  12. package/dist/cjs/runtime/utils.js +2 -0
  13. package/dist/esm/cli/index.mjs +9 -0
  14. package/dist/esm/cli/tanstackTypes/source.mjs +3 -1
  15. package/dist/esm/runtime/basepathRewrite.mjs +10 -3
  16. package/dist/esm/runtime/lifecycle.mjs +1 -1
  17. package/dist/esm/runtime/plugin.mjs +1 -1
  18. package/dist/esm/runtime/plugin.node.mjs +5 -1
  19. package/dist/esm/runtime/prefetchLink.mjs +1 -6
  20. package/dist/esm/runtime/register.mjs +1 -1
  21. package/dist/esm/runtime/routeTree/construction.mjs +2 -0
  22. package/dist/esm/runtime/router.mjs +27 -11
  23. package/dist/esm/runtime/state.mjs +1 -1
  24. package/dist/esm/runtime/utils.mjs +2 -0
  25. package/dist/esm-node/cli/index.mjs +9 -0
  26. package/dist/esm-node/cli/tanstackTypes/source.mjs +3 -1
  27. package/dist/esm-node/runtime/basepathRewrite.mjs +10 -3
  28. package/dist/esm-node/runtime/lifecycle.mjs +1 -1
  29. package/dist/esm-node/runtime/plugin.mjs +1 -1
  30. package/dist/esm-node/runtime/plugin.node.mjs +5 -1
  31. package/dist/esm-node/runtime/prefetchLink.mjs +1 -6
  32. package/dist/esm-node/runtime/register.mjs +1 -1
  33. package/dist/esm-node/runtime/routeTree/construction.mjs +2 -0
  34. package/dist/esm-node/runtime/router.mjs +27 -11
  35. package/dist/esm-node/runtime/state.mjs +1 -1
  36. package/dist/esm-node/runtime/utils.mjs +2 -0
  37. package/dist/types/runtime/basepathRewrite.d.ts +2 -1
  38. package/dist/types/runtime/lifecycle.d.ts +9 -2
  39. package/dist/types/runtime/register.d.ts +1 -1
  40. package/dist/types/runtime/router.d.ts +2 -13
  41. package/dist/types/runtime/state.d.ts +1 -1
  42. package/dist/types/runtime/types.d.ts +2 -1
  43. package/package.json +12 -9
  44. package/rstest.config.mts +1 -6
  45. package/src/cli/index.ts +10 -0
  46. package/src/cli/tanstackTypes/source.ts +3 -1
  47. package/src/runtime/basepathRewrite.ts +11 -2
  48. package/src/runtime/lifecycle.ts +14 -3
  49. package/src/runtime/plugin.node.tsx +9 -2
  50. package/src/runtime/plugin.tsx +5 -1
  51. package/src/runtime/prefetchLink.tsx +1 -23
  52. package/src/runtime/register.ts +1 -1
  53. package/src/runtime/routeTree/construction.ts +2 -0
  54. package/src/runtime/router.ts +25 -40
  55. package/src/runtime/state.ts +1 -1
  56. package/src/runtime/types.ts +2 -2
  57. package/src/runtime/utils.tsx +3 -1
  58. package/tests/realm-isolation.test.ts +8 -2
  59. package/tests/router/cli.test.ts +108 -9
  60. package/tests/router/dataMutation.test.tsx +17 -121
  61. package/tests/router/flightSerialization.roundtrip.test.tsx +9 -6
  62. package/tests/router/generateRouteArtifacts.test.ts +69 -29
  63. package/tests/router/hooks.test.ts +1 -18
  64. package/tests/router/loaderBridge.test.ts +0 -77
  65. package/tests/router/localisedIdentity.test.tsx +95 -0
  66. package/tests/router/prefetchLink.test.tsx +28 -16
  67. package/tests/router/prefetchLinkPreload.test.tsx +21 -28
  68. package/tests/router/register.test.ts +1 -1
  69. package/tests/router/routeHooks.test.ts +41 -105
  70. package/tests/router/routeTree.test.ts +0 -212
  71. package/tests/router/routerCompatibility.test.ts +32 -9
  72. package/tests/router/rsc.test.tsx +118 -136
  73. package/tests/router/rscDecoderSecurity.test.ts +24 -53
  74. package/tests/router/serverPlugin.test.ts +43 -1
  75. package/tests/router/tanstackTypes.test.ts +24 -364
  76. package/tests/router/fastDefaults.test.ts +0 -32
  77. package/tests/router/hydrationBoundary.test.tsx +0 -23
  78. package/tests/router/packageSurface.test.ts +0 -24
  79. package/tests/router/preloadRedirect.test.ts +0 -64
  80. package/tests/router/rscPayloadRouterMatrix.test.ts +0 -197
@@ -1,4 +1,5 @@
1
1
  import "node:module";
2
+ import { canonicaliseLocalisedRoutes } from "@modern-js/i18n-runtime-extensions";
2
3
  import { DefaultNotFound } from "@modern-js/runtime/context";
3
4
  import { rootRouteId } from "@tanstack/react-router";
4
5
  import { createRouteStaticData } from "../../shared/routeStaticData.mjs";
@@ -58,6 +59,7 @@ function getRootLikeRouteObject(routes) {
58
59
  return routes.find((route)=>'/' === route.path && !route.index);
59
60
  }
60
61
  function createRouteTreeFromRouteObjects(routes, options = {}) {
62
+ routes = canonicaliseLocalisedRoutes(routes);
61
63
  const rootLikeRoute = getRootLikeRouteObject(routes);
62
64
  const rootRevalidationState = {};
63
65
  const rootShouldRevalidate = rootLikeRoute?.shouldRevalidate;
@@ -1,17 +1,33 @@
1
1
  import "node:module";
2
- import * as __rspack_external__modern_js_runtime_router_internal_8b8183c5 from "@modern-js/runtime/router/internal";
2
+ import { routerProviderRegistryHooks } from "@modern-js/runtime/context";
3
+ import { routerPlugin } from "@modern-js/runtime/router/internal";
4
+ import { createRouterPlugin } from "@modern-js/runtime-extensions/router-provider";
5
+ import { createRouterStatePlugin } from "@modern-js/runtime-extensions/router-state-plugin";
3
6
  import { tanstackRouterProviderFactory } from "./register.mjs";
4
- function getRouterPlugin() {
5
- const createRouterPlugin = Reflect.get(__rspack_external__modern_js_runtime_router_internal_8b8183c5, 'createRouterPlugin');
6
- if ('function' == typeof createRouterPlugin) return createRouterPlugin([
7
+ const createProviderPlugin = createRouterPlugin({
8
+ defaultProvider: {
9
+ name: 'react-router',
10
+ factory: routerPlugin
11
+ },
12
+ registryHooks: routerProviderRegistryHooks,
13
+ localProviders: [
7
14
  {
8
15
  name: 'tanstack',
9
16
  factory: tanstackRouterProviderFactory
10
17
  }
11
- ]);
12
- const legacyRouterPlugin = Reflect.get(__rspack_external__modern_js_runtime_router_internal_8b8183c5, 'routerPlugin');
13
- if ('function' == typeof legacyRouterPlugin) return tanstackRouterProviderFactory;
14
- throw new Error('[@modern-js/plugin-tanstack] The installed @modern-js/runtime/router/internal exports neither createRouterPlugin nor routerPlugin. Install a compatible @modern-js/runtime version.');
15
- }
16
- const routerPlugin = getRouterPlugin();
17
- export { routerPlugin };
18
+ ]
19
+ });
20
+ const statePlugin = createRouterStatePlugin({
21
+ registryHooks: routerProviderRegistryHooks
22
+ });
23
+ const router_routerPlugin = (userConfig = {})=>{
24
+ const providerPlugin = createProviderPlugin(userConfig);
25
+ return {
26
+ ...providerPlugin,
27
+ setup (api) {
28
+ statePlugin.setup(api);
29
+ providerPlugin.setup(api);
30
+ }
31
+ };
32
+ };
33
+ export { router_routerPlugin as routerPlugin };
@@ -1,5 +1,5 @@
1
1
  import "node:module";
2
- import { getRouterRuntimeState } from "@modern-js/runtime/context";
2
+ import { getRouterRuntimeState } from "@modern-js/runtime-extensions/router-state";
3
3
  function getTanstackRouterState(context) {
4
4
  const state = getRouterRuntimeState(context);
5
5
  if (void 0 === state || 'tanstack' !== state.framework || void 0 === state.instance) return;
@@ -14,6 +14,7 @@ function withGlobalLayout(Component, globalApp) {
14
14
  }
15
15
  function toTanstackRouteObject(route, globalApp) {
16
16
  if ('nested' === route.type) return {
17
+ modernLocalisedRoute: Reflect.get(route, 'modernLocalisedRoute'),
17
18
  path: route.path,
18
19
  id: route.id,
19
20
  loader: route.loader,
@@ -38,6 +39,7 @@ function toTanstackRouteObject(route, globalApp) {
38
39
  if ('function' != typeof route.component && 'object' != typeof route.component) return null;
39
40
  const LayoutComponent = withGlobalLayout(route.component, globalApp);
40
41
  return {
42
+ modernLocalisedRoute: Reflect.get(route, 'modernLocalisedRoute'),
41
43
  path: route.path,
42
44
  element: /*#__PURE__*/ react.createElement(LayoutComponent)
43
45
  };
@@ -1,4 +1,5 @@
1
- export declare function createModernBasepathRewrite(basepath: string, caseSensitive?: boolean): {
1
+ import type { RouteObject } from '@modern-js/runtime-utils/router';
2
+ export declare function createModernBasepathRewrite(basepath: string, caseSensitive?: boolean, routes?: RouteObject[]): {
2
3
  input: ({ url }: {
3
4
  url: URL;
4
5
  }) => URL;
@@ -1,7 +1,14 @@
1
1
  /**
2
- * The router runtime state helpers are owned by @modern-js/runtime (the same
2
+ * The router runtime state helpers are owned by @modern-js/runtime-extensions (the same
3
3
  * implementation backs the built-in react-router provider and the SSR
4
4
  * pipeline). This module only re-exports them so every router provider
5
5
  * writes to the exact same runtime-context extension slot.
6
6
  */
7
- export { applyRouterRuntimeState, applyRouterServerPrepareResult, createRouterRuntimeState, createRouterServerSnapshot, getRouterRuntimeState, getRouterServerSnapshot, type RouterLifecycleContext, type RouterLifecyclePhase, } from '@modern-js/runtime/context';
7
+ import type { RouterLifecycleContext as NativeRouterLifecycleContext } from '@modern-js/runtime/context';
8
+ import type { InternalRouterServerSnapshot, RouterRouteMatchSnapshot } from '@modern-js/runtime-extensions/router-state';
9
+ export type RouterLifecycleContext = NativeRouterLifecycleContext & {
10
+ matches?: RouterRouteMatchSnapshot[];
11
+ cleanup?: () => void | Promise<void>;
12
+ serverSnapshot?: InternalRouterServerSnapshot;
13
+ };
14
+ export { applyRouterRuntimeState, applyRouterServerPrepareResult, createRouterRuntimeState, createRouterServerSnapshot, getRouterRuntimeState, getRouterServerSnapshot, type RouterLifecyclePhase, } from '@modern-js/runtime-extensions/router-state';
@@ -6,5 +6,5 @@
6
6
  * This module is intentionally side-effectful (see `sideEffects` in
7
7
  * package.json).
8
8
  */
9
- import { type RouterProviderFactory } from '@modern-js/runtime/context';
9
+ import { type RouterProviderFactory } from '@modern-js/runtime-extensions/router-provider';
10
10
  export declare const tanstackRouterProviderFactory: RouterProviderFactory;
@@ -1,13 +1,2 @@
1
- /**
2
- * Entry-injected router module for entrypoints that are NOT TanStack
3
- * file-route entrypoints (e.g. apps configuring
4
- * `router: { framework: 'tanstack', createRoutes }` in modern.runtime.ts).
5
- *
6
- * The CLI plugin injects `{ name: 'router', path: '<pkg>/runtime/router' }`
7
- * for those entries. This module creates a framework-resolving wrapper whose
8
- * app-owned provider realm contains this module graph's TanStack factory.
9
- */
10
- import * as runtimeRouter from '@modern-js/runtime/router/internal';
11
- type LegacyRouterPlugin = (typeof runtimeRouter)['routerPlugin'];
12
- export declare const routerPlugin: LegacyRouterPlugin;
13
- export {};
1
+ import { routerPlugin as nativeRouterPlugin } from '@modern-js/runtime/router/internal';
2
+ export declare const routerPlugin: typeof nativeRouterPlugin;
@@ -1,4 +1,4 @@
1
- import { type InternalRouterRuntimeState } from '@modern-js/runtime/context';
1
+ import { type InternalRouterRuntimeState } from '@modern-js/runtime-extensions/router-state';
2
2
  import type { AnyRouter } from '@tanstack/react-router';
3
3
  /**
4
4
  * Router runtime state as published by the TanStack router provider into the
@@ -1,7 +1,8 @@
1
1
  import type { ModernRoute } from '@modern-js/runtime/context';
2
2
  import type { RouteObject } from '@modern-js/runtime-utils/router';
3
3
  import type React from 'react';
4
- export type { InternalRouterServerSnapshot, ModernRoute, RouterFramework, } from '@modern-js/runtime/context';
4
+ export type { ModernRoute } from '@modern-js/runtime/context';
5
+ export type { InternalRouterServerSnapshot, RouterFramework, } from '@modern-js/runtime-extensions/router-state';
5
6
  /** TanStack-specific router config. */
6
7
  export type RouterConfig = {
7
8
  routesConfig: {
package/package.json CHANGED
@@ -18,7 +18,7 @@
18
18
  "modern.js",
19
19
  "tanstack-router"
20
20
  ],
21
- "version": "3.9.0-ultramodern.3",
21
+ "version": "3.9.0-ultramodern.5",
22
22
  "engines": {
23
23
  "node": ">=20"
24
24
  },
@@ -94,17 +94,19 @@
94
94
  }
95
95
  },
96
96
  "dependencies": {
97
- "@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.9.0-ultramodern.3",
98
- "@modern-js/runtime-utils": "npm:@bleedingdev/modern-js-runtime-utils@3.9.0-ultramodern.3",
99
- "@modern-js/types": "npm:@bleedingdev/modern-js-types@3.9.0-ultramodern.3",
100
- "@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.9.0-ultramodern.3",
97
+ "@modern-js/i18n-runtime-extensions": "npm:@bleedingdev/modern-js-i18n-runtime-extensions@3.9.0-ultramodern.5",
98
+ "@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.9.0-ultramodern.5",
99
+ "@modern-js/runtime-extensions": "npm:@bleedingdev/modern-js-runtime-extensions@3.9.0-ultramodern.5",
100
+ "@modern-js/runtime-utils": "npm:@bleedingdev/modern-js-runtime-utils@3.9.0-ultramodern.5",
101
+ "@modern-js/types": "npm:@bleedingdev/modern-js-types@3.9.0-ultramodern.5",
102
+ "@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.9.0-ultramodern.5",
101
103
  "@swc/helpers": "^0.5.23",
102
104
  "@tanstack/react-router": "1.170.33",
103
105
  "@tanstack/router-core": "1.171.28"
104
106
  },
105
107
  "peerDependencies": {
106
- "@modern-js/app-tools": "3.9.0-ultramodern.3",
107
- "@modern-js/runtime": "3.9.0-ultramodern.3",
108
+ "@modern-js/app-tools": "3.9.0-ultramodern.5",
109
+ "@modern-js/runtime": "3.9.0-ultramodern.5",
108
110
  "react": "^19.2.8",
109
111
  "react-dom": "^19.2.8",
110
112
  "react-server-dom-rspack": "0.1.0"
@@ -115,8 +117,9 @@
115
117
  }
116
118
  },
117
119
  "devDependencies": {
118
- "@modern-js/app-tools": "npm:@bleedingdev/modern-js-app-tools@3.9.0-ultramodern.3",
119
- "@modern-js/runtime": "npm:@bleedingdev/modern-js-runtime@3.9.0-ultramodern.3",
120
+ "@modern-js/app-tools": "npm:@bleedingdev/modern-js-app-tools@3.9.0-ultramodern.5",
121
+ "@modern-js/runtime": "npm:@bleedingdev/modern-js-runtime@3.9.0-ultramodern.5",
122
+ "@modern-js/server-utils": "npm:@bleedingdev/modern-js-server-utils@3.9.0-ultramodern.5",
120
123
  "@rslib/core": "1.0.0",
121
124
  "@scripts/rstest-config": "2.66.0",
122
125
  "@tanstack/history": "1.162.2",
package/rstest.config.mts CHANGED
@@ -26,18 +26,14 @@ export default {
26
26
  include: [
27
27
  'tests/realm-isolation.test.ts',
28
28
  'tests/router/cli.test.ts',
29
- 'tests/router/fastDefaults.test.ts',
30
29
  'tests/router/flightSerialization.roundtrip.test.tsx',
31
30
  'tests/router/generateRouteArtifacts.test.ts',
32
31
  'tests/router/hooks.test.ts',
33
32
  'tests/router/loaderBridge.test.ts',
34
- 'tests/router/packageSurface.test.ts',
35
- 'tests/router/preloadRedirect.test.ts',
36
- 'tests/router/register.test.ts',
33
+ 'tests/router/localisedIdentity.test.tsx',
37
34
  'tests/router/routeHooks.test.ts',
38
35
  'tests/router/serverPlugin.test.ts',
39
36
  'tests/router/rscDecoderSecurity.test.ts',
40
- 'tests/router/rscPayloadRouterMatrix.test.ts',
41
37
  'tests/router/rsc.test.tsx',
42
38
  'tests/router/slotUsageSanitizer.test.ts',
43
39
  'tests/router/ssrPreload.test.ts',
@@ -66,7 +62,6 @@ export default {
66
62
  include: [
67
63
  'tests/router/clientHydration.test.tsx',
68
64
  'tests/router/dataMutation.test.tsx',
69
- 'tests/router/hydrationBoundary.test.tsx',
70
65
  'tests/router/prefetchLink.test.tsx',
71
66
  'tests/router/prefetchLinkPreload.test.tsx',
72
67
  ],
package/src/cli/index.ts CHANGED
@@ -132,6 +132,16 @@ export function tanstackRouterPlugin(
132
132
  name: '@modern-js/plugin-tanstack',
133
133
  required: ['@modern-js/runtime'],
134
134
  setup: api => {
135
+ const { srcDirectory, serverCompileExcludedFiles = [] } =
136
+ api.getAppContext();
137
+ api.updateAppContext({
138
+ serverCompileExcludedFiles: [
139
+ ...new Set([
140
+ ...serverCompileExcludedFiles,
141
+ path.join(srcDirectory, generatedDirName, 'register.gen.d.ts'),
142
+ ]),
143
+ ],
144
+ });
135
145
  const nestedRoutesForServer: Record<string, unknown> = {};
136
146
 
137
147
  const isTanstackEntrypoint = (entrypoint: Entrypoint) => {
@@ -5,6 +5,7 @@ import type {
5
5
  AppTools,
6
6
  AppToolsContext,
7
7
  } from '@modern-js/app-tools';
8
+ import { canonicaliseLocalisedRoutes } from '@modern-js/i18n-runtime-extensions';
8
9
  import { makeLegalIdentifier } from '@modern-js/runtime/cli';
9
10
  import type { NestedRouteForCli, PageRoute } from '@modern-js/types';
10
11
  import { upath as path } from '@modern-js/utils';
@@ -45,8 +46,9 @@ export async function generateTanstackRouterTypesSourceForEntry(opts: {
45
46
  appContext,
46
47
  entryName,
47
48
  generatedDirName = 'modern-tanstack',
48
- routes,
49
+ routes: rawRoutes,
49
50
  } = opts;
51
+ const routes: RouteForCli[] = canonicaliseLocalisedRoutes(rawRoutes);
50
52
  const outDir = path.join(
51
53
  appContext.srcDirectory,
52
54
  generatedDirName,
@@ -1,4 +1,8 @@
1
1
  // @effect-diagnostics strictBooleanExpressions:off
2
+ import { createLocalisedRouteRewrite } from '@modern-js/i18n-runtime-extensions';
3
+ import type { RouteObject } from '@modern-js/runtime-utils/router';
4
+ import { composeRewrites } from '@tanstack/react-router';
5
+
2
6
  function normalizeBasepath(basepath: string): string {
3
7
  if (!basepath) {
4
8
  return '/';
@@ -15,10 +19,12 @@ function normalizeBasepath(basepath: string): string {
15
19
  export function createModernBasepathRewrite(
16
20
  basepath: string,
17
21
  caseSensitive = false,
22
+ routes: RouteObject[] = [],
18
23
  ) {
24
+ const localisedRewrite = createLocalisedRouteRewrite(routes);
19
25
  const normalizedBasepath = normalizeBasepath(basepath);
20
26
  if (normalizedBasepath === '/') {
21
- return undefined;
27
+ return localisedRewrite;
22
28
  }
23
29
 
24
30
  const normalizedBasepathWithSlash = `${normalizedBasepath}/`;
@@ -29,7 +35,7 @@ export function createModernBasepathRewrite(
29
35
  ? normalizedBasepathWithSlash
30
36
  : normalizedBasepathWithSlash.toLowerCase();
31
37
 
32
- return {
38
+ const basepathRewrite = {
33
39
  input: ({ url }: { url: URL }) => {
34
40
  const pathname = caseSensitive
35
41
  ? url.pathname
@@ -57,4 +63,7 @@ export function createModernBasepathRewrite(
57
63
  return url;
58
64
  },
59
65
  };
66
+ return localisedRewrite
67
+ ? composeRewrites([basepathRewrite, localisedRewrite])
68
+ : basepathRewrite;
60
69
  }
@@ -1,9 +1,21 @@
1
1
  /**
2
- * The router runtime state helpers are owned by @modern-js/runtime (the same
2
+ * The router runtime state helpers are owned by @modern-js/runtime-extensions (the same
3
3
  * implementation backs the built-in react-router provider and the SSR
4
4
  * pipeline). This module only re-exports them so every router provider
5
5
  * writes to the exact same runtime-context extension slot.
6
6
  */
7
+
8
+ import type { RouterLifecycleContext as NativeRouterLifecycleContext } from '@modern-js/runtime/context';
9
+ import type {
10
+ InternalRouterServerSnapshot,
11
+ RouterRouteMatchSnapshot,
12
+ } from '@modern-js/runtime-extensions/router-state';
13
+
14
+ export type RouterLifecycleContext = NativeRouterLifecycleContext & {
15
+ matches?: RouterRouteMatchSnapshot[];
16
+ cleanup?: () => void | Promise<void>;
17
+ serverSnapshot?: InternalRouterServerSnapshot;
18
+ };
7
19
  export {
8
20
  applyRouterRuntimeState,
9
21
  applyRouterServerPrepareResult,
@@ -11,6 +23,5 @@ export {
11
23
  createRouterServerSnapshot,
12
24
  getRouterRuntimeState,
13
25
  getRouterServerSnapshot,
14
- type RouterLifecycleContext,
15
26
  type RouterLifecyclePhase,
16
- } from '@modern-js/runtime/context';
27
+ } from '@modern-js/runtime-extensions/router-state';
@@ -1,10 +1,10 @@
1
1
  // @effect-diagnostics asyncFunction:off newPromise:off strictBooleanExpressions:off unnecessaryArrowBlock:off
2
2
  import {
3
3
  getGlobalEnableRsc,
4
- type InternalRouterServerSnapshot,
5
4
  InternalRuntimeContext,
6
5
  type TInternalRuntimeContext,
7
6
  } from '@modern-js/runtime/context';
7
+ import type { InternalRouterServerSnapshot } from '@modern-js/runtime-extensions/router-state';
8
8
  import {
9
9
  createRequestContext,
10
10
  type RequestContext,
@@ -35,6 +35,7 @@ import {
35
35
  type TanstackRouterPluginAPI,
36
36
  type TanstackRouterRuntimePlugin,
37
37
  } from './pluginShared';
38
+ import { Link } from './prefetchLink';
38
39
  import {
39
40
  createRouteTreeFromRouteObjects,
40
41
  getModernRouteIdsFromMatches,
@@ -98,6 +99,8 @@ export const tanstackRouterPlugin = (
98
99
  return;
99
100
  }
100
101
 
102
+ context.router = { Link };
103
+
101
104
  const {
102
105
  request,
103
106
  nonce,
@@ -134,7 +137,11 @@ export const tanstackRouterPlugin = (
134
137
  initialEntries: [initialHref],
135
138
  });
136
139
 
137
- const rewrite = createModernBasepathRewrite(_basename);
140
+ const rewrite = createModernBasepathRewrite(
141
+ _basename,
142
+ false,
143
+ modifiedRouteObjects,
144
+ );
138
145
  const routerLifecycleContext: RouterLifecycleContext = {
139
146
  framework: 'tanstack',
140
147
  phase: 'ssr-prepare',
@@ -149,7 +149,11 @@ export const tanstackRouterPlugin = (
149
149
  const history = supportHtml5History
150
150
  ? createBrowserHistory()
151
151
  : createHashHistory();
152
- const rewrite = createModernBasepathRewrite(_basename);
152
+ const rewrite = createModernBasepathRewrite(
153
+ _basename,
154
+ false,
155
+ getRouteObjects(),
156
+ );
153
157
  const serializationAdapters = getGlobalEnableRsc()
154
158
  ? getTanstackRscSerializationAdapters()
155
159
  : undefined;
@@ -1,3 +1,4 @@
1
+ import { resolvePreloadFromPrefetch } from '@modern-js/runtime-extensions';
1
2
  import {
2
3
  type AnyRouter,
3
4
  type LinkComponentProps,
@@ -8,29 +9,6 @@ import type { AnchorHTMLAttributes, ReactElement, ReactNode, Ref } from 'react';
8
9
 
9
10
  export type PrefetchBehavior = 'intent' | 'render' | 'viewport' | 'none';
10
11
 
11
- function resolvePreloadFromPrefetch(
12
- prefetch: PrefetchBehavior | undefined,
13
- preload: unknown,
14
- ) {
15
- if (typeof preload !== 'undefined') {
16
- return preload;
17
- }
18
-
19
- if (prefetch === 'none') {
20
- return false;
21
- }
22
-
23
- if (
24
- prefetch === 'intent' ||
25
- prefetch === 'render' ||
26
- prefetch === 'viewport'
27
- ) {
28
- return prefetch;
29
- }
30
-
31
- return 'viewport';
32
- }
33
-
34
12
  export type LinkProps<
35
13
  TRouter extends AnyRouter = RegisteredRouter,
36
14
  TFrom extends string = string,
@@ -10,7 +10,7 @@ import {
10
10
  type RouterProviderFactory,
11
11
  type RouterProviderPlugin,
12
12
  registerRouterProvider,
13
- } from '@modern-js/runtime/context';
13
+ } from '@modern-js/runtime-extensions/router-provider';
14
14
  import { tanstackRouterPlugin } from './plugin';
15
15
 
16
16
  // TanStack runtime plugin types its API against its own hook registry,
@@ -1,5 +1,6 @@
1
1
  // @effect-diagnostics asyncFunction:off strictBooleanExpressions:off
2
2
 
3
+ import { canonicaliseLocalisedRoutes } from '@modern-js/i18n-runtime-extensions';
3
4
  import { DefaultNotFound } from '@modern-js/runtime/context';
4
5
  import type { RouteObject } from '@modern-js/runtime-utils/router';
5
6
  import type { AnyRoute, AnyRouter } from '@tanstack/react-router';
@@ -121,6 +122,7 @@ export function createRouteTreeFromRouteObjects(
121
122
  routes: RouteObject[],
122
123
  options: RouteTreeOptions = {},
123
124
  ): ModernTanstackRootRoute {
125
+ routes = canonicaliseLocalisedRoutes(routes);
124
126
  const rootLikeRoute = getRootLikeRouteObject(routes) as
125
127
  | ModernRouteObject
126
128
  | undefined;
@@ -1,43 +1,28 @@
1
- /**
2
- * Entry-injected router module for entrypoints that are NOT TanStack
3
- * file-route entrypoints (e.g. apps configuring
4
- * `router: { framework: 'tanstack', createRoutes }` in modern.runtime.ts).
5
- *
6
- * The CLI plugin injects `{ name: 'router', path: '<pkg>/runtime/router' }`
7
- * for those entries. This module creates a framework-resolving wrapper whose
8
- * app-owned provider realm contains this module graph's TanStack factory.
9
- */
10
- import * as runtimeRouter from '@modern-js/runtime/router/internal';
1
+ /** App-local composition for custom entries using runtime router selection. */
2
+ import { routerProviderRegistryHooks } from '@modern-js/runtime/context';
3
+ import { routerPlugin as nativeRouterPlugin } from '@modern-js/runtime/router/internal';
4
+ import { createRouterPlugin } from '@modern-js/runtime-extensions/router-provider';
5
+ import { createRouterStatePlugin } from '@modern-js/runtime-extensions/router-state-plugin';
11
6
  import { tanstackRouterProviderFactory } from './register';
12
7
 
13
- type LegacyRouterPlugin = (typeof runtimeRouter)['routerPlugin'];
14
- type CurrentRouterPluginCreator = (
15
- localProviders: readonly {
16
- name: string;
17
- factory: typeof tanstackRouterProviderFactory;
18
- }[],
19
- ) => LegacyRouterPlugin;
8
+ const createProviderPlugin = createRouterPlugin({
9
+ defaultProvider: { name: 'react-router', factory: nativeRouterPlugin },
10
+ registryHooks: routerProviderRegistryHooks,
11
+ localProviders: [
12
+ { name: 'tanstack', factory: tanstackRouterProviderFactory },
13
+ ],
14
+ });
15
+ const statePlugin = createRouterStatePlugin({
16
+ registryHooks: routerProviderRegistryHooks,
17
+ });
20
18
 
21
- function getRouterPlugin(): LegacyRouterPlugin {
22
- const createRouterPlugin = Reflect.get(runtimeRouter, 'createRouterPlugin') as
23
- | CurrentRouterPluginCreator
24
- | undefined;
25
-
26
- if (typeof createRouterPlugin === 'function') {
27
- return createRouterPlugin([
28
- { name: 'tanstack', factory: tanstackRouterProviderFactory },
29
- ]);
30
- }
31
-
32
- const legacyRouterPlugin = Reflect.get(runtimeRouter, 'routerPlugin');
33
-
34
- if (typeof legacyRouterPlugin === 'function') {
35
- return tanstackRouterProviderFactory as unknown as LegacyRouterPlugin;
36
- }
37
-
38
- throw new Error(
39
- '[@modern-js/plugin-tanstack] The installed @modern-js/runtime/router/internal exports neither createRouterPlugin nor routerPlugin. Install a compatible @modern-js/runtime version.',
40
- );
41
- }
42
-
43
- export const routerPlugin: LegacyRouterPlugin = getRouterPlugin();
19
+ export const routerPlugin: typeof nativeRouterPlugin = (userConfig = {}) => {
20
+ const providerPlugin = createProviderPlugin(userConfig);
21
+ return {
22
+ ...providerPlugin,
23
+ setup(api) {
24
+ statePlugin.setup(api);
25
+ providerPlugin.setup(api);
26
+ },
27
+ };
28
+ };
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  getRouterRuntimeState,
3
3
  type InternalRouterRuntimeState,
4
- } from '@modern-js/runtime/context';
4
+ } from '@modern-js/runtime-extensions/router-state';
5
5
  import type { AnyRouter } from '@tanstack/react-router';
6
6
 
7
7
  /**
@@ -2,11 +2,11 @@ import type { ModernRoute } from '@modern-js/runtime/context';
2
2
  import type { RouteObject } from '@modern-js/runtime-utils/router';
3
3
  import type React from 'react';
4
4
 
5
+ export type { ModernRoute } from '@modern-js/runtime/context';
5
6
  export type {
6
7
  InternalRouterServerSnapshot,
7
- ModernRoute,
8
8
  RouterFramework,
9
- } from '@modern-js/runtime/context';
9
+ } from '@modern-js/runtime-extensions/router-state';
10
10
 
11
11
  /** TanStack-specific router config. */
12
12
  export type RouterConfig = {
@@ -54,6 +54,7 @@ function toTanstackRouteObject(
54
54
  ): RouteObject | null {
55
55
  if (route.type === 'nested') {
56
56
  return {
57
+ modernLocalisedRoute: Reflect.get(route, 'modernLocalisedRoute'),
57
58
  path: route.path,
58
59
  id: route.id,
59
60
  loader: route.loader,
@@ -91,9 +92,10 @@ function toTanstackRouteObject(
91
92
  globalApp,
92
93
  );
93
94
  return {
95
+ modernLocalisedRoute: Reflect.get(route, 'modernLocalisedRoute'),
94
96
  path: route.path,
95
97
  element: React.createElement(LayoutComponent),
96
- };
98
+ } as ModernRouteObject;
97
99
  }
98
100
 
99
101
  export function createTanstackRouteObjectsFromConfig({
@@ -1,4 +1,4 @@
1
- import type { RouterProviderFactory } from '@modern-js/runtime/context';
1
+ import type { RouterProviderFactory } from '@modern-js/runtime-extensions/router-provider';
2
2
  import { rstest } from '@rstest/core';
3
3
  import type { RouterExtendsHooks } from '../src/runtime/hooks';
4
4
 
@@ -12,6 +12,7 @@ type RouterModule = typeof import('../src/runtime/router');
12
12
 
13
13
  type Vertical = {
14
14
  context: RuntimeContextModule;
15
+ providers: typeof import('@modern-js/runtime-extensions/router-provider');
15
16
  factory: RouterProviderFactory;
16
17
  hooks: RouterExtendsHooks;
17
18
  Link: unknown;
@@ -32,8 +33,12 @@ async function loadVertical(): Promise<Vertical> {
32
33
  const { Link } = await import('../src/runtime/prefetchLink');
33
34
  const { useMatches } = await import('../src/runtime/routeHooks');
34
35
 
36
+ const providers = await import(
37
+ '@modern-js/runtime-extensions/router-provider'
38
+ );
35
39
  return {
36
40
  context,
41
+ providers,
37
42
  factory: tanstackRouterProviderFactory,
38
43
  hooks: routerProviderRegistryHooks,
39
44
  Link,
@@ -50,6 +55,7 @@ function setupRouterWrapper(vertical: Vertical) {
50
55
  const plugin = vertical.routerPlugin({ framework: 'tanstack' });
51
56
  plugin.setup?.({
52
57
  getHooks: () => vertical.hooks,
58
+ onAfterCreateRouter: () => undefined,
53
59
  getRuntimeConfig: () => ({ router: { framework: 'tanstack' } }),
54
60
  onBeforeRender: listener => {
55
61
  onBeforeRender = listener;
@@ -101,7 +107,7 @@ describe('TanStack provider runtime-realm isolation', () => {
101
107
 
102
108
  // Mixed-version callers that do not supply a realm still see the shared
103
109
  // keep-first fallback, but production wrappers no longer consume it.
104
- expect(verticalB.context.resolveRouterProvider('tanstack')).toBe(
110
+ expect(verticalB.providers.resolveRouterProvider('tanstack')).toBe(
105
111
  verticalA.factory,
106
112
  );
107
113
  expect(warnSpy).toHaveBeenCalledWith(