@bleedingdev/modern-js-plugin-tanstack 3.9.0-ultramodern.11 → 3.9.0-ultramodern.13

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 (81) hide show
  1. package/dist/cjs/cli/artifacts.js +2 -1
  2. package/dist/cjs/cli/tanstackTypes/canonicalRoutes.js +7 -7
  3. package/dist/cjs/cli/tanstackTypes/shared.js +0 -14
  4. package/dist/cjs/cli/tanstackTypes/source.js +28 -63
  5. package/dist/cjs/runtime/lifecycle.js +0 -3
  6. package/dist/cjs/runtime/loaderBridge.js +10 -4
  7. package/dist/cjs/runtime/navigation.js +102 -0
  8. package/dist/cjs/runtime/plugin.js +6 -1
  9. package/dist/cjs/runtime/plugin.node.js +81 -63
  10. package/dist/cjs/runtime/routeTree/construction.js +19 -61
  11. package/dist/cjs/runtime/routeTree/loaders.js +1 -1
  12. package/dist/cjs/runtime/routeTree/paths.js +0 -6
  13. package/dist/cjs/runtime/ssrManagedTags.js +2 -12
  14. package/dist/cjs/runtime/utils.js +1 -0
  15. package/dist/cjs/shared/routeDescriptor.js +60 -0
  16. package/dist/esm/cli/artifacts.mjs +2 -1
  17. package/dist/esm/cli/index.mjs +1 -7
  18. package/dist/esm/cli/tanstackTypes/canonicalRoutes.mjs +7 -7
  19. package/dist/esm/cli/tanstackTypes/shared.mjs +1 -9
  20. package/dist/esm/cli/tanstackTypes/source.mjs +29 -64
  21. package/dist/esm/runtime/lifecycle.mjs +1 -1
  22. package/dist/esm/runtime/loaderBridge.mjs +8 -5
  23. package/dist/esm/runtime/navigation.mjs +64 -0
  24. package/dist/esm/runtime/plugin.mjs +6 -1
  25. package/dist/esm/runtime/plugin.node.mjs +83 -65
  26. package/dist/esm/runtime/routeTree/construction.mjs +19 -61
  27. package/dist/esm/runtime/routeTree/loaders.mjs +2 -2
  28. package/dist/esm/runtime/routeTree/paths.mjs +1 -4
  29. package/dist/esm/runtime/ssrManagedTags.mjs +1 -8
  30. package/dist/esm/runtime/utils.mjs +1 -0
  31. package/dist/esm/shared/routeDescriptor.mjs +22 -0
  32. package/dist/esm-node/cli/artifacts.mjs +2 -1
  33. package/dist/esm-node/cli/index.mjs +1 -7
  34. package/dist/esm-node/cli/tanstackTypes/canonicalRoutes.mjs +7 -7
  35. package/dist/esm-node/cli/tanstackTypes/shared.mjs +1 -9
  36. package/dist/esm-node/cli/tanstackTypes/source.mjs +29 -64
  37. package/dist/esm-node/runtime/lifecycle.mjs +1 -1
  38. package/dist/esm-node/runtime/loaderBridge.mjs +8 -5
  39. package/dist/esm-node/runtime/navigation.mjs +65 -0
  40. package/dist/esm-node/runtime/plugin.mjs +6 -1
  41. package/dist/esm-node/runtime/plugin.node.mjs +83 -65
  42. package/dist/esm-node/runtime/routeTree/construction.mjs +19 -61
  43. package/dist/esm-node/runtime/routeTree/loaders.mjs +2 -2
  44. package/dist/esm-node/runtime/routeTree/paths.mjs +1 -4
  45. package/dist/esm-node/runtime/ssrManagedTags.mjs +1 -8
  46. package/dist/esm-node/runtime/utils.mjs +1 -0
  47. package/dist/esm-node/shared/routeDescriptor.mjs +23 -0
  48. package/dist/types/cli/tanstackTypes/shared.d.ts +0 -2
  49. package/dist/types/runtime/lifecycle.d.ts +1 -1
  50. package/dist/types/runtime/loaderBridge.d.ts +1 -0
  51. package/dist/types/runtime/navigation.d.ts +4 -0
  52. package/dist/types/runtime/routeTree/paths.d.ts +0 -2
  53. package/dist/types/runtime/ssrManagedTags.d.ts +0 -1
  54. package/dist/types/runtime/ssrTypes.d.ts +0 -7
  55. package/dist/types/shared/routeDescriptor.d.ts +26 -0
  56. package/package.json +21 -21
  57. package/src/cli/artifacts.ts +6 -1
  58. package/src/cli/tanstackTypes/canonicalRoutes.ts +17 -12
  59. package/src/cli/tanstackTypes/shared.ts +0 -14
  60. package/src/cli/tanstackTypes/source.ts +43 -119
  61. package/src/runtime/lifecycle.ts +0 -1
  62. package/src/runtime/loaderBridge.ts +9 -9
  63. package/src/runtime/navigation.ts +78 -0
  64. package/src/runtime/plugin.node.tsx +111 -91
  65. package/src/runtime/plugin.tsx +6 -0
  66. package/src/runtime/routeTree/construction.ts +32 -112
  67. package/src/runtime/routeTree/loaders.ts +2 -4
  68. package/src/runtime/routeTree/paths.ts +0 -6
  69. package/src/runtime/ssrManagedTags.ts +0 -17
  70. package/src/runtime/ssrTypes.ts +0 -7
  71. package/src/runtime/utils.tsx +1 -0
  72. package/src/shared/routeDescriptor.ts +46 -0
  73. package/tests/router/clientNavigation.ts +13 -0
  74. package/tests/router/generateRouteArtifacts.test.ts +36 -2
  75. package/tests/router/localisedIdentity.test.tsx +7 -2
  76. package/tests/router/prefetchLink.test.tsx +29 -0
  77. package/tests/router/routeHooks.test.ts +2 -1
  78. package/tests/router/serverPlugin.test.ts +150 -0
  79. package/tests/router/tanstackTypes.test.ts +121 -2
  80. package/dist/esm/rslib-runtime~1.mjs +0 -18
  81. package/dist/esm-node/rslib-runtime~0.mjs +0 -19
package/package.json CHANGED
@@ -18,7 +18,7 @@
18
18
  "modern.js",
19
19
  "tanstack-router"
20
20
  ],
21
- "version": "3.9.0-ultramodern.11",
21
+ "version": "3.9.0-ultramodern.13",
22
22
  "engines": {
23
23
  "node": ">=20"
24
24
  },
@@ -94,19 +94,19 @@
94
94
  }
95
95
  },
96
96
  "dependencies": {
97
- "@modern-js/i18n-runtime-extensions": "npm:@bleedingdev/modern-js-i18n-runtime-extensions@3.9.0-ultramodern.11",
98
- "@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.9.0-ultramodern.11",
99
- "@modern-js/runtime-extensions": "npm:@bleedingdev/modern-js-runtime-extensions@3.9.0-ultramodern.11",
100
- "@modern-js/runtime-utils": "npm:@bleedingdev/modern-js-runtime-utils@3.9.0-ultramodern.11",
101
- "@modern-js/types": "npm:@bleedingdev/modern-js-types@3.9.0-ultramodern.11",
102
- "@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.9.0-ultramodern.11",
97
+ "@modern-js/i18n-runtime-extensions": "npm:@bleedingdev/modern-js-i18n-runtime-extensions@3.9.0-ultramodern.13",
98
+ "@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.9.0-ultramodern.13",
99
+ "@modern-js/runtime-extensions": "npm:@bleedingdev/modern-js-runtime-extensions@3.9.0-ultramodern.13",
100
+ "@modern-js/runtime-utils": "npm:@bleedingdev/modern-js-runtime-utils@3.9.0-ultramodern.13",
101
+ "@modern-js/types": "npm:@bleedingdev/modern-js-types@3.9.0-ultramodern.13",
102
+ "@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.9.0-ultramodern.13",
103
103
  "@swc/helpers": "^0.5.23",
104
- "@tanstack/react-router": "1.170.33",
105
- "@tanstack/router-core": "1.171.28"
104
+ "@tanstack/react-router": "1.170.39",
105
+ "@tanstack/router-core": "1.171.32"
106
106
  },
107
107
  "peerDependencies": {
108
- "@modern-js/app-tools": "3.9.0-ultramodern.11",
109
- "@modern-js/runtime": "3.9.0-ultramodern.11",
108
+ "@modern-js/app-tools": "3.9.0-ultramodern.13",
109
+ "@modern-js/runtime": "3.9.0-ultramodern.13",
110
110
  "react": "^19.2.8",
111
111
  "react-dom": "^19.2.8",
112
112
  "react-server-dom-rspack": "0.1.0"
@@ -117,20 +117,20 @@
117
117
  }
118
118
  },
119
119
  "devDependencies": {
120
- "@modern-js/app-tools": "npm:@bleedingdev/modern-js-app-tools@3.9.0-ultramodern.11",
121
- "@modern-js/runtime": "npm:@bleedingdev/modern-js-runtime@3.9.0-ultramodern.11",
122
- "@modern-js/server-utils": "npm:@bleedingdev/modern-js-server-utils@3.9.0-ultramodern.11",
123
- "@rslib/core": "1.0.0",
120
+ "@modern-js/app-tools": "npm:@bleedingdev/modern-js-app-tools@3.9.0-ultramodern.13",
121
+ "@modern-js/runtime": "npm:@bleedingdev/modern-js-runtime@3.9.0-ultramodern.13",
122
+ "@modern-js/server-utils": "npm:@bleedingdev/modern-js-server-utils@3.9.0-ultramodern.13",
123
+ "@rslib/core": "1.0.2",
124
124
  "@scripts/rstest-config": "2.66.0",
125
- "@tanstack/history": "1.162.2",
125
+ "@tanstack/history": "1.162.4",
126
126
  "@testing-library/react": "^16.3.3",
127
127
  "@tsconfig/strictest": "2.0.8",
128
- "@types/node": "^26.4.1",
129
- "@types/react": "^19.2.18",
130
- "@types/react-dom": "^19.2.7",
128
+ "@types/node": "^26.6.2",
129
+ "@types/react": "^19.3.0",
130
+ "@types/react-dom": "^19.3.0",
131
131
  "@typescript/native-preview": "7.0.0-dev.20260707.2",
132
- "react": "^19.2.8",
133
- "react-dom": "^19.2.8",
132
+ "react": "^19.3.0",
133
+ "react-dom": "^19.3.0",
134
134
  "react-server-dom-rspack": "0.1.0"
135
135
  },
136
136
  "sideEffects": [
@@ -251,5 +251,10 @@ export async function generateTanstackRouteArtifacts(opts: {
251
251
  internalPlugins: (utils as { INTERNAL_RUNTIME_PLUGINS: unknown })
252
252
  .INTERNAL_RUNTIME_PLUGINS as never,
253
253
  });
254
- await cli.init({ ...runOptions, command: 'build' });
254
+ const { appContext } = await cli.init({ ...runOptions, command: 'build' });
255
+ // Preparation owns the artifact writes; there is no dev server or bundler
256
+ // phase after it. Release plugin resources before the headless caller exits.
257
+ // Failed initialization propagates unchanged: it has not returned a context
258
+ // whose lifecycle this caller can close.
259
+ await appContext.hooks.onBeforeExit.call();
255
260
  }
@@ -1,7 +1,10 @@
1
1
  // @effect-diagnostics asyncFunction:off strictBooleanExpressions:off
2
2
 
3
3
  import type { NestedRouteForCli, PageRoute } from '@modern-js/types';
4
- import { MAIN_ENTRY_NAME } from '@modern-js/utils';
4
+ import {
5
+ describeRouteTree,
6
+ type RouteDescriptor,
7
+ } from '../../shared/routeDescriptor';
5
8
 
6
9
  import { toTanstackPath } from './shared';
7
10
 
@@ -16,6 +19,7 @@ const LOCALE_PARAM_SEGMENTS = new Set([
16
19
 
17
20
  type CanonicalAwareRoute = (NestedRouteForCli | PageRoute) & {
18
21
  modernCanonicalPath?: string;
22
+ modernLocalisedRoute?: { canonicalPath: string };
19
23
  index?: boolean;
20
24
  isRoot?: boolean;
21
25
  children?: CanonicalAwareRoute[];
@@ -101,12 +105,18 @@ export function collectCanonicalRoutesForEntry(
101
105
  }
102
106
  };
103
107
 
104
- const visit = (route: CanonicalAwareRoute, parentPath: string) => {
108
+ const visit = (
109
+ descriptor: RouteDescriptor<CanonicalAwareRoute>,
110
+ parentPath: string,
111
+ ) => {
112
+ const { source: route, children } = descriptor;
105
113
  let currentPath = parentPath;
106
114
 
107
- if (typeof route.modernCanonicalPath === 'string') {
115
+ const canonicalPath =
116
+ route.modernLocalisedRoute?.canonicalPath ?? route.modernCanonicalPath;
117
+ if (typeof canonicalPath === 'string') {
108
118
  hasI18nSurface = true;
109
- currentPath = normalizeJoined(route.modernCanonicalPath);
119
+ currentPath = canonicalPath ? normalizeJoined(canonicalPath) : '';
110
120
  } else if (typeof route.path === 'string' && route.path.length > 0) {
111
121
  const segments = route.path
112
122
  .replace(/\[(.+?)\]/g, ':$1')
@@ -125,7 +135,6 @@ export function collectCanonicalRoutesForEntry(
125
135
  : parentPath;
126
136
  }
127
137
 
128
- const children = route.children;
129
138
  if (children && children.length > 0) {
130
139
  for (const child of children) {
131
140
  visit(child, currentPath);
@@ -137,13 +146,9 @@ export function collectCanonicalRoutesForEntry(
137
146
  record(currentPath || '/');
138
147
  };
139
148
 
140
- const rootModern = routes.find(
141
- route => (route as CanonicalAwareRoute).isRoot,
142
- ) as CanonicalAwareRoute | undefined;
143
- const topLevel = rootModern ? (rootModern.children ?? []) : routes;
144
-
145
- for (const route of topLevel) {
146
- visit(route as CanonicalAwareRoute, '');
149
+ for (const route of describeRouteTree(routes as CanonicalAwareRoute[])
150
+ .children) {
151
+ visit(route, '');
147
152
  }
148
153
 
149
154
  if (!hasI18nSurface || canonicalParams.size === 0) {
@@ -71,20 +71,6 @@ export function pickRouteSearchContractModules(
71
71
  };
72
72
  }
73
73
 
74
- export function isPathlessLayout(route: NestedRouteForCli | PageRoute) {
75
- const extras = route as RouteExtras;
76
- return (
77
- extras.type === 'nested' &&
78
- typeof extras.index !== 'boolean' &&
79
- typeof extras.path === 'undefined'
80
- );
81
- }
82
-
83
- export function isIndexRoute(route: NestedRouteForCli | PageRoute) {
84
- const extras = route as RouteExtras;
85
- return extras.type === 'nested' && Boolean(extras.index);
86
- }
87
-
88
74
  export function createRouteStaticDataSnippet(opts: {
89
75
  modernRouteId?: string;
90
76
  loaderName?: string | null;
@@ -1,25 +1,22 @@
1
1
  // @effect-diagnostics asyncFunction:off strictBooleanExpressions:off
2
2
 
3
- import type {
4
- AppNormalizedConfig,
5
- AppTools,
6
- AppToolsContext,
7
- } from '@modern-js/app-tools';
8
- import { canonicaliseLocalisedRoutes } from '@modern-js/i18n-runtime-extensions';
3
+ import type { AppToolsContext } from '@modern-js/app-tools';
9
4
  import { makeLegalIdentifier } from '@modern-js/runtime/cli';
10
5
  import type { NestedRouteForCli, PageRoute } from '@modern-js/types';
11
6
  import { upath as path } from '@modern-js/utils';
12
7
 
8
+ import {
9
+ describeRouteTree,
10
+ type RouteDescriptor,
11
+ } from '../../shared/routeDescriptor';
12
+
13
13
  import {
14
14
  createRouteStaticDataSnippet,
15
- isIndexRoute,
16
- isPathlessLayout,
17
15
  normalizeRelativeImport,
18
16
  pickModernLoaderModule,
19
17
  pickRouteSearchContractModules,
20
18
  quote,
21
19
  resolveFileNoExt,
22
- toTanstackPath,
23
20
  } from './shared';
24
21
 
25
22
  type RouteForCli = NestedRouteForCli | PageRoute;
@@ -46,22 +43,15 @@ export async function generateTanstackRouterTypesSourceForEntry(opts: {
46
43
  appContext,
47
44
  entryName,
48
45
  generatedDirName = 'modern-tanstack',
49
- routes: rawRoutes,
46
+ routes,
50
47
  } = opts;
51
- const routes: RouteForCli[] = canonicaliseLocalisedRoutes(rawRoutes);
52
48
  const outDir = path.join(
53
49
  appContext.srcDirectory,
54
50
  generatedDirName,
55
51
  entryName,
56
52
  );
57
53
 
58
- const rootModern = routes.find(r => {
59
- const extras = r as RouteExtras;
60
- return r && extras.type === 'nested' && extras.isRoot;
61
- }) as NestedRouteForCli | undefined;
62
-
63
- const rootExtras = rootModern as RouteExtras | undefined;
64
- const topLevel = rootModern ? rootExtras?.children || [] : routes;
54
+ const { root: rootModern, children: topLevel } = describeRouteTree(routes);
65
55
 
66
56
  const imports: string[] = [];
67
57
  const statements: string[] = [];
@@ -92,15 +82,11 @@ export async function generateTanstackRouterTypesSourceForEntry(opts: {
92
82
  // value carries the internal `@_modern_js_src` alias, which the app's
93
83
  // tsconfig does not map — the generated file must use relative
94
84
  // imports.
95
- const resolvedNoExt = await resolveRouteModuleNoExt(componentPath);
96
- if (!resolvedNoExt) {
85
+ const relImport = await resolveRouteImport(componentPath);
86
+ if (!relImport) {
97
87
  return null;
98
88
  }
99
89
 
100
- const relImport = normalizeRelativeImport(
101
- path.relative(outDir, resolvedNoExt),
102
- );
103
-
104
90
  const componentName = `component_${componentIndex++}`;
105
91
  imports.push(`import ${componentName} from ${quote(relImport)};`);
106
92
  return componentName;
@@ -111,7 +97,7 @@ export async function generateTanstackRouterTypesSourceForEntry(opts: {
111
97
  return pendingImportName;
112
98
  };
113
99
 
114
- const resolveRouteModuleNoExt = async (aliasedNoExtPath: string) => {
100
+ const resolveRouteImport = async (aliasedNoExtPath: string) => {
115
101
  const prefix = `${appContext.internalSrcAlias}/`;
116
102
  let absNoExt: string;
117
103
  if (aliasedNoExtPath.startsWith(prefix)) {
@@ -124,7 +110,10 @@ export async function generateTanstackRouterTypesSourceForEntry(opts: {
124
110
  absNoExt = path.join(appContext.srcDirectory, aliasedNoExtPath);
125
111
  }
126
112
 
127
- return resolveFileNoExt(absNoExt);
113
+ const resolvedNoExt = await resolveFileNoExt(absNoExt);
114
+ return resolvedNoExt
115
+ ? normalizeRelativeImport(path.relative(outDir, resolvedNoExt))
116
+ : null;
128
117
  };
129
118
 
130
119
  const getImportNamesForLoader = async (
@@ -143,15 +132,11 @@ export async function generateTanstackRouterTypesSourceForEntry(opts: {
143
132
  };
144
133
  }
145
134
 
146
- const resolvedNoExt = await resolveRouteModuleNoExt(aliasedNoExtPath);
147
- if (!resolvedNoExt) {
135
+ const relImport = await resolveRouteImport(aliasedNoExtPath);
136
+ if (!relImport) {
148
137
  return null;
149
138
  }
150
139
 
151
- const relImport = normalizeRelativeImport(
152
- path.relative(outDir, resolvedNoExt),
153
- );
154
-
155
140
  const importName = `loader_${loaderIndex++}`;
156
141
  const actionName = hasAction
157
142
  ? importName.replace(/^loader_/, 'action_')
@@ -182,14 +167,10 @@ export async function generateTanstackRouterTypesSourceForEntry(opts: {
182
167
  return existing;
183
168
  }
184
169
 
185
- const resolvedNoExt = await resolveRouteModuleNoExt(aliasedNoExtPath);
186
- if (!resolvedNoExt) {
170
+ const relImport = await resolveRouteImport(aliasedNoExtPath);
171
+ if (!relImport) {
187
172
  return null;
188
173
  }
189
-
190
- const relImport = normalizeRelativeImport(
191
- path.relative(outDir, resolvedNoExt),
192
- );
193
174
  const importName =
194
175
  exportName === 'validateSearch'
195
176
  ? `validateSearch_${validateSearchIndex++}`
@@ -217,15 +198,8 @@ export async function generateTanstackRouterTypesSourceForEntry(opts: {
217
198
  return reserveRouteVarName(`route_${base}`);
218
199
  };
219
200
 
220
- const buildRoute = async (opts: {
221
- parentVar: string;
222
- route: NestedRouteForCli | PageRoute;
223
- }): Promise<string> => {
224
- const { parentVar, route } = opts;
225
-
226
- const varName = createRouteVarName(route);
201
+ const createRouteOptions = async (route: RouteForCli, hasSplat: boolean) => {
227
202
  const routeExtras = route as RouteExtras;
228
-
229
203
  const loaderInfo = pickModernLoaderModule(route);
230
204
  const routeAction = routeExtras.action;
231
205
  const loaderImports = loaderInfo
@@ -251,10 +225,7 @@ export async function generateTanstackRouterTypesSourceForEntry(opts: {
251
225
  )
252
226
  : null;
253
227
 
254
- const rawPath = routeExtras.path;
255
- const hasSplat = typeof rawPath === 'string' && rawPath.includes('*');
256
-
257
- const routeOpts: string[] = [`getParentRoute: () => ${parentVar},`];
228
+ const routeOpts: string[] = [];
258
229
 
259
230
  const componentName = await getImportNameForComponent(
260
231
  routeExtras._component,
@@ -263,14 +234,6 @@ export async function generateTanstackRouterTypesSourceForEntry(opts: {
263
234
  routeOpts.push(`component: ${componentName},`);
264
235
  }
265
236
 
266
- if (isPathlessLayout(route)) {
267
- const id = routeExtras.id;
268
- routeOpts.push(`id: ${quote(id || 'pathless')},`);
269
- } else {
270
- const p = isIndexRoute(route) ? '/' : toTanstackPath(rawPath || '');
271
- routeOpts.push(`path: ${quote(p)},`);
272
- }
273
-
274
237
  if (loaderName) {
275
238
  routeOpts.push(
276
239
  `loader: modernLoaderToTanstack({ hasSplat: ${hasSplat} }, ${loaderName}),`,
@@ -292,7 +255,22 @@ export async function generateTanstackRouterTypesSourceForEntry(opts: {
292
255
  routeOpts.push(staticDataSnippet);
293
256
  }
294
257
 
295
- const children = routeExtras.children;
258
+ return routeOpts;
259
+ };
260
+
261
+ const buildRoute = async (
262
+ descriptor: RouteDescriptor<RouteForCli>,
263
+ parentVar: string,
264
+ ): Promise<string> => {
265
+ const { source: route, location, hasSplat, children } = descriptor;
266
+ const varName = createRouteVarName(route);
267
+ const routeOpts = [
268
+ `getParentRoute: () => ${parentVar},`,
269
+ ...Object.entries(location).map(
270
+ ([key, value]) => `${key}: ${quote(value)},`,
271
+ ),
272
+ ...(await createRouteOptions(route, hasSplat)),
273
+ ];
296
274
  const hasChildren = Boolean(children && children.length > 0);
297
275
  const routeCtorVarName = hasChildren
298
276
  ? reserveRouteVarName(`${varName}__base`)
@@ -305,9 +283,7 @@ export async function generateTanstackRouterTypesSourceForEntry(opts: {
305
283
  if (children && children.length > 0) {
306
284
  const childVars: string[] = [];
307
285
  for (const child of children) {
308
- childVars.push(
309
- await buildRoute({ parentVar: routeCtorVarName, route: child }),
310
- );
286
+ childVars.push(await buildRoute(child, routeCtorVarName));
311
287
  }
312
288
  statements.push(
313
289
  `const ${varName} = ${routeCtorVarName}.addChildren([${childVars.join(', ')}]);`,
@@ -317,60 +293,14 @@ export async function generateTanstackRouterTypesSourceForEntry(opts: {
317
293
  return varName;
318
294
  };
319
295
 
320
- const rootLoaderInfo = rootModern ? pickModernLoaderModule(rootModern) : null;
321
- const rootAction = rootExtras?.action;
322
- const rootLoaderImports = rootLoaderInfo?.loaderPath
323
- ? await getImportNamesForLoader(
324
- rootLoaderInfo.loaderPath,
325
- rootLoaderInfo.inline,
326
- Boolean(
327
- rootLoaderInfo.inline && rootAction === rootLoaderInfo.loaderPath,
328
- ),
329
- )
330
- : null;
331
- const rootLoaderName = rootLoaderImports?.loaderName || null;
332
- const rootActionName = rootLoaderImports?.actionName || null;
333
- const rootSearchContractInfo = rootModern
334
- ? pickRouteSearchContractModules(rootModern)
335
- : null;
336
- const rootValidateSearchName = rootSearchContractInfo?.validateSearchPath
337
- ? await getImportNameForSearchContract(
338
- rootSearchContractInfo.validateSearchPath,
339
- 'validateSearch',
340
- )
341
- : null;
342
- const rootLoaderDepsName = rootSearchContractInfo?.loaderDepsPath
343
- ? await getImportNameForSearchContract(
344
- rootSearchContractInfo.loaderDepsPath,
345
- 'loaderDeps',
346
- )
347
- : null;
348
-
349
296
  const topLevelVars: string[] = [];
350
297
  for (const route of topLevel) {
351
- topLevelVars.push(await buildRoute({ parentVar: 'rootRoute', route }));
298
+ topLevelVars.push(await buildRoute(route, 'rootRoute'));
352
299
  }
353
300
 
354
- const rootOpts: string[] = [];
355
-
356
- const rootComponentName = await getImportNameForComponent(
357
- rootExtras?._component,
358
- );
359
- if (rootComponentName) {
360
- rootOpts.push(`component: ${rootComponentName},`);
361
- }
362
-
363
- if (rootLoaderName) {
364
- rootOpts.push(
365
- `loader: modernLoaderToTanstack({ hasSplat: false }, ${rootLoaderName}),`,
366
- );
367
- }
368
- if (rootValidateSearchName) {
369
- rootOpts.push(`validateSearch: ${rootValidateSearchName},`);
370
- }
371
- if (rootLoaderDepsName) {
372
- rootOpts.push(`loaderDeps: ${rootLoaderDepsName},`);
373
- }
301
+ const rootOpts = rootModern
302
+ ? await createRouteOptions(rootModern, false)
303
+ : [];
374
304
 
375
305
  const routerGenTs = `// This file is auto-generated by Modern.js. Do not edit manually.
376
306
 
@@ -388,13 +318,7 @@ ${imports.join('\n')}
388
318
 
389
319
  export const rootRoute = createRootRouteWithContext<ModernRouterContext>()({
390
320
  ${rootOpts.join('\n ')}
391
- ${
392
- createRouteStaticDataSnippet({
393
- modernRouteId: rootExtras?.id,
394
- loaderName: rootLoaderName,
395
- actionName: rootActionName,
396
- }) || ''
397
- }
321
+
398
322
  });
399
323
 
400
324
  ${statements.join('\n\n')}
@@ -19,7 +19,6 @@ export type RouterLifecycleContext = NativeRouterLifecycleContext & {
19
19
  export {
20
20
  applyRouterRuntimeState,
21
21
  applyRouterServerPrepareResult,
22
- createRouterRuntimeState,
23
22
  createRouterServerSnapshot,
24
23
  getRouterRuntimeState,
25
24
  getRouterServerSnapshot,
@@ -98,15 +98,15 @@ type LoaderLikeContext = {
98
98
  params?: Record<string, string>;
99
99
  };
100
100
 
101
- function getLoaderSignal(ctx: LoaderLikeContext | undefined): AbortSignal {
102
- const abortSignal = ctx?.abortController?.signal;
103
- if (abortSignal instanceof AbortSignal) {
104
- return abortSignal;
105
- }
106
- if (ctx?.signal instanceof AbortSignal) {
107
- return ctx.signal;
108
- }
109
- return new AbortController().signal;
101
+ export function getLoaderSignal(
102
+ ctx: LoaderLikeContext | undefined,
103
+ ): AbortSignal {
104
+ const matchSignal = ctx?.abortController?.signal ?? ctx?.signal;
105
+ const requestSignal = ctx?.context?.request?.signal;
106
+ const signals = [matchSignal, requestSignal].filter(
107
+ (signal): signal is AbortSignal => signal instanceof AbortSignal,
108
+ );
109
+ return AbortSignal.any(signals);
110
110
  }
111
111
 
112
112
  export function getLoaderHref(ctx: LoaderLikeContext | undefined): string {
@@ -0,0 +1,78 @@
1
+ import type {
2
+ RouterNavigationCapability,
3
+ RouterNavigationSnapshot,
4
+ } from '@modern-js/runtime-extensions/router-state';
5
+ import type { AnyRouter } from '@tanstack/react-router';
6
+ import { Link } from './prefetchLink';
7
+
8
+ /** Native TanStack navigation, exposed through the provider contract. */
9
+ export function createTanstackNavigation(
10
+ router: AnyRouter,
11
+ ): RouterNavigationCapability {
12
+ let location: AnyRouter['state']['location'] | undefined;
13
+ let matches: AnyRouter['state']['matches'] | undefined;
14
+ let snapshot: RouterNavigationSnapshot;
15
+ return {
16
+ Link: Link as RouterNavigationCapability['Link'],
17
+ getSnapshot() {
18
+ const state = router.state;
19
+ if (
20
+ location !== state.location ||
21
+ matches === undefined ||
22
+ matches.length !== state.matches.length ||
23
+ matches.some(
24
+ (match, index) => match.params !== state.matches[index]?.params,
25
+ )
26
+ ) {
27
+ location = state.location;
28
+ matches = state.matches;
29
+ snapshot = {
30
+ location: {
31
+ pathname: location.pathname,
32
+ search: location.searchStr,
33
+ hash:
34
+ location.hash !== '' ? `#${location.hash.replace(/^#/, '')}` : '',
35
+ },
36
+ params: Object.assign({}, ...matches.map(match => match.params)),
37
+ };
38
+ }
39
+ return snapshot;
40
+ },
41
+ subscribe(listener) {
42
+ const stops = (
43
+ ['onBeforeNavigate', 'onBeforeLoad', 'onLoad', 'onResolved'] as const
44
+ ).map(event => router.subscribe(event, listener));
45
+ return () => {
46
+ for (const stop of stops) stop();
47
+ };
48
+ },
49
+ navigate(href, options) {
50
+ return router.navigate({
51
+ to: href,
52
+ replace: options?.replace,
53
+ ...(options?.state === undefined
54
+ ? {}
55
+ : { state: options.state as never }),
56
+ });
57
+ },
58
+ createLinkProps(target) {
59
+ const preload =
60
+ target.preload !== undefined
61
+ ? target.preload
62
+ : target.prefetch === 'none'
63
+ ? false
64
+ : target.prefetch;
65
+ return {
66
+ to: target.pathname,
67
+ ...(target.search !== undefined ? { search: target.search } : {}),
68
+ ...(target.hash !== undefined && target.hash !== ''
69
+ ? { hash: target.hash }
70
+ : {}),
71
+ ...(target.hashScrollIntoView === undefined
72
+ ? {}
73
+ : { hashScrollIntoView: target.hashScrollIntoView }),
74
+ ...(preload === undefined ? {} : { preload }),
75
+ };
76
+ },
77
+ };
78
+ }