@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.
Files changed (73) hide show
  1. package/dist/cjs/cli/index.js +15 -6
  2. package/dist/cjs/cli/routeSplitting.js +83 -0
  3. package/dist/cjs/cli/tanstackTypes.js +146 -58
  4. package/dist/cjs/runtime/hydrationBoundary.js +44 -0
  5. package/dist/cjs/runtime/index.js +321 -69
  6. package/dist/cjs/runtime/outlet.js +54 -0
  7. package/dist/cjs/runtime/plugin.js +194 -90
  8. package/dist/cjs/runtime/plugin.node.js +29 -11
  9. package/dist/cjs/runtime/plugin.worker.js +49 -0
  10. package/dist/cjs/runtime/routeTree.js +72 -12
  11. package/dist/cjs/runtime/types.js +27 -1
  12. package/dist/esm/cli/index.mjs +7 -7
  13. package/dist/esm/cli/routeSplitting.mjs +43 -0
  14. package/dist/esm/cli/tanstackTypes.mjs +146 -58
  15. package/dist/esm/runtime/hydrationBoundary.mjs +10 -0
  16. package/dist/esm/runtime/index.mjs +3 -2
  17. package/dist/esm/runtime/outlet.mjs +17 -0
  18. package/dist/esm/runtime/plugin.mjs +197 -93
  19. package/dist/esm/runtime/plugin.node.mjs +30 -12
  20. package/dist/esm/runtime/plugin.worker.mjs +1 -0
  21. package/dist/esm/runtime/routeTree.mjs +73 -13
  22. package/dist/esm/runtime/types.mjs +7 -0
  23. package/dist/esm-node/cli/index.mjs +7 -7
  24. package/dist/esm-node/cli/routeSplitting.mjs +44 -0
  25. package/dist/esm-node/cli/tanstackTypes.mjs +146 -58
  26. package/dist/esm-node/runtime/hydrationBoundary.mjs +11 -0
  27. package/dist/esm-node/runtime/index.mjs +3 -2
  28. package/dist/esm-node/runtime/outlet.mjs +18 -0
  29. package/dist/esm-node/runtime/plugin.mjs +197 -93
  30. package/dist/esm-node/runtime/plugin.node.mjs +30 -12
  31. package/dist/esm-node/runtime/plugin.worker.mjs +2 -0
  32. package/dist/esm-node/runtime/routeTree.mjs +73 -13
  33. package/dist/esm-node/runtime/types.mjs +7 -0
  34. package/dist/types/cli/index.d.ts +4 -0
  35. package/dist/types/cli/routeSplitting.d.ts +29 -0
  36. package/dist/types/runtime/hydrationBoundary.d.ts +2 -0
  37. package/dist/types/runtime/index.d.ts +5 -2
  38. package/dist/types/runtime/outlet.d.ts +2 -0
  39. package/dist/types/runtime/plugin.d.ts +1 -1
  40. package/dist/types/runtime/plugin.node.d.ts +1 -1
  41. package/dist/types/runtime/plugin.worker.d.ts +1 -0
  42. package/dist/types/runtime/types.d.ts +7 -0
  43. package/package.json +14 -14
  44. package/src/cli/index.ts +32 -18
  45. package/src/cli/routeSplitting.ts +81 -0
  46. package/src/cli/tanstackTypes.ts +217 -67
  47. package/src/runtime/basepathRewrite.ts +1 -0
  48. package/src/runtime/dataMutation.tsx +1 -0
  49. package/src/runtime/hydrationBoundary.tsx +12 -0
  50. package/src/runtime/index.tsx +107 -2
  51. package/src/runtime/lifecycle.ts +1 -0
  52. package/src/runtime/outlet.tsx +42 -0
  53. package/src/runtime/plugin.node.tsx +58 -8
  54. package/src/runtime/plugin.tsx +354 -149
  55. package/src/runtime/plugin.worker.tsx +4 -0
  56. package/src/runtime/routeTree.ts +195 -23
  57. package/src/runtime/rsc/ClientSlot.tsx +1 -0
  58. package/src/runtime/rsc/CompositeComponent.tsx +1 -0
  59. package/src/runtime/rsc/ReplayableStream.ts +1 -0
  60. package/src/runtime/rsc/RscNodeRenderer.tsx +1 -0
  61. package/src/runtime/rsc/client.tsx +2 -3
  62. package/src/runtime/rsc/createRscProxy.tsx +1 -0
  63. package/src/runtime/rsc/payloadRouter.ts +1 -0
  64. package/src/runtime/rsc/server.tsx +1 -0
  65. package/src/runtime/rsc/slotUsageSanitizer.ts +1 -0
  66. package/src/runtime/ssr-shim.d.ts +1 -3
  67. package/src/runtime/types.ts +13 -0
  68. package/src/runtime/utils.tsx +1 -0
  69. package/tests/router/cli.test.ts +239 -0
  70. package/tests/router/fastDefaults.test.ts +25 -0
  71. package/tests/router/hydrationBoundary.test.tsx +23 -0
  72. package/tests/router/routeTree.test.ts +416 -1
  73. package/tests/router/tanstackTypes.test.ts +184 -0
@@ -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 prefix = `${appContext.internalSrcAlias}/`;
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 => buildRoute({ parentVar: varName, route: 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: Record<string, unknown> = {};
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 Object.keys(staticData).length > 0 ? staticData : undefined;
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 async (ctx: any): Promise<LoaderResult> => {
596
+ return (ctx: any): Promise<LoaderResult> => {
422
597
  try {
423
- const signal: AbortSignal =
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?.params || {}, opts.hasSplat);
608
+ const params = mapParamsForModernLoader(getLoaderParams(ctx), opts.hasSplat);
443
609
 
444
- const result = await (modernLoader as any)({
445
- request,
446
- params,
447
- context: ctx?.context?.requestContext,
448
- });
449
-
450
- if (isResponse(result)) {
451
- if (isRedirectResponse(result)) {
452
- const location = result.headers.get('Location') || '/';
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
- if (isResponse(err)) {
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: ['/'],
@@ -1,3 +1,4 @@
1
+ // @effect-diagnostics strictBooleanExpressions:off
1
2
  function normalizeBasepath(basepath: string): string {
2
3
  if (!basepath) {
3
4
  return '/';
@@ -1,3 +1,4 @@
1
+ // @effect-diagnostics asyncFunction:off extendsNativeError:off strictBooleanExpressions:off
1
2
  import type { AnyRouter } from '@tanstack/react-router';
2
3
  import { useRouter } from '@tanstack/react-router';
3
4
  import type React from 'react';
@@ -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
+ }
@@ -1,5 +1,104 @@
1
- export * from '@tanstack/react-router';
2
- export { useMatch } from '@tanstack/react-router';
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';
@@ -1,3 +1,4 @@
1
+ // @effect-diagnostics strictBooleanExpressions:off
1
2
  import type { TInternalRuntimeContext } from '@modern-js/runtime/context';
2
3
  import type { RouteObject } from '@modern-js/runtime-utils/router';
3
4
  import type {
@@ -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
+ }