@bleedingdev/modern-js-plugin-tanstack 3.2.0-ultramodern.12 → 3.2.0-ultramodern.120

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 (87) hide show
  1. package/dist/cjs/cli/index.js +47 -9
  2. package/dist/cjs/cli/routeSplitting.js +87 -0
  3. package/dist/cjs/cli/tanstackTypes.js +230 -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 +28 -6
  34. package/dist/esm/cli/routeSplitting.mjs +43 -0
  35. package/dist/esm/cli/tanstackTypes.mjs +219 -59
  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 +28 -6
  46. package/dist/esm-node/cli/routeSplitting.mjs +44 -0
  47. package/dist/esm-node/cli/tanstackTypes.mjs +219 -59
  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 +7 -1
  58. package/dist/types/cli/routeSplitting.d.ts +29 -0
  59. package/dist/types/cli/tanstackTypes.d.ts +9 -0
  60. package/dist/types/runtime/hooks.d.ts +9 -24
  61. package/dist/types/runtime/hydrationBoundary.d.ts +2 -0
  62. package/dist/types/runtime/index.d.ts +5 -2
  63. package/dist/types/runtime/outlet.d.ts +2 -0
  64. package/dist/types/runtime/plugin.d.ts +1 -1
  65. package/dist/types/runtime/plugin.node.d.ts +1 -1
  66. package/dist/types/runtime/plugin.worker.d.ts +1 -0
  67. package/dist/types/runtime/types.d.ts +7 -0
  68. package/package.json +20 -20
  69. package/src/cli/index.ts +59 -2
  70. package/src/cli/routeSplitting.ts +81 -0
  71. package/src/cli/tanstackTypes.ts +347 -67
  72. package/src/runtime/hydrationBoundary.tsx +12 -0
  73. package/src/runtime/index.tsx +107 -2
  74. package/src/runtime/outlet.tsx +48 -0
  75. package/src/runtime/plugin.node.tsx +58 -8
  76. package/src/runtime/plugin.tsx +372 -157
  77. package/src/runtime/plugin.worker.tsx +4 -0
  78. package/src/runtime/prefetchLink.tsx +1 -1
  79. package/src/runtime/routeTree.ts +194 -23
  80. package/src/runtime/ssr-shim.d.ts +1 -3
  81. package/src/runtime/types.ts +13 -0
  82. package/tests/router/cli.test.ts +315 -0
  83. package/tests/router/fastDefaults.test.ts +25 -0
  84. package/tests/router/hydrationBoundary.test.tsx +23 -0
  85. package/tests/router/prefetchLink.test.tsx +43 -7
  86. package/tests/router/routeTree.test.ts +416 -1
  87. package/tests/router/tanstackTypes.test.ts +415 -1
@@ -24,11 +24,15 @@ function __webpack_require__(moduleId) {
24
24
  };
25
25
  })();
26
26
  (()=>{
27
- __webpack_require__.d = (exports1, definition)=>{
28
- for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
29
- enumerable: true,
30
- get: definition[key]
31
- });
27
+ __webpack_require__.d = (exports1, getters, values)=>{
28
+ var define = (defs, kind)=>{
29
+ for(var key in defs)if (__webpack_require__.o(defs, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
30
+ enumerable: true,
31
+ [kind]: defs[key]
32
+ });
33
+ };
34
+ define(getters, "get");
35
+ define(values, "value");
32
36
  };
33
37
  })();
34
38
  (()=>{
@@ -1,6 +1,7 @@
1
1
  import node_path from "node:path";
2
2
  import { NESTED_ROUTE_SPEC_FILE, filterRoutesForServer, fs } from "@modern-js/utils";
3
- import { generateTanstackRouterTypesSourceForEntry, isTanstackRouterFrameworkEnabled } from "./tanstackTypes.mjs";
3
+ import { createTanstackRsbuildRouteSplittingProfile, isTanstackStartRouteModuleSource, resolveTanstackRouteCodeSplittingEnabled } from "./routeSplitting.mjs";
4
+ import { collectCanonicalRoutesForEntry, 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";
6
7
  __webpack_require__.add({
@@ -31,6 +32,14 @@ async function writeFileIfChanged(filePath, content) {
31
32
  function createRegisterDtsContent(opts) {
32
33
  const importStatements = opts.entries.map((entryName, index)=>`import type { router as router${index} } from './${entryName}/router.gen';`).join('\n');
33
34
  const routerUnionType = opts.entries.map((_, index)=>`typeof router${index}`).join(' | ');
35
+ const canonicalEntries = Object.entries(opts.canonicalRoutes ?? {});
36
+ const canonicalRoutesAugmentation = canonicalEntries.length > 0 ? `
37
+ declare module '${opts.i18nRuntimeModule || '@modern-js/plugin-i18n/runtime'}' {
38
+ interface UltramodernCanonicalRoutes {
39
+ ${canonicalEntries.map(([routePath, paramsType])=>` '${routePath}': ${paramsType};`).join('\n')}
40
+ }
41
+ }
42
+ ` : '';
34
43
  return `// This file is auto-generated by Modern.js. Do not edit manually.
35
44
 
36
45
  ${importStatements}
@@ -40,15 +49,17 @@ declare module '${opts.runtimeModule}' {
40
49
  router: ${routerUnionType};
41
50
  }
42
51
  }
43
- `;
52
+ ${canonicalRoutesAugmentation}`;
44
53
  }
45
54
  async function writeTanstackRegisterFile(opts) {
46
- const { entries, generatedDirName = DEFAULT_GENERATED_DIR_NAME, runtimeModule = '@modern-js/plugin-tanstack/runtime', srcDirectory } = opts;
55
+ const { entries, generatedDirName = DEFAULT_GENERATED_DIR_NAME, runtimeModule = '@modern-js/plugin-tanstack/runtime', srcDirectory, canonicalRoutes, i18nRuntimeModule } = opts;
47
56
  if (0 === entries.length) return;
48
57
  const registerDtsPath = node_path.join(srcDirectory, generatedDirName, 'register.gen.d.ts');
49
58
  await writeFileIfChanged(registerDtsPath, createRegisterDtsContent({
50
59
  entries,
51
- runtimeModule
60
+ runtimeModule,
61
+ canonicalRoutes,
62
+ i18nRuntimeModule
52
63
  }));
53
64
  }
54
65
  async function writeTanstackRouterTypesForEntries(opts) {
@@ -69,15 +80,25 @@ async function writeTanstackRouterTypesForEntries(opts) {
69
80
  if (mainEntryName && b === mainEntryName) return 1;
70
81
  return a.localeCompare(b);
71
82
  });
83
+ let canonicalRoutes = null;
84
+ for (const entryName of registerEntries){
85
+ const entryCanonicalRoutes = collectCanonicalRoutesForEntry(routesByEntry[entryName]);
86
+ if (entryCanonicalRoutes) canonicalRoutes = {
87
+ ...entryCanonicalRoutes,
88
+ ...canonicalRoutes ?? {}
89
+ };
90
+ }
72
91
  await writeTanstackRegisterFile({
73
92
  entries: registerEntries,
74
93
  generatedDirName,
75
- srcDirectory: appContext.srcDirectory
94
+ srcDirectory: appContext.srcDirectory,
95
+ canonicalRoutes
76
96
  });
77
97
  }
78
98
  function tanstackRouterPlugin(options = {}) {
79
99
  const routesDir = options.routesDir || DEFAULT_ROUTES_DIR;
80
100
  const generatedDirName = options.generatedDirName || DEFAULT_GENERATED_DIR_NAME;
101
+ const routeSplittingProfile = createTanstackRsbuildRouteSplittingProfile(options);
81
102
  return {
82
103
  name: '@modern-js/plugin-tanstack',
83
104
  required: [
@@ -113,6 +134,7 @@ function tanstackRouterPlugin(options = {}) {
113
134
  entry: entry || getRuntimeRouterCli().isRouteEntry(entryPath, routesDir)
114
135
  }));
115
136
  api.config(()=>({
137
+ ...routeSplittingProfile.defaultConfig,
116
138
  source: {
117
139
  include: [
118
140
  /[\\/]node_modules[\\/]@tanstack[\\/]react-router[\\/]/,
@@ -195,4 +217,4 @@ function tanstackRouterPlugin(options = {}) {
195
217
  }
196
218
  const src_cli = tanstackRouterPlugin;
197
219
  export default src_cli;
198
- export { generateTanstackRouterTypesSourceForEntry, isTanstackRouterFrameworkEnabled, tanstackRouterPlugin, writeTanstackRegisterFile, writeTanstackRouterTypesForEntries };
220
+ export { collectCanonicalRoutesForEntry, 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
  }
@@ -69,6 +77,78 @@ function createRouteStaticDataSnippet(opts) {
69
77
  if (!staticDataLines.length) return null;
70
78
  return `staticData: createRouteStaticData({\n ${staticDataLines.join('\n ')}\n }),`;
71
79
  }
80
+ const LOCALE_PARAM_SEGMENTS = new Set([
81
+ ':lang',
82
+ ':locale',
83
+ ':language',
84
+ '$lang',
85
+ '$locale',
86
+ '$language'
87
+ ]);
88
+ function paramsTypeForCanonicalPath(canonicalPath) {
89
+ const fields = [];
90
+ for (const segment of canonicalPath.split('/'))if (segment) {
91
+ if ('*' === segment || '$' === segment) {
92
+ fields.push("'_splat'?: string");
93
+ continue;
94
+ }
95
+ if (segment.startsWith('{-$') && segment.endsWith('}')) {
96
+ fields.push(`${JSON.stringify(segment.slice(3, -1))}?: string`);
97
+ continue;
98
+ }
99
+ if (segment.startsWith('$')) {
100
+ fields.push(`${JSON.stringify(segment.slice(1))}: string`);
101
+ continue;
102
+ }
103
+ if (segment.startsWith(':')) {
104
+ const optional = segment.endsWith('?');
105
+ const name = segment.slice(1, optional ? void 0 : segment.length);
106
+ fields.push(`${JSON.stringify(optional ? name.slice(0, -1) : name)}${optional ? '?' : ''}: string`);
107
+ }
108
+ }
109
+ return fields.length > 0 ? `{ ${fields.join('; ')} }` : 'Record<string, never>';
110
+ }
111
+ function collectCanonicalRoutesForEntry(routes) {
112
+ const canonicalParams = new Map();
113
+ let hasI18nSurface = false;
114
+ const normalizeJoined = (joined)=>{
115
+ const collapsed = joined.replace(/\/+/g, '/');
116
+ const withLeading = collapsed.startsWith('/') ? collapsed : `/${collapsed}`;
117
+ return withLeading.length > 1 ? withLeading.replace(/\/+$/, '') : withLeading;
118
+ };
119
+ const record = (canonicalPath)=>{
120
+ const normalized = normalizeJoined(canonicalPath || '/');
121
+ const key = toTanstackPath(normalized);
122
+ if (!canonicalParams.has(key)) canonicalParams.set(key, paramsTypeForCanonicalPath(normalized));
123
+ };
124
+ const visit = (route, parentPath)=>{
125
+ let currentPath = parentPath;
126
+ if ('string' == typeof route.modernCanonicalPath) {
127
+ hasI18nSurface = true;
128
+ currentPath = normalizeJoined(route.modernCanonicalPath);
129
+ } else if ('string' == typeof route.path && route.path.length > 0) {
130
+ const segments = route.path.replace(/\[(.+?)\]/g, ':$1').split('/').filter(Boolean);
131
+ if ('' === parentPath && LOCALE_PARAM_SEGMENTS.has(segments[0])) {
132
+ hasI18nSurface = true;
133
+ segments.shift();
134
+ }
135
+ currentPath = segments.length ? normalizeJoined(`${parentPath}/${segments.join('/')}`) : parentPath;
136
+ }
137
+ const children = route.children;
138
+ if (children && children.length > 0) {
139
+ for (const child of children)visit(child, currentPath);
140
+ return;
141
+ }
142
+ record(currentPath || '/');
143
+ };
144
+ const rootModern = routes.find((route)=>route.isRoot);
145
+ const topLevel = rootModern ? rootModern.children ?? [] : routes;
146
+ for (const route of topLevel)visit(route, '');
147
+ if (!hasI18nSurface || 0 === canonicalParams.size) return null;
148
+ return Object.fromEntries([
149
+ ...canonicalParams.entries()
150
+ ].sort(([a], [b])=>a.localeCompare(b)));
151
+ }
72
152
  async function isTanstackRouterFrameworkEnabled(appContext) {
73
153
  const runtimeConfigBase = path.join(appContext.srcDirectory, appContext.runtimeConfigFile);
74
154
  const runtimeConfigFile = findExists(JS_OR_TS_EXTS.map((ext)=>`${runtimeConfigBase}${ext}`));
@@ -88,8 +168,21 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
88
168
  const imports = [];
89
169
  const statements = [];
90
170
  const loaderImportMap = new Map();
171
+ const searchContractImportMap = new Map();
172
+ const usedRouteVarNames = new Set();
91
173
  let loaderIndex = 0;
174
+ let validateSearchIndex = 0;
175
+ let loaderDepsIndex = 0;
92
176
  let routeIndex = 0;
177
+ const resolveRouteModuleNoExt = async (aliasedNoExtPath)=>{
178
+ const prefix = `${appContext.internalSrcAlias}/`;
179
+ let absNoExt;
180
+ if (aliasedNoExtPath.startsWith(prefix)) {
181
+ const rel = aliasedNoExtPath.slice(prefix.length);
182
+ absNoExt = path.join(appContext.srcDirectory, rel);
183
+ } else absNoExt = path.isAbsolute(aliasedNoExtPath) ? aliasedNoExtPath : path.join(appContext.srcDirectory, aliasedNoExtPath);
184
+ return resolveFileNoExt(absNoExt);
185
+ };
93
186
  const getImportNamesForLoader = async (aliasedNoExtPath, inline, hasAction)=>{
94
187
  const key = `${inline ? 'inline' : 'default'}:${hasAction ? 'action' : 'loader'}:${aliasedNoExtPath}`;
95
188
  const existing = loaderImportMap.get(key);
@@ -97,13 +190,7 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
97
190
  loaderName: existing,
98
191
  actionName: hasAction ? existing.replace(/^loader_/, 'action_') : null
99
192
  };
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);
193
+ const resolvedNoExt = await resolveRouteModuleNoExt(aliasedNoExtPath);
107
194
  if (!resolvedNoExt) return null;
108
195
  const relImport = normalizeRelativeImport(path.relative(outDir, resolvedNoExt));
109
196
  const importName = `loader_${loaderIndex++}`;
@@ -121,10 +208,29 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
121
208
  actionName
122
209
  };
123
210
  };
211
+ const getImportNameForSearchContract = async (aliasedNoExtPath, exportName)=>{
212
+ const key = `${exportName}:${aliasedNoExtPath}`;
213
+ const existing = searchContractImportMap.get(key);
214
+ if (existing) return existing;
215
+ const resolvedNoExt = await resolveRouteModuleNoExt(aliasedNoExtPath);
216
+ if (!resolvedNoExt) return null;
217
+ const relImport = normalizeRelativeImport(path.relative(outDir, resolvedNoExt));
218
+ const importName = 'validateSearch' === exportName ? `validateSearch_${validateSearchIndex++}` : `loaderDeps_${loaderDepsIndex++}`;
219
+ imports.push(`import { ${exportName} as ${importName} } from ${quote(relImport)};`);
220
+ searchContractImportMap.set(key, importName);
221
+ return importName;
222
+ };
223
+ const reserveRouteVarName = (preferred)=>{
224
+ let candidate = preferred;
225
+ let suffix = 1;
226
+ while(usedRouteVarNames.has(candidate))candidate = `${preferred}_${suffix++}`;
227
+ usedRouteVarNames.add(candidate);
228
+ return candidate;
229
+ };
124
230
  const createRouteVarName = (route)=>{
125
231
  const id = route.id;
126
232
  const base = id ? makeLegalIdentifier(id) : `r_${routeIndex++}`;
127
- return `route_${base}`;
233
+ return reserveRouteVarName(`route_${base}`);
128
234
  };
129
235
  const buildRoute = async (opts)=>{
130
236
  const { parentVar, route } = opts;
@@ -134,6 +240,9 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
134
240
  const loaderImports = loaderInfo ? await getImportNamesForLoader(loaderInfo.loaderPath, loaderInfo.inline, Boolean(loaderInfo.inline && routeAction === loaderInfo.loaderPath)) : null;
135
241
  const loaderName = loaderImports?.loaderName || null;
136
242
  const actionName = loaderImports?.actionName || null;
243
+ const searchContractInfo = pickRouteSearchContractModules(route);
244
+ const validateSearchName = searchContractInfo.validateSearchPath ? await getImportNameForSearchContract(searchContractInfo.validateSearchPath, 'validateSearch') : null;
245
+ const loaderDepsName = searchContractInfo.loaderDepsPath ? await getImportNameForSearchContract(searchContractInfo.loaderDepsPath, 'loaderDeps') : null;
137
246
  const rawPath = route.path;
138
247
  const hasSplat = 'string' == typeof rawPath && rawPath.includes('*');
139
248
  const routeOpts = [
@@ -147,20 +256,24 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
147
256
  routeOpts.push(`path: ${quote(p)},`);
148
257
  }
149
258
  if (loaderName) routeOpts.push(`loader: modernLoaderToTanstack({ hasSplat: ${hasSplat} }, ${loaderName}),`);
259
+ if (validateSearchName) routeOpts.push(`validateSearch: ${validateSearchName},`);
260
+ if (loaderDepsName) routeOpts.push(`loaderDeps: ${loaderDepsName},`);
150
261
  const staticDataSnippet = createRouteStaticDataSnippet({
151
262
  modernRouteId: route.id,
152
263
  loaderName,
153
264
  actionName
154
265
  });
155
266
  if (staticDataSnippet) routeOpts.push(staticDataSnippet);
156
- statements.push(`const ${varName} = createRoute({\n ${routeOpts.join('\n ')}\n});`);
157
267
  const children = route.children;
268
+ const hasChildren = Boolean(children && children.length > 0);
269
+ const routeCtorVarName = hasChildren ? reserveRouteVarName(`${varName}__base`) : varName;
270
+ statements.push(`const ${routeCtorVarName} = createRoute({\n ${routeOpts.join('\n ')}\n});`);
158
271
  if (children && children.length > 0) {
159
272
  const childVars = await Promise.all(children.map((child)=>buildRoute({
160
- parentVar: varName,
273
+ parentVar: routeCtorVarName,
161
274
  route: child
162
275
  })));
163
- statements.push(`${varName}.addChildren([${childVars.join(', ')}]);`);
276
+ statements.push(`const ${varName} = ${routeCtorVarName}.addChildren([${childVars.join(', ')}]);`);
164
277
  }
165
278
  return varName;
166
279
  };
@@ -169,17 +282,23 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
169
282
  const rootLoaderImports = rootLoaderInfo?.loaderPath ? await getImportNamesForLoader(rootLoaderInfo.loaderPath, rootLoaderInfo.inline, Boolean(rootLoaderInfo.inline && rootAction === rootLoaderInfo.loaderPath)) : null;
170
283
  const rootLoaderName = rootLoaderImports?.loaderName || null;
171
284
  const rootActionName = rootLoaderImports?.actionName || null;
285
+ const rootSearchContractInfo = rootModern ? pickRouteSearchContractModules(rootModern) : null;
286
+ const rootValidateSearchName = rootSearchContractInfo?.validateSearchPath ? await getImportNameForSearchContract(rootSearchContractInfo.validateSearchPath, 'validateSearch') : null;
287
+ const rootLoaderDepsName = rootSearchContractInfo?.loaderDepsPath ? await getImportNameForSearchContract(rootSearchContractInfo.loaderDepsPath, 'loaderDeps') : null;
172
288
  const topLevelVars = await Promise.all(topLevel.map((route)=>buildRoute({
173
289
  parentVar: 'rootRoute',
174
290
  route
175
291
  })));
176
292
  const rootOpts = [];
177
293
  if (rootLoaderName) rootOpts.push(`loader: modernLoaderToTanstack({ hasSplat: false }, ${rootLoaderName}),`);
294
+ if (rootValidateSearchName) rootOpts.push(`validateSearch: ${rootValidateSearchName},`);
295
+ if (rootLoaderDepsName) rootOpts.push(`loaderDeps: ${rootLoaderDepsName},`);
178
296
  const routerGenTs = `/* eslint-disable */
179
297
  // This file is auto-generated by Modern.js. Do not edit manually.
180
298
 
181
299
  import {
182
300
  createMemoryHistory,
301
+ modernTanstackRouterFastDefaults,
183
302
  createRootRouteWithContext,
184
303
  createRoute,
185
304
  createRouter,
@@ -207,7 +326,7 @@ function isRedirectResponse(res: Response) {
207
326
  }
208
327
 
209
328
  function throwTanstackRedirect(location: string) {
210
- const target = location || '/';
329
+ const target = location.length > 0 ? location : '/';
211
330
  try {
212
331
  void new URL(target);
213
332
  throw redirect({ href: target });
@@ -233,21 +352,87 @@ function createRouteStaticData(opts: {
233
352
  modernRouteAction?: unknown;
234
353
  modernRouteLoader?: unknown;
235
354
  }) {
236
- const staticData: Record<string, unknown> = {};
355
+ const staticData: {
356
+ modernRouteId?: string;
357
+ modernRouteAction?: unknown;
358
+ modernRouteLoader?: unknown;
359
+ } = {};
237
360
 
238
- if (opts.modernRouteId) {
361
+ if (typeof opts.modernRouteId === 'string' && opts.modernRouteId.length > 0) {
239
362
  staticData.modernRouteId = opts.modernRouteId;
240
363
  }
241
364
 
242
- if (opts.modernRouteLoader) {
365
+ if (typeof opts.modernRouteLoader !== 'undefined') {
243
366
  staticData.modernRouteLoader = opts.modernRouteLoader;
244
367
  }
245
368
 
246
- if (opts.modernRouteAction) {
369
+ if (typeof opts.modernRouteAction !== 'undefined') {
247
370
  staticData.modernRouteAction = opts.modernRouteAction;
248
371
  }
249
372
 
250
- return Object.keys(staticData).length > 0 ? staticData : undefined;
373
+ return staticData;
374
+ }
375
+
376
+ function getLoaderSignal(ctx: any): AbortSignal {
377
+ const abortSignal = ctx?.abortController?.signal;
378
+ if (abortSignal instanceof AbortSignal) {
379
+ return abortSignal;
380
+ }
381
+ if (ctx?.signal instanceof AbortSignal) {
382
+ return ctx.signal;
383
+ }
384
+ return new AbortController().signal;
385
+ }
386
+
387
+ function getLoaderHref(ctx: any): string {
388
+ if (typeof ctx?.location === 'string') {
389
+ return ctx.location;
390
+ }
391
+
392
+ const publicHref = ctx?.location?.publicHref;
393
+ if (typeof publicHref === 'string') {
394
+ return publicHref;
395
+ }
396
+
397
+ const href = ctx?.location?.href;
398
+ if (typeof href === 'string') {
399
+ return href;
400
+ }
401
+
402
+ const urlHref = ctx?.location?.url?.href;
403
+ return typeof urlHref === 'string' ? urlHref : '';
404
+ }
405
+
406
+ function getLoaderParams(ctx: any): Record<string, string> {
407
+ return typeof ctx?.params === 'object' && ctx.params !== null ? ctx.params : {};
408
+ }
409
+
410
+ function handleModernLoaderResult<LoaderResult>(result: LoaderResult): LoaderResult {
411
+ if (isResponse(result)) {
412
+ if (isRedirectResponse(result)) {
413
+ const location = result.headers.get('Location') ?? '/';
414
+ throwTanstackRedirect(location);
415
+ }
416
+ if (result.status === 404) {
417
+ throw notFound();
418
+ }
419
+ }
420
+
421
+ return result;
422
+ }
423
+
424
+ function handleModernLoaderError(err: unknown): never {
425
+ if (isResponse(err)) {
426
+ if (isRedirectResponse(err)) {
427
+ const location = err.headers.get('Location') ?? '/';
428
+ throwTanstackRedirect(location);
429
+ }
430
+ if (err.status === 404) {
431
+ throw notFound();
432
+ }
433
+ }
434
+
435
+ throw err;
251
436
  }
252
437
 
253
438
  function modernLoaderToTanstack<TLoader extends (args: any) => any>(
@@ -256,57 +441,31 @@ function modernLoaderToTanstack<TLoader extends (args: any) => any>(
256
441
  ) {
257
442
  type LoaderResult = Awaited<ReturnType<TLoader>>;
258
443
 
259
- return async (ctx: any): Promise<LoaderResult> => {
444
+ return (ctx: any): Promise<LoaderResult> => {
260
445
  try {
261
- const signal: AbortSignal =
262
- ctx?.abortController?.signal ||
263
- ctx?.signal ||
264
- new AbortController().signal;
446
+ const signal = getLoaderSignal(ctx);
265
447
  const baseRequest: Request | undefined =
266
448
  ctx?.context?.request instanceof Request ? ctx.context.request : undefined;
267
449
 
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
- '';
450
+ const href = getLoaderHref(ctx);
275
451
 
276
- const request = baseRequest
452
+ const request = baseRequest !== undefined
277
453
  ? new Request(baseRequest, { signal })
278
454
  : new Request(href, { signal });
279
455
 
280
- const params = mapParamsForModernLoader(ctx?.params || {}, opts.hasSplat);
281
-
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
- }
456
+ const params = mapParamsForModernLoader(getLoaderParams(ctx), opts.hasSplat);
297
457
 
298
- return result as LoaderResult;
458
+ return Promise.resolve(
459
+ (modernLoader as any)({
460
+ request,
461
+ params,
462
+ context: ctx?.context?.requestContext,
463
+ }),
464
+ )
465
+ .then((result: LoaderResult) => handleModernLoaderResult(result))
466
+ .catch(handleModernLoaderError);
299
467
  } 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;
468
+ handleModernLoaderError(err);
310
469
  }
311
470
  };
312
471
  }
@@ -327,6 +486,7 @@ ${statements.join('\n\n')}
327
486
  export const routeTree = rootRoute.addChildren([${topLevelVars.join(', ')}]);
328
487
 
329
488
  export const router = createRouter({
489
+ ...modernTanstackRouterFastDefaults,
330
490
  routeTree,
331
491
  history: createMemoryHistory({
332
492
  initialEntries: ['/'],
@@ -338,4 +498,4 @@ export const router = createRouter({
338
498
  routerGenTs
339
499
  };
340
500
  }
341
- export { generateTanstackRouterTypesSourceForEntry, isTanstackRouterFrameworkEnabled };
501
+ export { collectCanonicalRoutesForEntry, generateTanstackRouterTypesSourceForEntry, isTanstackRouterFrameworkEnabled };
@@ -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 };
@@ -1,6 +1,7 @@
1
- export * from "@tanstack/react-router";
2
- export { useMatch } from "@tanstack/react-router";
1
+ 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";
3
2
  export { Form, RouteActionResponseError, useFetcher } from "./dataMutation.mjs";
3
+ export { Outlet } from "./outlet.mjs";
4
4
  export { tanstackRouterPlugin as default, tanstackRouterPlugin } from "./plugin.mjs";
5
5
  export { Link, NavLink } from "./prefetchLink.mjs";
6
6
  export { CompositeComponent } from "./rsc/client.mjs";
7
+ export { getModernTanstackRouterFastDefaults, modernTanstackRouterFastDefaults } from "./types.mjs";
@@ -0,0 +1,17 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { Outlet } from "@tanstack/react-router";
3
+ import { createElement, memo } from "react";
4
+ const outlet_Outlet = /*#__PURE__*/ memo(function() {
5
+ return /*#__PURE__*/ jsx(Outlet, {});
6
+ });
7
+ function withModernRouteMatchContext(component, _routeId) {
8
+ if (null == component) return component;
9
+ const Component = component;
10
+ const WrappedRouteComponent = (props)=>/*#__PURE__*/ createElement(Component, props);
11
+ const preloadable = component;
12
+ if ('function' == typeof preloadable.load) WrappedRouteComponent.load = preloadable.load.bind(preloadable);
13
+ if ('function' == typeof preloadable.preload) WrappedRouteComponent.preload = preloadable.preload.bind(preloadable);
14
+ else if ('function' == typeof preloadable.load) WrappedRouteComponent.preload = WrappedRouteComponent.load;
15
+ return WrappedRouteComponent;
16
+ }
17
+ export { outlet_Outlet as Outlet, withModernRouteMatchContext };