@bleedingdev/modern-js-plugin-tanstack 3.9.0-ultramodern.8 → 3.9.3-ultramodern.1

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 (112) hide show
  1. package/dist/cjs/cli/artifacts.js +15 -1
  2. package/dist/cjs/cli/index.js +13 -2
  3. package/dist/cjs/cli/routeMetadata.js +131 -0
  4. package/dist/cjs/cli/rscRouteLayer.js +86 -0
  5. package/dist/cjs/cli/tanstackTypes/canonicalRoutes.js +7 -7
  6. package/dist/cjs/cli/tanstackTypes/shared.js +0 -14
  7. package/dist/cjs/cli/tanstackTypes/source.js +28 -63
  8. package/dist/cjs/runtime/lifecycle.js +0 -3
  9. package/dist/cjs/runtime/loaderBridge.js +10 -4
  10. package/dist/cjs/runtime/navigation.js +102 -0
  11. package/dist/cjs/runtime/plugin.js +10 -1
  12. package/dist/cjs/runtime/plugin.node.js +89 -64
  13. package/dist/cjs/runtime/routeTree/construction.js +19 -61
  14. package/dist/cjs/runtime/routeTree/loaders.js +1 -1
  15. package/dist/cjs/runtime/routeTree/paths.js +0 -6
  16. package/dist/cjs/runtime/ssrManagedTags.js +2 -12
  17. package/dist/cjs/runtime/utils.js +1 -0
  18. package/dist/cjs/shared/routeDescriptor.js +60 -0
  19. package/dist/esm/cli/artifacts.mjs +13 -2
  20. package/dist/esm/cli/index.mjs +13 -11
  21. package/dist/esm/cli/routeMetadata.mjs +71 -0
  22. package/dist/esm/cli/rscRouteLayer.mjs +35 -0
  23. package/dist/esm/cli/tanstackTypes/canonicalRoutes.mjs +7 -7
  24. package/dist/esm/cli/tanstackTypes/shared.mjs +1 -9
  25. package/dist/esm/cli/tanstackTypes/source.mjs +29 -64
  26. package/dist/esm/runtime/lifecycle.mjs +1 -1
  27. package/dist/esm/runtime/loaderBridge.mjs +8 -5
  28. package/dist/esm/runtime/navigation.mjs +64 -0
  29. package/dist/esm/runtime/plugin.mjs +10 -1
  30. package/dist/esm/runtime/plugin.node.mjs +91 -66
  31. package/dist/esm/runtime/routeTree/construction.mjs +19 -61
  32. package/dist/esm/runtime/routeTree/loaders.mjs +2 -2
  33. package/dist/esm/runtime/routeTree/paths.mjs +1 -4
  34. package/dist/esm/runtime/ssrManagedTags.mjs +1 -8
  35. package/dist/esm/runtime/utils.mjs +1 -0
  36. package/dist/esm/shared/routeDescriptor.mjs +22 -0
  37. package/dist/esm-node/cli/artifacts.mjs +13 -2
  38. package/dist/esm-node/cli/index.mjs +13 -11
  39. package/dist/esm-node/cli/routeMetadata.mjs +72 -0
  40. package/dist/esm-node/cli/rscRouteLayer.mjs +36 -0
  41. package/dist/esm-node/cli/tanstackTypes/canonicalRoutes.mjs +7 -7
  42. package/dist/esm-node/cli/tanstackTypes/shared.mjs +1 -9
  43. package/dist/esm-node/cli/tanstackTypes/source.mjs +29 -64
  44. package/dist/esm-node/runtime/lifecycle.mjs +1 -1
  45. package/dist/esm-node/runtime/loaderBridge.mjs +8 -5
  46. package/dist/esm-node/runtime/navigation.mjs +65 -0
  47. package/dist/esm-node/runtime/plugin.mjs +10 -1
  48. package/dist/esm-node/runtime/plugin.node.mjs +91 -66
  49. package/dist/esm-node/runtime/routeTree/construction.mjs +19 -61
  50. package/dist/esm-node/runtime/routeTree/loaders.mjs +2 -2
  51. package/dist/esm-node/runtime/routeTree/paths.mjs +1 -4
  52. package/dist/esm-node/runtime/ssrManagedTags.mjs +1 -8
  53. package/dist/esm-node/runtime/utils.mjs +1 -0
  54. package/dist/esm-node/shared/routeDescriptor.mjs +23 -0
  55. package/dist/types/cli/artifacts.d.ts +16 -7
  56. package/dist/types/cli/index.d.ts +1 -1
  57. package/dist/types/cli/routeMetadata.d.ts +18 -0
  58. package/dist/types/cli/rscRouteLayer.d.ts +11 -0
  59. package/dist/types/cli/tanstackTypes/shared.d.ts +0 -2
  60. package/dist/types/cli/tanstackTypes/source.d.ts +3 -2
  61. package/dist/types/runtime/lifecycle.d.ts +1 -1
  62. package/dist/types/runtime/loaderBridge.d.ts +1 -0
  63. package/dist/types/runtime/navigation.d.ts +4 -0
  64. package/dist/types/runtime/pluginShared.d.ts +5 -0
  65. package/dist/types/runtime/routeTree/paths.d.ts +0 -2
  66. package/dist/types/runtime/ssrManagedTags.d.ts +0 -1
  67. package/dist/types/runtime/ssrTypes.d.ts +0 -7
  68. package/dist/types/shared/routeDescriptor.d.ts +26 -0
  69. package/package.json +32 -26
  70. package/src/cli/artifacts.ts +59 -8
  71. package/src/cli/index.ts +17 -4
  72. package/src/cli/routeMetadata.ts +121 -0
  73. package/src/cli/rscRouteLayer.ts +93 -0
  74. package/src/cli/tanstackTypes/canonicalRoutes.ts +17 -12
  75. package/src/cli/tanstackTypes/shared.ts +0 -14
  76. package/src/cli/tanstackTypes/source.ts +48 -120
  77. package/src/runtime/lifecycle.ts +0 -1
  78. package/src/runtime/loaderBridge.ts +9 -9
  79. package/src/runtime/navigation.ts +78 -0
  80. package/src/runtime/plugin.node.tsx +121 -91
  81. package/src/runtime/plugin.tsx +10 -0
  82. package/src/runtime/pluginShared.ts +12 -0
  83. package/src/runtime/routeTree/construction.ts +32 -112
  84. package/src/runtime/routeTree/loaders.ts +2 -4
  85. package/src/runtime/routeTree/paths.ts +0 -6
  86. package/src/runtime/ssrManagedTags.ts +0 -17
  87. package/src/runtime/ssrTypes.ts +0 -7
  88. package/src/runtime/utils.tsx +1 -0
  89. package/src/shared/routeDescriptor.ts +46 -0
  90. package/dist/esm/rslib-runtime~1.mjs +0 -18
  91. package/dist/esm-node/rslib-runtime~0.mjs +0 -19
  92. package/rslib.config.mts +0 -4
  93. package/rstest.config.mts +0 -53
  94. package/tests/router/cli.test.ts +0 -420
  95. package/tests/router/clientHydration.test.tsx +0 -200
  96. package/tests/router/dataMutation.test.tsx +0 -428
  97. package/tests/router/fixtures/router-gen.oxlint.json +0 -5
  98. package/tests/router/flightSerialization.roundtrip.test.tsx +0 -140
  99. package/tests/router/generateRouteArtifacts.test.ts +0 -146
  100. package/tests/router/loaderBridge.test.ts +0 -207
  101. package/tests/router/localisedIdentity.test.tsx +0 -95
  102. package/tests/router/prefetchLink.test.tsx +0 -122
  103. package/tests/router/prefetchLinkPreload.test.tsx +0 -65
  104. package/tests/router/routeHooks.test.ts +0 -49
  105. package/tests/router/routeTree.test.ts +0 -240
  106. package/tests/router/rsc.test.tsx +0 -480
  107. package/tests/router/serverPlugin.test.ts +0 -157
  108. package/tests/router/slotUsageSanitizer.test.ts +0 -23
  109. package/tests/router/ssrPreload.test.ts +0 -30
  110. package/tests/router/tanstackTypes.test.ts +0 -384
  111. package/tsconfig.json +0 -12
  112. package/tsconfig.tsgo.json +0 -6
@@ -43,14 +43,6 @@ function pickRouteSearchContractModules(route) {
43
43
  loaderDepsPath: 'string' == typeof loaderDepsPath ? loaderDepsPath : null
44
44
  };
45
45
  }
46
- function isPathlessLayout(route) {
47
- const extras = route;
48
- return 'nested' === extras.type && 'boolean' != typeof extras.index && void 0 === extras.path;
49
- }
50
- function isIndexRoute(route) {
51
- const extras = route;
52
- return 'nested' === extras.type && Boolean(extras.index);
53
- }
54
46
  function createRouteStaticDataSnippet(opts) {
55
47
  const staticData = createRouteStaticData({
56
48
  modernRouteId: opts.modernRouteId && quote(opts.modernRouteId),
@@ -61,4 +53,4 @@ function createRouteStaticDataSnippet(opts) {
61
53
  return entries.length > 0 ? `staticData: createRouteStaticData({\n ${entries.map(([key, value])=>`${key}: ${value},`).join('\n ')}\n }),` : null;
62
54
  }
63
55
  export { toTanstackPath } from "../../runtime/routeTree/paths.mjs";
64
- export { createRouteStaticDataSnippet, isIndexRoute, isPathlessLayout, normalizeRelativeImport, pickModernLoaderModule, pickRouteSearchContractModules, quote, resolveFileNoExt };
56
+ export { createRouteStaticDataSnippet, normalizeRelativeImport, pickModernLoaderModule, pickRouteSearchContractModules, quote, resolveFileNoExt };
@@ -1,18 +1,12 @@
1
1
  import "node:module";
2
- import { canonicaliseLocalisedRoutes } from "@modern-js/i18n-runtime-extensions";
3
2
  import { makeLegalIdentifier } from "@modern-js/runtime/cli";
4
3
  import { upath } from "@modern-js/utils";
5
- import { createRouteStaticDataSnippet, isIndexRoute, isPathlessLayout, normalizeRelativeImport, pickModernLoaderModule, pickRouteSearchContractModules, quote, resolveFileNoExt, toTanstackPath } from "./shared.mjs";
4
+ import { describeRouteTree } from "../../shared/routeDescriptor.mjs";
5
+ import { createRouteStaticDataSnippet, normalizeRelativeImport, pickModernLoaderModule, pickRouteSearchContractModules, quote, resolveFileNoExt } from "./shared.mjs";
6
6
  async function generateTanstackRouterTypesSourceForEntry(opts) {
7
- const { appContext, entryName, generatedDirName = 'modern-tanstack', routes: rawRoutes } = opts;
8
- const routes = canonicaliseLocalisedRoutes(rawRoutes);
7
+ const { appContext, entryName, generatedDirName = 'modern-tanstack', routes } = opts;
9
8
  const outDir = upath.join(appContext.srcDirectory, generatedDirName, entryName);
10
- const rootModern = routes.find((r)=>{
11
- const extras = r;
12
- return r && 'nested' === extras.type && extras.isRoot;
13
- });
14
- const rootExtras = rootModern;
15
- const topLevel = rootModern ? rootExtras?.children || [] : routes;
9
+ const { root: rootModern, children: topLevel } = describeRouteTree(routes);
16
10
  const imports = [];
17
11
  const statements = [];
18
12
  const loaderImportMap = new Map();
@@ -29,9 +23,8 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
29
23
  let pendingImportName = componentImportMap.get(componentPath);
30
24
  if (!pendingImportName) {
31
25
  pendingImportName = (async ()=>{
32
- const resolvedNoExt = await resolveRouteModuleNoExt(componentPath);
33
- if (!resolvedNoExt) return null;
34
- const relImport = normalizeRelativeImport(upath.relative(outDir, resolvedNoExt));
26
+ const relImport = await resolveRouteImport(componentPath);
27
+ if (!relImport) return null;
35
28
  const componentName = `component_${componentIndex++}`;
36
29
  imports.push(`import ${componentName} from ${quote(relImport)};`);
37
30
  return componentName;
@@ -40,14 +33,15 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
40
33
  }
41
34
  return pendingImportName;
42
35
  };
43
- const resolveRouteModuleNoExt = async (aliasedNoExtPath)=>{
36
+ const resolveRouteImport = async (aliasedNoExtPath)=>{
44
37
  const prefix = `${appContext.internalSrcAlias}/`;
45
38
  let absNoExt;
46
39
  if (aliasedNoExtPath.startsWith(prefix)) {
47
40
  const rel = aliasedNoExtPath.slice(prefix.length);
48
41
  absNoExt = upath.join(appContext.srcDirectory, rel);
49
42
  } else absNoExt = upath.isAbsolute(aliasedNoExtPath) ? aliasedNoExtPath : upath.join(appContext.srcDirectory, aliasedNoExtPath);
50
- return resolveFileNoExt(absNoExt);
43
+ const resolvedNoExt = await resolveFileNoExt(absNoExt);
44
+ return resolvedNoExt ? normalizeRelativeImport(upath.relative(outDir, resolvedNoExt)) : null;
51
45
  };
52
46
  const getImportNamesForLoader = async (aliasedNoExtPath, inline, hasAction)=>{
53
47
  const key = `${inline ? 'inline' : 'default'}:${hasAction ? 'action' : 'loader'}:${aliasedNoExtPath}`;
@@ -56,9 +50,8 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
56
50
  loaderName: existing,
57
51
  actionName: hasAction ? existing.replace(/^loader_/, 'action_') : null
58
52
  };
59
- const resolvedNoExt = await resolveRouteModuleNoExt(aliasedNoExtPath);
60
- if (!resolvedNoExt) return null;
61
- const relImport = normalizeRelativeImport(upath.relative(outDir, resolvedNoExt));
53
+ const relImport = await resolveRouteImport(aliasedNoExtPath);
54
+ if (!relImport) return null;
62
55
  const importName = `loader_${loaderIndex++}`;
63
56
  const actionName = hasAction ? importName.replace(/^loader_/, 'action_') : null;
64
57
  if (inline) {
@@ -78,9 +71,8 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
78
71
  const key = `${exportName}:${aliasedNoExtPath}`;
79
72
  const existing = searchContractImportMap.get(key);
80
73
  if (existing) return existing;
81
- const resolvedNoExt = await resolveRouteModuleNoExt(aliasedNoExtPath);
82
- if (!resolvedNoExt) return null;
83
- const relImport = normalizeRelativeImport(upath.relative(outDir, resolvedNoExt));
74
+ const relImport = await resolveRouteImport(aliasedNoExtPath);
75
+ if (!relImport) return null;
84
76
  const importName = 'validateSearch' === exportName ? `validateSearch_${validateSearchIndex++}` : `loaderDeps_${loaderDepsIndex++}`;
85
77
  imports.push(`import { ${exportName} as ${importName} } from ${quote(relImport)};`);
86
78
  searchContractImportMap.set(key, importName);
@@ -98,9 +90,7 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
98
90
  const base = id ? makeLegalIdentifier(id) : `r_${routeIndex++}`;
99
91
  return reserveRouteVarName(`route_${base}`);
100
92
  };
101
- const buildRoute = async (opts)=>{
102
- const { parentVar, route } = opts;
103
- const varName = createRouteVarName(route);
93
+ const createRouteOptions = async (route, hasSplat)=>{
104
94
  const routeExtras = route;
105
95
  const loaderInfo = pickModernLoaderModule(route);
106
96
  const routeAction = routeExtras.action;
@@ -110,20 +100,9 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
110
100
  const searchContractInfo = pickRouteSearchContractModules(route);
111
101
  const validateSearchName = searchContractInfo.validateSearchPath ? await getImportNameForSearchContract(searchContractInfo.validateSearchPath, 'validateSearch') : null;
112
102
  const loaderDepsName = searchContractInfo.loaderDepsPath ? await getImportNameForSearchContract(searchContractInfo.loaderDepsPath, 'loaderDeps') : null;
113
- const rawPath = routeExtras.path;
114
- const hasSplat = 'string' == typeof rawPath && rawPath.includes('*');
115
- const routeOpts = [
116
- `getParentRoute: () => ${parentVar},`
117
- ];
103
+ const routeOpts = [];
118
104
  const componentName = await getImportNameForComponent(routeExtras._component);
119
105
  if (componentName) routeOpts.push(`component: ${componentName},`);
120
- if (isPathlessLayout(route)) {
121
- const id = routeExtras.id;
122
- routeOpts.push(`id: ${quote(id || 'pathless')},`);
123
- } else {
124
- const p = isIndexRoute(route) ? '/' : toTanstackPath(rawPath || '');
125
- routeOpts.push(`path: ${quote(p)},`);
126
- }
127
106
  if (loaderName) routeOpts.push(`loader: modernLoaderToTanstack({ hasSplat: ${hasSplat} }, ${loaderName}),`);
128
107
  if (validateSearchName) routeOpts.push(`validateSearch: ${validateSearchName},`);
129
108
  if (loaderDepsName) routeOpts.push(`loaderDeps: ${loaderDepsName},`);
@@ -133,39 +112,29 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
133
112
  actionName
134
113
  });
135
114
  if (staticDataSnippet) routeOpts.push(staticDataSnippet);
136
- const children = routeExtras.children;
115
+ return routeOpts;
116
+ };
117
+ const buildRoute = async (descriptor, parentVar)=>{
118
+ const { source: route, location, hasSplat, children } = descriptor;
119
+ const varName = createRouteVarName(route);
120
+ const routeOpts = [
121
+ `getParentRoute: () => ${parentVar},`,
122
+ ...Object.entries(location).map(([key, value])=>`${key}: ${quote(value)},`),
123
+ ...await createRouteOptions(route, hasSplat)
124
+ ];
137
125
  const hasChildren = Boolean(children && children.length > 0);
138
126
  const routeCtorVarName = hasChildren ? reserveRouteVarName(`${varName}__base`) : varName;
139
127
  statements.push(`const ${routeCtorVarName} = createRoute({\n ${routeOpts.join('\n ')}\n});`);
140
128
  if (children && children.length > 0) {
141
129
  const childVars = [];
142
- for (const child of children)childVars.push(await buildRoute({
143
- parentVar: routeCtorVarName,
144
- route: child
145
- }));
130
+ for (const child of children)childVars.push(await buildRoute(child, routeCtorVarName));
146
131
  statements.push(`const ${varName} = ${routeCtorVarName}.addChildren([${childVars.join(', ')}]);`);
147
132
  }
148
133
  return varName;
149
134
  };
150
- const rootLoaderInfo = rootModern ? pickModernLoaderModule(rootModern) : null;
151
- const rootAction = rootExtras?.action;
152
- const rootLoaderImports = rootLoaderInfo?.loaderPath ? await getImportNamesForLoader(rootLoaderInfo.loaderPath, rootLoaderInfo.inline, Boolean(rootLoaderInfo.inline && rootAction === rootLoaderInfo.loaderPath)) : null;
153
- const rootLoaderName = rootLoaderImports?.loaderName || null;
154
- const rootActionName = rootLoaderImports?.actionName || null;
155
- const rootSearchContractInfo = rootModern ? pickRouteSearchContractModules(rootModern) : null;
156
- const rootValidateSearchName = rootSearchContractInfo?.validateSearchPath ? await getImportNameForSearchContract(rootSearchContractInfo.validateSearchPath, 'validateSearch') : null;
157
- const rootLoaderDepsName = rootSearchContractInfo?.loaderDepsPath ? await getImportNameForSearchContract(rootSearchContractInfo.loaderDepsPath, 'loaderDeps') : null;
158
135
  const topLevelVars = [];
159
- for (const route of topLevel)topLevelVars.push(await buildRoute({
160
- parentVar: 'rootRoute',
161
- route
162
- }));
163
- const rootOpts = [];
164
- const rootComponentName = await getImportNameForComponent(rootExtras?._component);
165
- if (rootComponentName) rootOpts.push(`component: ${rootComponentName},`);
166
- if (rootLoaderName) rootOpts.push(`loader: modernLoaderToTanstack({ hasSplat: false }, ${rootLoaderName}),`);
167
- if (rootValidateSearchName) rootOpts.push(`validateSearch: ${rootValidateSearchName},`);
168
- if (rootLoaderDepsName) rootOpts.push(`loaderDeps: ${rootLoaderDepsName},`);
136
+ for (const route of topLevel)topLevelVars.push(await buildRoute(route, 'rootRoute'));
137
+ const rootOpts = rootModern ? await createRouteOptions(rootModern, false) : [];
169
138
  const routerGenTs = `// This file is auto-generated by Modern.js. Do not edit manually.
170
139
 
171
140
  import {
@@ -182,11 +151,7 @@ ${imports.join('\n')}
182
151
 
183
152
  export const rootRoute = createRootRouteWithContext<ModernRouterContext>()({
184
153
  ${rootOpts.join('\n ')}
185
- ${createRouteStaticDataSnippet({
186
- modernRouteId: rootExtras?.id,
187
- loaderName: rootLoaderName,
188
- actionName: rootActionName
189
- }) || ''}
154
+
190
155
  });
191
156
 
192
157
  ${statements.join('\n\n')}
@@ -1,2 +1,2 @@
1
1
  import "node:module";
2
- export { applyRouterRuntimeState, applyRouterServerPrepareResult, createRouterRuntimeState, createRouterServerSnapshot, getRouterRuntimeState, getRouterServerSnapshot } from "@modern-js/runtime-extensions/router-state";
2
+ export { applyRouterRuntimeState, applyRouterServerPrepareResult, createRouterServerSnapshot, getRouterRuntimeState, getRouterServerSnapshot } from "@modern-js/runtime-extensions/router-state";
@@ -37,10 +37,13 @@ function mapSplatParamsForModernLoader(params, hasSplat) {
37
37
  return rest;
38
38
  }
39
39
  function getLoaderSignal(ctx) {
40
- const abortSignal = ctx?.abortController?.signal;
41
- if (abortSignal instanceof AbortSignal) return abortSignal;
42
- if (ctx?.signal instanceof AbortSignal) return ctx.signal;
43
- return new AbortController().signal;
40
+ const matchSignal = ctx?.abortController?.signal ?? ctx?.signal;
41
+ const requestSignal = ctx?.context?.request?.signal;
42
+ const signals = [
43
+ matchSignal,
44
+ requestSignal
45
+ ].filter((signal)=>signal instanceof AbortSignal);
46
+ return AbortSignal.any(signals);
44
47
  }
45
48
  function getLoaderHref(ctx) {
46
49
  if ('string' == typeof ctx?.location) return ctx.location;
@@ -102,4 +105,4 @@ function modernLoaderToTanstack(opts, modernLoader) {
102
105
  };
103
106
  }
104
107
  export { createRouteStaticData } from "../shared/routeStaticData.mjs";
105
- export { getLoaderHref, handleModernLoaderError, handleModernLoaderResult, isAbsoluteUrl, isRedirectResponse, isResponse, isTanstackRedirect, mapSplatParamsForModernLoader, modernLoaderToTanstack, throwTanstackRedirect };
108
+ export { getLoaderHref, getLoaderSignal, handleModernLoaderError, handleModernLoaderResult, isAbsoluteUrl, isRedirectResponse, isResponse, isTanstackRedirect, mapSplatParamsForModernLoader, modernLoaderToTanstack, throwTanstackRedirect };
@@ -0,0 +1,65 @@
1
+ import "node:module";
2
+ import { Link } from "./prefetchLink.mjs";
3
+ function createTanstackNavigation(router) {
4
+ let location;
5
+ let matches;
6
+ let snapshot;
7
+ return {
8
+ Link: Link,
9
+ getSnapshot () {
10
+ const state = router.state;
11
+ if (location !== state.location || void 0 === matches || matches.length !== state.matches.length || matches.some((match, index)=>match.params !== state.matches[index]?.params)) {
12
+ location = state.location;
13
+ matches = state.matches;
14
+ snapshot = {
15
+ location: {
16
+ pathname: location.pathname,
17
+ search: location.searchStr,
18
+ hash: '' !== location.hash ? `#${location.hash.replace(/^#/, '')}` : ''
19
+ },
20
+ params: Object.assign({}, ...matches.map((match)=>match.params))
21
+ };
22
+ }
23
+ return snapshot;
24
+ },
25
+ subscribe (listener) {
26
+ const stops = [
27
+ 'onBeforeNavigate',
28
+ 'onBeforeLoad',
29
+ 'onLoad',
30
+ 'onResolved'
31
+ ].map((event)=>router.subscribe(event, listener));
32
+ return ()=>{
33
+ for (const stop of stops)stop();
34
+ };
35
+ },
36
+ navigate (href, options) {
37
+ return router.navigate({
38
+ to: href,
39
+ replace: options?.replace,
40
+ ...options?.state === void 0 ? {} : {
41
+ state: options.state
42
+ }
43
+ });
44
+ },
45
+ createLinkProps (target) {
46
+ const preload = void 0 !== target.preload ? target.preload : 'none' === target.prefetch ? false : target.prefetch;
47
+ return {
48
+ to: target.pathname,
49
+ ...void 0 !== target.search ? {
50
+ search: target.search
51
+ } : {},
52
+ ...void 0 !== target.hash && '' !== target.hash ? {
53
+ hash: target.hash
54
+ } : {},
55
+ ...void 0 === target.hashScrollIntoView ? {} : {
56
+ hashScrollIntoView: target.hashScrollIntoView
57
+ },
58
+ ...void 0 === preload ? {} : {
59
+ preload
60
+ }
61
+ };
62
+ }
63
+ };
64
+ }
65
+ export { createTanstackNavigation };
@@ -1,6 +1,7 @@
1
1
  import "node:module";
2
2
  import { jsx } from "react/jsx-runtime";
3
3
  import { InternalRuntimeContext, getGlobalEnableRsc } from "@modern-js/runtime/context";
4
+ import { createRouterStatePlugin } from "@modern-js/runtime-extensions/router-state-plugin";
4
5
  import { normalizePathname } from "@modern-js/runtime-utils/url";
5
6
  import { RouterProvider, createBrowserHistory, createHashHistory, createRouter, useLocation, useNavigate, useRouter } from "@tanstack/react-router";
6
7
  import { useContext, useMemo } from "react";
@@ -11,6 +12,7 @@ import { ModernRouterClient, getTanstackSsrHydrationPromise, hasTanstackSsrHydra
11
12
  import { routerProviderRegistryHooks } from "./hooks.mjs";
12
13
  import { wrapTanstackSsrHydrationBoundary } from "./hydrationBoundary.mjs";
13
14
  import { applyRouterRuntimeState } from "./lifecycle.mjs";
15
+ import { createTanstackNavigation } from "./navigation.mjs";
14
16
  import { createTanstackRouteObjects, getMergedRouterConfig, getTanstackRouteConfig, joinBasename } from "./pluginShared.mjs";
15
17
  import { Link } from "./prefetchLink.mjs";
16
18
  import { useMatches } from "./routeHooks.mjs";
@@ -22,6 +24,9 @@ const tanstackRouterPlugin = (userConfig = {})=>{
22
24
  name: '@modern-js/plugin-router-tanstack',
23
25
  registryHooks: routerProviderRegistryHooks,
24
26
  setup: (api)=>{
27
+ createRouterStatePlugin({
28
+ registryHooks: routerProviderRegistryHooks
29
+ }).setup(api);
25
30
  const hooks = api.getHooks();
26
31
  let cachedRouteObjects;
27
32
  let cachedRouteTree = null;
@@ -143,10 +148,14 @@ const tanstackRouterPlugin = (userConfig = {})=>{
143
148
  runtimeContext
144
149
  ]);
145
150
  if (!router) return App ? /*#__PURE__*/ jsx(App, {}) : null;
151
+ const navigation = useMemo(()=>createTanstackNavigation(router), [
152
+ router
153
+ ]);
146
154
  const runtimeState = applyRouterRuntimeState(runtimeContext, {
147
155
  framework: 'tanstack',
148
156
  basename: _basename,
149
- instance: router
157
+ instance: router,
158
+ navigation
150
159
  });
151
160
  const lifecycleContext = {
152
161
  framework: 'tanstack',
@@ -1,6 +1,7 @@
1
1
  import "node:module";
2
2
  import { jsx } from "react/jsx-runtime";
3
3
  import { InternalRuntimeContext, getGlobalEnableRsc } from "@modern-js/runtime/context";
4
+ import { createRouterStatePlugin } from "@modern-js/runtime-extensions/router-state-plugin";
4
5
  import { createRequestContext } from "@modern-js/runtime-utils/node";
5
6
  import { time } from "@modern-js/runtime-utils/time";
6
7
  import { LOADER_REPORTER_NAME } from "@modern-js/utils/universal/constants";
@@ -9,13 +10,14 @@ import { useContext } from "react";
9
10
  import { createModernBasepathRewrite } from "./basepathRewrite.mjs";
10
11
  import { routerProviderRegistryHooks } from "./hooks.mjs";
11
12
  import { wrapTanstackSsrHydrationBoundary } from "./hydrationBoundary.mjs";
12
- import { applyRouterServerPrepareResult, createRouterServerSnapshot, getRouterRuntimeState } from "./lifecycle.mjs";
13
+ import { applyRouterRuntimeState, applyRouterServerPrepareResult, getRouterRuntimeState, getRouterServerSnapshot } from "./lifecycle.mjs";
14
+ import { createTanstackNavigation } from "./navigation.mjs";
13
15
  import { createTanstackRouteObjects, getTanstackRouteConfig, joinBasename } from "./pluginShared.mjs";
14
16
  import { Link } from "./prefetchLink.mjs";
15
17
  import { createRouteTreeFromRouteObjects, getModernRouteIdsFromMatches } from "./routeTree/index.mjs";
16
18
  import { createTanstackRscServerPayload, handleTanstackRscRedirect } from "./rsc/payloadRouter.mjs";
17
19
  import { getTanstackRscRouter, setTanstackRscRouter, setTanstackRscServerPayload } from "./rsc/payloadStorage.mjs";
18
- import { attachServerSsrUtils, collectRouterErrors, createGetSsrHref, routerManagedTagsToHtml, waitForRouterSerialization } from "./ssrManagedTags.mjs";
20
+ import { attachServerSsrUtils, collectRouterErrors, createGetSsrHref, routerManagedTagsToHtml } from "./ssrManagedTags.mjs";
19
21
  import { preloadMatchedRouteComponents } from "./ssrPreload.mjs";
20
22
  import { getModernTanstackRouterFastDefaults } from "./types.mjs";
21
23
  const tanstackRouterPlugin = (userConfig = {})=>{
@@ -23,6 +25,9 @@ const tanstackRouterPlugin = (userConfig = {})=>{
23
25
  name: '@modern-js/plugin-router-tanstack',
24
26
  registryHooks: routerProviderRegistryHooks,
25
27
  setup: (api)=>{
28
+ createRouterStatePlugin({
29
+ registryHooks: routerProviderRegistryHooks
30
+ }).setup(api);
26
31
  api.onBeforeRender(async (context, interrupt)=>{
27
32
  const routeConfig = getTanstackRouteConfig(api, userConfig);
28
33
  const { mergedConfig } = routeConfig;
@@ -88,76 +93,96 @@ const tanstackRouterPlugin = (userConfig = {})=>{
88
93
  });
89
94
  const serverRouter = tanstackRouter;
90
95
  if (enableRsc) setTanstackRscRouter(serverRouter);
91
- await attachServerSsrUtils(serverRouter);
92
- const end = time();
93
- try {
94
- await tanstackRouter.load({
95
- sync: true
96
- });
97
- } finally{
98
- const cost = end();
99
- context.ssrContext?.onTiming?.(LOADER_REPORTER_NAME, cost);
100
- }
101
- const serverLoadResult = serverRouter._serverResult;
102
- if (!serverLoadResult) {
103
- try {
104
- serverRouter.serverSsr?.cleanup?.();
105
- } catch {}
106
- throw new Error('TanStack Router completed an SSR load without a server result.');
107
- }
108
- if ('redirect' === serverLoadResult.type) {
109
- const { redirect } = serverLoadResult;
110
- try {
111
- serverRouter.serverSsr?.cleanup?.();
112
- } catch {}
113
- return interrupt(isRSCNavigation ? handleTanstackRscRedirect(redirect.headers, _basename, redirect.status) : redirect);
114
- }
115
- const routerErrors = collectRouterErrors(tanstackRouter);
116
- if (routerErrors && 'clientRender' === loaderFailureMode) {
117
- context.ssrContext?.response?.status(200);
118
- try {
119
- serverRouter.serverSsr?.cleanup?.();
120
- } catch {}
121
- throw Object.values(routerErrors)[0];
122
- }
123
- await preloadMatchedRouteComponents(serverRouter);
124
- context.ssrContext?.response?.status(serverLoadResult.status);
125
- await serverRouter.serverSsr?.dehydrate?.();
126
- if (enableRsc) {
127
- if (isRSCNavigation) await waitForRouterSerialization(serverRouter);
128
- setTanstackRscServerPayload(createTanstackRscServerPayload(serverRouter, {
129
- omitClientLoaderData: isRSCNavigation
130
- }));
131
- }
132
- const ssrScriptTags = serverRouter.serverSsr?.takeBufferedScripts?.();
133
- const hydrationScripts = routerManagedTagsToHtml(ssrScriptTags);
134
- const matchedRouteIds = getModernRouteIdsFromMatches(serverRouter);
135
- const routerServerSnapshot = createRouterServerSnapshot({
96
+ let disposed = false;
97
+ let prepared = false;
98
+ const onAbort = ()=>controller.abort(rawRequest.signal.reason);
99
+ const cleanup = ()=>{
100
+ if (disposed) return;
101
+ disposed = true;
102
+ rawRequest.signal.removeEventListener('abort', onAbort);
103
+ controller.abort();
104
+ serverRouter.serverSsr?.cleanup();
105
+ };
106
+ applyRouterRuntimeState(context, {
136
107
  framework: 'tanstack',
137
108
  basename: _basename,
138
- statusCode: serverLoadResult.status,
139
- errors: routerErrors,
140
- matchedRouteIds,
141
- hydrationScripts
109
+ instance: serverRouter,
110
+ cleanup
111
+ });
112
+ rawRequest.signal.addEventListener('abort', onAbort, {
113
+ once: true
142
114
  });
143
- const runtimeContext = applyRouterServerPrepareResult(context, {
144
- snapshot: routerServerSnapshot,
145
- cleanup: ()=>serverRouter.serverSsr?.cleanup?.(),
146
- state: {
115
+ try {
116
+ rawRequest.signal.throwIfAborted();
117
+ await attachServerSsrUtils(serverRouter);
118
+ rawRequest.signal.throwIfAborted();
119
+ const end = time();
120
+ try {
121
+ await tanstackRouter.load({
122
+ sync: true
123
+ });
124
+ } finally{
125
+ const cost = end();
126
+ context.ssrContext?.onTiming?.(LOADER_REPORTER_NAME, cost);
127
+ }
128
+ rawRequest.signal.throwIfAborted();
129
+ const serverLoadResult = serverRouter._serverResult;
130
+ if (!serverLoadResult) throw new Error('TanStack Router completed an SSR load without a server result.');
131
+ if ('redirect' === serverLoadResult.type) {
132
+ const { redirect } = serverLoadResult;
133
+ return interrupt(isRSCNavigation ? handleTanstackRscRedirect(redirect.headers, _basename, redirect.status) : redirect);
134
+ }
135
+ const routerErrors = collectRouterErrors(tanstackRouter);
136
+ if (routerErrors && 'clientRender' === loaderFailureMode) {
137
+ context.ssrContext?.response?.status(200);
138
+ throw Object.values(routerErrors)[0];
139
+ }
140
+ await preloadMatchedRouteComponents(serverRouter);
141
+ context.ssrContext?.response?.status(serverLoadResult.status);
142
+ await serverRouter.serverSsr?.dehydrate();
143
+ if (enableRsc) {
144
+ setTanstackRscServerPayload(createTanstackRscServerPayload(serverRouter, {
145
+ omitClientLoaderData: isRSCNavigation
146
+ }));
147
+ if (isRSCNavigation) context.ssrContext.response.setHeader('Content-Type', 'text/x-component');
148
+ }
149
+ const initialScriptTags = serverRouter.serverSsr?.takeInitialHydrationScriptTags();
150
+ const hydrationScripts = initialScriptTags ? routerManagedTagsToHtml([
151
+ ...initialScriptTags.before,
152
+ initialScriptTags.boundary
153
+ ]) : [];
154
+ const matchedRouteIds = getModernRouteIdsFromMatches(serverRouter);
155
+ const routerServerSnapshot = {
147
156
  framework: 'tanstack',
148
157
  basename: _basename,
149
- instance: serverRouter,
150
- hydrationScripts,
158
+ statusCode: serverLoadResult.status,
159
+ errors: routerErrors,
151
160
  matchedRouteIds,
152
- serverSnapshot: routerServerSnapshot
153
- }
154
- });
155
- hooks.onAfterCreateRouter.call({
156
- ...routerLifecycleContext,
157
- router: serverRouter,
158
- serverSnapshot: routerServerSnapshot,
159
- runtimeContext
160
- });
161
+ hydrationScripts
162
+ };
163
+ const runtimeContext = applyRouterServerPrepareResult(context, {
164
+ snapshot: routerServerSnapshot,
165
+ cleanup,
166
+ state: {
167
+ framework: 'tanstack',
168
+ basename: _basename,
169
+ instance: serverRouter,
170
+ navigation: createTanstackNavigation(tanstackRouter)
171
+ }
172
+ });
173
+ rawRequest.signal.throwIfAborted();
174
+ hooks.onAfterCreateRouter.call({
175
+ ...routerLifecycleContext,
176
+ router: serverRouter,
177
+ serverSnapshot: getRouterServerSnapshot(runtimeContext),
178
+ runtimeContext
179
+ });
180
+ prepared = true;
181
+ } finally{
182
+ if (!prepared) try {
183
+ cleanup();
184
+ } catch {}
185
+ }
161
186
  });
162
187
  api.wrapRoot((App)=>{
163
188
  const getRouteApp = ()=>{
@@ -1,24 +1,20 @@
1
1
  import "node:module";
2
- import { canonicaliseLocalisedRoutes } from "@modern-js/i18n-runtime-extensions";
3
2
  import { DefaultNotFound } from "@modern-js/runtime/context";
4
3
  import { rootRouteId } from "@tanstack/react-router";
4
+ import { describeRouteTree } from "../../shared/routeDescriptor.mjs";
5
5
  import { createRouteStaticData } from "../../shared/routeStaticData.mjs";
6
6
  import { withModernRouteMatchContext } from "../outlet.mjs";
7
7
  import { toErrorComponent, toPendingComponent, toRouteComponent } from "./components.mjs";
8
8
  import { createModernShouldReload, wrapRouteObjectLoader } from "./loaders.mjs";
9
- import { isRouteObjectPathlessLayout, toTanstackPath } from "./paths.mjs";
10
9
  import { mergeModernRouteHandle } from "./staticData.mjs";
11
10
  import { createTanstackRootRoute, createTanstackRoute, wrapRouteComponentWithModernContext } from "./tanstackRoutes.mjs";
12
- function createRouteFromRouteObject(opts) {
13
- const { options = {}, parent, pathlessFallbackId = 'pathless', routeObject } = opts;
11
+ function createRouteOptions(routeObject, options) {
14
12
  const modernRouteObject = routeObject;
15
13
  const revalidationState = {};
16
14
  const shouldRevalidate = modernRouteObject.shouldRevalidate;
17
15
  const shouldReload = createModernShouldReload(shouldRevalidate, revalidationState);
18
- const stableFallbackId = routeObject.id || modernRouteObject.file || pathlessFallbackId;
19
16
  const component = toRouteComponent(routeObject);
20
17
  const base = {
21
- getParentRoute: ()=>parent,
22
18
  component,
23
19
  pendingComponent: toPendingComponent(routeObject),
24
20
  errorComponent: toErrorComponent(routeObject),
@@ -39,67 +35,29 @@ function createRouteFromRouteObject(opts) {
39
35
  };
40
36
  if (modernRouteObject.inValidSSRRoute) base.ssr = false;
41
37
  if (shouldReload) base.shouldReload = shouldReload;
42
- if (isRouteObjectPathlessLayout(routeObject)) base.id = stableFallbackId;
43
- else base.path = routeObject.index ? '/' : toTanstackPath(routeObject.path || '');
44
- const route = createTanstackRoute(base);
45
- wrapRouteComponentWithModernContext(route, component, routeObject.id);
46
- const children = routeObject.children;
47
- if (children && children.length > 0) {
48
- const childRoutes = children.map((child, index)=>createRouteFromRouteObject({
49
- options,
50
- parent: route,
51
- pathlessFallbackId: `pathless-${index}`,
52
- routeObject: child
53
- }));
54
- route.addChildren(childRoutes);
55
- }
56
- return route;
38
+ return base;
57
39
  }
58
- function getRootLikeRouteObject(routes) {
59
- return routes.find((route)=>'/' === route.path && !route.index);
40
+ function createRouteFromDescriptor(descriptor, parent, options) {
41
+ const { source, location, children } = descriptor;
42
+ const routeOptions = createRouteOptions(source, options);
43
+ const route = createTanstackRoute({
44
+ ...routeOptions,
45
+ ...location,
46
+ getParentRoute: ()=>parent
47
+ });
48
+ wrapRouteComponentWithModernContext(route, routeOptions.component, source.id);
49
+ if (children.length) route.addChildren(children.map((child)=>createRouteFromDescriptor(child, route, options)));
50
+ return route;
60
51
  }
61
52
  function createRouteTreeFromRouteObjects(routes, options = {}) {
62
- routes = canonicaliseLocalisedRoutes(routes);
63
- const rootLikeRoute = getRootLikeRouteObject(routes);
64
- const rootRevalidationState = {};
65
- const rootShouldRevalidate = rootLikeRoute?.shouldRevalidate;
66
- const rootShouldReload = createModernShouldReload(rootShouldRevalidate, rootRevalidationState);
67
- const rootComponent = rootLikeRoute ? toRouteComponent(rootLikeRoute) : void 0;
53
+ const { root, children } = describeRouteTree(routes);
68
54
  const rootRouteOptions = {
69
- component: rootComponent,
70
- pendingComponent: rootLikeRoute ? toPendingComponent(rootLikeRoute) : void 0,
71
- errorComponent: rootLikeRoute ? toErrorComponent(rootLikeRoute) : void 0,
72
- validateSearch: rootLikeRoute?.validateSearch,
73
- loaderDeps: rootLikeRoute?.loaderDeps,
74
- notFoundComponent: DefaultNotFound,
75
- staticData: createRouteStaticData({
76
- modernRouteId: rootLikeRoute?.id,
77
- modernRouteAction: rootLikeRoute?.action,
78
- modernRouteHandle: rootLikeRoute ? mergeModernRouteHandle(rootLikeRoute) : void 0,
79
- modernRouteHasAction: rootLikeRoute?.hasAction || Boolean(rootLikeRoute?.action),
80
- modernRouteHasClientLoader: rootLikeRoute?.hasClientLoader || void 0 !== rootLikeRoute?.clientData,
81
- modernRouteHasLoader: rootLikeRoute?.hasLoader || 'function' == typeof rootLikeRoute?.loader,
82
- modernRouteIsClientComponent: rootLikeRoute?.isClientComponent,
83
- modernRouteLoader: rootLikeRoute?.loader,
84
- modernRouteShouldRevalidate: rootShouldRevalidate
85
- }),
86
- loader: rootLikeRoute ? wrapRouteObjectLoader(rootLikeRoute, rootRevalidationState, options) : void 0
55
+ ...root ? createRouteOptions(root, options) : {},
56
+ notFoundComponent: DefaultNotFound
87
57
  };
88
- if (rootShouldReload) rootRouteOptions.shouldReload = rootShouldReload;
89
- if (rootLikeRoute?.inValidSSRRoute) rootRouteOptions.ssr = false;
90
58
  const rootRoute = createTanstackRootRoute(rootRouteOptions);
91
- if (rootComponent) rootRoute.options.component = withModernRouteMatchContext(rootComponent, rootRouteId);
92
- const topLevel = rootLikeRoute ? [
93
- ...rootLikeRoute.children || [],
94
- ...routes.filter((route)=>route !== rootLikeRoute)
95
- ] : routes;
96
- const childRoutes = topLevel.map((routeObject, index)=>createRouteFromRouteObject({
97
- options,
98
- parent: rootRoute,
99
- pathlessFallbackId: `pathless-${index}`,
100
- routeObject
101
- }));
102
- rootRoute.addChildren(childRoutes);
59
+ if (rootRouteOptions.component) rootRoute.options.component = withModernRouteMatchContext(rootRouteOptions.component, rootRouteId);
60
+ rootRoute.addChildren(children.map((child)=>createRouteFromDescriptor(child, rootRoute, options)));
103
61
  return rootRoute;
104
62
  }
105
63
  function getModernRouteIdsFromMatches(router) {