@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.
- package/dist/cjs/cli/tanstackTypes/source.js +3 -1
- package/dist/cjs/runtime/basepathRewrite.js +13 -6
- package/dist/cjs/runtime/plugin.js +1 -1
- package/dist/cjs/runtime/plugin.node.js +5 -1
- package/dist/cjs/runtime/routeTree/construction.js +2 -0
- package/dist/cjs/runtime/utils.js +2 -0
- package/dist/esm/cli/tanstackTypes/source.mjs +3 -1
- package/dist/esm/runtime/basepathRewrite.mjs +10 -3
- package/dist/esm/runtime/plugin.mjs +1 -1
- package/dist/esm/runtime/plugin.node.mjs +5 -1
- package/dist/esm/runtime/routeTree/construction.mjs +2 -0
- package/dist/esm/runtime/utils.mjs +2 -0
- package/dist/esm-node/cli/tanstackTypes/source.mjs +3 -1
- package/dist/esm-node/runtime/basepathRewrite.mjs +10 -3
- package/dist/esm-node/runtime/plugin.mjs +1 -1
- package/dist/esm-node/runtime/plugin.node.mjs +5 -1
- package/dist/esm-node/runtime/routeTree/construction.mjs +2 -0
- package/dist/esm-node/runtime/utils.mjs +2 -0
- package/dist/types/runtime/basepathRewrite.d.ts +2 -1
- package/package.json +10 -9
- package/rstest.config.mts +1 -0
- package/src/cli/tanstackTypes/source.ts +3 -1
- package/src/runtime/basepathRewrite.ts +11 -2
- package/src/runtime/plugin.node.tsx +8 -1
- package/src/runtime/plugin.tsx +5 -1
- package/src/runtime/routeTree/construction.ts +2 -0
- package/src/runtime/utils.tsx +3 -1
- package/tests/router/cli.test.ts +3 -1
- package/tests/router/localisedIdentity.test.tsx +95 -0
- package/tests/router/serverPlugin.test.ts +43 -1
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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/
|
|
98
|
-
"@modern-js/
|
|
99
|
-
"@modern-js/
|
|
100
|
-
"@modern-js/
|
|
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.
|
|
107
|
-
"@modern-js/runtime": "3.9.0-ultramodern.
|
|
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.
|
|
119
|
-
"@modern-js/runtime": "npm:@bleedingdev/modern-js-runtime@3.9.0-ultramodern.
|
|
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
|
|
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
|
-
|
|
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(
|
|
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',
|
package/src/runtime/plugin.tsx
CHANGED
|
@@ -149,7 +149,11 @@ export const tanstackRouterPlugin = (
|
|
|
149
149
|
const history = supportHtml5History
|
|
150
150
|
? createBrowserHistory()
|
|
151
151
|
: createHashHistory();
|
|
152
|
-
const rewrite = createModernBasepathRewrite(
|
|
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;
|
package/src/runtime/utils.tsx
CHANGED
|
@@ -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({
|
package/tests/router/cli.test.ts
CHANGED
|
@@ -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.
|
|
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 {
|
|
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
|
{
|