@bleedingdev/modern-js-plugin-tanstack 3.2.0-ultramodern.9 → 3.2.0-ultramodern.90
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/index.js +12 -0
- package/dist/cjs/cli/routeSplitting.js +83 -0
- package/dist/cjs/cli/tanstackTypes.js +146 -58
- package/dist/cjs/runtime/index.js +38 -6
- package/dist/cjs/runtime/plugin.js +6 -5
- package/dist/cjs/runtime/plugin.node.js +27 -10
- package/dist/cjs/runtime/plugin.worker.js +49 -0
- package/dist/cjs/runtime/routeTree.js +55 -4
- package/dist/cjs/runtime/types.js +27 -1
- package/dist/esm/cli/index.mjs +4 -1
- package/dist/esm/cli/routeSplitting.mjs +43 -0
- package/dist/esm/cli/tanstackTypes.mjs +146 -58
- package/dist/esm/runtime/index.mjs +2 -1
- package/dist/esm/runtime/plugin.mjs +10 -9
- package/dist/esm/runtime/plugin.node.mjs +28 -11
- package/dist/esm/runtime/plugin.worker.mjs +1 -0
- package/dist/esm/runtime/routeTree.mjs +55 -4
- package/dist/esm/runtime/types.mjs +7 -0
- package/dist/esm-node/cli/index.mjs +4 -1
- package/dist/esm-node/cli/routeSplitting.mjs +44 -0
- package/dist/esm-node/cli/tanstackTypes.mjs +146 -58
- package/dist/esm-node/runtime/index.mjs +2 -1
- package/dist/esm-node/runtime/plugin.mjs +10 -9
- package/dist/esm-node/runtime/plugin.node.mjs +28 -11
- package/dist/esm-node/runtime/plugin.worker.mjs +2 -0
- package/dist/esm-node/runtime/routeTree.mjs +55 -4
- package/dist/esm-node/runtime/types.mjs +7 -0
- package/dist/types/cli/index.d.ts +4 -0
- package/dist/types/cli/routeSplitting.d.ts +29 -0
- package/dist/types/runtime/index.d.ts +3 -1
- package/dist/types/runtime/plugin.d.ts +1 -1
- package/dist/types/runtime/plugin.node.d.ts +1 -1
- package/dist/types/runtime/plugin.worker.d.ts +1 -0
- package/dist/types/runtime/types.d.ts +7 -0
- package/package.json +14 -14
- package/src/cli/index.ts +17 -0
- package/src/cli/routeSplitting.ts +81 -0
- package/src/cli/tanstackTypes.ts +216 -67
- package/src/runtime/index.tsx +13 -1
- package/src/runtime/plugin.node.tsx +54 -7
- package/src/runtime/plugin.tsx +8 -5
- package/src/runtime/plugin.worker.tsx +4 -0
- package/src/runtime/routeTree.ts +125 -8
- package/src/runtime/types.ts +13 -0
- package/tests/router/cli.test.ts +239 -0
- package/tests/router/fastDefaults.test.ts +25 -0
- package/tests/router/routeTree.test.ts +193 -1
- package/tests/router/tanstackTypes.test.ts +184 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import "node:module";
|
|
2
|
+
const TANSTACK_START_ROUTE_FACTORY_CALLS = [
|
|
3
|
+
'createFileRoute',
|
|
4
|
+
'createRootRoute',
|
|
5
|
+
'createRootRouteWithContext'
|
|
6
|
+
];
|
|
7
|
+
const TANSTACK_START_ROUTE_FACTORY_REGEX = /\b(createFileRoute|createRootRoute|createRootRouteWithContext)\s*(?:<|\()/;
|
|
8
|
+
function isTanstackStartRouteModuleSource(source) {
|
|
9
|
+
return TANSTACK_START_ROUTE_FACTORY_REGEX.test(source);
|
|
10
|
+
}
|
|
11
|
+
function resolveTanstackRouteCodeSplittingEnabled(option) {
|
|
12
|
+
if ('boolean' == typeof option) return option;
|
|
13
|
+
return option?.enabled ?? true;
|
|
14
|
+
}
|
|
15
|
+
function createTanstackRsbuildRouteSplittingProfile(opts) {
|
|
16
|
+
return {
|
|
17
|
+
defaultConfig: {
|
|
18
|
+
output: {
|
|
19
|
+
splitRouteChunks: resolveTanstackRouteCodeSplittingEnabled(opts.routeCodeSplitting)
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
modernRouteChunks: {
|
|
23
|
+
enabled: resolveTanstackRouteCodeSplittingEnabled(opts.routeCodeSplitting),
|
|
24
|
+
owner: 'modern'
|
|
25
|
+
},
|
|
26
|
+
builderChunkSplit: {
|
|
27
|
+
owner: 'modern-rsbuild',
|
|
28
|
+
preserved: true
|
|
29
|
+
},
|
|
30
|
+
tanstackStartRspackSplitter: {
|
|
31
|
+
compatible: false,
|
|
32
|
+
reason: 'TanStack Start Rsbuild route splitting is tied to TanStack file-route factory modules; Modern generates TanStack route trees from Modern route metadata and owns route chunking through output.splitRouteChunks.',
|
|
33
|
+
clientDeleteNodes: [
|
|
34
|
+
'ssr',
|
|
35
|
+
'server',
|
|
36
|
+
'headers'
|
|
37
|
+
],
|
|
38
|
+
routeFactoryCalls: [
|
|
39
|
+
...TANSTACK_START_ROUTE_FACTORY_CALLS
|
|
40
|
+
]
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
export { createTanstackRsbuildRouteSplittingProfile, isTanstackStartRouteModuleSource, resolveTanstackRouteCodeSplittingEnabled };
|
|
@@ -56,6 +56,14 @@ function pickModernLoaderModule(route) {
|
|
|
56
56
|
inline
|
|
57
57
|
};
|
|
58
58
|
}
|
|
59
|
+
function pickRouteSearchContractModules(route) {
|
|
60
|
+
const validateSearchPath = route.validateSearch;
|
|
61
|
+
const loaderDepsPath = route.loaderDeps;
|
|
62
|
+
return {
|
|
63
|
+
validateSearchPath: 'string' == typeof validateSearchPath ? validateSearchPath : null,
|
|
64
|
+
loaderDepsPath: 'string' == typeof loaderDepsPath ? loaderDepsPath : null
|
|
65
|
+
};
|
|
66
|
+
}
|
|
59
67
|
function isPathlessLayout(route) {
|
|
60
68
|
return 'nested' === route.type && 'boolean' != typeof route.index && void 0 === route.path;
|
|
61
69
|
}
|
|
@@ -89,8 +97,21 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
|
|
|
89
97
|
const imports = [];
|
|
90
98
|
const statements = [];
|
|
91
99
|
const loaderImportMap = new Map();
|
|
100
|
+
const searchContractImportMap = new Map();
|
|
101
|
+
const usedRouteVarNames = new Set();
|
|
92
102
|
let loaderIndex = 0;
|
|
103
|
+
let validateSearchIndex = 0;
|
|
104
|
+
let loaderDepsIndex = 0;
|
|
93
105
|
let routeIndex = 0;
|
|
106
|
+
const resolveRouteModuleNoExt = async (aliasedNoExtPath)=>{
|
|
107
|
+
const prefix = `${appContext.internalSrcAlias}/`;
|
|
108
|
+
let absNoExt;
|
|
109
|
+
if (aliasedNoExtPath.startsWith(prefix)) {
|
|
110
|
+
const rel = aliasedNoExtPath.slice(prefix.length);
|
|
111
|
+
absNoExt = path.join(appContext.srcDirectory, rel);
|
|
112
|
+
} else absNoExt = path.isAbsolute(aliasedNoExtPath) ? aliasedNoExtPath : path.join(appContext.srcDirectory, aliasedNoExtPath);
|
|
113
|
+
return resolveFileNoExt(absNoExt);
|
|
114
|
+
};
|
|
94
115
|
const getImportNamesForLoader = async (aliasedNoExtPath, inline, hasAction)=>{
|
|
95
116
|
const key = `${inline ? 'inline' : 'default'}:${hasAction ? 'action' : 'loader'}:${aliasedNoExtPath}`;
|
|
96
117
|
const existing = loaderImportMap.get(key);
|
|
@@ -98,13 +119,7 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
|
|
|
98
119
|
loaderName: existing,
|
|
99
120
|
actionName: hasAction ? existing.replace(/^loader_/, 'action_') : null
|
|
100
121
|
};
|
|
101
|
-
const
|
|
102
|
-
let absNoExt;
|
|
103
|
-
if (aliasedNoExtPath.startsWith(prefix)) {
|
|
104
|
-
const rel = aliasedNoExtPath.slice(prefix.length);
|
|
105
|
-
absNoExt = path.join(appContext.srcDirectory, rel);
|
|
106
|
-
} else absNoExt = path.isAbsolute(aliasedNoExtPath) ? aliasedNoExtPath : path.join(appContext.srcDirectory, aliasedNoExtPath);
|
|
107
|
-
const resolvedNoExt = await resolveFileNoExt(absNoExt);
|
|
122
|
+
const resolvedNoExt = await resolveRouteModuleNoExt(aliasedNoExtPath);
|
|
108
123
|
if (!resolvedNoExt) return null;
|
|
109
124
|
const relImport = normalizeRelativeImport(path.relative(outDir, resolvedNoExt));
|
|
110
125
|
const importName = `loader_${loaderIndex++}`;
|
|
@@ -122,10 +137,29 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
|
|
|
122
137
|
actionName
|
|
123
138
|
};
|
|
124
139
|
};
|
|
140
|
+
const getImportNameForSearchContract = async (aliasedNoExtPath, exportName)=>{
|
|
141
|
+
const key = `${exportName}:${aliasedNoExtPath}`;
|
|
142
|
+
const existing = searchContractImportMap.get(key);
|
|
143
|
+
if (existing) return existing;
|
|
144
|
+
const resolvedNoExt = await resolveRouteModuleNoExt(aliasedNoExtPath);
|
|
145
|
+
if (!resolvedNoExt) return null;
|
|
146
|
+
const relImport = normalizeRelativeImport(path.relative(outDir, resolvedNoExt));
|
|
147
|
+
const importName = 'validateSearch' === exportName ? `validateSearch_${validateSearchIndex++}` : `loaderDeps_${loaderDepsIndex++}`;
|
|
148
|
+
imports.push(`import { ${exportName} as ${importName} } from ${quote(relImport)};`);
|
|
149
|
+
searchContractImportMap.set(key, importName);
|
|
150
|
+
return importName;
|
|
151
|
+
};
|
|
152
|
+
const reserveRouteVarName = (preferred)=>{
|
|
153
|
+
let candidate = preferred;
|
|
154
|
+
let suffix = 1;
|
|
155
|
+
while(usedRouteVarNames.has(candidate))candidate = `${preferred}_${suffix++}`;
|
|
156
|
+
usedRouteVarNames.add(candidate);
|
|
157
|
+
return candidate;
|
|
158
|
+
};
|
|
125
159
|
const createRouteVarName = (route)=>{
|
|
126
160
|
const id = route.id;
|
|
127
161
|
const base = id ? makeLegalIdentifier(id) : `r_${routeIndex++}`;
|
|
128
|
-
return `route_${base}
|
|
162
|
+
return reserveRouteVarName(`route_${base}`);
|
|
129
163
|
};
|
|
130
164
|
const buildRoute = async (opts)=>{
|
|
131
165
|
const { parentVar, route } = opts;
|
|
@@ -135,6 +169,9 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
|
|
|
135
169
|
const loaderImports = loaderInfo ? await getImportNamesForLoader(loaderInfo.loaderPath, loaderInfo.inline, Boolean(loaderInfo.inline && routeAction === loaderInfo.loaderPath)) : null;
|
|
136
170
|
const loaderName = loaderImports?.loaderName || null;
|
|
137
171
|
const actionName = loaderImports?.actionName || null;
|
|
172
|
+
const searchContractInfo = pickRouteSearchContractModules(route);
|
|
173
|
+
const validateSearchName = searchContractInfo.validateSearchPath ? await getImportNameForSearchContract(searchContractInfo.validateSearchPath, 'validateSearch') : null;
|
|
174
|
+
const loaderDepsName = searchContractInfo.loaderDepsPath ? await getImportNameForSearchContract(searchContractInfo.loaderDepsPath, 'loaderDeps') : null;
|
|
138
175
|
const rawPath = route.path;
|
|
139
176
|
const hasSplat = 'string' == typeof rawPath && rawPath.includes('*');
|
|
140
177
|
const routeOpts = [
|
|
@@ -148,20 +185,24 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
|
|
|
148
185
|
routeOpts.push(`path: ${quote(p)},`);
|
|
149
186
|
}
|
|
150
187
|
if (loaderName) routeOpts.push(`loader: modernLoaderToTanstack({ hasSplat: ${hasSplat} }, ${loaderName}),`);
|
|
188
|
+
if (validateSearchName) routeOpts.push(`validateSearch: ${validateSearchName},`);
|
|
189
|
+
if (loaderDepsName) routeOpts.push(`loaderDeps: ${loaderDepsName},`);
|
|
151
190
|
const staticDataSnippet = createRouteStaticDataSnippet({
|
|
152
191
|
modernRouteId: route.id,
|
|
153
192
|
loaderName,
|
|
154
193
|
actionName
|
|
155
194
|
});
|
|
156
195
|
if (staticDataSnippet) routeOpts.push(staticDataSnippet);
|
|
157
|
-
statements.push(`const ${varName} = createRoute({\n ${routeOpts.join('\n ')}\n});`);
|
|
158
196
|
const children = route.children;
|
|
197
|
+
const hasChildren = Boolean(children && children.length > 0);
|
|
198
|
+
const routeCtorVarName = hasChildren ? reserveRouteVarName(`${varName}__base`) : varName;
|
|
199
|
+
statements.push(`const ${routeCtorVarName} = createRoute({\n ${routeOpts.join('\n ')}\n});`);
|
|
159
200
|
if (children && children.length > 0) {
|
|
160
201
|
const childVars = await Promise.all(children.map((child)=>buildRoute({
|
|
161
|
-
parentVar:
|
|
202
|
+
parentVar: routeCtorVarName,
|
|
162
203
|
route: child
|
|
163
204
|
})));
|
|
164
|
-
statements.push(
|
|
205
|
+
statements.push(`const ${varName} = ${routeCtorVarName}.addChildren([${childVars.join(', ')}]);`);
|
|
165
206
|
}
|
|
166
207
|
return varName;
|
|
167
208
|
};
|
|
@@ -170,17 +211,23 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
|
|
|
170
211
|
const rootLoaderImports = rootLoaderInfo?.loaderPath ? await getImportNamesForLoader(rootLoaderInfo.loaderPath, rootLoaderInfo.inline, Boolean(rootLoaderInfo.inline && rootAction === rootLoaderInfo.loaderPath)) : null;
|
|
171
212
|
const rootLoaderName = rootLoaderImports?.loaderName || null;
|
|
172
213
|
const rootActionName = rootLoaderImports?.actionName || null;
|
|
214
|
+
const rootSearchContractInfo = rootModern ? pickRouteSearchContractModules(rootModern) : null;
|
|
215
|
+
const rootValidateSearchName = rootSearchContractInfo?.validateSearchPath ? await getImportNameForSearchContract(rootSearchContractInfo.validateSearchPath, 'validateSearch') : null;
|
|
216
|
+
const rootLoaderDepsName = rootSearchContractInfo?.loaderDepsPath ? await getImportNameForSearchContract(rootSearchContractInfo.loaderDepsPath, 'loaderDeps') : null;
|
|
173
217
|
const topLevelVars = await Promise.all(topLevel.map((route)=>buildRoute({
|
|
174
218
|
parentVar: 'rootRoute',
|
|
175
219
|
route
|
|
176
220
|
})));
|
|
177
221
|
const rootOpts = [];
|
|
178
222
|
if (rootLoaderName) rootOpts.push(`loader: modernLoaderToTanstack({ hasSplat: false }, ${rootLoaderName}),`);
|
|
223
|
+
if (rootValidateSearchName) rootOpts.push(`validateSearch: ${rootValidateSearchName},`);
|
|
224
|
+
if (rootLoaderDepsName) rootOpts.push(`loaderDeps: ${rootLoaderDepsName},`);
|
|
179
225
|
const routerGenTs = `/* eslint-disable */
|
|
180
226
|
// This file is auto-generated by Modern.js. Do not edit manually.
|
|
181
227
|
|
|
182
228
|
import {
|
|
183
229
|
createMemoryHistory,
|
|
230
|
+
modernTanstackRouterFastDefaults,
|
|
184
231
|
createRootRouteWithContext,
|
|
185
232
|
createRoute,
|
|
186
233
|
createRouter,
|
|
@@ -208,7 +255,7 @@ function isRedirectResponse(res: Response) {
|
|
|
208
255
|
}
|
|
209
256
|
|
|
210
257
|
function throwTanstackRedirect(location: string) {
|
|
211
|
-
const target = location
|
|
258
|
+
const target = location.length > 0 ? location : '/';
|
|
212
259
|
try {
|
|
213
260
|
void new URL(target);
|
|
214
261
|
throw redirect({ href: target });
|
|
@@ -234,21 +281,87 @@ function createRouteStaticData(opts: {
|
|
|
234
281
|
modernRouteAction?: unknown;
|
|
235
282
|
modernRouteLoader?: unknown;
|
|
236
283
|
}) {
|
|
237
|
-
const staticData:
|
|
284
|
+
const staticData: {
|
|
285
|
+
modernRouteId?: string;
|
|
286
|
+
modernRouteAction?: unknown;
|
|
287
|
+
modernRouteLoader?: unknown;
|
|
288
|
+
} = {};
|
|
238
289
|
|
|
239
|
-
if (opts.modernRouteId) {
|
|
290
|
+
if (typeof opts.modernRouteId === 'string' && opts.modernRouteId.length > 0) {
|
|
240
291
|
staticData.modernRouteId = opts.modernRouteId;
|
|
241
292
|
}
|
|
242
293
|
|
|
243
|
-
if (opts.modernRouteLoader) {
|
|
294
|
+
if (typeof opts.modernRouteLoader !== 'undefined') {
|
|
244
295
|
staticData.modernRouteLoader = opts.modernRouteLoader;
|
|
245
296
|
}
|
|
246
297
|
|
|
247
|
-
if (opts.modernRouteAction) {
|
|
298
|
+
if (typeof opts.modernRouteAction !== 'undefined') {
|
|
248
299
|
staticData.modernRouteAction = opts.modernRouteAction;
|
|
249
300
|
}
|
|
250
301
|
|
|
251
|
-
return
|
|
302
|
+
return staticData;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function getLoaderSignal(ctx: any): AbortSignal {
|
|
306
|
+
const abortSignal = ctx?.abortController?.signal;
|
|
307
|
+
if (abortSignal instanceof AbortSignal) {
|
|
308
|
+
return abortSignal;
|
|
309
|
+
}
|
|
310
|
+
if (ctx?.signal instanceof AbortSignal) {
|
|
311
|
+
return ctx.signal;
|
|
312
|
+
}
|
|
313
|
+
return new AbortController().signal;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function getLoaderHref(ctx: any): string {
|
|
317
|
+
if (typeof ctx?.location === 'string') {
|
|
318
|
+
return ctx.location;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
const publicHref = ctx?.location?.publicHref;
|
|
322
|
+
if (typeof publicHref === 'string') {
|
|
323
|
+
return publicHref;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const href = ctx?.location?.href;
|
|
327
|
+
if (typeof href === 'string') {
|
|
328
|
+
return href;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const urlHref = ctx?.location?.url?.href;
|
|
332
|
+
return typeof urlHref === 'string' ? urlHref : '';
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function getLoaderParams(ctx: any): Record<string, string> {
|
|
336
|
+
return typeof ctx?.params === 'object' && ctx.params !== null ? ctx.params : {};
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function handleModernLoaderResult<LoaderResult>(result: LoaderResult): LoaderResult {
|
|
340
|
+
if (isResponse(result)) {
|
|
341
|
+
if (isRedirectResponse(result)) {
|
|
342
|
+
const location = result.headers.get('Location') ?? '/';
|
|
343
|
+
throwTanstackRedirect(location);
|
|
344
|
+
}
|
|
345
|
+
if (result.status === 404) {
|
|
346
|
+
throw notFound();
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
return result;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function handleModernLoaderError(err: unknown): never {
|
|
354
|
+
if (isResponse(err)) {
|
|
355
|
+
if (isRedirectResponse(err)) {
|
|
356
|
+
const location = err.headers.get('Location') ?? '/';
|
|
357
|
+
throwTanstackRedirect(location);
|
|
358
|
+
}
|
|
359
|
+
if (err.status === 404) {
|
|
360
|
+
throw notFound();
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
throw err;
|
|
252
365
|
}
|
|
253
366
|
|
|
254
367
|
function modernLoaderToTanstack<TLoader extends (args: any) => any>(
|
|
@@ -257,57 +370,31 @@ function modernLoaderToTanstack<TLoader extends (args: any) => any>(
|
|
|
257
370
|
) {
|
|
258
371
|
type LoaderResult = Awaited<ReturnType<TLoader>>;
|
|
259
372
|
|
|
260
|
-
return
|
|
373
|
+
return (ctx: any): Promise<LoaderResult> => {
|
|
261
374
|
try {
|
|
262
|
-
const signal
|
|
263
|
-
ctx?.abortController?.signal ||
|
|
264
|
-
ctx?.signal ||
|
|
265
|
-
new AbortController().signal;
|
|
375
|
+
const signal = getLoaderSignal(ctx);
|
|
266
376
|
const baseRequest: Request | undefined =
|
|
267
377
|
ctx?.context?.request instanceof Request ? ctx.context.request : undefined;
|
|
268
378
|
|
|
269
|
-
const href =
|
|
270
|
-
typeof ctx?.location === 'string'
|
|
271
|
-
? ctx.location
|
|
272
|
-
: ctx?.location?.publicHref ||
|
|
273
|
-
ctx?.location?.href ||
|
|
274
|
-
ctx?.location?.url?.href ||
|
|
275
|
-
'';
|
|
379
|
+
const href = getLoaderHref(ctx);
|
|
276
380
|
|
|
277
|
-
const request = baseRequest
|
|
381
|
+
const request = baseRequest !== undefined
|
|
278
382
|
? new Request(baseRequest, { signal })
|
|
279
383
|
: new Request(href, { signal });
|
|
280
384
|
|
|
281
|
-
const params = mapParamsForModernLoader(ctx
|
|
385
|
+
const params = mapParamsForModernLoader(getLoaderParams(ctx), opts.hasSplat);
|
|
282
386
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
throwTanstackRedirect(location);
|
|
293
|
-
}
|
|
294
|
-
if (result.status === 404) {
|
|
295
|
-
throw notFound();
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
return result as LoaderResult;
|
|
387
|
+
return Promise.resolve(
|
|
388
|
+
(modernLoader as any)({
|
|
389
|
+
request,
|
|
390
|
+
params,
|
|
391
|
+
context: ctx?.context?.requestContext,
|
|
392
|
+
}),
|
|
393
|
+
)
|
|
394
|
+
.then((result: LoaderResult) => handleModernLoaderResult(result))
|
|
395
|
+
.catch(handleModernLoaderError);
|
|
300
396
|
} catch (err) {
|
|
301
|
-
|
|
302
|
-
if (isRedirectResponse(err)) {
|
|
303
|
-
const location = err.headers.get('Location') || '/';
|
|
304
|
-
throwTanstackRedirect(location);
|
|
305
|
-
}
|
|
306
|
-
if (err.status === 404) {
|
|
307
|
-
throw notFound();
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
throw err;
|
|
397
|
+
handleModernLoaderError(err);
|
|
311
398
|
}
|
|
312
399
|
};
|
|
313
400
|
}
|
|
@@ -328,6 +415,7 @@ ${statements.join('\n\n')}
|
|
|
328
415
|
export const routeTree = rootRoute.addChildren([${topLevelVars.join(', ')}]);
|
|
329
416
|
|
|
330
417
|
export const router = createRouter({
|
|
418
|
+
...modernTanstackRouterFastDefaults,
|
|
331
419
|
routeTree,
|
|
332
420
|
history: createMemoryHistory({
|
|
333
421
|
initialEntries: ['/'],
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import "node:module";
|
|
2
2
|
export * from "@tanstack/react-router";
|
|
3
|
-
export { useMatch } from "@tanstack/react-router";
|
|
3
|
+
export { Outlet, useLocation, useMatch, useMatches, useNavigate, useRouter } from "@tanstack/react-router";
|
|
4
4
|
export { Form, RouteActionResponseError, useFetcher } from "./dataMutation.mjs";
|
|
5
5
|
export { tanstackRouterPlugin as default, tanstackRouterPlugin } from "./plugin.mjs";
|
|
6
6
|
export { Link, NavLink } from "./prefetchLink.mjs";
|
|
7
7
|
export { CompositeComponent } from "./rsc/client.mjs";
|
|
8
|
+
export { getModernTanstackRouterFastDefaults, modernTanstackRouterFastDefaults } from "./types.mjs";
|
|
@@ -5,13 +5,15 @@ import { merge } from "@modern-js/runtime-utils/merge";
|
|
|
5
5
|
import { normalizePathname } from "@modern-js/runtime-utils/url";
|
|
6
6
|
import { RouterProvider, createBrowserHistory, createHashHistory, createRouter, useLocation, useMatches, useNavigate, useRouter } from "@tanstack/react-router";
|
|
7
7
|
import { RouterClient } from "@tanstack/react-router/ssr/client";
|
|
8
|
+
import { useContext, useMemo } from "react";
|
|
8
9
|
import { createModernBasepathRewrite } from "./basepathRewrite.mjs";
|
|
9
10
|
import { modifyRoutes, onAfterCreateRouter, onAfterHydrateRouter, onBeforeCreateRouter, onBeforeCreateRoutes, onBeforeHydrateRouter } from "./hooks.mjs";
|
|
10
11
|
import { applyRouterRuntimeState } from "./lifecycle.mjs";
|
|
12
|
+
import { Link } from "./prefetchLink.mjs";
|
|
11
13
|
import { createRouteTreeFromRouteObjects } from "./routeTree.mjs";
|
|
12
14
|
import { getTanstackRscSerializationAdapters } from "./rsc/client.mjs";
|
|
15
|
+
import { getModernTanstackRouterFastDefaults } from "./types.mjs";
|
|
13
16
|
import { createRouteObjectsFromConfig, urlJoin } from "./utils.mjs";
|
|
14
|
-
import * as __rspack_external_react from "react";
|
|
15
17
|
const BLOCKING_SUBSCRIBE_SYMBOL = Symbol.for('@modern-js/plugin-tanstack:blocking-subscribe');
|
|
16
18
|
const BLOCKING_STATE_SYMBOL = Symbol.for('@modern-js/plugin-tanstack:blocking-state');
|
|
17
19
|
function normalizeBase(b) {
|
|
@@ -74,6 +76,7 @@ const tanstackRouterPlugin = (userConfig = {})=>{
|
|
|
74
76
|
}
|
|
75
77
|
}
|
|
76
78
|
context.router = {
|
|
79
|
+
Link: Link,
|
|
77
80
|
useMatches: useMatches,
|
|
78
81
|
useLocation: useLocation,
|
|
79
82
|
useNavigate: useNavigate,
|
|
@@ -108,10 +111,10 @@ const tanstackRouterPlugin = (userConfig = {})=>{
|
|
|
108
111
|
let cachedRouter = null;
|
|
109
112
|
let cachedRouterBasepath = null;
|
|
110
113
|
const RouterWrapper = ()=>{
|
|
111
|
-
const runtimeContext =
|
|
114
|
+
const runtimeContext = useContext(InternalRuntimeContext);
|
|
112
115
|
const baseUrl = selectBasePath(location.pathname).replace(/^\/*/, '/');
|
|
113
116
|
const _basename = '/' === baseUrl ? urlJoin(baseUrl, runtimeContext._internalRouterBaseName || basename || '') : baseUrl;
|
|
114
|
-
const routeTree =
|
|
117
|
+
const routeTree = useMemo(()=>{
|
|
115
118
|
if (cachedRouteTree) return cachedRouteTree;
|
|
116
119
|
const routeObjects = getRouteObjects();
|
|
117
120
|
if (!routeObjects.length) return null;
|
|
@@ -121,7 +124,7 @@ const tanstackRouterPlugin = (userConfig = {})=>{
|
|
|
121
124
|
return cachedRouteTree;
|
|
122
125
|
}, []);
|
|
123
126
|
if (!routeTree) return App ? /*#__PURE__*/ jsx(App, {}) : null;
|
|
124
|
-
const router =
|
|
127
|
+
const router = useMemo(()=>{
|
|
125
128
|
const lifecycleContext = {
|
|
126
129
|
framework: 'tanstack',
|
|
127
130
|
phase: 'client-create',
|
|
@@ -143,6 +146,7 @@ const tanstackRouterPlugin = (userConfig = {})=>{
|
|
|
143
146
|
const rewrite = createModernBasepathRewrite(_basename);
|
|
144
147
|
const serializationAdapters = getGlobalEnableRsc() ? getTanstackRscSerializationAdapters() : void 0;
|
|
145
148
|
cachedRouter = createRouter({
|
|
149
|
+
...getModernTanstackRouterFastDefaults(mergedConfig),
|
|
146
150
|
routeTree,
|
|
147
151
|
basepath: '/',
|
|
148
152
|
rewrite,
|
|
@@ -186,11 +190,8 @@ const tanstackRouterPlugin = (userConfig = {})=>{
|
|
|
186
190
|
router,
|
|
187
191
|
runtimeContext: runtimeState
|
|
188
192
|
});
|
|
189
|
-
const RouterContent = hasSSRBootstrap ? /*#__PURE__*/ jsx(
|
|
190
|
-
|
|
191
|
-
children: /*#__PURE__*/ jsx(RouterClient, {
|
|
192
|
-
router: router
|
|
193
|
-
})
|
|
193
|
+
const RouterContent = hasSSRBootstrap ? /*#__PURE__*/ jsx(RouterClient, {
|
|
194
|
+
router: router
|
|
194
195
|
}) : /*#__PURE__*/ jsx(RouterProvider, {
|
|
195
196
|
router: router
|
|
196
197
|
});
|
|
@@ -6,24 +6,41 @@ 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/
|
|
10
|
-
import {
|
|
9
|
+
import { attachRouterServerSsrUtils } from "@tanstack/router-core/ssr/server";
|
|
10
|
+
import { useContext } from "react";
|
|
11
11
|
import { createModernBasepathRewrite } from "./basepathRewrite.mjs";
|
|
12
12
|
import { modifyRoutes, onAfterCreateRouter, onAfterHydrateRouter, onBeforeCreateRouter, onBeforeCreateRoutes, onBeforeHydrateRouter } from "./hooks.mjs";
|
|
13
13
|
import { applyRouterServerPrepareResult, createRouterServerSnapshot } from "./lifecycle.mjs";
|
|
14
14
|
import { createRouteTreeFromRouteObjects, getModernRouteIdsFromMatches } from "./routeTree.mjs";
|
|
15
15
|
import { createTanstackRscServerPayload, handleTanstackRscRedirect } from "./rsc/payloadRouter.mjs";
|
|
16
|
+
import { getModernTanstackRouterFastDefaults } from "./types.mjs";
|
|
16
17
|
import { createRouteObjectsFromConfig, urlJoin } from "./utils.mjs";
|
|
17
18
|
const setTanstackRscServerPayload = (payload)=>{
|
|
18
19
|
const storageContext = storage.useContext?.();
|
|
19
20
|
if (storageContext) storageContext.serverPayload = payload;
|
|
20
21
|
};
|
|
22
|
+
function isPromiseLike(value) {
|
|
23
|
+
return Boolean(value && 'function' == typeof value.then);
|
|
24
|
+
}
|
|
21
25
|
function isPreloadableRouteComponent(component) {
|
|
22
26
|
if (!component || 'function' != typeof component) return false;
|
|
23
27
|
const preloadable = component;
|
|
24
28
|
return 'function' == typeof preloadable.load || 'function' == typeof preloadable.preload;
|
|
25
29
|
}
|
|
30
|
+
function isReactLazyRouteComponent(component) {
|
|
31
|
+
return Boolean(component) && 'object' == typeof component && 'function' == typeof component._init && '_payload' in component;
|
|
32
|
+
}
|
|
33
|
+
async function preloadReactLazyRouteComponent(component) {
|
|
34
|
+
try {
|
|
35
|
+
component._init?.(component._payload);
|
|
36
|
+
} catch (thrown) {
|
|
37
|
+
if (!isPromiseLike(thrown)) throw thrown;
|
|
38
|
+
await thrown;
|
|
39
|
+
component._init?.(component._payload);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
26
42
|
async function preloadRouteComponent(component) {
|
|
43
|
+
if (isReactLazyRouteComponent(component)) return void await preloadReactLazyRouteComponent(component);
|
|
27
44
|
if (!isPreloadableRouteComponent(component)) return;
|
|
28
45
|
if ('function' == typeof component.load) return void await component.load({});
|
|
29
46
|
await component.preload?.({});
|
|
@@ -162,6 +179,7 @@ const tanstackRouterPlugin = (userConfig = {})=>{
|
|
|
162
179
|
};
|
|
163
180
|
hooks.onBeforeCreateRouter.call(routerLifecycleContext);
|
|
164
181
|
const tanstackRouter = createRouter({
|
|
182
|
+
...getModernTanstackRouterFastDefaults(mergedConfig),
|
|
165
183
|
routeTree,
|
|
166
184
|
history,
|
|
167
185
|
basepath: '/',
|
|
@@ -207,10 +225,12 @@ const tanstackRouterPlugin = (userConfig = {})=>{
|
|
|
207
225
|
await preloadMatchedRouteComponents(serverRouter);
|
|
208
226
|
context.ssrContext?.response.status(tanstackRouter.state.statusCode);
|
|
209
227
|
await serverRouter.serverSsr?.dehydrate?.();
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
228
|
+
if (isRSCNavigation) {
|
|
229
|
+
await waitForRouterSerialization(serverRouter);
|
|
230
|
+
setTanstackRscServerPayload(createTanstackRscServerPayload(serverRouter, {
|
|
231
|
+
omitClientLoaderData: true
|
|
232
|
+
}));
|
|
233
|
+
}
|
|
214
234
|
const ssrScriptTags = serverRouter.serverSsr?.takeBufferedScripts?.();
|
|
215
235
|
const hydrationScripts = routerManagedTagsToHtml(ssrScriptTags);
|
|
216
236
|
const matchedRouteIds = getModernRouteIdsFromMatches(serverRouter);
|
|
@@ -248,11 +268,8 @@ const tanstackRouterPlugin = (userConfig = {})=>{
|
|
|
248
268
|
if (!router) return App ? /*#__PURE__*/ jsx(App, {
|
|
249
269
|
...props
|
|
250
270
|
}) : null;
|
|
251
|
-
const routerWrapper = /*#__PURE__*/ jsx(
|
|
252
|
-
|
|
253
|
-
children: /*#__PURE__*/ jsx(RouterProvider, {
|
|
254
|
-
router: router
|
|
255
|
-
})
|
|
271
|
+
const routerWrapper = /*#__PURE__*/ jsx(RouterProvider, {
|
|
272
|
+
router: router
|
|
256
273
|
});
|
|
257
274
|
return App ? /*#__PURE__*/ jsx(App, {
|
|
258
275
|
children: routerWrapper
|
|
@@ -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,40 @@ function normalizeModernLoaderResponse(result) {
|
|
|
55
56
|
}
|
|
56
57
|
return normalizeModernLoaderResult(result);
|
|
57
58
|
}
|
|
59
|
+
function pickRouteModuleComponent(routeModule, seen = new Set()) {
|
|
60
|
+
if ('function' == typeof routeModule || routeModule && 'object' == typeof routeModule && '$$typeof' in routeModule) return routeModule;
|
|
61
|
+
if (!routeModule || 'object' != typeof routeModule) return;
|
|
62
|
+
if (seen.has(routeModule)) return;
|
|
63
|
+
seen.add(routeModule);
|
|
64
|
+
const module = routeModule;
|
|
65
|
+
for (const candidate of [
|
|
66
|
+
module.default,
|
|
67
|
+
module.Component
|
|
68
|
+
]){
|
|
69
|
+
const component = pickRouteModuleComponent(candidate, seen);
|
|
70
|
+
if (component) return component;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function createServerLazyImportComponent(lazyImport, fallbackComponent) {
|
|
74
|
+
if ("u" > typeof document) return fallbackComponent;
|
|
75
|
+
let resolvedComponent;
|
|
76
|
+
let pendingLoad;
|
|
77
|
+
const load = async ()=>{
|
|
78
|
+
if (resolvedComponent) return resolvedComponent;
|
|
79
|
+
const routeModule = await lazyImport();
|
|
80
|
+
const component = pickRouteModuleComponent(routeModule);
|
|
81
|
+
if (component) resolvedComponent = component;
|
|
82
|
+
return resolvedComponent;
|
|
83
|
+
};
|
|
84
|
+
const Component = (props)=>{
|
|
85
|
+
if (resolvedComponent) return createElement(resolvedComponent, props);
|
|
86
|
+
pendingLoad ||= load();
|
|
87
|
+
throw pendingLoad;
|
|
88
|
+
};
|
|
89
|
+
Component.load = load;
|
|
90
|
+
Component.preload = load;
|
|
91
|
+
return Component;
|
|
92
|
+
}
|
|
58
93
|
function isAbsoluteUrl(value) {
|
|
59
94
|
try {
|
|
60
95
|
new URL(value);
|
|
@@ -127,7 +162,7 @@ function wrapModernLoader(modernRoute, modernLoader, revalidationState, options
|
|
|
127
162
|
const signal = ctx?.abortController?.signal || ctx?.signal || new AbortController().signal;
|
|
128
163
|
const baseRequest = ctx?.context?.request instanceof Request ? ctx.context.request : void 0;
|
|
129
164
|
const href = 'string' == typeof ctx?.location ? ctx.location : ctx?.location?.publicHref || ctx?.location?.href || ctx?.location?.url?.href || '';
|
|
130
|
-
const request = baseRequest ? new Request(baseRequest, {
|
|
165
|
+
const request = void 0 !== baseRequest ? new Request(baseRequest, {
|
|
131
166
|
signal
|
|
132
167
|
}) : createModernRequest(href, signal);
|
|
133
168
|
const params = mapParamsForModernLoader({
|
|
@@ -192,7 +227,7 @@ function wrapRouteObjectLoader(route, revalidationState, options = {}) {
|
|
|
192
227
|
const signal = ctx?.abortController?.signal || ctx?.signal || new AbortController().signal;
|
|
193
228
|
const baseRequest = ctx?.context?.request instanceof Request ? ctx.context.request : void 0;
|
|
194
229
|
const href = 'string' == typeof ctx?.location ? ctx.location : ctx?.location?.publicHref || ctx?.location?.href || ctx?.location?.url?.href || '';
|
|
195
|
-
const request = baseRequest ? new Request(baseRequest, {
|
|
230
|
+
const request = void 0 !== baseRequest ? new Request(baseRequest, {
|
|
196
231
|
signal
|
|
197
232
|
}) : createModernRequest(href, signal);
|
|
198
233
|
const params = mapParamsForRouteObjectLoader({
|
|
@@ -229,10 +264,18 @@ function wrapRouteObjectLoader(route, revalidationState, options = {}) {
|
|
|
229
264
|
}
|
|
230
265
|
function toRouteComponent(routeObject) {
|
|
231
266
|
const route = routeObject;
|
|
267
|
+
const lazyImport = 'function' == typeof route.lazyImport ? route.lazyImport : void 0;
|
|
268
|
+
const fallbackComponent = route.Component ? route.Component : route.element ? ()=>route.element : void 0;
|
|
269
|
+
if (lazyImport && fallbackComponent) return createServerLazyImportComponent(lazyImport, fallbackComponent);
|
|
232
270
|
if (route.Component) return route.Component;
|
|
233
271
|
const element = route.element;
|
|
234
272
|
if (element) return ()=>element;
|
|
235
273
|
}
|
|
274
|
+
function toModernRouteComponent(route) {
|
|
275
|
+
const component = route.component || void 0;
|
|
276
|
+
if ('function' == typeof route.lazyImport && component) return createServerLazyImportComponent(route.lazyImport, component);
|
|
277
|
+
return component;
|
|
278
|
+
}
|
|
236
279
|
function toErrorComponent(routeObject) {
|
|
237
280
|
const route = routeObject;
|
|
238
281
|
if (route.ErrorBoundary) return route.ErrorBoundary;
|
|
@@ -275,6 +318,8 @@ function createRouteFromRouteObject(opts) {
|
|
|
275
318
|
component: toRouteComponent(routeObject),
|
|
276
319
|
pendingComponent: toPendingComponent(routeObject),
|
|
277
320
|
errorComponent: toErrorComponent(routeObject),
|
|
321
|
+
validateSearch: modernRouteObject.validateSearch,
|
|
322
|
+
loaderDeps: modernRouteObject.loaderDeps,
|
|
278
323
|
wrapInSuspense: true,
|
|
279
324
|
staticData: createRouteStaticData({
|
|
280
325
|
modernRouteId: routeObject.id,
|
|
@@ -313,7 +358,7 @@ function createRouteFromModernRoute(opts) {
|
|
|
313
358
|
const stableFallbackId = modernId || route._component || route.filename || route.data || ('function' == typeof route.loader ? route.id : void 0);
|
|
314
359
|
const pendingComponent = route.loading || route.pendingComponent;
|
|
315
360
|
const errorComponent = route.error || route.errorComponent;
|
|
316
|
-
const component = route
|
|
361
|
+
const component = toModernRouteComponent(route);
|
|
317
362
|
const modernLoader = route.loader;
|
|
318
363
|
const modernAction = route.action;
|
|
319
364
|
const modernShouldRevalidate = route.shouldRevalidate;
|
|
@@ -325,6 +370,8 @@ function createRouteFromModernRoute(opts) {
|
|
|
325
370
|
component: component || void 0,
|
|
326
371
|
pendingComponent: pendingComponent || void 0,
|
|
327
372
|
errorComponent: errorComponent || void 0,
|
|
373
|
+
validateSearch: route.validateSearch,
|
|
374
|
+
loaderDeps: route.loaderDeps,
|
|
328
375
|
wrapInSuspense: true,
|
|
329
376
|
staticData: createRouteStaticData({
|
|
330
377
|
modernRouteId: modernId,
|
|
@@ -360,7 +407,7 @@ function createRouteFromModernRoute(opts) {
|
|
|
360
407
|
}
|
|
361
408
|
function createRouteTreeFromModernRoutes(routes, options = {}) {
|
|
362
409
|
const rootModern = routes.find((r)=>r && 'nested' === r.type && r.isRoot);
|
|
363
|
-
const rootComponent = rootModern
|
|
410
|
+
const rootComponent = rootModern ? toModernRouteComponent(rootModern) : void 0;
|
|
364
411
|
const pendingComponent = rootModern?.loading;
|
|
365
412
|
const errorComponent = rootModern?.error;
|
|
366
413
|
const rootLoader = rootModern?.loader;
|
|
@@ -373,6 +420,8 @@ function createRouteTreeFromModernRoutes(routes, options = {}) {
|
|
|
373
420
|
component: rootComponent || void 0,
|
|
374
421
|
pendingComponent: pendingComponent || void 0,
|
|
375
422
|
errorComponent: errorComponent || void 0,
|
|
423
|
+
validateSearch: rootModern?.validateSearch,
|
|
424
|
+
loaderDeps: rootModern?.loaderDeps,
|
|
376
425
|
wrapInSuspense: true,
|
|
377
426
|
notFoundComponent: DefaultNotFound,
|
|
378
427
|
staticData: createRouteStaticData({
|
|
@@ -412,6 +461,8 @@ function createRouteTreeFromRouteObjects(routes, options = {}) {
|
|
|
412
461
|
component: rootLikeRoute ? toRouteComponent(rootLikeRoute) : void 0,
|
|
413
462
|
pendingComponent: rootLikeRoute ? toPendingComponent(rootLikeRoute) : void 0,
|
|
414
463
|
errorComponent: rootLikeRoute ? toErrorComponent(rootLikeRoute) : void 0,
|
|
464
|
+
validateSearch: rootLikeRoute?.validateSearch,
|
|
465
|
+
loaderDeps: rootLikeRoute?.loaderDeps,
|
|
415
466
|
wrapInSuspense: true,
|
|
416
467
|
notFoundComponent: DefaultNotFound,
|
|
417
468
|
staticData: createRouteStaticData({
|