@bleedingdev/modern-js-plugin-tanstack 3.2.0-ultramodern.1 → 3.2.0-ultramodern.100
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 +15 -6
- package/dist/cjs/cli/routeSplitting.js +83 -0
- package/dist/cjs/cli/tanstackTypes.js +146 -58
- package/dist/cjs/runtime/hydrationBoundary.js +44 -0
- package/dist/cjs/runtime/index.js +321 -69
- package/dist/cjs/runtime/outlet.js +54 -0
- package/dist/cjs/runtime/plugin.js +194 -90
- package/dist/cjs/runtime/plugin.node.js +29 -11
- package/dist/cjs/runtime/plugin.worker.js +49 -0
- package/dist/cjs/runtime/routeTree.js +72 -12
- package/dist/cjs/runtime/types.js +27 -1
- package/dist/esm/cli/index.mjs +7 -7
- package/dist/esm/cli/routeSplitting.mjs +43 -0
- package/dist/esm/cli/tanstackTypes.mjs +146 -58
- package/dist/esm/runtime/hydrationBoundary.mjs +10 -0
- package/dist/esm/runtime/index.mjs +3 -2
- package/dist/esm/runtime/outlet.mjs +17 -0
- package/dist/esm/runtime/plugin.mjs +197 -93
- package/dist/esm/runtime/plugin.node.mjs +30 -12
- package/dist/esm/runtime/plugin.worker.mjs +1 -0
- package/dist/esm/runtime/routeTree.mjs +73 -13
- package/dist/esm/runtime/types.mjs +7 -0
- package/dist/esm-node/cli/index.mjs +7 -7
- package/dist/esm-node/cli/routeSplitting.mjs +44 -0
- package/dist/esm-node/cli/tanstackTypes.mjs +146 -58
- package/dist/esm-node/runtime/hydrationBoundary.mjs +11 -0
- package/dist/esm-node/runtime/index.mjs +3 -2
- package/dist/esm-node/runtime/outlet.mjs +18 -0
- package/dist/esm-node/runtime/plugin.mjs +197 -93
- package/dist/esm-node/runtime/plugin.node.mjs +30 -12
- package/dist/esm-node/runtime/plugin.worker.mjs +2 -0
- package/dist/esm-node/runtime/routeTree.mjs +73 -13
- 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/hydrationBoundary.d.ts +2 -0
- package/dist/types/runtime/index.d.ts +5 -2
- package/dist/types/runtime/outlet.d.ts +2 -0
- 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 +32 -18
- package/src/cli/routeSplitting.ts +81 -0
- package/src/cli/tanstackTypes.ts +217 -67
- package/src/runtime/basepathRewrite.ts +1 -0
- package/src/runtime/dataMutation.tsx +1 -0
- package/src/runtime/hydrationBoundary.tsx +12 -0
- package/src/runtime/index.tsx +107 -2
- package/src/runtime/lifecycle.ts +1 -0
- package/src/runtime/outlet.tsx +42 -0
- package/src/runtime/plugin.node.tsx +58 -8
- package/src/runtime/plugin.tsx +354 -149
- package/src/runtime/plugin.worker.tsx +4 -0
- package/src/runtime/routeTree.ts +195 -23
- package/src/runtime/rsc/ClientSlot.tsx +1 -0
- package/src/runtime/rsc/CompositeComponent.tsx +1 -0
- package/src/runtime/rsc/ReplayableStream.ts +1 -0
- package/src/runtime/rsc/RscNodeRenderer.tsx +1 -0
- package/src/runtime/rsc/client.tsx +2 -3
- package/src/runtime/rsc/createRscProxy.tsx +1 -0
- package/src/runtime/rsc/payloadRouter.ts +1 -0
- package/src/runtime/rsc/server.tsx +1 -0
- package/src/runtime/rsc/slotUsageSanitizer.ts +1 -0
- package/src/runtime/ssr-shim.d.ts +1 -3
- package/src/runtime/types.ts +13 -0
- package/src/runtime/utils.tsx +1 -0
- package/tests/router/cli.test.ts +239 -0
- package/tests/router/fastDefaults.test.ts +25 -0
- package/tests/router/hydrationBoundary.test.tsx +23 -0
- package/tests/router/routeTree.test.ts +416 -1
- package/tests/router/tanstackTypes.test.ts +184 -0
package/src/cli/tanstackTypes.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// @effect-diagnostics asyncFunction:off nodeBuiltinImport:off strictBooleanExpressions:off
|
|
1
2
|
import type { AppToolsContext } from '@modern-js/app-tools';
|
|
2
3
|
import type { NestedRouteForCli, PageRoute } from '@modern-js/types';
|
|
3
4
|
import { findExists, formatImportPath, fs, slash } from '@modern-js/utils';
|
|
@@ -85,6 +86,17 @@ function pickModernLoaderModule(route: NestedRouteForCli | PageRoute) {
|
|
|
85
86
|
return { loaderPath, inline };
|
|
86
87
|
}
|
|
87
88
|
|
|
89
|
+
function pickRouteSearchContractModules(route: NestedRouteForCli | PageRoute) {
|
|
90
|
+
const validateSearchPath = (route as any).validateSearch;
|
|
91
|
+
const loaderDepsPath = (route as any).loaderDeps;
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
validateSearchPath:
|
|
95
|
+
typeof validateSearchPath === 'string' ? validateSearchPath : null,
|
|
96
|
+
loaderDepsPath: typeof loaderDepsPath === 'string' ? loaderDepsPath : null,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
88
100
|
function isPathlessLayout(route: NestedRouteForCli | PageRoute) {
|
|
89
101
|
return (
|
|
90
102
|
(route as any).type === 'nested' &&
|
|
@@ -181,9 +193,29 @@ export async function generateTanstackRouterTypesSourceForEntry(opts: {
|
|
|
181
193
|
const statements: string[] = [];
|
|
182
194
|
|
|
183
195
|
const loaderImportMap = new Map<string, string>();
|
|
196
|
+
const searchContractImportMap = new Map<string, string>();
|
|
197
|
+
const usedRouteVarNames = new Set<string>();
|
|
184
198
|
let loaderIndex = 0;
|
|
199
|
+
let validateSearchIndex = 0;
|
|
200
|
+
let loaderDepsIndex = 0;
|
|
185
201
|
let routeIndex = 0;
|
|
186
202
|
|
|
203
|
+
const resolveRouteModuleNoExt = async (aliasedNoExtPath: string) => {
|
|
204
|
+
const prefix = `${appContext.internalSrcAlias}/`;
|
|
205
|
+
let absNoExt: string;
|
|
206
|
+
if (aliasedNoExtPath.startsWith(prefix)) {
|
|
207
|
+
const rel = aliasedNoExtPath.slice(prefix.length);
|
|
208
|
+
absNoExt = path.join(appContext.srcDirectory, rel);
|
|
209
|
+
} else if (path.isAbsolute(aliasedNoExtPath)) {
|
|
210
|
+
absNoExt = aliasedNoExtPath;
|
|
211
|
+
} else {
|
|
212
|
+
// Unknown format; treat as already relative to src.
|
|
213
|
+
absNoExt = path.join(appContext.srcDirectory, aliasedNoExtPath);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return resolveFileNoExt(absNoExt);
|
|
217
|
+
};
|
|
218
|
+
|
|
187
219
|
const getImportNamesForLoader = async (
|
|
188
220
|
aliasedNoExtPath: string,
|
|
189
221
|
inline: boolean,
|
|
@@ -200,19 +232,7 @@ export async function generateTanstackRouterTypesSourceForEntry(opts: {
|
|
|
200
232
|
};
|
|
201
233
|
}
|
|
202
234
|
|
|
203
|
-
const
|
|
204
|
-
let absNoExt: string;
|
|
205
|
-
if (aliasedNoExtPath.startsWith(prefix)) {
|
|
206
|
-
const rel = aliasedNoExtPath.slice(prefix.length);
|
|
207
|
-
absNoExt = path.join(appContext.srcDirectory, rel);
|
|
208
|
-
} else if (path.isAbsolute(aliasedNoExtPath)) {
|
|
209
|
-
absNoExt = aliasedNoExtPath;
|
|
210
|
-
} else {
|
|
211
|
-
// Unknown format; treat as already relative to src.
|
|
212
|
-
absNoExt = path.join(appContext.srcDirectory, aliasedNoExtPath);
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
const resolvedNoExt = await resolveFileNoExt(absNoExt);
|
|
235
|
+
const resolvedNoExt = await resolveRouteModuleNoExt(aliasedNoExtPath);
|
|
216
236
|
if (!resolvedNoExt) {
|
|
217
237
|
return null;
|
|
218
238
|
}
|
|
@@ -241,10 +261,49 @@ export async function generateTanstackRouterTypesSourceForEntry(opts: {
|
|
|
241
261
|
return { loaderName: importName, actionName };
|
|
242
262
|
};
|
|
243
263
|
|
|
264
|
+
const getImportNameForSearchContract = async (
|
|
265
|
+
aliasedNoExtPath: string,
|
|
266
|
+
exportName: 'validateSearch' | 'loaderDeps',
|
|
267
|
+
) => {
|
|
268
|
+
const key = `${exportName}:${aliasedNoExtPath}`;
|
|
269
|
+
const existing = searchContractImportMap.get(key);
|
|
270
|
+
if (existing) {
|
|
271
|
+
return existing;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
const resolvedNoExt = await resolveRouteModuleNoExt(aliasedNoExtPath);
|
|
275
|
+
if (!resolvedNoExt) {
|
|
276
|
+
return null;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const relImport = normalizeRelativeImport(
|
|
280
|
+
path.relative(outDir, resolvedNoExt),
|
|
281
|
+
);
|
|
282
|
+
const importName =
|
|
283
|
+
exportName === 'validateSearch'
|
|
284
|
+
? `validateSearch_${validateSearchIndex++}`
|
|
285
|
+
: `loaderDeps_${loaderDepsIndex++}`;
|
|
286
|
+
imports.push(
|
|
287
|
+
`import { ${exportName} as ${importName} } from ${quote(relImport)};`,
|
|
288
|
+
);
|
|
289
|
+
searchContractImportMap.set(key, importName);
|
|
290
|
+
return importName;
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
const reserveRouteVarName = (preferred: string) => {
|
|
294
|
+
let candidate = preferred;
|
|
295
|
+
let suffix = 1;
|
|
296
|
+
while (usedRouteVarNames.has(candidate)) {
|
|
297
|
+
candidate = `${preferred}_${suffix++}`;
|
|
298
|
+
}
|
|
299
|
+
usedRouteVarNames.add(candidate);
|
|
300
|
+
return candidate;
|
|
301
|
+
};
|
|
302
|
+
|
|
244
303
|
const createRouteVarName = (route: NestedRouteForCli | PageRoute) => {
|
|
245
304
|
const id = (route as any).id as string | undefined;
|
|
246
305
|
const base = id ? makeLegalIdentifier(id) : `r_${routeIndex++}`;
|
|
247
|
-
return `route_${base}
|
|
306
|
+
return reserveRouteVarName(`route_${base}`);
|
|
248
307
|
};
|
|
249
308
|
|
|
250
309
|
const buildRoute = async (opts: {
|
|
@@ -266,6 +325,19 @@ export async function generateTanstackRouterTypesSourceForEntry(opts: {
|
|
|
266
325
|
: null;
|
|
267
326
|
const loaderName = loaderImports?.loaderName || null;
|
|
268
327
|
const actionName = loaderImports?.actionName || null;
|
|
328
|
+
const searchContractInfo = pickRouteSearchContractModules(route);
|
|
329
|
+
const validateSearchName = searchContractInfo.validateSearchPath
|
|
330
|
+
? await getImportNameForSearchContract(
|
|
331
|
+
searchContractInfo.validateSearchPath,
|
|
332
|
+
'validateSearch',
|
|
333
|
+
)
|
|
334
|
+
: null;
|
|
335
|
+
const loaderDepsName = searchContractInfo.loaderDepsPath
|
|
336
|
+
? await getImportNameForSearchContract(
|
|
337
|
+
searchContractInfo.loaderDepsPath,
|
|
338
|
+
'loaderDeps',
|
|
339
|
+
)
|
|
340
|
+
: null;
|
|
269
341
|
|
|
270
342
|
const rawPath = (route as any).path as string | undefined;
|
|
271
343
|
const hasSplat = typeof rawPath === 'string' && rawPath.includes('*');
|
|
@@ -285,6 +357,12 @@ export async function generateTanstackRouterTypesSourceForEntry(opts: {
|
|
|
285
357
|
`loader: modernLoaderToTanstack({ hasSplat: ${hasSplat} }, ${loaderName}),`,
|
|
286
358
|
);
|
|
287
359
|
}
|
|
360
|
+
if (validateSearchName) {
|
|
361
|
+
routeOpts.push(`validateSearch: ${validateSearchName},`);
|
|
362
|
+
}
|
|
363
|
+
if (loaderDepsName) {
|
|
364
|
+
routeOpts.push(`loaderDeps: ${loaderDepsName},`);
|
|
365
|
+
}
|
|
288
366
|
|
|
289
367
|
const staticDataSnippet = createRouteStaticDataSnippet({
|
|
290
368
|
modernRouteId: (route as any).id as string | undefined,
|
|
@@ -295,18 +373,27 @@ export async function generateTanstackRouterTypesSourceForEntry(opts: {
|
|
|
295
373
|
routeOpts.push(staticDataSnippet);
|
|
296
374
|
}
|
|
297
375
|
|
|
298
|
-
statements.push(
|
|
299
|
-
`const ${varName} = createRoute({\n ${routeOpts.join('\n ')}\n});`,
|
|
300
|
-
);
|
|
301
|
-
|
|
302
376
|
const children = (route as any).children as
|
|
303
377
|
| Array<NestedRouteForCli | PageRoute>
|
|
304
378
|
| undefined;
|
|
379
|
+
const hasChildren = Boolean(children && children.length > 0);
|
|
380
|
+
const routeCtorVarName = hasChildren
|
|
381
|
+
? reserveRouteVarName(`${varName}__base`)
|
|
382
|
+
: varName;
|
|
383
|
+
|
|
384
|
+
statements.push(
|
|
385
|
+
`const ${routeCtorVarName} = createRoute({\n ${routeOpts.join('\n ')}\n});`,
|
|
386
|
+
);
|
|
387
|
+
|
|
305
388
|
if (children && children.length > 0) {
|
|
306
389
|
const childVars = await Promise.all(
|
|
307
|
-
children.map(child =>
|
|
390
|
+
children.map(child =>
|
|
391
|
+
buildRoute({ parentVar: routeCtorVarName, route: child }),
|
|
392
|
+
),
|
|
393
|
+
);
|
|
394
|
+
statements.push(
|
|
395
|
+
`const ${varName} = ${routeCtorVarName}.addChildren([${childVars.join(', ')}]);`,
|
|
308
396
|
);
|
|
309
|
-
statements.push(`${varName}.addChildren([${childVars.join(', ')}]);`);
|
|
310
397
|
}
|
|
311
398
|
|
|
312
399
|
return varName;
|
|
@@ -325,6 +412,21 @@ export async function generateTanstackRouterTypesSourceForEntry(opts: {
|
|
|
325
412
|
: null;
|
|
326
413
|
const rootLoaderName = rootLoaderImports?.loaderName || null;
|
|
327
414
|
const rootActionName = rootLoaderImports?.actionName || null;
|
|
415
|
+
const rootSearchContractInfo = rootModern
|
|
416
|
+
? pickRouteSearchContractModules(rootModern)
|
|
417
|
+
: null;
|
|
418
|
+
const rootValidateSearchName = rootSearchContractInfo?.validateSearchPath
|
|
419
|
+
? await getImportNameForSearchContract(
|
|
420
|
+
rootSearchContractInfo.validateSearchPath,
|
|
421
|
+
'validateSearch',
|
|
422
|
+
)
|
|
423
|
+
: null;
|
|
424
|
+
const rootLoaderDepsName = rootSearchContractInfo?.loaderDepsPath
|
|
425
|
+
? await getImportNameForSearchContract(
|
|
426
|
+
rootSearchContractInfo.loaderDepsPath,
|
|
427
|
+
'loaderDeps',
|
|
428
|
+
)
|
|
429
|
+
: null;
|
|
328
430
|
|
|
329
431
|
const topLevelVars = await Promise.all(
|
|
330
432
|
topLevel.map(route => buildRoute({ parentVar: 'rootRoute', route })),
|
|
@@ -336,12 +438,19 @@ export async function generateTanstackRouterTypesSourceForEntry(opts: {
|
|
|
336
438
|
`loader: modernLoaderToTanstack({ hasSplat: false }, ${rootLoaderName}),`,
|
|
337
439
|
);
|
|
338
440
|
}
|
|
441
|
+
if (rootValidateSearchName) {
|
|
442
|
+
rootOpts.push(`validateSearch: ${rootValidateSearchName},`);
|
|
443
|
+
}
|
|
444
|
+
if (rootLoaderDepsName) {
|
|
445
|
+
rootOpts.push(`loaderDeps: ${rootLoaderDepsName},`);
|
|
446
|
+
}
|
|
339
447
|
|
|
340
448
|
const routerGenTs = `/* eslint-disable */
|
|
341
449
|
// This file is auto-generated by Modern.js. Do not edit manually.
|
|
342
450
|
|
|
343
451
|
import {
|
|
344
452
|
createMemoryHistory,
|
|
453
|
+
modernTanstackRouterFastDefaults,
|
|
345
454
|
createRootRouteWithContext,
|
|
346
455
|
createRoute,
|
|
347
456
|
createRouter,
|
|
@@ -369,7 +478,7 @@ function isRedirectResponse(res: Response) {
|
|
|
369
478
|
}
|
|
370
479
|
|
|
371
480
|
function throwTanstackRedirect(location: string) {
|
|
372
|
-
const target = location
|
|
481
|
+
const target = location.length > 0 ? location : '/';
|
|
373
482
|
try {
|
|
374
483
|
void new URL(target);
|
|
375
484
|
throw redirect({ href: target });
|
|
@@ -395,21 +504,87 @@ function createRouteStaticData(opts: {
|
|
|
395
504
|
modernRouteAction?: unknown;
|
|
396
505
|
modernRouteLoader?: unknown;
|
|
397
506
|
}) {
|
|
398
|
-
const staticData:
|
|
507
|
+
const staticData: {
|
|
508
|
+
modernRouteId?: string;
|
|
509
|
+
modernRouteAction?: unknown;
|
|
510
|
+
modernRouteLoader?: unknown;
|
|
511
|
+
} = {};
|
|
399
512
|
|
|
400
|
-
if (opts.modernRouteId) {
|
|
513
|
+
if (typeof opts.modernRouteId === 'string' && opts.modernRouteId.length > 0) {
|
|
401
514
|
staticData.modernRouteId = opts.modernRouteId;
|
|
402
515
|
}
|
|
403
516
|
|
|
404
|
-
if (opts.modernRouteLoader) {
|
|
517
|
+
if (typeof opts.modernRouteLoader !== 'undefined') {
|
|
405
518
|
staticData.modernRouteLoader = opts.modernRouteLoader;
|
|
406
519
|
}
|
|
407
520
|
|
|
408
|
-
if (opts.modernRouteAction) {
|
|
521
|
+
if (typeof opts.modernRouteAction !== 'undefined') {
|
|
409
522
|
staticData.modernRouteAction = opts.modernRouteAction;
|
|
410
523
|
}
|
|
411
524
|
|
|
412
|
-
return
|
|
525
|
+
return staticData;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
function getLoaderSignal(ctx: any): AbortSignal {
|
|
529
|
+
const abortSignal = ctx?.abortController?.signal;
|
|
530
|
+
if (abortSignal instanceof AbortSignal) {
|
|
531
|
+
return abortSignal;
|
|
532
|
+
}
|
|
533
|
+
if (ctx?.signal instanceof AbortSignal) {
|
|
534
|
+
return ctx.signal;
|
|
535
|
+
}
|
|
536
|
+
return new AbortController().signal;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
function getLoaderHref(ctx: any): string {
|
|
540
|
+
if (typeof ctx?.location === 'string') {
|
|
541
|
+
return ctx.location;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
const publicHref = ctx?.location?.publicHref;
|
|
545
|
+
if (typeof publicHref === 'string') {
|
|
546
|
+
return publicHref;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
const href = ctx?.location?.href;
|
|
550
|
+
if (typeof href === 'string') {
|
|
551
|
+
return href;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
const urlHref = ctx?.location?.url?.href;
|
|
555
|
+
return typeof urlHref === 'string' ? urlHref : '';
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
function getLoaderParams(ctx: any): Record<string, string> {
|
|
559
|
+
return typeof ctx?.params === 'object' && ctx.params !== null ? ctx.params : {};
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
function handleModernLoaderResult<LoaderResult>(result: LoaderResult): LoaderResult {
|
|
563
|
+
if (isResponse(result)) {
|
|
564
|
+
if (isRedirectResponse(result)) {
|
|
565
|
+
const location = result.headers.get('Location') ?? '/';
|
|
566
|
+
throwTanstackRedirect(location);
|
|
567
|
+
}
|
|
568
|
+
if (result.status === 404) {
|
|
569
|
+
throw notFound();
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
return result;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
function handleModernLoaderError(err: unknown): never {
|
|
577
|
+
if (isResponse(err)) {
|
|
578
|
+
if (isRedirectResponse(err)) {
|
|
579
|
+
const location = err.headers.get('Location') ?? '/';
|
|
580
|
+
throwTanstackRedirect(location);
|
|
581
|
+
}
|
|
582
|
+
if (err.status === 404) {
|
|
583
|
+
throw notFound();
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
throw err;
|
|
413
588
|
}
|
|
414
589
|
|
|
415
590
|
function modernLoaderToTanstack<TLoader extends (args: any) => any>(
|
|
@@ -418,57 +593,31 @@ function modernLoaderToTanstack<TLoader extends (args: any) => any>(
|
|
|
418
593
|
) {
|
|
419
594
|
type LoaderResult = Awaited<ReturnType<TLoader>>;
|
|
420
595
|
|
|
421
|
-
return
|
|
596
|
+
return (ctx: any): Promise<LoaderResult> => {
|
|
422
597
|
try {
|
|
423
|
-
const signal
|
|
424
|
-
ctx?.abortController?.signal ||
|
|
425
|
-
ctx?.signal ||
|
|
426
|
-
new AbortController().signal;
|
|
598
|
+
const signal = getLoaderSignal(ctx);
|
|
427
599
|
const baseRequest: Request | undefined =
|
|
428
600
|
ctx?.context?.request instanceof Request ? ctx.context.request : undefined;
|
|
429
601
|
|
|
430
|
-
const href =
|
|
431
|
-
typeof ctx?.location === 'string'
|
|
432
|
-
? ctx.location
|
|
433
|
-
: ctx?.location?.publicHref ||
|
|
434
|
-
ctx?.location?.href ||
|
|
435
|
-
ctx?.location?.url?.href ||
|
|
436
|
-
'';
|
|
602
|
+
const href = getLoaderHref(ctx);
|
|
437
603
|
|
|
438
|
-
const request = baseRequest
|
|
604
|
+
const request = baseRequest !== undefined
|
|
439
605
|
? new Request(baseRequest, { signal })
|
|
440
606
|
: new Request(href, { signal });
|
|
441
607
|
|
|
442
|
-
const params = mapParamsForModernLoader(ctx
|
|
608
|
+
const params = mapParamsForModernLoader(getLoaderParams(ctx), opts.hasSplat);
|
|
443
609
|
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
throwTanstackRedirect(location);
|
|
454
|
-
}
|
|
455
|
-
if (result.status === 404) {
|
|
456
|
-
throw notFound();
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
return result as LoaderResult;
|
|
610
|
+
return Promise.resolve(
|
|
611
|
+
(modernLoader as any)({
|
|
612
|
+
request,
|
|
613
|
+
params,
|
|
614
|
+
context: ctx?.context?.requestContext,
|
|
615
|
+
}),
|
|
616
|
+
)
|
|
617
|
+
.then((result: LoaderResult) => handleModernLoaderResult(result))
|
|
618
|
+
.catch(handleModernLoaderError);
|
|
461
619
|
} catch (err) {
|
|
462
|
-
|
|
463
|
-
if (isRedirectResponse(err)) {
|
|
464
|
-
const location = err.headers.get('Location') || '/';
|
|
465
|
-
throwTanstackRedirect(location);
|
|
466
|
-
}
|
|
467
|
-
if (err.status === 404) {
|
|
468
|
-
throw notFound();
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
throw err;
|
|
620
|
+
handleModernLoaderError(err);
|
|
472
621
|
}
|
|
473
622
|
};
|
|
474
623
|
}
|
|
@@ -491,6 +640,7 @@ ${statements.join('\n\n')}
|
|
|
491
640
|
export const routeTree = rootRoute.addChildren([${topLevelVars.join(', ')}]);
|
|
492
641
|
|
|
493
642
|
export const router = createRouter({
|
|
643
|
+
...modernTanstackRouterFastDefaults,
|
|
494
644
|
routeTree,
|
|
495
645
|
history: createMemoryHistory({
|
|
496
646
|
initialEntries: ['/'],
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type ReactElement, Suspense } from 'react';
|
|
2
|
+
|
|
3
|
+
export function wrapTanstackSsrHydrationBoundary(
|
|
4
|
+
routerContent: ReactElement,
|
|
5
|
+
shouldWrap: boolean,
|
|
6
|
+
) {
|
|
7
|
+
if (shouldWrap) {
|
|
8
|
+
return <Suspense fallback={null}>{routerContent}</Suspense>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
return routerContent;
|
|
12
|
+
}
|
package/src/runtime/index.tsx
CHANGED
|
@@ -1,5 +1,104 @@
|
|
|
1
|
-
export * from '@tanstack/react-router';
|
|
2
|
-
export {
|
|
1
|
+
export type * from '@tanstack/react-router';
|
|
2
|
+
export {
|
|
3
|
+
Asset,
|
|
4
|
+
Await,
|
|
5
|
+
Block,
|
|
6
|
+
CatchBoundary,
|
|
7
|
+
CatchNotFound,
|
|
8
|
+
ClientOnly,
|
|
9
|
+
cleanPath,
|
|
10
|
+
composeRewrites,
|
|
11
|
+
createBrowserHistory,
|
|
12
|
+
createControlledPromise,
|
|
13
|
+
createFileRoute,
|
|
14
|
+
createHashHistory,
|
|
15
|
+
createHistory,
|
|
16
|
+
createLazyFileRoute,
|
|
17
|
+
createLazyRoute,
|
|
18
|
+
createLink,
|
|
19
|
+
createMemoryHistory,
|
|
20
|
+
createRootRoute,
|
|
21
|
+
createRootRouteWithContext,
|
|
22
|
+
createRoute,
|
|
23
|
+
createRouteMask,
|
|
24
|
+
createRouter,
|
|
25
|
+
createRouterConfig,
|
|
26
|
+
createSerializationAdapter,
|
|
27
|
+
DEFAULT_PROTOCOL_ALLOWLIST,
|
|
28
|
+
DefaultGlobalNotFound,
|
|
29
|
+
deepEqual,
|
|
30
|
+
defaultParseSearch,
|
|
31
|
+
defaultStringifySearch,
|
|
32
|
+
defer,
|
|
33
|
+
ErrorComponent,
|
|
34
|
+
FileRoute,
|
|
35
|
+
FileRouteLoader,
|
|
36
|
+
functionalUpdate,
|
|
37
|
+
getRouteApi,
|
|
38
|
+
HeadContent,
|
|
39
|
+
interpolatePath,
|
|
40
|
+
isMatch,
|
|
41
|
+
isNotFound,
|
|
42
|
+
isPlainArray,
|
|
43
|
+
isPlainObject,
|
|
44
|
+
isRedirect,
|
|
45
|
+
joinPaths,
|
|
46
|
+
LazyRoute,
|
|
47
|
+
lazyFn,
|
|
48
|
+
lazyRouteComponent,
|
|
49
|
+
linkOptions,
|
|
50
|
+
Match,
|
|
51
|
+
Matches,
|
|
52
|
+
MatchRoute,
|
|
53
|
+
Navigate,
|
|
54
|
+
NotFoundRoute,
|
|
55
|
+
notFound,
|
|
56
|
+
parseSearchWith,
|
|
57
|
+
RootRoute,
|
|
58
|
+
Route,
|
|
59
|
+
RouteApi,
|
|
60
|
+
Router,
|
|
61
|
+
RouterContextProvider,
|
|
62
|
+
RouterProvider,
|
|
63
|
+
reactUse,
|
|
64
|
+
redirect,
|
|
65
|
+
replaceEqualDeep,
|
|
66
|
+
resolvePath,
|
|
67
|
+
retainSearchParams,
|
|
68
|
+
rootRouteId,
|
|
69
|
+
rootRouteWithContext,
|
|
70
|
+
ScriptOnce,
|
|
71
|
+
Scripts,
|
|
72
|
+
ScrollRestoration,
|
|
73
|
+
SearchParamError,
|
|
74
|
+
stringifySearchWith,
|
|
75
|
+
stripSearchParams,
|
|
76
|
+
trimPath,
|
|
77
|
+
trimPathLeft,
|
|
78
|
+
trimPathRight,
|
|
79
|
+
useAwaited,
|
|
80
|
+
useBlocker,
|
|
81
|
+
useCanGoBack,
|
|
82
|
+
useChildMatches,
|
|
83
|
+
useElementScrollRestoration,
|
|
84
|
+
useHydrated,
|
|
85
|
+
useLayoutEffect,
|
|
86
|
+
useLinkProps,
|
|
87
|
+
useLoaderData,
|
|
88
|
+
useLoaderDeps,
|
|
89
|
+
useLocation,
|
|
90
|
+
useMatch,
|
|
91
|
+
useMatches,
|
|
92
|
+
useMatchRoute,
|
|
93
|
+
useNavigate,
|
|
94
|
+
useParams,
|
|
95
|
+
useParentMatches,
|
|
96
|
+
useRouteContext,
|
|
97
|
+
useRouter,
|
|
98
|
+
useRouterState,
|
|
99
|
+
useSearch,
|
|
100
|
+
useTags,
|
|
101
|
+
} from '@tanstack/react-router';
|
|
3
102
|
export type {
|
|
4
103
|
Fetcher,
|
|
5
104
|
FetcherState,
|
|
@@ -12,6 +111,7 @@ export {
|
|
|
12
111
|
RouteActionResponseError,
|
|
13
112
|
useFetcher,
|
|
14
113
|
} from './dataMutation';
|
|
114
|
+
export { Outlet } from './outlet';
|
|
15
115
|
export {
|
|
16
116
|
tanstackRouterPlugin,
|
|
17
117
|
tanstackRouterPlugin as default,
|
|
@@ -28,3 +128,8 @@ export type {
|
|
|
28
128
|
CompositeComponentProps,
|
|
29
129
|
} from './rsc/client';
|
|
30
130
|
export { CompositeComponent } from './rsc/client';
|
|
131
|
+
export type { RouterConfig } from './types';
|
|
132
|
+
export {
|
|
133
|
+
getModernTanstackRouterFastDefaults,
|
|
134
|
+
modernTanstackRouterFastDefaults,
|
|
135
|
+
} from './types';
|
package/src/runtime/lifecycle.ts
CHANGED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Outlet as TanstackOutlet } from '@tanstack/react-router';
|
|
2
|
+
import {
|
|
3
|
+
type ComponentProps,
|
|
4
|
+
createElement,
|
|
5
|
+
type ElementType,
|
|
6
|
+
memo,
|
|
7
|
+
} from 'react';
|
|
8
|
+
|
|
9
|
+
type PreloadableComponent = ElementType<Record<string, unknown>> & {
|
|
10
|
+
load?: () => Promise<unknown>;
|
|
11
|
+
preload?: () => Promise<unknown>;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export const Outlet = memo(function ModernTanstackOutlet() {
|
|
15
|
+
return <TanstackOutlet />;
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
export function withModernRouteMatchContext(
|
|
19
|
+
component: unknown,
|
|
20
|
+
_routeId: string,
|
|
21
|
+
): unknown {
|
|
22
|
+
if (!component) {
|
|
23
|
+
return component;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const Component = component as ElementType<Record<string, unknown>>;
|
|
27
|
+
const WrappedRouteComponent = (
|
|
28
|
+
props: ComponentProps<ElementType<Record<string, unknown>>>,
|
|
29
|
+
) => createElement(Component, props);
|
|
30
|
+
|
|
31
|
+
const preloadable = component as PreloadableComponent;
|
|
32
|
+
if (typeof preloadable.load === 'function') {
|
|
33
|
+
WrappedRouteComponent.load = preloadable.load.bind(preloadable);
|
|
34
|
+
}
|
|
35
|
+
if (typeof preloadable.preload === 'function') {
|
|
36
|
+
WrappedRouteComponent.preload = preloadable.preload.bind(preloadable);
|
|
37
|
+
} else if (typeof preloadable.load === 'function') {
|
|
38
|
+
WrappedRouteComponent.preload = WrappedRouteComponent.load;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return WrappedRouteComponent;
|
|
42
|
+
}
|