@bleedingdev/modern-js-plugin-tanstack 3.9.0-ultramodern.2 → 3.9.0-ultramodern.4

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.
@@ -30,11 +30,13 @@ __webpack_require__.r(__webpack_exports__);
30
30
  __webpack_require__.d(__webpack_exports__, {
31
31
  generateTanstackRouterTypesSourceForEntry: ()=>generateTanstackRouterTypesSourceForEntry
32
32
  });
33
+ const i18n_runtime_extensions_namespaceObject = require("@modern-js/i18n-runtime-extensions");
33
34
  const cli_namespaceObject = require("@modern-js/runtime/cli");
34
35
  const utils_namespaceObject = require("@modern-js/utils");
35
36
  const external_shared_js_namespaceObject = require("./shared.js");
36
37
  async function generateTanstackRouterTypesSourceForEntry(opts) {
37
- const { appContext, entryName, generatedDirName = 'modern-tanstack', routes } = opts;
38
+ const { appContext, entryName, generatedDirName = 'modern-tanstack', routes: rawRoutes } = opts;
39
+ const routes = (0, i18n_runtime_extensions_namespaceObject.canonicaliseLocalisedRoutes)(rawRoutes);
38
40
  const outDir = utils_namespaceObject.upath.join(appContext.srcDirectory, generatedDirName, entryName);
39
41
  const rootModern = routes.find((r)=>{
40
42
  const extras = r;
@@ -27,19 +27,25 @@ var __webpack_require__ = {};
27
27
  })();
28
28
  var __webpack_exports__ = {};
29
29
  __webpack_require__.r(__webpack_exports__);
30
+ __webpack_require__.d(__webpack_exports__, {
31
+ createModernBasepathRewrite: ()=>createModernBasepathRewrite
32
+ });
33
+ const i18n_runtime_extensions_namespaceObject = require("@modern-js/i18n-runtime-extensions");
34
+ const react_router_namespaceObject = require("@tanstack/react-router");
30
35
  function normalizeBasepath(basepath) {
31
36
  if (!basepath) return '/';
32
37
  let normalized = basepath.startsWith('/') ? basepath : `/${basepath}`;
33
38
  if (normalized.length > 1 && normalized.endsWith('/')) normalized = normalized.slice(0, -1);
34
39
  return normalized || '/';
35
40
  }
36
- function createModernBasepathRewrite(basepath, caseSensitive = false) {
41
+ function createModernBasepathRewrite(basepath, caseSensitive = false, routes = []) {
42
+ const localisedRewrite = (0, i18n_runtime_extensions_namespaceObject.createLocalisedRouteRewrite)(routes);
37
43
  const normalizedBasepath = normalizeBasepath(basepath);
38
- if ('/' === normalizedBasepath) return;
44
+ if ('/' === normalizedBasepath) return localisedRewrite;
39
45
  const normalizedBasepathWithSlash = `${normalizedBasepath}/`;
40
46
  const checkBasepath = caseSensitive ? normalizedBasepath : normalizedBasepath.toLowerCase();
41
47
  const checkBasepathWithSlash = caseSensitive ? normalizedBasepathWithSlash : normalizedBasepathWithSlash.toLowerCase();
42
- return {
48
+ const basepathRewrite = {
43
49
  input: ({ url })=>{
44
50
  const pathname = caseSensitive ? url.pathname : url.pathname.toLowerCase();
45
51
  if (pathname === checkBasepath) url.pathname = '/';
@@ -53,10 +59,11 @@ function createModernBasepathRewrite(basepath, caseSensitive = false) {
53
59
  return url;
54
60
  }
55
61
  };
62
+ return localisedRewrite ? (0, react_router_namespaceObject.composeRewrites)([
63
+ basepathRewrite,
64
+ localisedRewrite
65
+ ]) : basepathRewrite;
56
66
  }
57
- __webpack_require__.d(__webpack_exports__, {
58
- createModernBasepathRewrite: ()=>createModernBasepathRewrite
59
- });
60
67
  exports.createModernBasepathRewrite = __webpack_exports__.createModernBasepathRewrite;
61
68
  for(var __rspack_i in __webpack_exports__)if (-1 === [
62
69
  "createModernBasepathRewrite"
@@ -110,7 +110,7 @@ const tanstackRouterPlugin = (userConfig = {})=>{
110
110
  const mergedConfig = getMergedConfig();
111
111
  const { supportHtml5History = true } = mergedConfig;
112
112
  const history = supportHtml5History ? (0, react_router_namespaceObject.createBrowserHistory)() : (0, react_router_namespaceObject.createHashHistory)();
113
- const rewrite = (0, external_basepathRewrite_js_namespaceObject.createModernBasepathRewrite)(_basename);
113
+ const rewrite = (0, external_basepathRewrite_js_namespaceObject.createModernBasepathRewrite)(_basename, false, getRouteObjects());
114
114
  const serializationAdapters = (0, context_namespaceObject.getGlobalEnableRsc)() ? (0, client_js_namespaceObject.getTanstackRscSerializationAdapters)() : void 0;
115
115
  const routerFastDefaults = (0, external_types_js_namespaceObject.getModernTanstackRouterFastDefaults)(mergedConfig);
116
116
  cachedRouter = (0, react_router_namespaceObject.createRouter)({
@@ -43,6 +43,7 @@ const external_hooks_js_namespaceObject = require("./hooks.js");
43
43
  const external_hydrationBoundary_js_namespaceObject = require("./hydrationBoundary.js");
44
44
  const external_lifecycle_js_namespaceObject = require("./lifecycle.js");
45
45
  const external_pluginShared_js_namespaceObject = require("./pluginShared.js");
46
+ const external_prefetchLink_js_namespaceObject = require("./prefetchLink.js");
46
47
  const index_js_namespaceObject = require("./routeTree/index.js");
47
48
  const payloadRouter_js_namespaceObject = require("./rsc/payloadRouter.js");
48
49
  const payloadStorage_js_namespaceObject = require("./rsc/payloadStorage.js");
@@ -68,6 +69,9 @@ const tanstackRouterPlugin = (userConfig = {})=>{
68
69
  routeConfig
69
70
  });
70
71
  if (!modifiedRouteObjects.length) return;
72
+ context.router = {
73
+ Link: external_prefetchLink_js_namespaceObject.Link
74
+ };
71
75
  const { request, nonce, baseUrl, loaderFailureMode = 'errorBoundary' } = context.ssrContext;
72
76
  const _basename = '/' === baseUrl ? (0, external_pluginShared_js_namespaceObject.joinBasename)(baseUrl, basename || '') : baseUrl;
73
77
  const rawRequest = request.raw;
@@ -90,7 +94,7 @@ const tanstackRouterPlugin = (userConfig = {})=>{
90
94
  initialHref
91
95
  ]
92
96
  });
93
- const rewrite = (0, external_basepathRewrite_js_namespaceObject.createModernBasepathRewrite)(_basename);
97
+ const rewrite = (0, external_basepathRewrite_js_namespaceObject.createModernBasepathRewrite)(_basename, false, modifiedRouteObjects);
94
98
  const routerLifecycleContext = {
95
99
  framework: 'tanstack',
96
100
  phase: 'ssr-prepare',
@@ -31,6 +31,7 @@ __webpack_require__.d(__webpack_exports__, {
31
31
  createRouteTreeFromRouteObjects: ()=>createRouteTreeFromRouteObjects,
32
32
  getModernRouteIdsFromMatches: ()=>getModernRouteIdsFromMatches
33
33
  });
34
+ const i18n_runtime_extensions_namespaceObject = require("@modern-js/i18n-runtime-extensions");
34
35
  const context_namespaceObject = require("@modern-js/runtime/context");
35
36
  const react_router_namespaceObject = require("@tanstack/react-router");
36
37
  const routeStaticData_js_namespaceObject = require("../../shared/routeStaticData.js");
@@ -90,6 +91,7 @@ function getRootLikeRouteObject(routes) {
90
91
  return routes.find((route)=>'/' === route.path && !route.index);
91
92
  }
92
93
  function createRouteTreeFromRouteObjects(routes, options = {}) {
94
+ routes = (0, i18n_runtime_extensions_namespaceObject.canonicaliseLocalisedRoutes)(routes);
93
95
  const rootLikeRoute = getRootLikeRouteObject(routes);
94
96
  const rootRevalidationState = {};
95
97
  const rootShouldRevalidate = rootLikeRoute?.shouldRevalidate;
@@ -55,6 +55,7 @@ function withGlobalLayout(Component, globalApp) {
55
55
  }
56
56
  function toTanstackRouteObject(route, globalApp) {
57
57
  if ('nested' === route.type) return {
58
+ modernLocalisedRoute: Reflect.get(route, 'modernLocalisedRoute'),
58
59
  path: route.path,
59
60
  id: route.id,
60
61
  loader: route.loader,
@@ -79,6 +80,7 @@ function toTanstackRouteObject(route, globalApp) {
79
80
  if ('function' != typeof route.component && 'object' != typeof route.component) return null;
80
81
  const LayoutComponent = withGlobalLayout(route.component, globalApp);
81
82
  return {
83
+ modernLocalisedRoute: Reflect.get(route, 'modernLocalisedRoute'),
82
84
  path: route.path,
83
85
  element: /*#__PURE__*/ (external_react_default()).createElement(LayoutComponent)
84
86
  };
@@ -1,8 +1,10 @@
1
+ import { canonicaliseLocalisedRoutes } from "@modern-js/i18n-runtime-extensions";
1
2
  import { makeLegalIdentifier } from "@modern-js/runtime/cli";
2
3
  import { upath } from "@modern-js/utils";
3
4
  import { createRouteStaticDataSnippet, isIndexRoute, isPathlessLayout, normalizeRelativeImport, pickModernLoaderModule, pickRouteSearchContractModules, quote, resolveFileNoExt, toTanstackPath } from "./shared.mjs";
4
5
  async function generateTanstackRouterTypesSourceForEntry(opts) {
5
- const { appContext, entryName, generatedDirName = 'modern-tanstack', routes } = opts;
6
+ const { appContext, entryName, generatedDirName = 'modern-tanstack', routes: rawRoutes } = opts;
7
+ const routes = canonicaliseLocalisedRoutes(rawRoutes);
6
8
  const outDir = upath.join(appContext.srcDirectory, generatedDirName, entryName);
7
9
  const rootModern = routes.find((r)=>{
8
10
  const extras = r;
@@ -1,16 +1,19 @@
1
+ import { createLocalisedRouteRewrite } from "@modern-js/i18n-runtime-extensions";
2
+ import { composeRewrites } from "@tanstack/react-router";
1
3
  function normalizeBasepath(basepath) {
2
4
  if (!basepath) return '/';
3
5
  let normalized = basepath.startsWith('/') ? basepath : `/${basepath}`;
4
6
  if (normalized.length > 1 && normalized.endsWith('/')) normalized = normalized.slice(0, -1);
5
7
  return normalized || '/';
6
8
  }
7
- function createModernBasepathRewrite(basepath, caseSensitive = false) {
9
+ function createModernBasepathRewrite(basepath, caseSensitive = false, routes = []) {
10
+ const localisedRewrite = createLocalisedRouteRewrite(routes);
8
11
  const normalizedBasepath = normalizeBasepath(basepath);
9
- if ('/' === normalizedBasepath) return;
12
+ if ('/' === normalizedBasepath) return localisedRewrite;
10
13
  const normalizedBasepathWithSlash = `${normalizedBasepath}/`;
11
14
  const checkBasepath = caseSensitive ? normalizedBasepath : normalizedBasepath.toLowerCase();
12
15
  const checkBasepathWithSlash = caseSensitive ? normalizedBasepathWithSlash : normalizedBasepathWithSlash.toLowerCase();
13
- return {
16
+ const basepathRewrite = {
14
17
  input: ({ url })=>{
15
18
  const pathname = caseSensitive ? url.pathname : url.pathname.toLowerCase();
16
19
  if (pathname === checkBasepath) url.pathname = '/';
@@ -24,5 +27,9 @@ function createModernBasepathRewrite(basepath, caseSensitive = false) {
24
27
  return url;
25
28
  }
26
29
  };
30
+ return localisedRewrite ? composeRewrites([
31
+ basepathRewrite,
32
+ localisedRewrite
33
+ ]) : basepathRewrite;
27
34
  }
28
35
  export { createModernBasepathRewrite };
@@ -77,7 +77,7 @@ const tanstackRouterPlugin = (userConfig = {})=>{
77
77
  const mergedConfig = getMergedConfig();
78
78
  const { supportHtml5History = true } = mergedConfig;
79
79
  const history = supportHtml5History ? createBrowserHistory() : createHashHistory();
80
- const rewrite = createModernBasepathRewrite(_basename);
80
+ const rewrite = createModernBasepathRewrite(_basename, false, getRouteObjects());
81
81
  const serializationAdapters = getGlobalEnableRsc() ? getTanstackRscSerializationAdapters() : void 0;
82
82
  const routerFastDefaults = getModernTanstackRouterFastDefaults(mergedConfig);
83
83
  cachedRouter = createRouter({
@@ -10,6 +10,7 @@ import { routerProviderRegistryHooks } from "./hooks.mjs";
10
10
  import { wrapTanstackSsrHydrationBoundary } from "./hydrationBoundary.mjs";
11
11
  import { applyRouterServerPrepareResult, createRouterServerSnapshot, getRouterRuntimeState } from "./lifecycle.mjs";
12
12
  import { createTanstackRouteObjects, getTanstackRouteConfig, joinBasename } from "./pluginShared.mjs";
13
+ import { Link } from "./prefetchLink.mjs";
13
14
  import { createRouteTreeFromRouteObjects, getModernRouteIdsFromMatches } from "./routeTree/index.mjs";
14
15
  import { createTanstackRscServerPayload, handleTanstackRscRedirect } from "./rsc/payloadRouter.mjs";
15
16
  import { getTanstackRscRouter, setTanstackRscRouter, setTanstackRscServerPayload } from "./rsc/payloadStorage.mjs";
@@ -35,6 +36,9 @@ const tanstackRouterPlugin = (userConfig = {})=>{
35
36
  routeConfig
36
37
  });
37
38
  if (!modifiedRouteObjects.length) return;
39
+ context.router = {
40
+ Link: Link
41
+ };
38
42
  const { request, nonce, baseUrl, loaderFailureMode = 'errorBoundary' } = context.ssrContext;
39
43
  const _basename = '/' === baseUrl ? joinBasename(baseUrl, basename || '') : baseUrl;
40
44
  const rawRequest = request.raw;
@@ -57,7 +61,7 @@ const tanstackRouterPlugin = (userConfig = {})=>{
57
61
  initialHref
58
62
  ]
59
63
  });
60
- const rewrite = createModernBasepathRewrite(_basename);
64
+ const rewrite = createModernBasepathRewrite(_basename, false, modifiedRouteObjects);
61
65
  const routerLifecycleContext = {
62
66
  framework: 'tanstack',
63
67
  phase: 'ssr-prepare',
@@ -1,3 +1,4 @@
1
+ import { canonicaliseLocalisedRoutes } from "@modern-js/i18n-runtime-extensions";
1
2
  import { DefaultNotFound } from "@modern-js/runtime/context";
2
3
  import { rootRouteId } from "@tanstack/react-router";
3
4
  import { createRouteStaticData } from "../../shared/routeStaticData.mjs";
@@ -57,6 +58,7 @@ function getRootLikeRouteObject(routes) {
57
58
  return routes.find((route)=>'/' === route.path && !route.index);
58
59
  }
59
60
  function createRouteTreeFromRouteObjects(routes, options = {}) {
61
+ routes = canonicaliseLocalisedRoutes(routes);
60
62
  const rootLikeRoute = getRootLikeRouteObject(routes);
61
63
  const rootRevalidationState = {};
62
64
  const rootShouldRevalidate = rootLikeRoute?.shouldRevalidate;
@@ -13,6 +13,7 @@ function withGlobalLayout(Component, globalApp) {
13
13
  }
14
14
  function toTanstackRouteObject(route, globalApp) {
15
15
  if ('nested' === route.type) return {
16
+ modernLocalisedRoute: Reflect.get(route, 'modernLocalisedRoute'),
16
17
  path: route.path,
17
18
  id: route.id,
18
19
  loader: route.loader,
@@ -37,6 +38,7 @@ function toTanstackRouteObject(route, globalApp) {
37
38
  if ('function' != typeof route.component && 'object' != typeof route.component) return null;
38
39
  const LayoutComponent = withGlobalLayout(route.component, globalApp);
39
40
  return {
41
+ modernLocalisedRoute: Reflect.get(route, 'modernLocalisedRoute'),
40
42
  path: route.path,
41
43
  element: /*#__PURE__*/ react.createElement(LayoutComponent)
42
44
  };
@@ -1,9 +1,11 @@
1
1
  import "node:module";
2
+ import { canonicaliseLocalisedRoutes } from "@modern-js/i18n-runtime-extensions";
2
3
  import { makeLegalIdentifier } from "@modern-js/runtime/cli";
3
4
  import { upath } from "@modern-js/utils";
4
5
  import { createRouteStaticDataSnippet, isIndexRoute, isPathlessLayout, normalizeRelativeImport, pickModernLoaderModule, pickRouteSearchContractModules, quote, resolveFileNoExt, toTanstackPath } from "./shared.mjs";
5
6
  async function generateTanstackRouterTypesSourceForEntry(opts) {
6
- const { appContext, entryName, generatedDirName = 'modern-tanstack', routes } = opts;
7
+ const { appContext, entryName, generatedDirName = 'modern-tanstack', routes: rawRoutes } = opts;
8
+ const routes = canonicaliseLocalisedRoutes(rawRoutes);
7
9
  const outDir = upath.join(appContext.srcDirectory, generatedDirName, entryName);
8
10
  const rootModern = routes.find((r)=>{
9
11
  const extras = r;
@@ -1,17 +1,20 @@
1
1
  import "node:module";
2
+ import { createLocalisedRouteRewrite } from "@modern-js/i18n-runtime-extensions";
3
+ import { composeRewrites } from "@tanstack/react-router";
2
4
  function normalizeBasepath(basepath) {
3
5
  if (!basepath) return '/';
4
6
  let normalized = basepath.startsWith('/') ? basepath : `/${basepath}`;
5
7
  if (normalized.length > 1 && normalized.endsWith('/')) normalized = normalized.slice(0, -1);
6
8
  return normalized || '/';
7
9
  }
8
- function createModernBasepathRewrite(basepath, caseSensitive = false) {
10
+ function createModernBasepathRewrite(basepath, caseSensitive = false, routes = []) {
11
+ const localisedRewrite = createLocalisedRouteRewrite(routes);
9
12
  const normalizedBasepath = normalizeBasepath(basepath);
10
- if ('/' === normalizedBasepath) return;
13
+ if ('/' === normalizedBasepath) return localisedRewrite;
11
14
  const normalizedBasepathWithSlash = `${normalizedBasepath}/`;
12
15
  const checkBasepath = caseSensitive ? normalizedBasepath : normalizedBasepath.toLowerCase();
13
16
  const checkBasepathWithSlash = caseSensitive ? normalizedBasepathWithSlash : normalizedBasepathWithSlash.toLowerCase();
14
- return {
17
+ const basepathRewrite = {
15
18
  input: ({ url })=>{
16
19
  const pathname = caseSensitive ? url.pathname : url.pathname.toLowerCase();
17
20
  if (pathname === checkBasepath) url.pathname = '/';
@@ -25,5 +28,9 @@ function createModernBasepathRewrite(basepath, caseSensitive = false) {
25
28
  return url;
26
29
  }
27
30
  };
31
+ return localisedRewrite ? composeRewrites([
32
+ basepathRewrite,
33
+ localisedRewrite
34
+ ]) : basepathRewrite;
28
35
  }
29
36
  export { createModernBasepathRewrite };
@@ -78,7 +78,7 @@ const tanstackRouterPlugin = (userConfig = {})=>{
78
78
  const mergedConfig = getMergedConfig();
79
79
  const { supportHtml5History = true } = mergedConfig;
80
80
  const history = supportHtml5History ? createBrowserHistory() : createHashHistory();
81
- const rewrite = createModernBasepathRewrite(_basename);
81
+ const rewrite = createModernBasepathRewrite(_basename, false, getRouteObjects());
82
82
  const serializationAdapters = getGlobalEnableRsc() ? getTanstackRscSerializationAdapters() : void 0;
83
83
  const routerFastDefaults = getModernTanstackRouterFastDefaults(mergedConfig);
84
84
  cachedRouter = createRouter({
@@ -11,6 +11,7 @@ import { routerProviderRegistryHooks } from "./hooks.mjs";
11
11
  import { wrapTanstackSsrHydrationBoundary } from "./hydrationBoundary.mjs";
12
12
  import { applyRouterServerPrepareResult, createRouterServerSnapshot, getRouterRuntimeState } from "./lifecycle.mjs";
13
13
  import { createTanstackRouteObjects, getTanstackRouteConfig, joinBasename } from "./pluginShared.mjs";
14
+ import { Link } from "./prefetchLink.mjs";
14
15
  import { createRouteTreeFromRouteObjects, getModernRouteIdsFromMatches } from "./routeTree/index.mjs";
15
16
  import { createTanstackRscServerPayload, handleTanstackRscRedirect } from "./rsc/payloadRouter.mjs";
16
17
  import { getTanstackRscRouter, setTanstackRscRouter, setTanstackRscServerPayload } from "./rsc/payloadStorage.mjs";
@@ -36,6 +37,9 @@ const tanstackRouterPlugin = (userConfig = {})=>{
36
37
  routeConfig
37
38
  });
38
39
  if (!modifiedRouteObjects.length) return;
40
+ context.router = {
41
+ Link: Link
42
+ };
39
43
  const { request, nonce, baseUrl, loaderFailureMode = 'errorBoundary' } = context.ssrContext;
40
44
  const _basename = '/' === baseUrl ? joinBasename(baseUrl, basename || '') : baseUrl;
41
45
  const rawRequest = request.raw;
@@ -58,7 +62,7 @@ const tanstackRouterPlugin = (userConfig = {})=>{
58
62
  initialHref
59
63
  ]
60
64
  });
61
- const rewrite = createModernBasepathRewrite(_basename);
65
+ const rewrite = createModernBasepathRewrite(_basename, false, modifiedRouteObjects);
62
66
  const routerLifecycleContext = {
63
67
  framework: 'tanstack',
64
68
  phase: 'ssr-prepare',
@@ -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;
@@ -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;
package/package.json CHANGED
@@ -18,7 +18,7 @@
18
18
  "modern.js",
19
19
  "tanstack-router"
20
20
  ],
21
- "version": "3.9.0-ultramodern.2",
21
+ "version": "3.9.0-ultramodern.4",
22
22
  "engines": {
23
23
  "node": ">=20"
24
24
  },
@@ -94,17 +94,18 @@
94
94
  }
95
95
  },
96
96
  "dependencies": {
97
- "@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.9.0-ultramodern.2",
98
- "@modern-js/runtime-utils": "npm:@bleedingdev/modern-js-runtime-utils@3.9.0-ultramodern.2",
99
- "@modern-js/types": "npm:@bleedingdev/modern-js-types@3.9.0-ultramodern.2",
100
- "@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.9.0-ultramodern.2",
97
+ "@modern-js/i18n-runtime-extensions": "npm:@bleedingdev/modern-js-i18n-runtime-extensions@3.9.0-ultramodern.4",
98
+ "@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.9.0-ultramodern.4",
99
+ "@modern-js/runtime-utils": "npm:@bleedingdev/modern-js-runtime-utils@3.9.0-ultramodern.4",
100
+ "@modern-js/types": "npm:@bleedingdev/modern-js-types@3.9.0-ultramodern.4",
101
+ "@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.9.0-ultramodern.4",
101
102
  "@swc/helpers": "^0.5.23",
102
103
  "@tanstack/react-router": "1.170.33",
103
104
  "@tanstack/router-core": "1.171.28"
104
105
  },
105
106
  "peerDependencies": {
106
- "@modern-js/app-tools": "3.9.0-ultramodern.2",
107
- "@modern-js/runtime": "3.9.0-ultramodern.2",
107
+ "@modern-js/app-tools": "3.9.0-ultramodern.4",
108
+ "@modern-js/runtime": "3.9.0-ultramodern.4",
108
109
  "react": "^19.2.8",
109
110
  "react-dom": "^19.2.8",
110
111
  "react-server-dom-rspack": "0.1.0"
@@ -115,8 +116,8 @@
115
116
  }
116
117
  },
117
118
  "devDependencies": {
118
- "@modern-js/app-tools": "npm:@bleedingdev/modern-js-app-tools@3.9.0-ultramodern.2",
119
- "@modern-js/runtime": "npm:@bleedingdev/modern-js-runtime@3.9.0-ultramodern.2",
119
+ "@modern-js/app-tools": "npm:@bleedingdev/modern-js-app-tools@3.9.0-ultramodern.4",
120
+ "@modern-js/runtime": "npm:@bleedingdev/modern-js-runtime@3.9.0-ultramodern.4",
120
121
  "@rslib/core": "1.0.0",
121
122
  "@scripts/rstest-config": "2.66.0",
122
123
  "@tanstack/history": "1.162.2",
package/rstest.config.mts CHANGED
@@ -31,6 +31,7 @@ export default {
31
31
  'tests/router/generateRouteArtifacts.test.ts',
32
32
  'tests/router/hooks.test.ts',
33
33
  'tests/router/loaderBridge.test.ts',
34
+ 'tests/router/localisedIdentity.test.tsx',
34
35
  'tests/router/packageSurface.test.ts',
35
36
  'tests/router/preloadRedirect.test.ts',
36
37
  'tests/router/register.test.ts',
@@ -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
  }
@@ -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,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;
@@ -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({
@@ -624,7 +624,9 @@ describe('tanstack router cli plugin', () => {
624
624
  // the old `.replace('cjs', 'esm')` never matched the bundled dist/esm
625
625
  // runtime when the CLI was loaded through the ESM condition.
626
626
  expect(stringEntries[0]).toBe(path.resolve(__dirname, '..', '..'));
627
- expect(stringEntries[0]).not.toContain('esm');
627
+ expect(stringEntries[0]).not.toMatch(
628
+ /[\\/]dist[\\/]esm(?:-node)?(?:[\\/]|$)/,
629
+ );
628
630
  });
629
631
 
630
632
  test('emits the plugin-i18n augmentation only when plugin-i18n is registered', async () => {
@@ -0,0 +1,95 @@
1
+ import { applyLocalisedUrlsToRoutes } from '@modern-js/i18n-runtime-extensions';
2
+ import {
3
+ createMemoryHistory,
4
+ createRouter,
5
+ Outlet,
6
+ RouterProvider,
7
+ useLoaderData,
8
+ useParams,
9
+ } from '@tanstack/react-router';
10
+ import { createElement } from 'react';
11
+ import { renderToString } from 'react-dom/server';
12
+ import { createModernBasepathRewrite } from '../../src/runtime/basepathRewrite';
13
+ import { createRouteTreeFromRouteObjects } from '../../src/runtime/routeTree';
14
+ import { createTanstackRouteObjectsFromConfig } from '../../src/runtime/utils';
15
+
16
+ function SearchPage() {
17
+ const data = useLoaderData({ from: '/$lang/search' }) as { language: string };
18
+ return createElement('p', null, `search:${data.language}`);
19
+ }
20
+
21
+ function ResourcePage() {
22
+ const params = useParams({ from: '/$lang/resources/$id' }) as { id: string };
23
+ return createElement('p', null, `resource:${params.id}`);
24
+ }
25
+
26
+ test('localized URLs preserve native loader and params route identity in SSR and navigation', async () => {
27
+ const routes = applyLocalisedUrlsToRoutes(
28
+ [
29
+ {
30
+ type: 'nested',
31
+ id: 'lang',
32
+ path: ':lang',
33
+ component: Outlet,
34
+ children: [
35
+ {
36
+ type: 'nested',
37
+ id: 'search',
38
+ path: 'search',
39
+ component: SearchPage,
40
+ loader: ({ params }: { params: { lang: string } }) => ({
41
+ language: params.lang,
42
+ }),
43
+ },
44
+ {
45
+ type: 'nested',
46
+ id: 'resource',
47
+ path: 'resources/:id',
48
+ component: ResourcePage,
49
+ },
50
+ ],
51
+ },
52
+ ],
53
+ ['en', 'cs'],
54
+ {
55
+ '/search': { en: '/find', cs: '/hledat' },
56
+ '/resources/:id': { en: '/resources/:id', cs: '/zdroje/:id' },
57
+ },
58
+ );
59
+ const routeObjects = createTanstackRouteObjectsFromConfig({
60
+ routesConfig: { routes },
61
+ })!;
62
+ const history = createMemoryHistory({
63
+ initialEntries: ['/base/cs/hledat?q=tractor#results'],
64
+ });
65
+ const router = createRouter({
66
+ context: {
67
+ request: new Request(
68
+ 'https://example.test/base/cs/hledat?q=tractor#results',
69
+ ),
70
+ },
71
+ routeTree: createRouteTreeFromRouteObjects(routeObjects),
72
+ history,
73
+ rewrite: createModernBasepathRewrite('/base', false, routeObjects),
74
+ });
75
+ await router.load();
76
+ expect(router.state.matches.at(-1)?.routeId).toBe('/$lang/search');
77
+ expect(renderToString(createElement(RouterProvider, { router }))).toContain(
78
+ 'search:cs',
79
+ );
80
+ expect(history.location.href).toBe('/base/cs/hledat?q=tractor#results');
81
+
82
+ await router.navigate({ to: '/en/search', search: { q: 'tractor' } });
83
+ expect(history.location.href).toBe('/base/en/find?q=tractor');
84
+ expect(router.state.matches.at(-1)?.routeId).toBe('/$lang/search');
85
+ expect(renderToString(createElement(RouterProvider, { router }))).toContain(
86
+ 'search:en',
87
+ );
88
+
89
+ await router.navigate({ to: '/cs/zdroje/a%2Fb' });
90
+ expect(history.location.href).toBe('/base/cs/zdroje/a%2Fb');
91
+ expect(router.state.matches.at(-1)?.routeId).toBe('/$lang/resources/$id');
92
+ expect(renderToString(createElement(RouterProvider, { router }))).toContain(
93
+ 'resource:a/b',
94
+ );
95
+ });
@@ -1,6 +1,12 @@
1
1
  import type { TInternalRuntimeContext } from '@modern-js/runtime/context';
2
2
  import { routerProviderRegistryHooks } from '@modern-js/runtime/context';
3
- import { getRouterServerSnapshot } from '../../src/runtime/lifecycle';
3
+ import { type AnyRouter, RouterProvider } from '@tanstack/react-router';
4
+ import { createElement } from 'react';
5
+ import { renderToString } from 'react-dom/server';
6
+ import {
7
+ getRouterRuntimeState,
8
+ getRouterServerSnapshot,
9
+ } from '../../src/runtime/lifecycle';
4
10
  import { tanstackRouterPlugin } from '../../src/runtime/plugin.node';
5
11
  import type { TanstackRouterPluginAPI } from '../../src/runtime/pluginShared';
6
12
  import type { RouterConfig } from '../../src/runtime/types';
@@ -50,6 +56,42 @@ describe('tanstack server plugin router results', () => {
50
56
  rstest.restoreAllMocks();
51
57
  });
52
58
 
59
+ test.each([
60
+ '/cs/login',
61
+ '/en/login',
62
+ ])('renders native link active attributes during SSR at %s', async pathname => {
63
+ const { context } = createServerContext(pathname);
64
+ const beforeRender = collectBeforeRender(() => [
65
+ {
66
+ id: 'login',
67
+ path: '/:lang/login',
68
+ Component: () => {
69
+ const Link = context.router?.Link;
70
+ if (!Link) {
71
+ throw new Error('SSR router did not provide its native Link');
72
+ }
73
+ return createElement(Link, { to: '/cs' }, 'Home');
74
+ },
75
+ },
76
+ ]);
77
+ await beforeRender(context, value => value);
78
+
79
+ expect(context.router?.Link).toBeTypeOf('function');
80
+ const html = renderToString(
81
+ createElement(RouterProvider, {
82
+ router: getRouterRuntimeState(context)?.instance as AnyRouter,
83
+ }),
84
+ );
85
+ expect(html).toContain('href="/cs"');
86
+ if (pathname === '/cs/login') {
87
+ expect(html).toContain('data-status="active"');
88
+ expect(html).toContain('aria-current="page"');
89
+ } else {
90
+ expect(html).not.toContain('data-status="active"');
91
+ expect(html).not.toContain('aria-current="page"');
92
+ }
93
+ });
94
+
53
95
  test('uses the router render result as the HTTP and hydration status', async () => {
54
96
  const beforeRender = collectBeforeRender(() => [
55
97
  {