@bleedingdev/modern-js-plugin-tanstack 3.2.0-ultramodern.11 → 3.2.0-ultramodern.110

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 (86) hide show
  1. package/dist/cjs/cli/index.js +21 -5
  2. package/dist/cjs/cli/routeSplitting.js +87 -0
  3. package/dist/cjs/cli/tanstackTypes.js +155 -63
  4. package/dist/cjs/cli.js +12 -8
  5. package/dist/cjs/runtime/DefaultNotFound.js +9 -5
  6. package/dist/cjs/runtime/basepathRewrite.js +12 -8
  7. package/dist/cjs/runtime/dataMutation.js +9 -5
  8. package/dist/cjs/runtime/hooks.js +9 -5
  9. package/dist/cjs/runtime/hydrationBoundary.js +48 -0
  10. package/dist/cjs/runtime/index.js +330 -74
  11. package/dist/cjs/runtime/lifecycle.js +15 -11
  12. package/dist/cjs/runtime/outlet.js +58 -0
  13. package/dist/cjs/runtime/plugin.js +203 -98
  14. package/dist/cjs/runtime/plugin.node.js +38 -16
  15. package/dist/cjs/runtime/plugin.worker.js +53 -0
  16. package/dist/cjs/runtime/prefetchLink.js +10 -6
  17. package/dist/cjs/runtime/routeTree.js +81 -17
  18. package/dist/cjs/runtime/rsc/ClientSlot.js +9 -5
  19. package/dist/cjs/runtime/rsc/CompositeComponent.js +9 -5
  20. package/dist/cjs/runtime/rsc/ReplayableStream.js +14 -9
  21. package/dist/cjs/runtime/rsc/RscNodeRenderer.js +9 -5
  22. package/dist/cjs/runtime/rsc/SlotContext.js +9 -5
  23. package/dist/cjs/runtime/rsc/client.js +9 -5
  24. package/dist/cjs/runtime/rsc/createRscProxy.js +9 -5
  25. package/dist/cjs/runtime/rsc/index.js +9 -5
  26. package/dist/cjs/runtime/rsc/payloadRouter.js +9 -5
  27. package/dist/cjs/runtime/rsc/server.js +9 -5
  28. package/dist/cjs/runtime/rsc/slotUsageSanitizer.js +9 -5
  29. package/dist/cjs/runtime/rsc/symbols.js +20 -15
  30. package/dist/cjs/runtime/types.js +31 -1
  31. package/dist/cjs/runtime/utils.js +9 -5
  32. package/dist/cjs/runtime.js +9 -5
  33. package/dist/esm/cli/index.mjs +4 -1
  34. package/dist/esm/cli/routeSplitting.mjs +43 -0
  35. package/dist/esm/cli/tanstackTypes.mjs +146 -58
  36. package/dist/esm/runtime/hydrationBoundary.mjs +10 -0
  37. package/dist/esm/runtime/index.mjs +3 -2
  38. package/dist/esm/runtime/outlet.mjs +17 -0
  39. package/dist/esm/runtime/plugin.mjs +197 -96
  40. package/dist/esm/runtime/plugin.node.mjs +30 -12
  41. package/dist/esm/runtime/plugin.worker.mjs +1 -0
  42. package/dist/esm/runtime/prefetchLink.mjs +1 -1
  43. package/dist/esm/runtime/routeTree.mjs +73 -13
  44. package/dist/esm/runtime/types.mjs +7 -0
  45. package/dist/esm-node/cli/index.mjs +4 -1
  46. package/dist/esm-node/cli/routeSplitting.mjs +44 -0
  47. package/dist/esm-node/cli/tanstackTypes.mjs +146 -58
  48. package/dist/esm-node/runtime/hydrationBoundary.mjs +11 -0
  49. package/dist/esm-node/runtime/index.mjs +3 -2
  50. package/dist/esm-node/runtime/outlet.mjs +18 -0
  51. package/dist/esm-node/runtime/plugin.mjs +197 -96
  52. package/dist/esm-node/runtime/plugin.node.mjs +30 -12
  53. package/dist/esm-node/runtime/plugin.worker.mjs +2 -0
  54. package/dist/esm-node/runtime/prefetchLink.mjs +1 -1
  55. package/dist/esm-node/runtime/routeTree.mjs +73 -13
  56. package/dist/esm-node/runtime/types.mjs +7 -0
  57. package/dist/types/cli/index.d.ts +4 -0
  58. package/dist/types/cli/routeSplitting.d.ts +29 -0
  59. package/dist/types/runtime/hooks.d.ts +9 -24
  60. package/dist/types/runtime/hydrationBoundary.d.ts +2 -0
  61. package/dist/types/runtime/index.d.ts +5 -2
  62. package/dist/types/runtime/outlet.d.ts +2 -0
  63. package/dist/types/runtime/plugin.d.ts +1 -1
  64. package/dist/types/runtime/plugin.node.d.ts +1 -1
  65. package/dist/types/runtime/plugin.worker.d.ts +1 -0
  66. package/dist/types/runtime/types.d.ts +7 -0
  67. package/package.json +19 -19
  68. package/src/cli/index.ts +17 -0
  69. package/src/cli/routeSplitting.ts +81 -0
  70. package/src/cli/tanstackTypes.ts +216 -67
  71. package/src/runtime/hydrationBoundary.tsx +12 -0
  72. package/src/runtime/index.tsx +107 -2
  73. package/src/runtime/outlet.tsx +48 -0
  74. package/src/runtime/plugin.node.tsx +58 -8
  75. package/src/runtime/plugin.tsx +372 -157
  76. package/src/runtime/plugin.worker.tsx +4 -0
  77. package/src/runtime/prefetchLink.tsx +1 -1
  78. package/src/runtime/routeTree.ts +194 -23
  79. package/src/runtime/ssr-shim.d.ts +1 -3
  80. package/src/runtime/types.ts +13 -0
  81. package/tests/router/cli.test.ts +239 -0
  82. package/tests/router/fastDefaults.test.ts +25 -0
  83. package/tests/router/hydrationBoundary.test.tsx +23 -0
  84. package/tests/router/prefetchLink.test.tsx +43 -7
  85. package/tests/router/routeTree.test.ts +416 -1
  86. package/tests/router/tanstackTypes.test.ts +184 -0
@@ -1,5 +1,7 @@
1
- import { createRootRoute, createRoute, notFound, redirect } from "@tanstack/react-router";
1
+ import { createRootRoute, createRoute, notFound, redirect, rootRouteId } from "@tanstack/react-router";
2
+ import { createElement } from "react";
2
3
  import { DefaultNotFound } from "./DefaultNotFound.mjs";
4
+ import { withModernRouteMatchContext } from "./outlet.mjs";
3
5
  import { isTanstackRscPayloadNavigationEnabled, loadTanstackRscRouteData } from "./rsc/payloadRouter.mjs";
4
6
  function createTanstackRoute(options) {
5
7
  return createRoute(options);
@@ -7,6 +9,10 @@ function createTanstackRoute(options) {
7
9
  function createTanstackRootRoute(options) {
8
10
  return createRootRoute(options);
9
11
  }
12
+ function wrapRouteComponentWithModernContext(route, component, routeId) {
13
+ const routeMatchId = routeId || route.id;
14
+ if (component && routeMatchId) route.options.component = withModernRouteMatchContext(component, routeMatchId);
15
+ }
10
16
  function toTanstackPath(pathname) {
11
17
  return pathname.split('/').map((segment)=>{
12
18
  if (!segment) return segment;
@@ -54,6 +60,40 @@ function normalizeModernLoaderResponse(result) {
54
60
  }
55
61
  return normalizeModernLoaderResult(result);
56
62
  }
63
+ function pickRouteModuleComponent(routeModule, seen = new Set()) {
64
+ if ('function' == typeof routeModule || routeModule && 'object' == typeof routeModule && '$$typeof' in routeModule) return routeModule;
65
+ if (!routeModule || 'object' != typeof routeModule) return;
66
+ if (seen.has(routeModule)) return;
67
+ seen.add(routeModule);
68
+ const module = routeModule;
69
+ for (const candidate of [
70
+ module.default,
71
+ module.Component
72
+ ]){
73
+ const component = pickRouteModuleComponent(candidate, seen);
74
+ if (component) return component;
75
+ }
76
+ }
77
+ function createServerLazyImportComponent(lazyImport, fallbackComponent) {
78
+ if ("u" > typeof document) return fallbackComponent;
79
+ let resolvedComponent;
80
+ let pendingLoad;
81
+ const load = async ()=>{
82
+ if (resolvedComponent) return resolvedComponent;
83
+ const routeModule = await lazyImport();
84
+ const component = pickRouteModuleComponent(routeModule);
85
+ if (component) resolvedComponent = component;
86
+ return resolvedComponent;
87
+ };
88
+ const Component = (props)=>{
89
+ if (resolvedComponent) return createElement(resolvedComponent, props);
90
+ pendingLoad ||= load();
91
+ throw pendingLoad;
92
+ };
93
+ Component.load = load;
94
+ Component.preload = load;
95
+ return Component;
96
+ }
57
97
  function isAbsoluteUrl(value) {
58
98
  try {
59
99
  new URL(value);
@@ -126,7 +166,7 @@ function wrapModernLoader(modernRoute, modernLoader, revalidationState, options
126
166
  const signal = ctx?.abortController?.signal || ctx?.signal || new AbortController().signal;
127
167
  const baseRequest = ctx?.context?.request instanceof Request ? ctx.context.request : void 0;
128
168
  const href = 'string' == typeof ctx?.location ? ctx.location : ctx?.location?.publicHref || ctx?.location?.href || ctx?.location?.url?.href || '';
129
- const request = baseRequest ? new Request(baseRequest, {
169
+ const request = void 0 !== baseRequest ? new Request(baseRequest, {
130
170
  signal
131
171
  }) : createModernRequest(href, signal);
132
172
  const params = mapParamsForModernLoader({
@@ -191,7 +231,7 @@ function wrapRouteObjectLoader(route, revalidationState, options = {}) {
191
231
  const signal = ctx?.abortController?.signal || ctx?.signal || new AbortController().signal;
192
232
  const baseRequest = ctx?.context?.request instanceof Request ? ctx.context.request : void 0;
193
233
  const href = 'string' == typeof ctx?.location ? ctx.location : ctx?.location?.publicHref || ctx?.location?.href || ctx?.location?.url?.href || '';
194
- const request = baseRequest ? new Request(baseRequest, {
234
+ const request = void 0 !== baseRequest ? new Request(baseRequest, {
195
235
  signal
196
236
  }) : createModernRequest(href, signal);
197
237
  const params = mapParamsForRouteObjectLoader({
@@ -228,10 +268,18 @@ function wrapRouteObjectLoader(route, revalidationState, options = {}) {
228
268
  }
229
269
  function toRouteComponent(routeObject) {
230
270
  const route = routeObject;
271
+ const lazyImport = 'function' == typeof route.lazyImport ? route.lazyImport : void 0;
272
+ const fallbackComponent = route.Component ? route.Component : route.element ? ()=>route.element : void 0;
273
+ if (lazyImport && fallbackComponent) return createServerLazyImportComponent(lazyImport, fallbackComponent);
231
274
  if (route.Component) return route.Component;
232
275
  const element = route.element;
233
276
  if (element) return ()=>element;
234
277
  }
278
+ function toModernRouteComponent(route) {
279
+ const component = route.component || void 0;
280
+ if ('function' == typeof route.lazyImport && component) return createServerLazyImportComponent(route.lazyImport, component);
281
+ return component;
282
+ }
235
283
  function toErrorComponent(routeObject) {
236
284
  const route = routeObject;
237
285
  if (route.ErrorBoundary) return route.ErrorBoundary;
@@ -269,12 +317,14 @@ function createRouteFromRouteObject(opts) {
269
317
  const shouldRevalidate = modernRouteObject.shouldRevalidate;
270
318
  const shouldReload = createModernShouldReload(shouldRevalidate, revalidationState);
271
319
  const stableFallbackId = routeObject.id || modernRouteObject.file || routeObject.path || 'pathless';
320
+ const component = toRouteComponent(routeObject);
272
321
  const base = {
273
322
  getParentRoute: ()=>parent,
274
- component: toRouteComponent(routeObject),
323
+ component,
275
324
  pendingComponent: toPendingComponent(routeObject),
276
325
  errorComponent: toErrorComponent(routeObject),
277
- wrapInSuspense: true,
326
+ validateSearch: modernRouteObject.validateSearch,
327
+ loaderDeps: modernRouteObject.loaderDeps,
278
328
  staticData: createRouteStaticData({
279
329
  modernRouteId: routeObject.id,
280
330
  modernRouteAction: modernRouteObject.action,
@@ -293,6 +343,7 @@ function createRouteFromRouteObject(opts) {
293
343
  if (isRouteObjectPathlessLayout(routeObject)) base.id = stableFallbackId;
294
344
  else base.path = routeObject.index ? '/' : toTanstackPath(routeObject.path || '');
295
345
  const route = createTanstackRoute(base);
346
+ wrapRouteComponentWithModernContext(route, component, routeObject.id);
296
347
  const children = routeObject.children;
297
348
  if (children && children.length > 0) {
298
349
  const childRoutes = children.map((child)=>createRouteFromRouteObject({
@@ -312,7 +363,7 @@ function createRouteFromModernRoute(opts) {
312
363
  const stableFallbackId = modernId || route._component || route.filename || route.data || ('function' == typeof route.loader ? route.id : void 0);
313
364
  const pendingComponent = route.loading || route.pendingComponent;
314
365
  const errorComponent = route.error || route.errorComponent;
315
- const component = route.component;
366
+ const component = toModernRouteComponent(route);
316
367
  const modernLoader = route.loader;
317
368
  const modernAction = route.action;
318
369
  const modernShouldRevalidate = route.shouldRevalidate;
@@ -324,7 +375,8 @@ function createRouteFromModernRoute(opts) {
324
375
  component: component || void 0,
325
376
  pendingComponent: pendingComponent || void 0,
326
377
  errorComponent: errorComponent || void 0,
327
- wrapInSuspense: true,
378
+ validateSearch: route.validateSearch,
379
+ loaderDeps: route.loaderDeps,
328
380
  staticData: createRouteStaticData({
329
381
  modernRouteId: modernId,
330
382
  modernRouteAction: modernAction,
@@ -346,6 +398,7 @@ function createRouteFromModernRoute(opts) {
346
398
  base.path = isIndexRoute ? '/' : toTanstackPath(rawPath || '');
347
399
  }
348
400
  const tanstackRoute = createTanstackRoute(base);
401
+ wrapRouteComponentWithModernContext(tanstackRoute, component, modernId);
349
402
  const children = route.children;
350
403
  if (children && children.length > 0) {
351
404
  const childRoutes = children.map((child)=>createRouteFromModernRoute({
@@ -359,7 +412,7 @@ function createRouteFromModernRoute(opts) {
359
412
  }
360
413
  function createRouteTreeFromModernRoutes(routes, options = {}) {
361
414
  const rootModern = routes.find((r)=>r && 'nested' === r.type && r.isRoot);
362
- const rootComponent = rootModern?.component;
415
+ const rootComponent = rootModern ? toModernRouteComponent(rootModern) : void 0;
363
416
  const pendingComponent = rootModern?.loading;
364
417
  const errorComponent = rootModern?.error;
365
418
  const rootLoader = rootModern?.loader;
@@ -372,7 +425,8 @@ function createRouteTreeFromModernRoutes(routes, options = {}) {
372
425
  component: rootComponent || void 0,
373
426
  pendingComponent: pendingComponent || void 0,
374
427
  errorComponent: errorComponent || void 0,
375
- wrapInSuspense: true,
428
+ validateSearch: rootModern?.validateSearch,
429
+ loaderDeps: rootModern?.loaderDeps,
376
430
  notFoundComponent: DefaultNotFound,
377
431
  staticData: createRouteStaticData({
378
432
  modernRouteId: rootModernId,
@@ -390,6 +444,7 @@ function createRouteTreeFromModernRoutes(routes, options = {}) {
390
444
  if (rootShouldReload) rootRouteOptions.shouldReload = rootShouldReload;
391
445
  if (rootModern?.inValidSSRRoute) rootRouteOptions.ssr = false;
392
446
  const rootRoute = createTanstackRootRoute(rootRouteOptions);
447
+ if (rootComponent) rootRoute.options.component = withModernRouteMatchContext(rootComponent, rootRouteId);
393
448
  const topLevel = rootModern ? rootModern.children || [] : routes;
394
449
  const childRoutes = topLevel.map((child)=>createRouteFromModernRoute({
395
450
  options,
@@ -407,11 +462,13 @@ function createRouteTreeFromRouteObjects(routes, options = {}) {
407
462
  const rootRevalidationState = {};
408
463
  const rootShouldRevalidate = rootLikeRoute?.shouldRevalidate;
409
464
  const rootShouldReload = createModernShouldReload(rootShouldRevalidate, rootRevalidationState);
465
+ const rootComponent = rootLikeRoute ? toRouteComponent(rootLikeRoute) : void 0;
410
466
  const rootRouteOptions = {
411
- component: rootLikeRoute ? toRouteComponent(rootLikeRoute) : void 0,
467
+ component: rootComponent,
412
468
  pendingComponent: rootLikeRoute ? toPendingComponent(rootLikeRoute) : void 0,
413
469
  errorComponent: rootLikeRoute ? toErrorComponent(rootLikeRoute) : void 0,
414
- wrapInSuspense: true,
470
+ validateSearch: rootLikeRoute?.validateSearch,
471
+ loaderDeps: rootLikeRoute?.loaderDeps,
415
472
  notFoundComponent: DefaultNotFound,
416
473
  staticData: createRouteStaticData({
417
474
  modernRouteId: rootLikeRoute?.id,
@@ -429,6 +486,7 @@ function createRouteTreeFromRouteObjects(routes, options = {}) {
429
486
  if (rootShouldReload) rootRouteOptions.shouldReload = rootShouldReload;
430
487
  if (rootLikeRoute?.inValidSSRRoute) rootRouteOptions.ssr = false;
431
488
  const rootRoute = createTanstackRootRoute(rootRouteOptions);
489
+ if (rootComponent) rootRoute.options.component = withModernRouteMatchContext(rootComponent, rootRouteId);
432
490
  const topLevel = rootLikeRoute ? [
433
491
  ...rootLikeRoute.children || [],
434
492
  ...routes.filter((route)=>route !== rootLikeRoute)
@@ -443,9 +501,11 @@ function createRouteTreeFromRouteObjects(routes, options = {}) {
443
501
  }
444
502
  function getModernRouteIdsFromMatches(router) {
445
503
  const matches = router.state.matches || [];
504
+ const routesById = router.routesById;
446
505
  const ids = matches.map((match)=>{
447
- const route = match.route;
448
- return route?.options?.staticData?.modernRouteId;
506
+ const normalizedMatch = match;
507
+ const routeId = 'string' == typeof normalizedMatch.routeId ? normalizedMatch.routeId : void 0;
508
+ return normalizedMatch.route?.options?.staticData?.modernRouteId ?? (routeId ? routesById?.[routeId]?.options?.staticData?.modernRouteId : void 0);
449
509
  }).filter((id)=>'string' == typeof id);
450
510
  return Array.from(new Set(ids));
451
511
  }
@@ -0,0 +1,7 @@
1
+ const modernTanstackRouterFastDefaults = {
2
+ defaultStructuralSharing: true
3
+ };
4
+ const getModernTanstackRouterFastDefaults = (config = {})=>({
5
+ defaultStructuralSharing: config.defaultStructuralSharing ?? modernTanstackRouterFastDefaults.defaultStructuralSharing
6
+ });
7
+ export { getModernTanstackRouterFastDefaults, modernTanstackRouterFastDefaults };
@@ -1,6 +1,7 @@
1
1
  import "node:module";
2
2
  import node_path from "node:path";
3
3
  import { NESTED_ROUTE_SPEC_FILE, filterRoutesForServer, fs } from "@modern-js/utils";
4
+ import { createTanstackRsbuildRouteSplittingProfile, isTanstackStartRouteModuleSource, resolveTanstackRouteCodeSplittingEnabled } from "./routeSplitting.mjs";
4
5
  import { generateTanstackRouterTypesSourceForEntry, isTanstackRouterFrameworkEnabled } from "./tanstackTypes.mjs";
5
6
  import { __webpack_require__ } from "../rslib-runtime.mjs";
6
7
  import { fileURLToPath as __rspack_fileURLToPath } from "node:url";
@@ -82,6 +83,7 @@ async function writeTanstackRouterTypesForEntries(opts) {
82
83
  function tanstackRouterPlugin(options = {}) {
83
84
  const routesDir = options.routesDir || DEFAULT_ROUTES_DIR;
84
85
  const generatedDirName = options.generatedDirName || DEFAULT_GENERATED_DIR_NAME;
86
+ const routeSplittingProfile = createTanstackRsbuildRouteSplittingProfile(options);
85
87
  return {
86
88
  name: '@modern-js/plugin-tanstack',
87
89
  required: [
@@ -117,6 +119,7 @@ function tanstackRouterPlugin(options = {}) {
117
119
  entry: entry || getRuntimeRouterCli().isRouteEntry(entryPath, routesDir)
118
120
  }));
119
121
  api.config(()=>({
122
+ ...routeSplittingProfile.defaultConfig,
120
123
  source: {
121
124
  include: [
122
125
  /[\\/]node_modules[\\/]@tanstack[\\/]react-router[\\/]/,
@@ -199,4 +202,4 @@ function tanstackRouterPlugin(options = {}) {
199
202
  }
200
203
  const src_cli = tanstackRouterPlugin;
201
204
  export default src_cli;
202
- export { generateTanstackRouterTypesSourceForEntry, isTanstackRouterFrameworkEnabled, tanstackRouterPlugin, writeTanstackRegisterFile, writeTanstackRouterTypesForEntries };
205
+ export { createTanstackRsbuildRouteSplittingProfile, generateTanstackRouterTypesSourceForEntry, isTanstackRouterFrameworkEnabled, isTanstackStartRouteModuleSource, resolveTanstackRouteCodeSplittingEnabled, tanstackRouterPlugin, writeTanstackRegisterFile, writeTanstackRouterTypesForEntries };
@@ -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 prefix = `${appContext.internalSrcAlias}/`;
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: varName,
202
+ parentVar: routeCtorVarName,
162
203
  route: child
163
204
  })));
164
- statements.push(`${varName}.addChildren([${childVars.join(', ')}]);`);
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: Record<string, unknown> = {};
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 Object.keys(staticData).length > 0 ? staticData : undefined;
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 async (ctx: any): Promise<LoaderResult> => {
373
+ return (ctx: any): Promise<LoaderResult> => {
261
374
  try {
262
- const signal: AbortSignal =
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?.params || {}, opts.hasSplat);
385
+ const params = mapParamsForModernLoader(getLoaderParams(ctx), opts.hasSplat);
282
386
 
283
- const result = await (modernLoader as any)({
284
- request,
285
- params,
286
- context: ctx?.context?.requestContext,
287
- });
288
-
289
- if (isResponse(result)) {
290
- if (isRedirectResponse(result)) {
291
- const location = result.headers.get('Location') || '/';
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
- if (isResponse(err)) {
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: ['/'],
@@ -0,0 +1,11 @@
1
+ import "node:module";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import { Suspense } from "react";
4
+ function wrapTanstackSsrHydrationBoundary(routerContent, shouldWrap) {
5
+ if (shouldWrap) return /*#__PURE__*/ jsx(Suspense, {
6
+ fallback: null,
7
+ children: routerContent
8
+ });
9
+ return routerContent;
10
+ }
11
+ export { wrapTanstackSsrHydrationBoundary };
@@ -1,7 +1,8 @@
1
1
  import "node:module";
2
- export * from "@tanstack/react-router";
3
- export { useMatch } from "@tanstack/react-router";
2
+ export { Asset, Await, Block, CatchBoundary, CatchNotFound, ClientOnly, DEFAULT_PROTOCOL_ALLOWLIST, DefaultGlobalNotFound, ErrorComponent, FileRoute, FileRouteLoader, HeadContent, LazyRoute, Match, MatchRoute, Matches, Navigate, NotFoundRoute, RootRoute, Route, RouteApi, Router, RouterContextProvider, RouterProvider, ScriptOnce, Scripts, ScrollRestoration, SearchParamError, cleanPath, composeRewrites, createBrowserHistory, createControlledPromise, createFileRoute, createHashHistory, createHistory, createLazyFileRoute, createLazyRoute, createLink, createMemoryHistory, createRootRoute, createRootRouteWithContext, createRoute, createRouteMask, createRouter, createRouterConfig, createSerializationAdapter, deepEqual, defaultParseSearch, defaultStringifySearch, defer, functionalUpdate, getRouteApi, interpolatePath, isMatch, isNotFound, isPlainArray, isPlainObject, isRedirect, joinPaths, lazyFn, lazyRouteComponent, linkOptions, notFound, parseSearchWith, reactUse, redirect, replaceEqualDeep, resolvePath, retainSearchParams, rootRouteId, rootRouteWithContext, stringifySearchWith, stripSearchParams, trimPath, trimPathLeft, trimPathRight, useAwaited, useBlocker, useCanGoBack, useChildMatches, useElementScrollRestoration, useHydrated, useLayoutEffect, useLinkProps, useLoaderData, useLoaderDeps, useLocation, useMatch, useMatchRoute, useMatches, useNavigate, useParams, useParentMatches, useRouteContext, useRouter, useRouterState, useSearch, useTags } from "@tanstack/react-router";
4
3
  export { Form, RouteActionResponseError, useFetcher } from "./dataMutation.mjs";
4
+ export { Outlet } from "./outlet.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";
@@ -0,0 +1,18 @@
1
+ import "node:module";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import { Outlet } from "@tanstack/react-router";
4
+ import { createElement, memo } from "react";
5
+ const outlet_Outlet = /*#__PURE__*/ memo(function() {
6
+ return /*#__PURE__*/ jsx(Outlet, {});
7
+ });
8
+ function withModernRouteMatchContext(component, _routeId) {
9
+ if (null == component) return component;
10
+ const Component = component;
11
+ const WrappedRouteComponent = (props)=>/*#__PURE__*/ createElement(Component, props);
12
+ const preloadable = component;
13
+ if ('function' == typeof preloadable.load) WrappedRouteComponent.load = preloadable.load.bind(preloadable);
14
+ if ('function' == typeof preloadable.preload) WrappedRouteComponent.preload = preloadable.preload.bind(preloadable);
15
+ else if ('function' == typeof preloadable.load) WrappedRouteComponent.preload = WrappedRouteComponent.load;
16
+ return WrappedRouteComponent;
17
+ }
18
+ export { outlet_Outlet as Outlet, withModernRouteMatchContext };