@bleedingdev/modern-js-plugin-tanstack 3.2.0-ultramodern.6 → 3.2.0-ultramodern.62
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.js +20 -6
- package/dist/cjs/runtime/plugin.js +2 -0
- package/dist/cjs/runtime/plugin.node.js +17 -1
- package/dist/cjs/runtime/plugin.worker.js +49 -0
- package/dist/cjs/runtime/routeTree.js +38 -2
- package/dist/esm/cli/tanstackTypes.mjs +20 -6
- package/dist/esm/runtime/plugin.mjs +2 -0
- package/dist/esm/runtime/plugin.node.mjs +17 -1
- package/dist/esm/runtime/plugin.worker.mjs +1 -0
- package/dist/esm/runtime/routeTree.mjs +38 -2
- package/dist/esm-node/cli/tanstackTypes.mjs +20 -6
- package/dist/esm-node/runtime/plugin.mjs +2 -0
- package/dist/esm-node/runtime/plugin.node.mjs +17 -1
- package/dist/esm-node/runtime/plugin.worker.mjs +2 -0
- package/dist/esm-node/runtime/routeTree.mjs +38 -2
- package/dist/types/runtime/plugin.worker.d.ts +1 -0
- package/package.json +14 -14
- package/src/cli/tanstackTypes.ts +33 -9
- package/src/runtime/plugin.node.tsx +42 -1
- package/src/runtime/plugin.tsx +2 -0
- package/src/runtime/plugin.worker.tsx +4 -0
- package/src/runtime/routeTree.ts +101 -2
- package/tests/router/routeTree.test.ts +72 -1
- package/tests/router/tanstackTypes.test.ts +64 -0
|
@@ -127,6 +127,7 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
|
|
|
127
127
|
const imports = [];
|
|
128
128
|
const statements = [];
|
|
129
129
|
const loaderImportMap = new Map();
|
|
130
|
+
const usedRouteVarNames = new Set();
|
|
130
131
|
let loaderIndex = 0;
|
|
131
132
|
let routeIndex = 0;
|
|
132
133
|
const getImportNamesForLoader = async (aliasedNoExtPath, inline, hasAction)=>{
|
|
@@ -160,10 +161,17 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
|
|
|
160
161
|
actionName
|
|
161
162
|
};
|
|
162
163
|
};
|
|
164
|
+
const reserveRouteVarName = (preferred)=>{
|
|
165
|
+
let candidate = preferred;
|
|
166
|
+
let suffix = 1;
|
|
167
|
+
while(usedRouteVarNames.has(candidate))candidate = `${preferred}_${suffix++}`;
|
|
168
|
+
usedRouteVarNames.add(candidate);
|
|
169
|
+
return candidate;
|
|
170
|
+
};
|
|
163
171
|
const createRouteVarName = (route)=>{
|
|
164
172
|
const id = route.id;
|
|
165
173
|
const base = id ? makeLegalIdentifier(id) : `r_${routeIndex++}`;
|
|
166
|
-
return `route_${base}
|
|
174
|
+
return reserveRouteVarName(`route_${base}`);
|
|
167
175
|
};
|
|
168
176
|
const buildRoute = async (opts)=>{
|
|
169
177
|
const { parentVar, route } = opts;
|
|
@@ -192,14 +200,16 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
|
|
|
192
200
|
actionName
|
|
193
201
|
});
|
|
194
202
|
if (staticDataSnippet) routeOpts.push(staticDataSnippet);
|
|
195
|
-
statements.push(`const ${varName} = createRoute({\n ${routeOpts.join('\n ')}\n});`);
|
|
196
203
|
const children = route.children;
|
|
204
|
+
const hasChildren = Boolean(children && children.length > 0);
|
|
205
|
+
const routeCtorVarName = hasChildren ? reserveRouteVarName(`${varName}__base`) : varName;
|
|
206
|
+
statements.push(`const ${routeCtorVarName} = createRoute({\n ${routeOpts.join('\n ')}\n});`);
|
|
197
207
|
if (children && children.length > 0) {
|
|
198
208
|
const childVars = await Promise.all(children.map((child)=>buildRoute({
|
|
199
|
-
parentVar:
|
|
209
|
+
parentVar: routeCtorVarName,
|
|
200
210
|
route: child
|
|
201
211
|
})));
|
|
202
|
-
statements.push(
|
|
212
|
+
statements.push(`const ${varName} = ${routeCtorVarName}.addChildren([${childVars.join(', ')}]);`);
|
|
203
213
|
}
|
|
204
214
|
return varName;
|
|
205
215
|
};
|
|
@@ -272,7 +282,11 @@ function createRouteStaticData(opts: {
|
|
|
272
282
|
modernRouteAction?: unknown;
|
|
273
283
|
modernRouteLoader?: unknown;
|
|
274
284
|
}) {
|
|
275
|
-
const staticData:
|
|
285
|
+
const staticData: {
|
|
286
|
+
modernRouteId?: string;
|
|
287
|
+
modernRouteAction?: unknown;
|
|
288
|
+
modernRouteLoader?: unknown;
|
|
289
|
+
} = {};
|
|
276
290
|
|
|
277
291
|
if (opts.modernRouteId) {
|
|
278
292
|
staticData.modernRouteId = opts.modernRouteId;
|
|
@@ -286,7 +300,7 @@ function createRouteStaticData(opts: {
|
|
|
286
300
|
staticData.modernRouteAction = opts.modernRouteAction;
|
|
287
301
|
}
|
|
288
302
|
|
|
289
|
-
return
|
|
303
|
+
return staticData;
|
|
290
304
|
}
|
|
291
305
|
|
|
292
306
|
function modernLoaderToTanstack<TLoader extends (args: any) => any>(
|
|
@@ -37,6 +37,7 @@ const external_react_namespaceObject = require("react");
|
|
|
37
37
|
const external_basepathRewrite_js_namespaceObject = require("./basepathRewrite.js");
|
|
38
38
|
const external_hooks_js_namespaceObject = require("./hooks.js");
|
|
39
39
|
const external_lifecycle_js_namespaceObject = require("./lifecycle.js");
|
|
40
|
+
const external_prefetchLink_js_namespaceObject = require("./prefetchLink.js");
|
|
40
41
|
const external_routeTree_js_namespaceObject = require("./routeTree.js");
|
|
41
42
|
const client_js_namespaceObject = require("./rsc/client.js");
|
|
42
43
|
const external_utils_js_namespaceObject = require("./utils.js");
|
|
@@ -102,6 +103,7 @@ const tanstackRouterPlugin = (userConfig = {})=>{
|
|
|
102
103
|
}
|
|
103
104
|
}
|
|
104
105
|
context.router = {
|
|
106
|
+
Link: external_prefetchLink_js_namespaceObject.Link,
|
|
105
107
|
useMatches: react_router_namespaceObject.useMatches,
|
|
106
108
|
useLocation: react_router_namespaceObject.useLocation,
|
|
107
109
|
useNavigate: react_router_namespaceObject.useNavigate,
|
|
@@ -34,7 +34,7 @@ const node_namespaceObject = require("@modern-js/runtime-utils/node");
|
|
|
34
34
|
const time_namespaceObject = require("@modern-js/runtime-utils/time");
|
|
35
35
|
const constants_namespaceObject = require("@modern-js/utils/universal/constants");
|
|
36
36
|
const react_router_namespaceObject = require("@tanstack/react-router");
|
|
37
|
-
const server_namespaceObject = require("@tanstack/
|
|
37
|
+
const server_namespaceObject = require("@tanstack/router-core/ssr/server");
|
|
38
38
|
const external_react_namespaceObject = require("react");
|
|
39
39
|
const external_basepathRewrite_js_namespaceObject = require("./basepathRewrite.js");
|
|
40
40
|
const external_hooks_js_namespaceObject = require("./hooks.js");
|
|
@@ -46,12 +46,28 @@ const setTanstackRscServerPayload = (payload)=>{
|
|
|
46
46
|
const storageContext = node_namespaceObject.storage.useContext?.();
|
|
47
47
|
if (storageContext) storageContext.serverPayload = payload;
|
|
48
48
|
};
|
|
49
|
+
function isPromiseLike(value) {
|
|
50
|
+
return Boolean(value && 'function' == typeof value.then);
|
|
51
|
+
}
|
|
49
52
|
function isPreloadableRouteComponent(component) {
|
|
50
53
|
if (!component || 'function' != typeof component) return false;
|
|
51
54
|
const preloadable = component;
|
|
52
55
|
return 'function' == typeof preloadable.load || 'function' == typeof preloadable.preload;
|
|
53
56
|
}
|
|
57
|
+
function isReactLazyRouteComponent(component) {
|
|
58
|
+
return Boolean(component) && 'object' == typeof component && 'function' == typeof component._init && '_payload' in component;
|
|
59
|
+
}
|
|
60
|
+
async function preloadReactLazyRouteComponent(component) {
|
|
61
|
+
try {
|
|
62
|
+
component._init?.(component._payload);
|
|
63
|
+
} catch (thrown) {
|
|
64
|
+
if (!isPromiseLike(thrown)) throw thrown;
|
|
65
|
+
await thrown;
|
|
66
|
+
component._init?.(component._payload);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
54
69
|
async function preloadRouteComponent(component) {
|
|
70
|
+
if (isReactLazyRouteComponent(component)) return void await preloadReactLazyRouteComponent(component);
|
|
55
71
|
if (!isPreloadableRouteComponent(component)) return;
|
|
56
72
|
if ('function' == typeof component.load) return void await component.load({});
|
|
57
73
|
await component.preload?.({});
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __webpack_require__ = {};
|
|
3
|
+
(()=>{
|
|
4
|
+
__webpack_require__.n = (module)=>{
|
|
5
|
+
var getter = module && module.__esModule ? ()=>module['default'] : ()=>module;
|
|
6
|
+
__webpack_require__.d(getter, {
|
|
7
|
+
a: getter
|
|
8
|
+
});
|
|
9
|
+
return getter;
|
|
10
|
+
};
|
|
11
|
+
})();
|
|
12
|
+
(()=>{
|
|
13
|
+
__webpack_require__.d = (exports1, definition)=>{
|
|
14
|
+
for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
|
|
15
|
+
enumerable: true,
|
|
16
|
+
get: definition[key]
|
|
17
|
+
});
|
|
18
|
+
};
|
|
19
|
+
})();
|
|
20
|
+
(()=>{
|
|
21
|
+
__webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
|
|
22
|
+
})();
|
|
23
|
+
(()=>{
|
|
24
|
+
__webpack_require__.r = (exports1)=>{
|
|
25
|
+
if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
|
|
26
|
+
value: 'Module'
|
|
27
|
+
});
|
|
28
|
+
Object.defineProperty(exports1, '__esModule', {
|
|
29
|
+
value: true
|
|
30
|
+
});
|
|
31
|
+
};
|
|
32
|
+
})();
|
|
33
|
+
var __webpack_exports__ = {};
|
|
34
|
+
__webpack_require__.r(__webpack_exports__);
|
|
35
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
36
|
+
default: ()=>external_plugin_node_js_default(),
|
|
37
|
+
tanstackRouterPlugin: ()=>external_plugin_node_js_namespaceObject.tanstackRouterPlugin
|
|
38
|
+
});
|
|
39
|
+
const external_plugin_node_js_namespaceObject = require("./plugin.node.js");
|
|
40
|
+
var external_plugin_node_js_default = /*#__PURE__*/ __webpack_require__.n(external_plugin_node_js_namespaceObject);
|
|
41
|
+
exports["default"] = __webpack_exports__["default"];
|
|
42
|
+
exports.tanstackRouterPlugin = __webpack_exports__.tanstackRouterPlugin;
|
|
43
|
+
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
44
|
+
"default",
|
|
45
|
+
"tanstackRouterPlugin"
|
|
46
|
+
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
47
|
+
Object.defineProperty(exports, '__esModule', {
|
|
48
|
+
value: true
|
|
49
|
+
});
|
|
@@ -29,6 +29,7 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
29
29
|
getModernRouteIdsFromMatches: ()=>getModernRouteIdsFromMatches
|
|
30
30
|
});
|
|
31
31
|
const react_router_namespaceObject = require("@tanstack/react-router");
|
|
32
|
+
const external_react_namespaceObject = require("react");
|
|
32
33
|
const external_DefaultNotFound_js_namespaceObject = require("./DefaultNotFound.js");
|
|
33
34
|
const payloadRouter_js_namespaceObject = require("./rsc/payloadRouter.js");
|
|
34
35
|
function createTanstackRoute(options) {
|
|
@@ -84,6 +85,33 @@ function normalizeModernLoaderResponse(result) {
|
|
|
84
85
|
}
|
|
85
86
|
return normalizeModernLoaderResult(result);
|
|
86
87
|
}
|
|
88
|
+
function pickRouteModuleComponent(routeModule) {
|
|
89
|
+
if ('function' == typeof routeModule || routeModule && 'object' == typeof routeModule && '$$typeof' in routeModule) return routeModule;
|
|
90
|
+
if (!routeModule || 'object' != typeof routeModule) return;
|
|
91
|
+
const module = routeModule;
|
|
92
|
+
const component = module.default || module.Component;
|
|
93
|
+
if ('function' == typeof component || component && 'object' == typeof component && '$$typeof' in component) return component;
|
|
94
|
+
}
|
|
95
|
+
function createServerLazyImportComponent(lazyImport, fallbackComponent) {
|
|
96
|
+
if ("u" > typeof document) return fallbackComponent;
|
|
97
|
+
let resolvedComponent;
|
|
98
|
+
let pendingLoad;
|
|
99
|
+
const load = async ()=>{
|
|
100
|
+
if (resolvedComponent) return resolvedComponent;
|
|
101
|
+
const routeModule = await lazyImport();
|
|
102
|
+
const component = pickRouteModuleComponent(routeModule);
|
|
103
|
+
if (component) resolvedComponent = component;
|
|
104
|
+
return resolvedComponent;
|
|
105
|
+
};
|
|
106
|
+
const Component = (props)=>{
|
|
107
|
+
if (resolvedComponent) return (0, external_react_namespaceObject.createElement)(resolvedComponent, props);
|
|
108
|
+
pendingLoad ||= load();
|
|
109
|
+
throw pendingLoad;
|
|
110
|
+
};
|
|
111
|
+
Component.load = load;
|
|
112
|
+
Component.preload = load;
|
|
113
|
+
return Component;
|
|
114
|
+
}
|
|
87
115
|
function isAbsoluteUrl(value) {
|
|
88
116
|
try {
|
|
89
117
|
new URL(value);
|
|
@@ -258,10 +286,18 @@ function wrapRouteObjectLoader(route, revalidationState, options = {}) {
|
|
|
258
286
|
}
|
|
259
287
|
function toRouteComponent(routeObject) {
|
|
260
288
|
const route = routeObject;
|
|
289
|
+
const lazyImport = 'function' == typeof route.lazyImport ? route.lazyImport : void 0;
|
|
290
|
+
const fallbackComponent = route.Component ? route.Component : route.element ? ()=>route.element : void 0;
|
|
291
|
+
if (lazyImport && fallbackComponent) return createServerLazyImportComponent(lazyImport, fallbackComponent);
|
|
261
292
|
if (route.Component) return route.Component;
|
|
262
293
|
const element = route.element;
|
|
263
294
|
if (element) return ()=>element;
|
|
264
295
|
}
|
|
296
|
+
function toModernRouteComponent(route) {
|
|
297
|
+
const component = route.component || void 0;
|
|
298
|
+
if ('function' == typeof route.lazyImport && component) return createServerLazyImportComponent(route.lazyImport, component);
|
|
299
|
+
return component;
|
|
300
|
+
}
|
|
265
301
|
function toErrorComponent(routeObject) {
|
|
266
302
|
const route = routeObject;
|
|
267
303
|
if (route.ErrorBoundary) return route.ErrorBoundary;
|
|
@@ -342,7 +378,7 @@ function createRouteFromModernRoute(opts) {
|
|
|
342
378
|
const stableFallbackId = modernId || route._component || route.filename || route.data || ('function' == typeof route.loader ? route.id : void 0);
|
|
343
379
|
const pendingComponent = route.loading || route.pendingComponent;
|
|
344
380
|
const errorComponent = route.error || route.errorComponent;
|
|
345
|
-
const component = route
|
|
381
|
+
const component = toModernRouteComponent(route);
|
|
346
382
|
const modernLoader = route.loader;
|
|
347
383
|
const modernAction = route.action;
|
|
348
384
|
const modernShouldRevalidate = route.shouldRevalidate;
|
|
@@ -389,7 +425,7 @@ function createRouteFromModernRoute(opts) {
|
|
|
389
425
|
}
|
|
390
426
|
function createRouteTreeFromModernRoutes(routes, options = {}) {
|
|
391
427
|
const rootModern = routes.find((r)=>r && 'nested' === r.type && r.isRoot);
|
|
392
|
-
const rootComponent = rootModern
|
|
428
|
+
const rootComponent = rootModern ? toModernRouteComponent(rootModern) : void 0;
|
|
393
429
|
const pendingComponent = rootModern?.loading;
|
|
394
430
|
const errorComponent = rootModern?.error;
|
|
395
431
|
const rootLoader = rootModern?.loader;
|
|
@@ -88,6 +88,7 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
|
|
|
88
88
|
const imports = [];
|
|
89
89
|
const statements = [];
|
|
90
90
|
const loaderImportMap = new Map();
|
|
91
|
+
const usedRouteVarNames = new Set();
|
|
91
92
|
let loaderIndex = 0;
|
|
92
93
|
let routeIndex = 0;
|
|
93
94
|
const getImportNamesForLoader = async (aliasedNoExtPath, inline, hasAction)=>{
|
|
@@ -121,10 +122,17 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
|
|
|
121
122
|
actionName
|
|
122
123
|
};
|
|
123
124
|
};
|
|
125
|
+
const reserveRouteVarName = (preferred)=>{
|
|
126
|
+
let candidate = preferred;
|
|
127
|
+
let suffix = 1;
|
|
128
|
+
while(usedRouteVarNames.has(candidate))candidate = `${preferred}_${suffix++}`;
|
|
129
|
+
usedRouteVarNames.add(candidate);
|
|
130
|
+
return candidate;
|
|
131
|
+
};
|
|
124
132
|
const createRouteVarName = (route)=>{
|
|
125
133
|
const id = route.id;
|
|
126
134
|
const base = id ? makeLegalIdentifier(id) : `r_${routeIndex++}`;
|
|
127
|
-
return `route_${base}
|
|
135
|
+
return reserveRouteVarName(`route_${base}`);
|
|
128
136
|
};
|
|
129
137
|
const buildRoute = async (opts)=>{
|
|
130
138
|
const { parentVar, route } = opts;
|
|
@@ -153,14 +161,16 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
|
|
|
153
161
|
actionName
|
|
154
162
|
});
|
|
155
163
|
if (staticDataSnippet) routeOpts.push(staticDataSnippet);
|
|
156
|
-
statements.push(`const ${varName} = createRoute({\n ${routeOpts.join('\n ')}\n});`);
|
|
157
164
|
const children = route.children;
|
|
165
|
+
const hasChildren = Boolean(children && children.length > 0);
|
|
166
|
+
const routeCtorVarName = hasChildren ? reserveRouteVarName(`${varName}__base`) : varName;
|
|
167
|
+
statements.push(`const ${routeCtorVarName} = createRoute({\n ${routeOpts.join('\n ')}\n});`);
|
|
158
168
|
if (children && children.length > 0) {
|
|
159
169
|
const childVars = await Promise.all(children.map((child)=>buildRoute({
|
|
160
|
-
parentVar:
|
|
170
|
+
parentVar: routeCtorVarName,
|
|
161
171
|
route: child
|
|
162
172
|
})));
|
|
163
|
-
statements.push(
|
|
173
|
+
statements.push(`const ${varName} = ${routeCtorVarName}.addChildren([${childVars.join(', ')}]);`);
|
|
164
174
|
}
|
|
165
175
|
return varName;
|
|
166
176
|
};
|
|
@@ -233,7 +243,11 @@ function createRouteStaticData(opts: {
|
|
|
233
243
|
modernRouteAction?: unknown;
|
|
234
244
|
modernRouteLoader?: unknown;
|
|
235
245
|
}) {
|
|
236
|
-
const staticData:
|
|
246
|
+
const staticData: {
|
|
247
|
+
modernRouteId?: string;
|
|
248
|
+
modernRouteAction?: unknown;
|
|
249
|
+
modernRouteLoader?: unknown;
|
|
250
|
+
} = {};
|
|
237
251
|
|
|
238
252
|
if (opts.modernRouteId) {
|
|
239
253
|
staticData.modernRouteId = opts.modernRouteId;
|
|
@@ -247,7 +261,7 @@ function createRouteStaticData(opts: {
|
|
|
247
261
|
staticData.modernRouteAction = opts.modernRouteAction;
|
|
248
262
|
}
|
|
249
263
|
|
|
250
|
-
return
|
|
264
|
+
return staticData;
|
|
251
265
|
}
|
|
252
266
|
|
|
253
267
|
function modernLoaderToTanstack<TLoader extends (args: any) => any>(
|
|
@@ -7,6 +7,7 @@ import { RouterClient } from "@tanstack/react-router/ssr/client";
|
|
|
7
7
|
import { createModernBasepathRewrite } from "./basepathRewrite.mjs";
|
|
8
8
|
import { modifyRoutes, onAfterCreateRouter, onAfterHydrateRouter, onBeforeCreateRouter, onBeforeCreateRoutes, onBeforeHydrateRouter } from "./hooks.mjs";
|
|
9
9
|
import { applyRouterRuntimeState } from "./lifecycle.mjs";
|
|
10
|
+
import { Link } from "./prefetchLink.mjs";
|
|
10
11
|
import { createRouteTreeFromRouteObjects } from "./routeTree.mjs";
|
|
11
12
|
import { getTanstackRscSerializationAdapters } from "./rsc/client.mjs";
|
|
12
13
|
import { createRouteObjectsFromConfig, urlJoin } from "./utils.mjs";
|
|
@@ -73,6 +74,7 @@ const tanstackRouterPlugin = (userConfig = {})=>{
|
|
|
73
74
|
}
|
|
74
75
|
}
|
|
75
76
|
context.router = {
|
|
77
|
+
Link: Link,
|
|
76
78
|
useMatches: useMatches,
|
|
77
79
|
useLocation: useLocation,
|
|
78
80
|
useNavigate: useNavigate,
|
|
@@ -5,7 +5,7 @@ import { createRequestContext, storage } from "@modern-js/runtime-utils/node";
|
|
|
5
5
|
import { time } from "@modern-js/runtime-utils/time";
|
|
6
6
|
import { LOADER_REPORTER_NAME } from "@modern-js/utils/universal/constants";
|
|
7
7
|
import { RouterProvider, createMemoryHistory, createRouter } from "@tanstack/react-router";
|
|
8
|
-
import { attachRouterServerSsrUtils } from "@tanstack/
|
|
8
|
+
import { attachRouterServerSsrUtils } from "@tanstack/router-core/ssr/server";
|
|
9
9
|
import { Suspense, useContext } from "react";
|
|
10
10
|
import { createModernBasepathRewrite } from "./basepathRewrite.mjs";
|
|
11
11
|
import { modifyRoutes, onAfterCreateRouter, onAfterHydrateRouter, onBeforeCreateRouter, onBeforeCreateRoutes, onBeforeHydrateRouter } from "./hooks.mjs";
|
|
@@ -17,12 +17,28 @@ const setTanstackRscServerPayload = (payload)=>{
|
|
|
17
17
|
const storageContext = storage.useContext?.();
|
|
18
18
|
if (storageContext) storageContext.serverPayload = payload;
|
|
19
19
|
};
|
|
20
|
+
function isPromiseLike(value) {
|
|
21
|
+
return Boolean(value && 'function' == typeof value.then);
|
|
22
|
+
}
|
|
20
23
|
function isPreloadableRouteComponent(component) {
|
|
21
24
|
if (!component || 'function' != typeof component) return false;
|
|
22
25
|
const preloadable = component;
|
|
23
26
|
return 'function' == typeof preloadable.load || 'function' == typeof preloadable.preload;
|
|
24
27
|
}
|
|
28
|
+
function isReactLazyRouteComponent(component) {
|
|
29
|
+
return Boolean(component) && 'object' == typeof component && 'function' == typeof component._init && '_payload' in component;
|
|
30
|
+
}
|
|
31
|
+
async function preloadReactLazyRouteComponent(component) {
|
|
32
|
+
try {
|
|
33
|
+
component._init?.(component._payload);
|
|
34
|
+
} catch (thrown) {
|
|
35
|
+
if (!isPromiseLike(thrown)) throw thrown;
|
|
36
|
+
await thrown;
|
|
37
|
+
component._init?.(component._payload);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
25
40
|
async function preloadRouteComponent(component) {
|
|
41
|
+
if (isReactLazyRouteComponent(component)) return void await preloadReactLazyRouteComponent(component);
|
|
26
42
|
if (!isPreloadableRouteComponent(component)) return;
|
|
27
43
|
if ('function' == typeof component.load) return void await component.load({});
|
|
28
44
|
await component.preload?.({});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default, tanstackRouterPlugin } from "./plugin.node.mjs";
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createRootRoute, createRoute, notFound, redirect } from "@tanstack/react-router";
|
|
2
|
+
import { createElement } from "react";
|
|
2
3
|
import { DefaultNotFound } from "./DefaultNotFound.mjs";
|
|
3
4
|
import { isTanstackRscPayloadNavigationEnabled, loadTanstackRscRouteData } from "./rsc/payloadRouter.mjs";
|
|
4
5
|
function createTanstackRoute(options) {
|
|
@@ -54,6 +55,33 @@ function normalizeModernLoaderResponse(result) {
|
|
|
54
55
|
}
|
|
55
56
|
return normalizeModernLoaderResult(result);
|
|
56
57
|
}
|
|
58
|
+
function pickRouteModuleComponent(routeModule) {
|
|
59
|
+
if ('function' == typeof routeModule || routeModule && 'object' == typeof routeModule && '$$typeof' in routeModule) return routeModule;
|
|
60
|
+
if (!routeModule || 'object' != typeof routeModule) return;
|
|
61
|
+
const module = routeModule;
|
|
62
|
+
const component = module.default || module.Component;
|
|
63
|
+
if ('function' == typeof component || component && 'object' == typeof component && '$$typeof' in component) return component;
|
|
64
|
+
}
|
|
65
|
+
function createServerLazyImportComponent(lazyImport, fallbackComponent) {
|
|
66
|
+
if ("u" > typeof document) return fallbackComponent;
|
|
67
|
+
let resolvedComponent;
|
|
68
|
+
let pendingLoad;
|
|
69
|
+
const load = async ()=>{
|
|
70
|
+
if (resolvedComponent) return resolvedComponent;
|
|
71
|
+
const routeModule = await lazyImport();
|
|
72
|
+
const component = pickRouteModuleComponent(routeModule);
|
|
73
|
+
if (component) resolvedComponent = component;
|
|
74
|
+
return resolvedComponent;
|
|
75
|
+
};
|
|
76
|
+
const Component = (props)=>{
|
|
77
|
+
if (resolvedComponent) return createElement(resolvedComponent, props);
|
|
78
|
+
pendingLoad ||= load();
|
|
79
|
+
throw pendingLoad;
|
|
80
|
+
};
|
|
81
|
+
Component.load = load;
|
|
82
|
+
Component.preload = load;
|
|
83
|
+
return Component;
|
|
84
|
+
}
|
|
57
85
|
function isAbsoluteUrl(value) {
|
|
58
86
|
try {
|
|
59
87
|
new URL(value);
|
|
@@ -228,10 +256,18 @@ function wrapRouteObjectLoader(route, revalidationState, options = {}) {
|
|
|
228
256
|
}
|
|
229
257
|
function toRouteComponent(routeObject) {
|
|
230
258
|
const route = routeObject;
|
|
259
|
+
const lazyImport = 'function' == typeof route.lazyImport ? route.lazyImport : void 0;
|
|
260
|
+
const fallbackComponent = route.Component ? route.Component : route.element ? ()=>route.element : void 0;
|
|
261
|
+
if (lazyImport && fallbackComponent) return createServerLazyImportComponent(lazyImport, fallbackComponent);
|
|
231
262
|
if (route.Component) return route.Component;
|
|
232
263
|
const element = route.element;
|
|
233
264
|
if (element) return ()=>element;
|
|
234
265
|
}
|
|
266
|
+
function toModernRouteComponent(route) {
|
|
267
|
+
const component = route.component || void 0;
|
|
268
|
+
if ('function' == typeof route.lazyImport && component) return createServerLazyImportComponent(route.lazyImport, component);
|
|
269
|
+
return component;
|
|
270
|
+
}
|
|
235
271
|
function toErrorComponent(routeObject) {
|
|
236
272
|
const route = routeObject;
|
|
237
273
|
if (route.ErrorBoundary) return route.ErrorBoundary;
|
|
@@ -312,7 +348,7 @@ function createRouteFromModernRoute(opts) {
|
|
|
312
348
|
const stableFallbackId = modernId || route._component || route.filename || route.data || ('function' == typeof route.loader ? route.id : void 0);
|
|
313
349
|
const pendingComponent = route.loading || route.pendingComponent;
|
|
314
350
|
const errorComponent = route.error || route.errorComponent;
|
|
315
|
-
const component = route
|
|
351
|
+
const component = toModernRouteComponent(route);
|
|
316
352
|
const modernLoader = route.loader;
|
|
317
353
|
const modernAction = route.action;
|
|
318
354
|
const modernShouldRevalidate = route.shouldRevalidate;
|
|
@@ -359,7 +395,7 @@ function createRouteFromModernRoute(opts) {
|
|
|
359
395
|
}
|
|
360
396
|
function createRouteTreeFromModernRoutes(routes, options = {}) {
|
|
361
397
|
const rootModern = routes.find((r)=>r && 'nested' === r.type && r.isRoot);
|
|
362
|
-
const rootComponent = rootModern
|
|
398
|
+
const rootComponent = rootModern ? toModernRouteComponent(rootModern) : void 0;
|
|
363
399
|
const pendingComponent = rootModern?.loading;
|
|
364
400
|
const errorComponent = rootModern?.error;
|
|
365
401
|
const rootLoader = rootModern?.loader;
|
|
@@ -89,6 +89,7 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
|
|
|
89
89
|
const imports = [];
|
|
90
90
|
const statements = [];
|
|
91
91
|
const loaderImportMap = new Map();
|
|
92
|
+
const usedRouteVarNames = new Set();
|
|
92
93
|
let loaderIndex = 0;
|
|
93
94
|
let routeIndex = 0;
|
|
94
95
|
const getImportNamesForLoader = async (aliasedNoExtPath, inline, hasAction)=>{
|
|
@@ -122,10 +123,17 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
|
|
|
122
123
|
actionName
|
|
123
124
|
};
|
|
124
125
|
};
|
|
126
|
+
const reserveRouteVarName = (preferred)=>{
|
|
127
|
+
let candidate = preferred;
|
|
128
|
+
let suffix = 1;
|
|
129
|
+
while(usedRouteVarNames.has(candidate))candidate = `${preferred}_${suffix++}`;
|
|
130
|
+
usedRouteVarNames.add(candidate);
|
|
131
|
+
return candidate;
|
|
132
|
+
};
|
|
125
133
|
const createRouteVarName = (route)=>{
|
|
126
134
|
const id = route.id;
|
|
127
135
|
const base = id ? makeLegalIdentifier(id) : `r_${routeIndex++}`;
|
|
128
|
-
return `route_${base}
|
|
136
|
+
return reserveRouteVarName(`route_${base}`);
|
|
129
137
|
};
|
|
130
138
|
const buildRoute = async (opts)=>{
|
|
131
139
|
const { parentVar, route } = opts;
|
|
@@ -154,14 +162,16 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
|
|
|
154
162
|
actionName
|
|
155
163
|
});
|
|
156
164
|
if (staticDataSnippet) routeOpts.push(staticDataSnippet);
|
|
157
|
-
statements.push(`const ${varName} = createRoute({\n ${routeOpts.join('\n ')}\n});`);
|
|
158
165
|
const children = route.children;
|
|
166
|
+
const hasChildren = Boolean(children && children.length > 0);
|
|
167
|
+
const routeCtorVarName = hasChildren ? reserveRouteVarName(`${varName}__base`) : varName;
|
|
168
|
+
statements.push(`const ${routeCtorVarName} = createRoute({\n ${routeOpts.join('\n ')}\n});`);
|
|
159
169
|
if (children && children.length > 0) {
|
|
160
170
|
const childVars = await Promise.all(children.map((child)=>buildRoute({
|
|
161
|
-
parentVar:
|
|
171
|
+
parentVar: routeCtorVarName,
|
|
162
172
|
route: child
|
|
163
173
|
})));
|
|
164
|
-
statements.push(
|
|
174
|
+
statements.push(`const ${varName} = ${routeCtorVarName}.addChildren([${childVars.join(', ')}]);`);
|
|
165
175
|
}
|
|
166
176
|
return varName;
|
|
167
177
|
};
|
|
@@ -234,7 +244,11 @@ function createRouteStaticData(opts: {
|
|
|
234
244
|
modernRouteAction?: unknown;
|
|
235
245
|
modernRouteLoader?: unknown;
|
|
236
246
|
}) {
|
|
237
|
-
const staticData:
|
|
247
|
+
const staticData: {
|
|
248
|
+
modernRouteId?: string;
|
|
249
|
+
modernRouteAction?: unknown;
|
|
250
|
+
modernRouteLoader?: unknown;
|
|
251
|
+
} = {};
|
|
238
252
|
|
|
239
253
|
if (opts.modernRouteId) {
|
|
240
254
|
staticData.modernRouteId = opts.modernRouteId;
|
|
@@ -248,7 +262,7 @@ function createRouteStaticData(opts: {
|
|
|
248
262
|
staticData.modernRouteAction = opts.modernRouteAction;
|
|
249
263
|
}
|
|
250
264
|
|
|
251
|
-
return
|
|
265
|
+
return staticData;
|
|
252
266
|
}
|
|
253
267
|
|
|
254
268
|
function modernLoaderToTanstack<TLoader extends (args: any) => any>(
|
|
@@ -8,6 +8,7 @@ import { RouterClient } from "@tanstack/react-router/ssr/client";
|
|
|
8
8
|
import { createModernBasepathRewrite } from "./basepathRewrite.mjs";
|
|
9
9
|
import { modifyRoutes, onAfterCreateRouter, onAfterHydrateRouter, onBeforeCreateRouter, onBeforeCreateRoutes, onBeforeHydrateRouter } from "./hooks.mjs";
|
|
10
10
|
import { applyRouterRuntimeState } from "./lifecycle.mjs";
|
|
11
|
+
import { Link } from "./prefetchLink.mjs";
|
|
11
12
|
import { createRouteTreeFromRouteObjects } from "./routeTree.mjs";
|
|
12
13
|
import { getTanstackRscSerializationAdapters } from "./rsc/client.mjs";
|
|
13
14
|
import { createRouteObjectsFromConfig, urlJoin } from "./utils.mjs";
|
|
@@ -74,6 +75,7 @@ const tanstackRouterPlugin = (userConfig = {})=>{
|
|
|
74
75
|
}
|
|
75
76
|
}
|
|
76
77
|
context.router = {
|
|
78
|
+
Link: Link,
|
|
77
79
|
useMatches: useMatches,
|
|
78
80
|
useLocation: useLocation,
|
|
79
81
|
useNavigate: useNavigate,
|
|
@@ -6,7 +6,7 @@ import { createRequestContext, storage } from "@modern-js/runtime-utils/node";
|
|
|
6
6
|
import { time } from "@modern-js/runtime-utils/time";
|
|
7
7
|
import { LOADER_REPORTER_NAME } from "@modern-js/utils/universal/constants";
|
|
8
8
|
import { RouterProvider, createMemoryHistory, createRouter } from "@tanstack/react-router";
|
|
9
|
-
import { attachRouterServerSsrUtils } from "@tanstack/
|
|
9
|
+
import { attachRouterServerSsrUtils } from "@tanstack/router-core/ssr/server";
|
|
10
10
|
import { Suspense, useContext } from "react";
|
|
11
11
|
import { createModernBasepathRewrite } from "./basepathRewrite.mjs";
|
|
12
12
|
import { modifyRoutes, onAfterCreateRouter, onAfterHydrateRouter, onBeforeCreateRouter, onBeforeCreateRoutes, onBeforeHydrateRouter } from "./hooks.mjs";
|
|
@@ -18,12 +18,28 @@ const setTanstackRscServerPayload = (payload)=>{
|
|
|
18
18
|
const storageContext = storage.useContext?.();
|
|
19
19
|
if (storageContext) storageContext.serverPayload = payload;
|
|
20
20
|
};
|
|
21
|
+
function isPromiseLike(value) {
|
|
22
|
+
return Boolean(value && 'function' == typeof value.then);
|
|
23
|
+
}
|
|
21
24
|
function isPreloadableRouteComponent(component) {
|
|
22
25
|
if (!component || 'function' != typeof component) return false;
|
|
23
26
|
const preloadable = component;
|
|
24
27
|
return 'function' == typeof preloadable.load || 'function' == typeof preloadable.preload;
|
|
25
28
|
}
|
|
29
|
+
function isReactLazyRouteComponent(component) {
|
|
30
|
+
return Boolean(component) && 'object' == typeof component && 'function' == typeof component._init && '_payload' in component;
|
|
31
|
+
}
|
|
32
|
+
async function preloadReactLazyRouteComponent(component) {
|
|
33
|
+
try {
|
|
34
|
+
component._init?.(component._payload);
|
|
35
|
+
} catch (thrown) {
|
|
36
|
+
if (!isPromiseLike(thrown)) throw thrown;
|
|
37
|
+
await thrown;
|
|
38
|
+
component._init?.(component._payload);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
26
41
|
async function preloadRouteComponent(component) {
|
|
42
|
+
if (isReactLazyRouteComponent(component)) return void await preloadReactLazyRouteComponent(component);
|
|
27
43
|
if (!isPreloadableRouteComponent(component)) return;
|
|
28
44
|
if ('function' == typeof component.load) return void await component.load({});
|
|
29
45
|
await component.preload?.({});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import "node:module";
|
|
2
2
|
import { createRootRoute, createRoute, notFound, redirect } from "@tanstack/react-router";
|
|
3
|
+
import { createElement } from "react";
|
|
3
4
|
import { DefaultNotFound } from "./DefaultNotFound.mjs";
|
|
4
5
|
import { isTanstackRscPayloadNavigationEnabled, loadTanstackRscRouteData } from "./rsc/payloadRouter.mjs";
|
|
5
6
|
function createTanstackRoute(options) {
|
|
@@ -55,6 +56,33 @@ function normalizeModernLoaderResponse(result) {
|
|
|
55
56
|
}
|
|
56
57
|
return normalizeModernLoaderResult(result);
|
|
57
58
|
}
|
|
59
|
+
function pickRouteModuleComponent(routeModule) {
|
|
60
|
+
if ('function' == typeof routeModule || routeModule && 'object' == typeof routeModule && '$$typeof' in routeModule) return routeModule;
|
|
61
|
+
if (!routeModule || 'object' != typeof routeModule) return;
|
|
62
|
+
const module = routeModule;
|
|
63
|
+
const component = module.default || module.Component;
|
|
64
|
+
if ('function' == typeof component || component && 'object' == typeof component && '$$typeof' in component) return component;
|
|
65
|
+
}
|
|
66
|
+
function createServerLazyImportComponent(lazyImport, fallbackComponent) {
|
|
67
|
+
if ("u" > typeof document) return fallbackComponent;
|
|
68
|
+
let resolvedComponent;
|
|
69
|
+
let pendingLoad;
|
|
70
|
+
const load = async ()=>{
|
|
71
|
+
if (resolvedComponent) return resolvedComponent;
|
|
72
|
+
const routeModule = await lazyImport();
|
|
73
|
+
const component = pickRouteModuleComponent(routeModule);
|
|
74
|
+
if (component) resolvedComponent = component;
|
|
75
|
+
return resolvedComponent;
|
|
76
|
+
};
|
|
77
|
+
const Component = (props)=>{
|
|
78
|
+
if (resolvedComponent) return createElement(resolvedComponent, props);
|
|
79
|
+
pendingLoad ||= load();
|
|
80
|
+
throw pendingLoad;
|
|
81
|
+
};
|
|
82
|
+
Component.load = load;
|
|
83
|
+
Component.preload = load;
|
|
84
|
+
return Component;
|
|
85
|
+
}
|
|
58
86
|
function isAbsoluteUrl(value) {
|
|
59
87
|
try {
|
|
60
88
|
new URL(value);
|
|
@@ -229,10 +257,18 @@ function wrapRouteObjectLoader(route, revalidationState, options = {}) {
|
|
|
229
257
|
}
|
|
230
258
|
function toRouteComponent(routeObject) {
|
|
231
259
|
const route = routeObject;
|
|
260
|
+
const lazyImport = 'function' == typeof route.lazyImport ? route.lazyImport : void 0;
|
|
261
|
+
const fallbackComponent = route.Component ? route.Component : route.element ? ()=>route.element : void 0;
|
|
262
|
+
if (lazyImport && fallbackComponent) return createServerLazyImportComponent(lazyImport, fallbackComponent);
|
|
232
263
|
if (route.Component) return route.Component;
|
|
233
264
|
const element = route.element;
|
|
234
265
|
if (element) return ()=>element;
|
|
235
266
|
}
|
|
267
|
+
function toModernRouteComponent(route) {
|
|
268
|
+
const component = route.component || void 0;
|
|
269
|
+
if ('function' == typeof route.lazyImport && component) return createServerLazyImportComponent(route.lazyImport, component);
|
|
270
|
+
return component;
|
|
271
|
+
}
|
|
236
272
|
function toErrorComponent(routeObject) {
|
|
237
273
|
const route = routeObject;
|
|
238
274
|
if (route.ErrorBoundary) return route.ErrorBoundary;
|
|
@@ -313,7 +349,7 @@ function createRouteFromModernRoute(opts) {
|
|
|
313
349
|
const stableFallbackId = modernId || route._component || route.filename || route.data || ('function' == typeof route.loader ? route.id : void 0);
|
|
314
350
|
const pendingComponent = route.loading || route.pendingComponent;
|
|
315
351
|
const errorComponent = route.error || route.errorComponent;
|
|
316
|
-
const component = route
|
|
352
|
+
const component = toModernRouteComponent(route);
|
|
317
353
|
const modernLoader = route.loader;
|
|
318
354
|
const modernAction = route.action;
|
|
319
355
|
const modernShouldRevalidate = route.shouldRevalidate;
|
|
@@ -360,7 +396,7 @@ function createRouteFromModernRoute(opts) {
|
|
|
360
396
|
}
|
|
361
397
|
function createRouteTreeFromModernRoutes(routes, options = {}) {
|
|
362
398
|
const rootModern = routes.find((r)=>r && 'nested' === r.type && r.isRoot);
|
|
363
|
-
const rootComponent = rootModern
|
|
399
|
+
const rootComponent = rootModern ? toModernRouteComponent(rootModern) : void 0;
|
|
364
400
|
const pendingComponent = rootModern?.loading;
|
|
365
401
|
const errorComponent = rootModern?.error;
|
|
366
402
|
const rootLoader = rootModern?.loader;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default, tanstackRouterPlugin, } from './plugin.node';
|
package/package.json
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"modern.js",
|
|
19
19
|
"tanstack-router"
|
|
20
20
|
],
|
|
21
|
-
"version": "3.2.0-ultramodern.
|
|
21
|
+
"version": "3.2.0-ultramodern.62",
|
|
22
22
|
"engines": {
|
|
23
23
|
"node": ">=20"
|
|
24
24
|
},
|
|
@@ -86,15 +86,15 @@
|
|
|
86
86
|
},
|
|
87
87
|
"dependencies": {
|
|
88
88
|
"@swc/helpers": "^0.5.21",
|
|
89
|
-
"@tanstack/react-router": "1.170.
|
|
90
|
-
"@tanstack/router-core": "1.
|
|
91
|
-
"@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.2.0-ultramodern.
|
|
92
|
-
"@modern-js/
|
|
93
|
-
"@modern-js/types": "npm:@bleedingdev/modern-js-types@3.2.0-ultramodern.
|
|
94
|
-
"@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.2.0-ultramodern.
|
|
89
|
+
"@tanstack/react-router": "1.170.8",
|
|
90
|
+
"@tanstack/router-core": "1.171.6",
|
|
91
|
+
"@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.2.0-ultramodern.62",
|
|
92
|
+
"@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.2.0-ultramodern.62",
|
|
93
|
+
"@modern-js/types": "npm:@bleedingdev/modern-js-types@3.2.0-ultramodern.62",
|
|
94
|
+
"@modern-js/runtime-utils": "npm:@bleedingdev/modern-js-runtime-utils@3.2.0-ultramodern.62"
|
|
95
95
|
},
|
|
96
96
|
"peerDependencies": {
|
|
97
|
-
"@modern-js/runtime": "3.2.0-ultramodern.
|
|
97
|
+
"@modern-js/runtime": "3.2.0-ultramodern.62",
|
|
98
98
|
"react": "^19.2.6",
|
|
99
99
|
"react-dom": "^19.2.6"
|
|
100
100
|
},
|
|
@@ -103,15 +103,15 @@
|
|
|
103
103
|
"@tanstack/history": "1.162.0",
|
|
104
104
|
"@testing-library/dom": "^10.4.1",
|
|
105
105
|
"@testing-library/react": "^16.3.2",
|
|
106
|
-
"@types/node": "^25.
|
|
107
|
-
"@types/react": "^19.2.
|
|
106
|
+
"@types/node": "^25.9.1",
|
|
107
|
+
"@types/react": "^19.2.15",
|
|
108
108
|
"@types/react-dom": "^19.2.3",
|
|
109
|
-
"@typescript/native-preview": "7.0.0-dev.
|
|
109
|
+
"@typescript/native-preview": "7.0.0-dev.20260527.2",
|
|
110
110
|
"react": "^19.2.6",
|
|
111
111
|
"react-dom": "^19.2.6",
|
|
112
|
-
"@modern-js/
|
|
113
|
-
"@
|
|
114
|
-
"@
|
|
112
|
+
"@modern-js/runtime": "npm:@bleedingdev/modern-js-runtime@3.2.0-ultramodern.62",
|
|
113
|
+
"@modern-js/app-tools": "npm:@bleedingdev/modern-js-app-tools@3.2.0-ultramodern.62",
|
|
114
|
+
"@scripts/rstest-config": "2.66.0"
|
|
115
115
|
},
|
|
116
116
|
"sideEffects": false,
|
|
117
117
|
"publishConfig": {
|
package/src/cli/tanstackTypes.ts
CHANGED
|
@@ -182,6 +182,7 @@ export async function generateTanstackRouterTypesSourceForEntry(opts: {
|
|
|
182
182
|
const statements: string[] = [];
|
|
183
183
|
|
|
184
184
|
const loaderImportMap = new Map<string, string>();
|
|
185
|
+
const usedRouteVarNames = new Set<string>();
|
|
185
186
|
let loaderIndex = 0;
|
|
186
187
|
let routeIndex = 0;
|
|
187
188
|
|
|
@@ -242,10 +243,20 @@ export async function generateTanstackRouterTypesSourceForEntry(opts: {
|
|
|
242
243
|
return { loaderName: importName, actionName };
|
|
243
244
|
};
|
|
244
245
|
|
|
246
|
+
const reserveRouteVarName = (preferred: string) => {
|
|
247
|
+
let candidate = preferred;
|
|
248
|
+
let suffix = 1;
|
|
249
|
+
while (usedRouteVarNames.has(candidate)) {
|
|
250
|
+
candidate = `${preferred}_${suffix++}`;
|
|
251
|
+
}
|
|
252
|
+
usedRouteVarNames.add(candidate);
|
|
253
|
+
return candidate;
|
|
254
|
+
};
|
|
255
|
+
|
|
245
256
|
const createRouteVarName = (route: NestedRouteForCli | PageRoute) => {
|
|
246
257
|
const id = (route as any).id as string | undefined;
|
|
247
258
|
const base = id ? makeLegalIdentifier(id) : `r_${routeIndex++}`;
|
|
248
|
-
return `route_${base}
|
|
259
|
+
return reserveRouteVarName(`route_${base}`);
|
|
249
260
|
};
|
|
250
261
|
|
|
251
262
|
const buildRoute = async (opts: {
|
|
@@ -296,18 +307,27 @@ export async function generateTanstackRouterTypesSourceForEntry(opts: {
|
|
|
296
307
|
routeOpts.push(staticDataSnippet);
|
|
297
308
|
}
|
|
298
309
|
|
|
299
|
-
statements.push(
|
|
300
|
-
`const ${varName} = createRoute({\n ${routeOpts.join('\n ')}\n});`,
|
|
301
|
-
);
|
|
302
|
-
|
|
303
310
|
const children = (route as any).children as
|
|
304
311
|
| Array<NestedRouteForCli | PageRoute>
|
|
305
312
|
| undefined;
|
|
313
|
+
const hasChildren = Boolean(children && children.length > 0);
|
|
314
|
+
const routeCtorVarName = hasChildren
|
|
315
|
+
? reserveRouteVarName(`${varName}__base`)
|
|
316
|
+
: varName;
|
|
317
|
+
|
|
318
|
+
statements.push(
|
|
319
|
+
`const ${routeCtorVarName} = createRoute({\n ${routeOpts.join('\n ')}\n});`,
|
|
320
|
+
);
|
|
321
|
+
|
|
306
322
|
if (children && children.length > 0) {
|
|
307
323
|
const childVars = await Promise.all(
|
|
308
|
-
children.map(child =>
|
|
324
|
+
children.map(child =>
|
|
325
|
+
buildRoute({ parentVar: routeCtorVarName, route: child }),
|
|
326
|
+
),
|
|
327
|
+
);
|
|
328
|
+
statements.push(
|
|
329
|
+
`const ${varName} = ${routeCtorVarName}.addChildren([${childVars.join(', ')}]);`,
|
|
309
330
|
);
|
|
310
|
-
statements.push(`${varName}.addChildren([${childVars.join(', ')}]);`);
|
|
311
331
|
}
|
|
312
332
|
|
|
313
333
|
return varName;
|
|
@@ -396,7 +416,11 @@ function createRouteStaticData(opts: {
|
|
|
396
416
|
modernRouteAction?: unknown;
|
|
397
417
|
modernRouteLoader?: unknown;
|
|
398
418
|
}) {
|
|
399
|
-
const staticData:
|
|
419
|
+
const staticData: {
|
|
420
|
+
modernRouteId?: string;
|
|
421
|
+
modernRouteAction?: unknown;
|
|
422
|
+
modernRouteLoader?: unknown;
|
|
423
|
+
} = {};
|
|
400
424
|
|
|
401
425
|
if (opts.modernRouteId) {
|
|
402
426
|
staticData.modernRouteId = opts.modernRouteId;
|
|
@@ -410,7 +434,7 @@ function createRouteStaticData(opts: {
|
|
|
410
434
|
staticData.modernRouteAction = opts.modernRouteAction;
|
|
411
435
|
}
|
|
412
436
|
|
|
413
|
-
return
|
|
437
|
+
return staticData;
|
|
414
438
|
}
|
|
415
439
|
|
|
416
440
|
function modernLoaderToTanstack<TLoader extends (args: any) => any>(
|
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
createRouter,
|
|
27
27
|
RouterProvider,
|
|
28
28
|
} from '@tanstack/react-router';
|
|
29
|
-
import { attachRouterServerSsrUtils } from '@tanstack/
|
|
29
|
+
import { attachRouterServerSsrUtils } from '@tanstack/router-core/ssr/server';
|
|
30
30
|
import type React from 'react';
|
|
31
31
|
import { Suspense, useContext } from 'react';
|
|
32
32
|
import { createModernBasepathRewrite } from './basepathRewrite';
|
|
@@ -119,6 +119,17 @@ type PreloadableRouteComponent = {
|
|
|
119
119
|
preload?: (props?: Record<string, unknown>) => Promise<unknown> | unknown;
|
|
120
120
|
};
|
|
121
121
|
|
|
122
|
+
type ReactLazyRouteComponent = {
|
|
123
|
+
_init?: (payload: unknown) => unknown;
|
|
124
|
+
_payload?: unknown;
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
function isPromiseLike(value: unknown): value is PromiseLike<unknown> {
|
|
128
|
+
return Boolean(
|
|
129
|
+
value && typeof (value as PromiseLike<unknown>).then === 'function',
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
122
133
|
type TanstackRouterWithServerSsr = AnyRouter & {
|
|
123
134
|
resolveRedirect?: (redirect: Response) => Response;
|
|
124
135
|
routesById?: Record<string, RouterRouteWithOptions>;
|
|
@@ -149,7 +160,37 @@ function isPreloadableRouteComponent(
|
|
|
149
160
|
);
|
|
150
161
|
}
|
|
151
162
|
|
|
163
|
+
function isReactLazyRouteComponent(
|
|
164
|
+
component: unknown,
|
|
165
|
+
): component is ReactLazyRouteComponent {
|
|
166
|
+
return (
|
|
167
|
+
Boolean(component) &&
|
|
168
|
+
typeof component === 'object' &&
|
|
169
|
+
typeof (component as ReactLazyRouteComponent)._init === 'function' &&
|
|
170
|
+
'_payload' in component
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async function preloadReactLazyRouteComponent(
|
|
175
|
+
component: ReactLazyRouteComponent,
|
|
176
|
+
) {
|
|
177
|
+
try {
|
|
178
|
+
component._init?.(component._payload);
|
|
179
|
+
} catch (thrown) {
|
|
180
|
+
if (!isPromiseLike(thrown)) {
|
|
181
|
+
throw thrown;
|
|
182
|
+
}
|
|
183
|
+
await thrown;
|
|
184
|
+
component._init?.(component._payload);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
152
188
|
async function preloadRouteComponent(component: unknown) {
|
|
189
|
+
if (isReactLazyRouteComponent(component)) {
|
|
190
|
+
await preloadReactLazyRouteComponent(component);
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
|
|
153
194
|
if (!isPreloadableRouteComponent(component)) {
|
|
154
195
|
return;
|
|
155
196
|
}
|
package/src/runtime/plugin.tsx
CHANGED
|
@@ -41,6 +41,7 @@ import {
|
|
|
41
41
|
applyRouterRuntimeState,
|
|
42
42
|
type RouterLifecycleContext,
|
|
43
43
|
} from './lifecycle';
|
|
44
|
+
import { Link } from './prefetchLink';
|
|
44
45
|
import { createRouteTreeFromRouteObjects } from './routeTree';
|
|
45
46
|
import { getTanstackRscSerializationAdapters } from './rsc/client';
|
|
46
47
|
import type { RouterConfig } from './types';
|
|
@@ -184,6 +185,7 @@ export const tanstackRouterPlugin = (
|
|
|
184
185
|
}
|
|
185
186
|
|
|
186
187
|
context.router = {
|
|
188
|
+
Link,
|
|
187
189
|
useMatches,
|
|
188
190
|
useLocation,
|
|
189
191
|
useNavigate,
|
package/src/runtime/routeTree.ts
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
notFound,
|
|
13
13
|
redirect,
|
|
14
14
|
} from '@tanstack/react-router';
|
|
15
|
+
import { createElement, type ElementType } from 'react';
|
|
15
16
|
import { DefaultNotFound } from './DefaultNotFound';
|
|
16
17
|
import {
|
|
17
18
|
isTanstackRscPayloadNavigationEnabled,
|
|
@@ -118,6 +119,15 @@ type ModernDeferredDataLike = {
|
|
|
118
119
|
__modern_deferred?: unknown;
|
|
119
120
|
data?: unknown;
|
|
120
121
|
};
|
|
122
|
+
type ModernRouteModule = {
|
|
123
|
+
Component?: unknown;
|
|
124
|
+
default?: unknown;
|
|
125
|
+
};
|
|
126
|
+
type PreloadableComponent = {
|
|
127
|
+
(props: Record<string, unknown>): ReturnType<typeof createElement>;
|
|
128
|
+
load?: () => Promise<unknown>;
|
|
129
|
+
preload?: () => Promise<unknown>;
|
|
130
|
+
};
|
|
121
131
|
type RouteTreeOptions = {
|
|
122
132
|
rscPayloadRouter?: boolean;
|
|
123
133
|
};
|
|
@@ -219,6 +229,72 @@ function normalizeModernLoaderResponse(result: unknown): unknown {
|
|
|
219
229
|
return normalizeModernLoaderResult(result);
|
|
220
230
|
}
|
|
221
231
|
|
|
232
|
+
function pickRouteModuleComponent(
|
|
233
|
+
routeModule: unknown,
|
|
234
|
+
): ElementType<Record<string, unknown>> | undefined {
|
|
235
|
+
if (
|
|
236
|
+
typeof routeModule === 'function' ||
|
|
237
|
+
(routeModule &&
|
|
238
|
+
typeof routeModule === 'object' &&
|
|
239
|
+
'$$typeof' in routeModule)
|
|
240
|
+
) {
|
|
241
|
+
return routeModule as ElementType<Record<string, unknown>>;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (!routeModule || typeof routeModule !== 'object') {
|
|
245
|
+
return undefined;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const module = routeModule as ModernRouteModule;
|
|
249
|
+
const component = module.default || module.Component;
|
|
250
|
+
if (
|
|
251
|
+
typeof component === 'function' ||
|
|
252
|
+
(component && typeof component === 'object' && '$$typeof' in component)
|
|
253
|
+
) {
|
|
254
|
+
return component as ElementType<Record<string, unknown>>;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return undefined;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function createServerLazyImportComponent(
|
|
261
|
+
lazyImport: () => unknown,
|
|
262
|
+
fallbackComponent?: unknown,
|
|
263
|
+
): PreloadableComponent | unknown {
|
|
264
|
+
if (typeof document !== 'undefined') {
|
|
265
|
+
return fallbackComponent;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
let resolvedComponent: ElementType<Record<string, unknown>> | undefined;
|
|
269
|
+
let pendingLoad: Promise<unknown> | undefined;
|
|
270
|
+
|
|
271
|
+
const load = async () => {
|
|
272
|
+
if (resolvedComponent) {
|
|
273
|
+
return resolvedComponent;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const routeModule = await lazyImport();
|
|
277
|
+
const component = pickRouteModuleComponent(routeModule);
|
|
278
|
+
if (component) {
|
|
279
|
+
resolvedComponent = component;
|
|
280
|
+
}
|
|
281
|
+
return resolvedComponent;
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
const Component: PreloadableComponent = props => {
|
|
285
|
+
if (resolvedComponent) {
|
|
286
|
+
return createElement(resolvedComponent, props);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
pendingLoad ||= load();
|
|
290
|
+
throw pendingLoad;
|
|
291
|
+
};
|
|
292
|
+
Component.load = load;
|
|
293
|
+
Component.preload = load;
|
|
294
|
+
|
|
295
|
+
return Component;
|
|
296
|
+
}
|
|
297
|
+
|
|
222
298
|
function isAbsoluteUrl(value: string) {
|
|
223
299
|
try {
|
|
224
300
|
void new URL(value);
|
|
@@ -519,6 +595,18 @@ function wrapRouteObjectLoader(
|
|
|
519
595
|
|
|
520
596
|
function toRouteComponent(routeObject: RouteObject): unknown {
|
|
521
597
|
const route = routeObject as ModernRouteObject;
|
|
598
|
+
const lazyImport =
|
|
599
|
+
typeof route.lazyImport === 'function' ? route.lazyImport : undefined;
|
|
600
|
+
const fallbackComponent = route.Component
|
|
601
|
+
? route.Component
|
|
602
|
+
: route.element
|
|
603
|
+
? () => route.element
|
|
604
|
+
: undefined;
|
|
605
|
+
|
|
606
|
+
if (lazyImport && fallbackComponent) {
|
|
607
|
+
return createServerLazyImportComponent(lazyImport, fallbackComponent);
|
|
608
|
+
}
|
|
609
|
+
|
|
522
610
|
if (route.Component) {
|
|
523
611
|
return route.Component;
|
|
524
612
|
}
|
|
@@ -529,6 +617,15 @@ function toRouteComponent(routeObject: RouteObject): unknown {
|
|
|
529
617
|
return undefined;
|
|
530
618
|
}
|
|
531
619
|
|
|
620
|
+
function toModernRouteComponent(route: ModernGeneratedRoute): unknown {
|
|
621
|
+
const component = route.component || undefined;
|
|
622
|
+
if (typeof route.lazyImport === 'function' && component) {
|
|
623
|
+
return createServerLazyImportComponent(route.lazyImport, component);
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
return component;
|
|
627
|
+
}
|
|
628
|
+
|
|
532
629
|
function toErrorComponent(routeObject: RouteObject): unknown {
|
|
533
630
|
const route = routeObject as ModernRouteObject;
|
|
534
631
|
if (route.ErrorBoundary) {
|
|
@@ -702,7 +799,7 @@ function createRouteFromModernRoute(opts: {
|
|
|
702
799
|
|
|
703
800
|
const pendingComponent = route.loading || route.pendingComponent;
|
|
704
801
|
const errorComponent = route.error || route.errorComponent;
|
|
705
|
-
const component = route
|
|
802
|
+
const component = toModernRouteComponent(route);
|
|
706
803
|
const modernLoader = route.loader;
|
|
707
804
|
const modernAction = route.action;
|
|
708
805
|
const modernShouldRevalidate = route.shouldRevalidate;
|
|
@@ -788,7 +885,9 @@ export function createRouteTreeFromModernRoutes(
|
|
|
788
885
|
(r as ModernGeneratedRoute).isRoot,
|
|
789
886
|
) as ModernGeneratedRoute | undefined;
|
|
790
887
|
|
|
791
|
-
const rootComponent = rootModern
|
|
888
|
+
const rootComponent = rootModern
|
|
889
|
+
? toModernRouteComponent(rootModern)
|
|
890
|
+
: undefined;
|
|
792
891
|
const pendingComponent = rootModern?.loading;
|
|
793
892
|
const errorComponent = rootModern?.error;
|
|
794
893
|
const rootLoader = rootModern?.loader;
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import type { RouteObject } from '@modern-js/runtime-utils/router';
|
|
2
2
|
import type { NestedRoute } from '@modern-js/types';
|
|
3
3
|
import { createMemoryHistory } from '@tanstack/history';
|
|
4
|
-
import { createRouter } from '@tanstack/react-router';
|
|
4
|
+
import { createRouter, Outlet, RouterProvider } from '@tanstack/react-router';
|
|
5
|
+
import type { ComponentType } from 'react';
|
|
6
|
+
import { createElement, lazy } from 'react';
|
|
7
|
+
import { renderToStaticMarkup } from 'react-dom/server';
|
|
5
8
|
import {
|
|
6
9
|
createRouteTreeFromModernRoutes,
|
|
7
10
|
createRouteTreeFromRouteObjects,
|
|
@@ -23,6 +26,7 @@ type TestRouteObject = RouteObject & {
|
|
|
23
26
|
hasLoader?: boolean;
|
|
24
27
|
inValidSSRRoute?: boolean;
|
|
25
28
|
isClientComponent?: boolean;
|
|
29
|
+
lazyImport?: () => Promise<{ default: ComponentType }>;
|
|
26
30
|
};
|
|
27
31
|
|
|
28
32
|
type TestNestedRoute = NestedRoute & {
|
|
@@ -234,6 +238,73 @@ describe('tanstack route tree from RouteObject[]', () => {
|
|
|
234
238
|
expect(splatParamValue).toBe('a/b/c');
|
|
235
239
|
});
|
|
236
240
|
|
|
241
|
+
test('preloads lazy Modern route components for server rendering', async () => {
|
|
242
|
+
const LazyRouteComponent = () =>
|
|
243
|
+
createElement('main', null, 'Lazy route ready');
|
|
244
|
+
const lazyImport = rstest.fn(async () => ({
|
|
245
|
+
default: LazyRouteComponent,
|
|
246
|
+
}));
|
|
247
|
+
const routes: TestRouteObject[] = [
|
|
248
|
+
{
|
|
249
|
+
id: 'root',
|
|
250
|
+
path: '/',
|
|
251
|
+
Component: () => null,
|
|
252
|
+
children: [
|
|
253
|
+
{
|
|
254
|
+
id: 'lazy',
|
|
255
|
+
path: 'lazy',
|
|
256
|
+
Component: lazy(lazyImport),
|
|
257
|
+
lazyImport,
|
|
258
|
+
},
|
|
259
|
+
],
|
|
260
|
+
},
|
|
261
|
+
];
|
|
262
|
+
|
|
263
|
+
const routeTree = createRouteTreeFromRouteObjects(routes);
|
|
264
|
+
const router = await loadRouteTree(routeTree, '/lazy');
|
|
265
|
+
const lazyRoute = getLooseRoute(router, '/lazy');
|
|
266
|
+
const component = lazyRoute.options.component as ComponentType & {
|
|
267
|
+
preload?: () => Promise<unknown>;
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
await component.preload?.();
|
|
271
|
+
|
|
272
|
+
expect(renderToStaticMarkup(createElement(component))).toContain(
|
|
273
|
+
'Lazy route ready',
|
|
274
|
+
);
|
|
275
|
+
expect(lazyImport).toHaveBeenCalled();
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
test('renders preloaded lazy child routes through TanStack router SSR', async () => {
|
|
279
|
+
const LazyRouteComponent = () =>
|
|
280
|
+
createElement('main', null, 'Lazy child route ready');
|
|
281
|
+
const lazyImport = rstest.fn(async () => ({
|
|
282
|
+
default: LazyRouteComponent,
|
|
283
|
+
}));
|
|
284
|
+
const routes: TestRouteObject[] = [
|
|
285
|
+
{
|
|
286
|
+
id: 'root',
|
|
287
|
+
path: '/',
|
|
288
|
+
Component: () => createElement('section', null, createElement(Outlet)),
|
|
289
|
+
children: [
|
|
290
|
+
{
|
|
291
|
+
id: 'lazy',
|
|
292
|
+
path: 'lazy',
|
|
293
|
+
Component: lazy(lazyImport),
|
|
294
|
+
lazyImport,
|
|
295
|
+
},
|
|
296
|
+
],
|
|
297
|
+
},
|
|
298
|
+
];
|
|
299
|
+
|
|
300
|
+
const routeTree = createRouteTreeFromRouteObjects(routes);
|
|
301
|
+
const router = await loadRouteTree(routeTree, '/lazy');
|
|
302
|
+
|
|
303
|
+
expect(
|
|
304
|
+
renderToStaticMarkup(createElement(RouterProvider, { router } as never)),
|
|
305
|
+
).toContain('Lazy child route ready');
|
|
306
|
+
});
|
|
307
|
+
|
|
237
308
|
test('preserves route handle and maps shouldRevalidate to shouldReload', async () => {
|
|
238
309
|
const shouldRevalidate = rstest.fn(({ nextUrl }: ShouldRevalidateArgs) =>
|
|
239
310
|
nextUrl.pathname.endsWith('/456'),
|
|
@@ -55,8 +55,72 @@ describe('tanstack router type generation', () => {
|
|
|
55
55
|
);
|
|
56
56
|
expect(routerGenTs).toContain('modernRouteLoader: loader_0');
|
|
57
57
|
expect(routerGenTs).toContain('modernRouteAction: action_0');
|
|
58
|
+
expect(routerGenTs).toContain('modernRouteId?: string;');
|
|
59
|
+
expect(routerGenTs).not.toContain(
|
|
60
|
+
'return Object.keys(staticData).length > 0 ? staticData : undefined;',
|
|
61
|
+
);
|
|
58
62
|
expect(routerGenTs).toContain(
|
|
59
63
|
"} from '@modern-js/plugin-tanstack/runtime';",
|
|
60
64
|
);
|
|
61
65
|
});
|
|
66
|
+
|
|
67
|
+
test('preserves typed child trees for localized nested route aliases', async () => {
|
|
68
|
+
tempDir = await mkdtemp(path.join(tmpdir(), 'modern-tanstack-types-'));
|
|
69
|
+
const srcDirectory = path.join(tempDir, 'src');
|
|
70
|
+
|
|
71
|
+
const { routerGenTs } = await generateTanstackRouterTypesSourceForEntry({
|
|
72
|
+
appContext: {
|
|
73
|
+
srcDirectory,
|
|
74
|
+
internalSrcAlias: '@/_',
|
|
75
|
+
} as any,
|
|
76
|
+
entryName: 'index',
|
|
77
|
+
routes: [
|
|
78
|
+
{
|
|
79
|
+
type: 'nested',
|
|
80
|
+
id: 'layout',
|
|
81
|
+
isRoot: true,
|
|
82
|
+
children: [
|
|
83
|
+
{
|
|
84
|
+
type: 'nested',
|
|
85
|
+
id: '(lang)/layout',
|
|
86
|
+
path: ':lang',
|
|
87
|
+
children: [
|
|
88
|
+
{
|
|
89
|
+
type: 'nested',
|
|
90
|
+
id: '(lang)/products/(slug)/page',
|
|
91
|
+
path: 'products/:slug',
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
type: 'nested',
|
|
95
|
+
id: '(lang)/products/(slug)/page__localised_produkty_slug',
|
|
96
|
+
path: 'produkty/:slug',
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
type: 'nested',
|
|
100
|
+
id: '(lang)/optional/(slug$)/page__localised_volitelne_slug',
|
|
101
|
+
path: 'volitelne/:slug?',
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
},
|
|
105
|
+
],
|
|
106
|
+
},
|
|
107
|
+
] as any,
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
expect(routerGenTs).toContain(
|
|
111
|
+
'const route__lang__layout__base = createRoute({',
|
|
112
|
+
);
|
|
113
|
+
expect(routerGenTs).toContain(
|
|
114
|
+
'getParentRoute: () => route__lang__layout__base,',
|
|
115
|
+
);
|
|
116
|
+
expect(routerGenTs).toContain('path: "produkty/$slug",');
|
|
117
|
+
expect(routerGenTs).toContain('path: "volitelne/{-$slug}",');
|
|
118
|
+
expect(routerGenTs).toContain(
|
|
119
|
+
'const route__lang__layout = route__lang__layout__base.addChildren([route__lang__products__slug__page, route__lang__products__slug__page__localised_produkty_slug, route__lang__optional__slug$__page__localised_volitelne_slug]);',
|
|
120
|
+
);
|
|
121
|
+
expect(routerGenTs).toContain(
|
|
122
|
+
'export const routeTree = rootRoute.addChildren([route__lang__layout]);',
|
|
123
|
+
);
|
|
124
|
+
expect(routerGenTs).not.toContain('route__lang__layout.addChildren([');
|
|
125
|
+
});
|
|
62
126
|
});
|