@bleedingdev/modern-js-plugin-tanstack 3.2.0-ultramodern.10 → 3.2.0-ultramodern.101

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 (60) hide show
  1. package/dist/cjs/cli/index.js +12 -0
  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 +4 -1
  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 +4 -1
  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 +17 -0
  45. package/src/cli/routeSplitting.ts +81 -0
  46. package/src/cli/tanstackTypes.ts +216 -67
  47. package/src/runtime/hydrationBoundary.tsx +12 -0
  48. package/src/runtime/index.tsx +107 -2
  49. package/src/runtime/outlet.tsx +42 -0
  50. package/src/runtime/plugin.node.tsx +57 -8
  51. package/src/runtime/plugin.tsx +353 -149
  52. package/src/runtime/plugin.worker.tsx +4 -0
  53. package/src/runtime/routeTree.ts +194 -23
  54. package/src/runtime/ssr-shim.d.ts +1 -3
  55. package/src/runtime/types.ts +13 -0
  56. package/tests/router/cli.test.ts +239 -0
  57. package/tests/router/fastDefaults.test.ts +25 -0
  58. package/tests/router/hydrationBoundary.test.tsx +23 -0
  59. package/tests/router/routeTree.test.ts +416 -1
  60. package/tests/router/tanstackTypes.test.ts +184 -0
@@ -29,7 +29,9 @@ __webpack_require__.d(__webpack_exports__, {
29
29
  getModernRouteIdsFromMatches: ()=>getModernRouteIdsFromMatches
30
30
  });
31
31
  const react_router_namespaceObject = require("@tanstack/react-router");
32
+ const external_react_namespaceObject = require("react");
32
33
  const external_DefaultNotFound_js_namespaceObject = require("./DefaultNotFound.js");
34
+ const external_outlet_js_namespaceObject = require("./outlet.js");
33
35
  const payloadRouter_js_namespaceObject = require("./rsc/payloadRouter.js");
34
36
  function createTanstackRoute(options) {
35
37
  return (0, react_router_namespaceObject.createRoute)(options);
@@ -37,6 +39,10 @@ function createTanstackRoute(options) {
37
39
  function createTanstackRootRoute(options) {
38
40
  return (0, react_router_namespaceObject.createRootRoute)(options);
39
41
  }
42
+ function wrapRouteComponentWithModernContext(route, component, routeId) {
43
+ const routeMatchId = routeId || route.id;
44
+ if (component && routeMatchId) route.options.component = (0, external_outlet_js_namespaceObject.withModernRouteMatchContext)(component, routeMatchId);
45
+ }
40
46
  function toTanstackPath(pathname) {
41
47
  return pathname.split('/').map((segment)=>{
42
48
  if (!segment) return segment;
@@ -84,6 +90,40 @@ function normalizeModernLoaderResponse(result) {
84
90
  }
85
91
  return normalizeModernLoaderResult(result);
86
92
  }
93
+ function pickRouteModuleComponent(routeModule, seen = new Set()) {
94
+ if ('function' == typeof routeModule || routeModule && 'object' == typeof routeModule && '$$typeof' in routeModule) return routeModule;
95
+ if (!routeModule || 'object' != typeof routeModule) return;
96
+ if (seen.has(routeModule)) return;
97
+ seen.add(routeModule);
98
+ const module = routeModule;
99
+ for (const candidate of [
100
+ module.default,
101
+ module.Component
102
+ ]){
103
+ const component = pickRouteModuleComponent(candidate, seen);
104
+ if (component) return component;
105
+ }
106
+ }
107
+ function createServerLazyImportComponent(lazyImport, fallbackComponent) {
108
+ if ("u" > typeof document) return fallbackComponent;
109
+ let resolvedComponent;
110
+ let pendingLoad;
111
+ const load = async ()=>{
112
+ if (resolvedComponent) return resolvedComponent;
113
+ const routeModule = await lazyImport();
114
+ const component = pickRouteModuleComponent(routeModule);
115
+ if (component) resolvedComponent = component;
116
+ return resolvedComponent;
117
+ };
118
+ const Component = (props)=>{
119
+ if (resolvedComponent) return (0, external_react_namespaceObject.createElement)(resolvedComponent, props);
120
+ pendingLoad ||= load();
121
+ throw pendingLoad;
122
+ };
123
+ Component.load = load;
124
+ Component.preload = load;
125
+ return Component;
126
+ }
87
127
  function isAbsoluteUrl(value) {
88
128
  try {
89
129
  new URL(value);
@@ -156,7 +196,7 @@ function wrapModernLoader(modernRoute, modernLoader, revalidationState, options
156
196
  const signal = ctx?.abortController?.signal || ctx?.signal || new AbortController().signal;
157
197
  const baseRequest = ctx?.context?.request instanceof Request ? ctx.context.request : void 0;
158
198
  const href = 'string' == typeof ctx?.location ? ctx.location : ctx?.location?.publicHref || ctx?.location?.href || ctx?.location?.url?.href || '';
159
- const request = baseRequest ? new Request(baseRequest, {
199
+ const request = void 0 !== baseRequest ? new Request(baseRequest, {
160
200
  signal
161
201
  }) : createModernRequest(href, signal);
162
202
  const params = mapParamsForModernLoader({
@@ -221,7 +261,7 @@ function wrapRouteObjectLoader(route, revalidationState, options = {}) {
221
261
  const signal = ctx?.abortController?.signal || ctx?.signal || new AbortController().signal;
222
262
  const baseRequest = ctx?.context?.request instanceof Request ? ctx.context.request : void 0;
223
263
  const href = 'string' == typeof ctx?.location ? ctx.location : ctx?.location?.publicHref || ctx?.location?.href || ctx?.location?.url?.href || '';
224
- const request = baseRequest ? new Request(baseRequest, {
264
+ const request = void 0 !== baseRequest ? new Request(baseRequest, {
225
265
  signal
226
266
  }) : createModernRequest(href, signal);
227
267
  const params = mapParamsForRouteObjectLoader({
@@ -258,10 +298,18 @@ function wrapRouteObjectLoader(route, revalidationState, options = {}) {
258
298
  }
259
299
  function toRouteComponent(routeObject) {
260
300
  const route = routeObject;
301
+ const lazyImport = 'function' == typeof route.lazyImport ? route.lazyImport : void 0;
302
+ const fallbackComponent = route.Component ? route.Component : route.element ? ()=>route.element : void 0;
303
+ if (lazyImport && fallbackComponent) return createServerLazyImportComponent(lazyImport, fallbackComponent);
261
304
  if (route.Component) return route.Component;
262
305
  const element = route.element;
263
306
  if (element) return ()=>element;
264
307
  }
308
+ function toModernRouteComponent(route) {
309
+ const component = route.component || void 0;
310
+ if ('function' == typeof route.lazyImport && component) return createServerLazyImportComponent(route.lazyImport, component);
311
+ return component;
312
+ }
265
313
  function toErrorComponent(routeObject) {
266
314
  const route = routeObject;
267
315
  if (route.ErrorBoundary) return route.ErrorBoundary;
@@ -299,12 +347,14 @@ function createRouteFromRouteObject(opts) {
299
347
  const shouldRevalidate = modernRouteObject.shouldRevalidate;
300
348
  const shouldReload = createModernShouldReload(shouldRevalidate, revalidationState);
301
349
  const stableFallbackId = routeObject.id || modernRouteObject.file || routeObject.path || 'pathless';
350
+ const component = toRouteComponent(routeObject);
302
351
  const base = {
303
352
  getParentRoute: ()=>parent,
304
- component: toRouteComponent(routeObject),
353
+ component,
305
354
  pendingComponent: toPendingComponent(routeObject),
306
355
  errorComponent: toErrorComponent(routeObject),
307
- wrapInSuspense: true,
356
+ validateSearch: modernRouteObject.validateSearch,
357
+ loaderDeps: modernRouteObject.loaderDeps,
308
358
  staticData: createRouteStaticData({
309
359
  modernRouteId: routeObject.id,
310
360
  modernRouteAction: modernRouteObject.action,
@@ -323,6 +373,7 @@ function createRouteFromRouteObject(opts) {
323
373
  if (isRouteObjectPathlessLayout(routeObject)) base.id = stableFallbackId;
324
374
  else base.path = routeObject.index ? '/' : toTanstackPath(routeObject.path || '');
325
375
  const route = createTanstackRoute(base);
376
+ wrapRouteComponentWithModernContext(route, component, routeObject.id);
326
377
  const children = routeObject.children;
327
378
  if (children && children.length > 0) {
328
379
  const childRoutes = children.map((child)=>createRouteFromRouteObject({
@@ -342,7 +393,7 @@ function createRouteFromModernRoute(opts) {
342
393
  const stableFallbackId = modernId || route._component || route.filename || route.data || ('function' == typeof route.loader ? route.id : void 0);
343
394
  const pendingComponent = route.loading || route.pendingComponent;
344
395
  const errorComponent = route.error || route.errorComponent;
345
- const component = route.component;
396
+ const component = toModernRouteComponent(route);
346
397
  const modernLoader = route.loader;
347
398
  const modernAction = route.action;
348
399
  const modernShouldRevalidate = route.shouldRevalidate;
@@ -354,7 +405,8 @@ function createRouteFromModernRoute(opts) {
354
405
  component: component || void 0,
355
406
  pendingComponent: pendingComponent || void 0,
356
407
  errorComponent: errorComponent || void 0,
357
- wrapInSuspense: true,
408
+ validateSearch: route.validateSearch,
409
+ loaderDeps: route.loaderDeps,
358
410
  staticData: createRouteStaticData({
359
411
  modernRouteId: modernId,
360
412
  modernRouteAction: modernAction,
@@ -376,6 +428,7 @@ function createRouteFromModernRoute(opts) {
376
428
  base.path = isIndexRoute ? '/' : toTanstackPath(rawPath || '');
377
429
  }
378
430
  const tanstackRoute = createTanstackRoute(base);
431
+ wrapRouteComponentWithModernContext(tanstackRoute, component, modernId);
379
432
  const children = route.children;
380
433
  if (children && children.length > 0) {
381
434
  const childRoutes = children.map((child)=>createRouteFromModernRoute({
@@ -389,7 +442,7 @@ function createRouteFromModernRoute(opts) {
389
442
  }
390
443
  function createRouteTreeFromModernRoutes(routes, options = {}) {
391
444
  const rootModern = routes.find((r)=>r && 'nested' === r.type && r.isRoot);
392
- const rootComponent = rootModern?.component;
445
+ const rootComponent = rootModern ? toModernRouteComponent(rootModern) : void 0;
393
446
  const pendingComponent = rootModern?.loading;
394
447
  const errorComponent = rootModern?.error;
395
448
  const rootLoader = rootModern?.loader;
@@ -402,7 +455,8 @@ function createRouteTreeFromModernRoutes(routes, options = {}) {
402
455
  component: rootComponent || void 0,
403
456
  pendingComponent: pendingComponent || void 0,
404
457
  errorComponent: errorComponent || void 0,
405
- wrapInSuspense: true,
458
+ validateSearch: rootModern?.validateSearch,
459
+ loaderDeps: rootModern?.loaderDeps,
406
460
  notFoundComponent: external_DefaultNotFound_js_namespaceObject.DefaultNotFound,
407
461
  staticData: createRouteStaticData({
408
462
  modernRouteId: rootModernId,
@@ -420,6 +474,7 @@ function createRouteTreeFromModernRoutes(routes, options = {}) {
420
474
  if (rootShouldReload) rootRouteOptions.shouldReload = rootShouldReload;
421
475
  if (rootModern?.inValidSSRRoute) rootRouteOptions.ssr = false;
422
476
  const rootRoute = createTanstackRootRoute(rootRouteOptions);
477
+ if (rootComponent) rootRoute.options.component = (0, external_outlet_js_namespaceObject.withModernRouteMatchContext)(rootComponent, react_router_namespaceObject.rootRouteId);
423
478
  const topLevel = rootModern ? rootModern.children || [] : routes;
424
479
  const childRoutes = topLevel.map((child)=>createRouteFromModernRoute({
425
480
  options,
@@ -437,11 +492,13 @@ function createRouteTreeFromRouteObjects(routes, options = {}) {
437
492
  const rootRevalidationState = {};
438
493
  const rootShouldRevalidate = rootLikeRoute?.shouldRevalidate;
439
494
  const rootShouldReload = createModernShouldReload(rootShouldRevalidate, rootRevalidationState);
495
+ const rootComponent = rootLikeRoute ? toRouteComponent(rootLikeRoute) : void 0;
440
496
  const rootRouteOptions = {
441
- component: rootLikeRoute ? toRouteComponent(rootLikeRoute) : void 0,
497
+ component: rootComponent,
442
498
  pendingComponent: rootLikeRoute ? toPendingComponent(rootLikeRoute) : void 0,
443
499
  errorComponent: rootLikeRoute ? toErrorComponent(rootLikeRoute) : void 0,
444
- wrapInSuspense: true,
500
+ validateSearch: rootLikeRoute?.validateSearch,
501
+ loaderDeps: rootLikeRoute?.loaderDeps,
445
502
  notFoundComponent: external_DefaultNotFound_js_namespaceObject.DefaultNotFound,
446
503
  staticData: createRouteStaticData({
447
504
  modernRouteId: rootLikeRoute?.id,
@@ -459,6 +516,7 @@ function createRouteTreeFromRouteObjects(routes, options = {}) {
459
516
  if (rootShouldReload) rootRouteOptions.shouldReload = rootShouldReload;
460
517
  if (rootLikeRoute?.inValidSSRRoute) rootRouteOptions.ssr = false;
461
518
  const rootRoute = createTanstackRootRoute(rootRouteOptions);
519
+ if (rootComponent) rootRoute.options.component = (0, external_outlet_js_namespaceObject.withModernRouteMatchContext)(rootComponent, react_router_namespaceObject.rootRouteId);
462
520
  const topLevel = rootLikeRoute ? [
463
521
  ...rootLikeRoute.children || [],
464
522
  ...routes.filter((route)=>route !== rootLikeRoute)
@@ -473,9 +531,11 @@ function createRouteTreeFromRouteObjects(routes, options = {}) {
473
531
  }
474
532
  function getModernRouteIdsFromMatches(router) {
475
533
  const matches = router.state.matches || [];
534
+ const routesById = router.routesById;
476
535
  const ids = matches.map((match)=>{
477
- const route = match.route;
478
- return route?.options?.staticData?.modernRouteId;
536
+ const normalizedMatch = match;
537
+ const routeId = 'string' == typeof normalizedMatch.routeId ? normalizedMatch.routeId : void 0;
538
+ return normalizedMatch.route?.options?.staticData?.modernRouteId ?? (routeId ? routesById?.[routeId]?.options?.staticData?.modernRouteId : void 0);
479
539
  }).filter((id)=>'string' == typeof id);
480
540
  return Array.from(new Set(ids));
481
541
  }
@@ -1,5 +1,16 @@
1
1
  "use strict";
2
2
  var __webpack_require__ = {};
3
+ (()=>{
4
+ __webpack_require__.d = (exports1, definition)=>{
5
+ for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
6
+ enumerable: true,
7
+ get: definition[key]
8
+ });
9
+ };
10
+ })();
11
+ (()=>{
12
+ __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
13
+ })();
3
14
  (()=>{
4
15
  __webpack_require__.r = (exports1)=>{
5
16
  if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
@@ -12,7 +23,22 @@ var __webpack_require__ = {};
12
23
  })();
13
24
  var __webpack_exports__ = {};
14
25
  __webpack_require__.r(__webpack_exports__);
15
- for(var __rspack_i in __webpack_exports__)exports[__rspack_i] = __webpack_exports__[__rspack_i];
26
+ __webpack_require__.d(__webpack_exports__, {
27
+ getModernTanstackRouterFastDefaults: ()=>getModernTanstackRouterFastDefaults,
28
+ modernTanstackRouterFastDefaults: ()=>modernTanstackRouterFastDefaults
29
+ });
30
+ const modernTanstackRouterFastDefaults = {
31
+ defaultStructuralSharing: true
32
+ };
33
+ const getModernTanstackRouterFastDefaults = (config = {})=>({
34
+ defaultStructuralSharing: config.defaultStructuralSharing ?? modernTanstackRouterFastDefaults.defaultStructuralSharing
35
+ });
36
+ exports.getModernTanstackRouterFastDefaults = __webpack_exports__.getModernTanstackRouterFastDefaults;
37
+ exports.modernTanstackRouterFastDefaults = __webpack_exports__.modernTanstackRouterFastDefaults;
38
+ for(var __rspack_i in __webpack_exports__)if (-1 === [
39
+ "getModernTanstackRouterFastDefaults",
40
+ "modernTanstackRouterFastDefaults"
41
+ ].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
16
42
  Object.defineProperty(exports, '__esModule', {
17
43
  value: true
18
44
  });
@@ -1,5 +1,6 @@
1
1
  import node_path from "node:path";
2
2
  import { NESTED_ROUTE_SPEC_FILE, filterRoutesForServer, fs } from "@modern-js/utils";
3
+ import { createTanstackRsbuildRouteSplittingProfile, isTanstackStartRouteModuleSource, resolveTanstackRouteCodeSplittingEnabled } from "./routeSplitting.mjs";
3
4
  import { generateTanstackRouterTypesSourceForEntry, isTanstackRouterFrameworkEnabled } from "./tanstackTypes.mjs";
4
5
  import { __webpack_require__ } from "../rslib-runtime.mjs";
5
6
  import * as __rspack_external__modern_js_runtime_cli_401ee077 from "@modern-js/runtime/cli";
@@ -78,6 +79,7 @@ async function writeTanstackRouterTypesForEntries(opts) {
78
79
  function tanstackRouterPlugin(options = {}) {
79
80
  const routesDir = options.routesDir || DEFAULT_ROUTES_DIR;
80
81
  const generatedDirName = options.generatedDirName || DEFAULT_GENERATED_DIR_NAME;
82
+ const routeSplittingProfile = createTanstackRsbuildRouteSplittingProfile(options);
81
83
  return {
82
84
  name: '@modern-js/plugin-tanstack',
83
85
  required: [
@@ -113,6 +115,7 @@ function tanstackRouterPlugin(options = {}) {
113
115
  entry: entry || getRuntimeRouterCli().isRouteEntry(entryPath, routesDir)
114
116
  }));
115
117
  api.config(()=>({
118
+ ...routeSplittingProfile.defaultConfig,
116
119
  source: {
117
120
  include: [
118
121
  /[\\/]node_modules[\\/]@tanstack[\\/]react-router[\\/]/,
@@ -195,4 +198,4 @@ function tanstackRouterPlugin(options = {}) {
195
198
  }
196
199
  const src_cli = tanstackRouterPlugin;
197
200
  export default src_cli;
198
- export { generateTanstackRouterTypesSourceForEntry, isTanstackRouterFrameworkEnabled, tanstackRouterPlugin, writeTanstackRegisterFile, writeTanstackRouterTypesForEntries };
201
+ export { createTanstackRsbuildRouteSplittingProfile, generateTanstackRouterTypesSourceForEntry, isTanstackRouterFrameworkEnabled, isTanstackStartRouteModuleSource, resolveTanstackRouteCodeSplittingEnabled, tanstackRouterPlugin, writeTanstackRegisterFile, writeTanstackRouterTypesForEntries };
@@ -0,0 +1,43 @@
1
+ const TANSTACK_START_ROUTE_FACTORY_CALLS = [
2
+ 'createFileRoute',
3
+ 'createRootRoute',
4
+ 'createRootRouteWithContext'
5
+ ];
6
+ const TANSTACK_START_ROUTE_FACTORY_REGEX = /\b(createFileRoute|createRootRoute|createRootRouteWithContext)\s*(?:<|\()/;
7
+ function isTanstackStartRouteModuleSource(source) {
8
+ return TANSTACK_START_ROUTE_FACTORY_REGEX.test(source);
9
+ }
10
+ function resolveTanstackRouteCodeSplittingEnabled(option) {
11
+ if ('boolean' == typeof option) return option;
12
+ return option?.enabled ?? true;
13
+ }
14
+ function createTanstackRsbuildRouteSplittingProfile(opts) {
15
+ return {
16
+ defaultConfig: {
17
+ output: {
18
+ splitRouteChunks: resolveTanstackRouteCodeSplittingEnabled(opts.routeCodeSplitting)
19
+ }
20
+ },
21
+ modernRouteChunks: {
22
+ enabled: resolveTanstackRouteCodeSplittingEnabled(opts.routeCodeSplitting),
23
+ owner: 'modern'
24
+ },
25
+ builderChunkSplit: {
26
+ owner: 'modern-rsbuild',
27
+ preserved: true
28
+ },
29
+ tanstackStartRspackSplitter: {
30
+ compatible: false,
31
+ 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.',
32
+ clientDeleteNodes: [
33
+ 'ssr',
34
+ 'server',
35
+ 'headers'
36
+ ],
37
+ routeFactoryCalls: [
38
+ ...TANSTACK_START_ROUTE_FACTORY_CALLS
39
+ ]
40
+ }
41
+ };
42
+ }
43
+ export { createTanstackRsbuildRouteSplittingProfile, isTanstackStartRouteModuleSource, resolveTanstackRouteCodeSplittingEnabled };
@@ -55,6 +55,14 @@ function pickModernLoaderModule(route) {
55
55
  inline
56
56
  };
57
57
  }
58
+ function pickRouteSearchContractModules(route) {
59
+ const validateSearchPath = route.validateSearch;
60
+ const loaderDepsPath = route.loaderDeps;
61
+ return {
62
+ validateSearchPath: 'string' == typeof validateSearchPath ? validateSearchPath : null,
63
+ loaderDepsPath: 'string' == typeof loaderDepsPath ? loaderDepsPath : null
64
+ };
65
+ }
58
66
  function isPathlessLayout(route) {
59
67
  return 'nested' === route.type && 'boolean' != typeof route.index && void 0 === route.path;
60
68
  }
@@ -88,8 +96,21 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
88
96
  const imports = [];
89
97
  const statements = [];
90
98
  const loaderImportMap = new Map();
99
+ const searchContractImportMap = new Map();
100
+ const usedRouteVarNames = new Set();
91
101
  let loaderIndex = 0;
102
+ let validateSearchIndex = 0;
103
+ let loaderDepsIndex = 0;
92
104
  let routeIndex = 0;
105
+ const resolveRouteModuleNoExt = async (aliasedNoExtPath)=>{
106
+ const prefix = `${appContext.internalSrcAlias}/`;
107
+ let absNoExt;
108
+ if (aliasedNoExtPath.startsWith(prefix)) {
109
+ const rel = aliasedNoExtPath.slice(prefix.length);
110
+ absNoExt = path.join(appContext.srcDirectory, rel);
111
+ } else absNoExt = path.isAbsolute(aliasedNoExtPath) ? aliasedNoExtPath : path.join(appContext.srcDirectory, aliasedNoExtPath);
112
+ return resolveFileNoExt(absNoExt);
113
+ };
93
114
  const getImportNamesForLoader = async (aliasedNoExtPath, inline, hasAction)=>{
94
115
  const key = `${inline ? 'inline' : 'default'}:${hasAction ? 'action' : 'loader'}:${aliasedNoExtPath}`;
95
116
  const existing = loaderImportMap.get(key);
@@ -97,13 +118,7 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
97
118
  loaderName: existing,
98
119
  actionName: hasAction ? existing.replace(/^loader_/, 'action_') : null
99
120
  };
100
- const prefix = `${appContext.internalSrcAlias}/`;
101
- let absNoExt;
102
- if (aliasedNoExtPath.startsWith(prefix)) {
103
- const rel = aliasedNoExtPath.slice(prefix.length);
104
- absNoExt = path.join(appContext.srcDirectory, rel);
105
- } else absNoExt = path.isAbsolute(aliasedNoExtPath) ? aliasedNoExtPath : path.join(appContext.srcDirectory, aliasedNoExtPath);
106
- const resolvedNoExt = await resolveFileNoExt(absNoExt);
121
+ const resolvedNoExt = await resolveRouteModuleNoExt(aliasedNoExtPath);
107
122
  if (!resolvedNoExt) return null;
108
123
  const relImport = normalizeRelativeImport(path.relative(outDir, resolvedNoExt));
109
124
  const importName = `loader_${loaderIndex++}`;
@@ -121,10 +136,29 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
121
136
  actionName
122
137
  };
123
138
  };
139
+ const getImportNameForSearchContract = async (aliasedNoExtPath, exportName)=>{
140
+ const key = `${exportName}:${aliasedNoExtPath}`;
141
+ const existing = searchContractImportMap.get(key);
142
+ if (existing) return existing;
143
+ const resolvedNoExt = await resolveRouteModuleNoExt(aliasedNoExtPath);
144
+ if (!resolvedNoExt) return null;
145
+ const relImport = normalizeRelativeImport(path.relative(outDir, resolvedNoExt));
146
+ const importName = 'validateSearch' === exportName ? `validateSearch_${validateSearchIndex++}` : `loaderDeps_${loaderDepsIndex++}`;
147
+ imports.push(`import { ${exportName} as ${importName} } from ${quote(relImport)};`);
148
+ searchContractImportMap.set(key, importName);
149
+ return importName;
150
+ };
151
+ const reserveRouteVarName = (preferred)=>{
152
+ let candidate = preferred;
153
+ let suffix = 1;
154
+ while(usedRouteVarNames.has(candidate))candidate = `${preferred}_${suffix++}`;
155
+ usedRouteVarNames.add(candidate);
156
+ return candidate;
157
+ };
124
158
  const createRouteVarName = (route)=>{
125
159
  const id = route.id;
126
160
  const base = id ? makeLegalIdentifier(id) : `r_${routeIndex++}`;
127
- return `route_${base}`;
161
+ return reserveRouteVarName(`route_${base}`);
128
162
  };
129
163
  const buildRoute = async (opts)=>{
130
164
  const { parentVar, route } = opts;
@@ -134,6 +168,9 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
134
168
  const loaderImports = loaderInfo ? await getImportNamesForLoader(loaderInfo.loaderPath, loaderInfo.inline, Boolean(loaderInfo.inline && routeAction === loaderInfo.loaderPath)) : null;
135
169
  const loaderName = loaderImports?.loaderName || null;
136
170
  const actionName = loaderImports?.actionName || null;
171
+ const searchContractInfo = pickRouteSearchContractModules(route);
172
+ const validateSearchName = searchContractInfo.validateSearchPath ? await getImportNameForSearchContract(searchContractInfo.validateSearchPath, 'validateSearch') : null;
173
+ const loaderDepsName = searchContractInfo.loaderDepsPath ? await getImportNameForSearchContract(searchContractInfo.loaderDepsPath, 'loaderDeps') : null;
137
174
  const rawPath = route.path;
138
175
  const hasSplat = 'string' == typeof rawPath && rawPath.includes('*');
139
176
  const routeOpts = [
@@ -147,20 +184,24 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
147
184
  routeOpts.push(`path: ${quote(p)},`);
148
185
  }
149
186
  if (loaderName) routeOpts.push(`loader: modernLoaderToTanstack({ hasSplat: ${hasSplat} }, ${loaderName}),`);
187
+ if (validateSearchName) routeOpts.push(`validateSearch: ${validateSearchName},`);
188
+ if (loaderDepsName) routeOpts.push(`loaderDeps: ${loaderDepsName},`);
150
189
  const staticDataSnippet = createRouteStaticDataSnippet({
151
190
  modernRouteId: route.id,
152
191
  loaderName,
153
192
  actionName
154
193
  });
155
194
  if (staticDataSnippet) routeOpts.push(staticDataSnippet);
156
- statements.push(`const ${varName} = createRoute({\n ${routeOpts.join('\n ')}\n});`);
157
195
  const children = route.children;
196
+ const hasChildren = Boolean(children && children.length > 0);
197
+ const routeCtorVarName = hasChildren ? reserveRouteVarName(`${varName}__base`) : varName;
198
+ statements.push(`const ${routeCtorVarName} = createRoute({\n ${routeOpts.join('\n ')}\n});`);
158
199
  if (children && children.length > 0) {
159
200
  const childVars = await Promise.all(children.map((child)=>buildRoute({
160
- parentVar: varName,
201
+ parentVar: routeCtorVarName,
161
202
  route: child
162
203
  })));
163
- statements.push(`${varName}.addChildren([${childVars.join(', ')}]);`);
204
+ statements.push(`const ${varName} = ${routeCtorVarName}.addChildren([${childVars.join(', ')}]);`);
164
205
  }
165
206
  return varName;
166
207
  };
@@ -169,17 +210,23 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
169
210
  const rootLoaderImports = rootLoaderInfo?.loaderPath ? await getImportNamesForLoader(rootLoaderInfo.loaderPath, rootLoaderInfo.inline, Boolean(rootLoaderInfo.inline && rootAction === rootLoaderInfo.loaderPath)) : null;
170
211
  const rootLoaderName = rootLoaderImports?.loaderName || null;
171
212
  const rootActionName = rootLoaderImports?.actionName || null;
213
+ const rootSearchContractInfo = rootModern ? pickRouteSearchContractModules(rootModern) : null;
214
+ const rootValidateSearchName = rootSearchContractInfo?.validateSearchPath ? await getImportNameForSearchContract(rootSearchContractInfo.validateSearchPath, 'validateSearch') : null;
215
+ const rootLoaderDepsName = rootSearchContractInfo?.loaderDepsPath ? await getImportNameForSearchContract(rootSearchContractInfo.loaderDepsPath, 'loaderDeps') : null;
172
216
  const topLevelVars = await Promise.all(topLevel.map((route)=>buildRoute({
173
217
  parentVar: 'rootRoute',
174
218
  route
175
219
  })));
176
220
  const rootOpts = [];
177
221
  if (rootLoaderName) rootOpts.push(`loader: modernLoaderToTanstack({ hasSplat: false }, ${rootLoaderName}),`);
222
+ if (rootValidateSearchName) rootOpts.push(`validateSearch: ${rootValidateSearchName},`);
223
+ if (rootLoaderDepsName) rootOpts.push(`loaderDeps: ${rootLoaderDepsName},`);
178
224
  const routerGenTs = `/* eslint-disable */
179
225
  // This file is auto-generated by Modern.js. Do not edit manually.
180
226
 
181
227
  import {
182
228
  createMemoryHistory,
229
+ modernTanstackRouterFastDefaults,
183
230
  createRootRouteWithContext,
184
231
  createRoute,
185
232
  createRouter,
@@ -207,7 +254,7 @@ function isRedirectResponse(res: Response) {
207
254
  }
208
255
 
209
256
  function throwTanstackRedirect(location: string) {
210
- const target = location || '/';
257
+ const target = location.length > 0 ? location : '/';
211
258
  try {
212
259
  void new URL(target);
213
260
  throw redirect({ href: target });
@@ -233,21 +280,87 @@ function createRouteStaticData(opts: {
233
280
  modernRouteAction?: unknown;
234
281
  modernRouteLoader?: unknown;
235
282
  }) {
236
- const staticData: Record<string, unknown> = {};
283
+ const staticData: {
284
+ modernRouteId?: string;
285
+ modernRouteAction?: unknown;
286
+ modernRouteLoader?: unknown;
287
+ } = {};
237
288
 
238
- if (opts.modernRouteId) {
289
+ if (typeof opts.modernRouteId === 'string' && opts.modernRouteId.length > 0) {
239
290
  staticData.modernRouteId = opts.modernRouteId;
240
291
  }
241
292
 
242
- if (opts.modernRouteLoader) {
293
+ if (typeof opts.modernRouteLoader !== 'undefined') {
243
294
  staticData.modernRouteLoader = opts.modernRouteLoader;
244
295
  }
245
296
 
246
- if (opts.modernRouteAction) {
297
+ if (typeof opts.modernRouteAction !== 'undefined') {
247
298
  staticData.modernRouteAction = opts.modernRouteAction;
248
299
  }
249
300
 
250
- return Object.keys(staticData).length > 0 ? staticData : undefined;
301
+ return staticData;
302
+ }
303
+
304
+ function getLoaderSignal(ctx: any): AbortSignal {
305
+ const abortSignal = ctx?.abortController?.signal;
306
+ if (abortSignal instanceof AbortSignal) {
307
+ return abortSignal;
308
+ }
309
+ if (ctx?.signal instanceof AbortSignal) {
310
+ return ctx.signal;
311
+ }
312
+ return new AbortController().signal;
313
+ }
314
+
315
+ function getLoaderHref(ctx: any): string {
316
+ if (typeof ctx?.location === 'string') {
317
+ return ctx.location;
318
+ }
319
+
320
+ const publicHref = ctx?.location?.publicHref;
321
+ if (typeof publicHref === 'string') {
322
+ return publicHref;
323
+ }
324
+
325
+ const href = ctx?.location?.href;
326
+ if (typeof href === 'string') {
327
+ return href;
328
+ }
329
+
330
+ const urlHref = ctx?.location?.url?.href;
331
+ return typeof urlHref === 'string' ? urlHref : '';
332
+ }
333
+
334
+ function getLoaderParams(ctx: any): Record<string, string> {
335
+ return typeof ctx?.params === 'object' && ctx.params !== null ? ctx.params : {};
336
+ }
337
+
338
+ function handleModernLoaderResult<LoaderResult>(result: LoaderResult): LoaderResult {
339
+ if (isResponse(result)) {
340
+ if (isRedirectResponse(result)) {
341
+ const location = result.headers.get('Location') ?? '/';
342
+ throwTanstackRedirect(location);
343
+ }
344
+ if (result.status === 404) {
345
+ throw notFound();
346
+ }
347
+ }
348
+
349
+ return result;
350
+ }
351
+
352
+ function handleModernLoaderError(err: unknown): never {
353
+ if (isResponse(err)) {
354
+ if (isRedirectResponse(err)) {
355
+ const location = err.headers.get('Location') ?? '/';
356
+ throwTanstackRedirect(location);
357
+ }
358
+ if (err.status === 404) {
359
+ throw notFound();
360
+ }
361
+ }
362
+
363
+ throw err;
251
364
  }
252
365
 
253
366
  function modernLoaderToTanstack<TLoader extends (args: any) => any>(
@@ -256,57 +369,31 @@ function modernLoaderToTanstack<TLoader extends (args: any) => any>(
256
369
  ) {
257
370
  type LoaderResult = Awaited<ReturnType<TLoader>>;
258
371
 
259
- return async (ctx: any): Promise<LoaderResult> => {
372
+ return (ctx: any): Promise<LoaderResult> => {
260
373
  try {
261
- const signal: AbortSignal =
262
- ctx?.abortController?.signal ||
263
- ctx?.signal ||
264
- new AbortController().signal;
374
+ const signal = getLoaderSignal(ctx);
265
375
  const baseRequest: Request | undefined =
266
376
  ctx?.context?.request instanceof Request ? ctx.context.request : undefined;
267
377
 
268
- const href =
269
- typeof ctx?.location === 'string'
270
- ? ctx.location
271
- : ctx?.location?.publicHref ||
272
- ctx?.location?.href ||
273
- ctx?.location?.url?.href ||
274
- '';
378
+ const href = getLoaderHref(ctx);
275
379
 
276
- const request = baseRequest
380
+ const request = baseRequest !== undefined
277
381
  ? new Request(baseRequest, { signal })
278
382
  : new Request(href, { signal });
279
383
 
280
- const params = mapParamsForModernLoader(ctx?.params || {}, opts.hasSplat);
384
+ const params = mapParamsForModernLoader(getLoaderParams(ctx), opts.hasSplat);
281
385
 
282
- const result = await (modernLoader as any)({
283
- request,
284
- params,
285
- context: ctx?.context?.requestContext,
286
- });
287
-
288
- if (isResponse(result)) {
289
- if (isRedirectResponse(result)) {
290
- const location = result.headers.get('Location') || '/';
291
- throwTanstackRedirect(location);
292
- }
293
- if (result.status === 404) {
294
- throw notFound();
295
- }
296
- }
297
-
298
- return result as LoaderResult;
386
+ return Promise.resolve(
387
+ (modernLoader as any)({
388
+ request,
389
+ params,
390
+ context: ctx?.context?.requestContext,
391
+ }),
392
+ )
393
+ .then((result: LoaderResult) => handleModernLoaderResult(result))
394
+ .catch(handleModernLoaderError);
299
395
  } catch (err) {
300
- if (isResponse(err)) {
301
- if (isRedirectResponse(err)) {
302
- const location = err.headers.get('Location') || '/';
303
- throwTanstackRedirect(location);
304
- }
305
- if (err.status === 404) {
306
- throw notFound();
307
- }
308
- }
309
- throw err;
396
+ handleModernLoaderError(err);
310
397
  }
311
398
  };
312
399
  }
@@ -327,6 +414,7 @@ ${statements.join('\n\n')}
327
414
  export const routeTree = rootRoute.addChildren([${topLevelVars.join(', ')}]);
328
415
 
329
416
  export const router = createRouter({
417
+ ...modernTanstackRouterFastDefaults,
330
418
  routeTree,
331
419
  history: createMemoryHistory({
332
420
  initialEntries: ['/'],
@@ -0,0 +1,10 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { Suspense } from "react";
3
+ function wrapTanstackSsrHydrationBoundary(routerContent, shouldWrap) {
4
+ if (shouldWrap) return /*#__PURE__*/ jsx(Suspense, {
5
+ fallback: null,
6
+ children: routerContent
7
+ });
8
+ return routerContent;
9
+ }
10
+ export { wrapTanstackSsrHydrationBoundary };