@bleedingdev/modern-js-plugin-tanstack 3.9.0-ultramodern.2 → 3.9.0-ultramodern.21

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 (137) hide show
  1. package/dist/cjs/cli/artifacts.js +15 -1
  2. package/dist/cjs/cli/index.js +12 -0
  3. package/dist/cjs/cli/routeMetadata.js +131 -0
  4. package/dist/cjs/cli/tanstackTypes/canonicalRoutes.js +7 -7
  5. package/dist/cjs/cli/tanstackTypes/shared.js +0 -14
  6. package/dist/cjs/cli/tanstackTypes/source.js +27 -60
  7. package/dist/cjs/runtime/basepathRewrite.js +13 -6
  8. package/dist/cjs/runtime/lifecycle.js +6 -9
  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 +7 -2
  12. package/dist/cjs/runtime/plugin.node.js +86 -64
  13. package/dist/cjs/runtime/prefetchLink.js +2 -7
  14. package/dist/cjs/runtime/register.js +0 -2
  15. package/dist/cjs/runtime/routeTree/construction.js +19 -59
  16. package/dist/cjs/runtime/routeTree/loaders.js +1 -1
  17. package/dist/cjs/runtime/routeTree/paths.js +0 -6
  18. package/dist/cjs/runtime/router.js +25 -9
  19. package/dist/cjs/runtime/ssrManagedTags.js +2 -12
  20. package/dist/cjs/runtime/state.js +2 -2
  21. package/dist/cjs/runtime/utils.js +3 -0
  22. package/dist/cjs/shared/routeDescriptor.js +60 -0
  23. package/dist/esm/cli/artifacts.mjs +13 -2
  24. package/dist/esm/cli/index.mjs +12 -9
  25. package/dist/esm/cli/routeMetadata.mjs +71 -0
  26. package/dist/esm/cli/tanstackTypes/canonicalRoutes.mjs +7 -7
  27. package/dist/esm/cli/tanstackTypes/shared.mjs +1 -9
  28. package/dist/esm/cli/tanstackTypes/source.mjs +28 -61
  29. package/dist/esm/runtime/basepathRewrite.mjs +10 -3
  30. package/dist/esm/runtime/lifecycle.mjs +1 -1
  31. package/dist/esm/runtime/loaderBridge.mjs +8 -5
  32. package/dist/esm/runtime/navigation.mjs +64 -0
  33. package/dist/esm/runtime/plugin.mjs +7 -2
  34. package/dist/esm/runtime/plugin.node.mjs +88 -66
  35. package/dist/esm/runtime/prefetchLink.mjs +1 -6
  36. package/dist/esm/runtime/register.mjs +0 -2
  37. package/dist/esm/runtime/routeTree/construction.mjs +19 -59
  38. package/dist/esm/runtime/routeTree/loaders.mjs +2 -2
  39. package/dist/esm/runtime/routeTree/paths.mjs +1 -4
  40. package/dist/esm/runtime/router.mjs +27 -11
  41. package/dist/esm/runtime/ssrManagedTags.mjs +1 -8
  42. package/dist/esm/runtime/state.mjs +1 -1
  43. package/dist/esm/runtime/utils.mjs +3 -0
  44. package/dist/esm/shared/routeDescriptor.mjs +22 -0
  45. package/dist/esm-node/cli/artifacts.mjs +13 -2
  46. package/dist/esm-node/cli/index.mjs +12 -9
  47. package/dist/esm-node/cli/routeMetadata.mjs +72 -0
  48. package/dist/esm-node/cli/tanstackTypes/canonicalRoutes.mjs +7 -7
  49. package/dist/esm-node/cli/tanstackTypes/shared.mjs +1 -9
  50. package/dist/esm-node/cli/tanstackTypes/source.mjs +28 -61
  51. package/dist/esm-node/runtime/basepathRewrite.mjs +10 -3
  52. package/dist/esm-node/runtime/lifecycle.mjs +1 -1
  53. package/dist/esm-node/runtime/loaderBridge.mjs +8 -5
  54. package/dist/esm-node/runtime/navigation.mjs +65 -0
  55. package/dist/esm-node/runtime/plugin.mjs +7 -2
  56. package/dist/esm-node/runtime/plugin.node.mjs +88 -66
  57. package/dist/esm-node/runtime/prefetchLink.mjs +1 -6
  58. package/dist/esm-node/runtime/register.mjs +0 -2
  59. package/dist/esm-node/runtime/routeTree/construction.mjs +19 -59
  60. package/dist/esm-node/runtime/routeTree/loaders.mjs +2 -2
  61. package/dist/esm-node/runtime/routeTree/paths.mjs +1 -4
  62. package/dist/esm-node/runtime/router.mjs +27 -11
  63. package/dist/esm-node/runtime/ssrManagedTags.mjs +1 -8
  64. package/dist/esm-node/runtime/state.mjs +1 -1
  65. package/dist/esm-node/runtime/utils.mjs +3 -0
  66. package/dist/esm-node/shared/routeDescriptor.mjs +23 -0
  67. package/dist/types/cli/artifacts.d.ts +13 -5
  68. package/dist/types/cli/index.d.ts +1 -1
  69. package/dist/types/cli/routeMetadata.d.ts +18 -0
  70. package/dist/types/cli/tanstackTypes/shared.d.ts +0 -2
  71. package/dist/types/runtime/basepathRewrite.d.ts +2 -1
  72. package/dist/types/runtime/lifecycle.d.ts +9 -2
  73. package/dist/types/runtime/loaderBridge.d.ts +1 -0
  74. package/dist/types/runtime/navigation.d.ts +4 -0
  75. package/dist/types/runtime/register.d.ts +1 -9
  76. package/dist/types/runtime/routeTree/paths.d.ts +0 -2
  77. package/dist/types/runtime/router.d.ts +2 -13
  78. package/dist/types/runtime/ssrManagedTags.d.ts +0 -1
  79. package/dist/types/runtime/ssrTypes.d.ts +0 -7
  80. package/dist/types/runtime/state.d.ts +1 -1
  81. package/dist/types/runtime/types.d.ts +2 -1
  82. package/dist/types/shared/routeDescriptor.d.ts +26 -0
  83. package/package.json +22 -19
  84. package/rstest.config.mts +2 -24
  85. package/src/cli/artifacts.ts +53 -6
  86. package/src/cli/index.ts +11 -2
  87. package/src/cli/routeMetadata.ts +121 -0
  88. package/src/cli/tanstackTypes/canonicalRoutes.ts +17 -12
  89. package/src/cli/tanstackTypes/shared.ts +0 -14
  90. package/src/cli/tanstackTypes/source.ts +42 -116
  91. package/src/runtime/basepathRewrite.ts +11 -2
  92. package/src/runtime/lifecycle.ts +14 -4
  93. package/src/runtime/loaderBridge.ts +9 -9
  94. package/src/runtime/navigation.ts +78 -0
  95. package/src/runtime/plugin.node.tsx +120 -93
  96. package/src/runtime/plugin.tsx +11 -1
  97. package/src/runtime/prefetchLink.tsx +1 -23
  98. package/src/runtime/register.ts +1 -12
  99. package/src/runtime/routeTree/construction.ts +32 -110
  100. package/src/runtime/routeTree/loaders.ts +2 -4
  101. package/src/runtime/routeTree/paths.ts +0 -6
  102. package/src/runtime/router.ts +25 -40
  103. package/src/runtime/ssrManagedTags.ts +0 -17
  104. package/src/runtime/ssrTypes.ts +0 -7
  105. package/src/runtime/state.ts +1 -1
  106. package/src/runtime/types.ts +2 -2
  107. package/src/runtime/utils.tsx +4 -1
  108. package/src/shared/routeDescriptor.ts +46 -0
  109. package/tests/router/cli.test.ts +148 -824
  110. package/tests/router/clientNavigation.ts +13 -0
  111. package/tests/router/dataMutation.test.tsx +17 -85
  112. package/tests/router/flightSerialization.roundtrip.test.tsx +9 -6
  113. package/tests/router/generateRouteArtifacts.test.ts +139 -27
  114. package/tests/router/loaderBridge.test.ts +0 -25
  115. package/tests/router/localisedIdentity.test.tsx +100 -0
  116. package/tests/router/prefetchLink.test.tsx +46 -21
  117. package/tests/router/prefetchLinkPreload.test.tsx +11 -32
  118. package/tests/router/routeHooks.test.ts +42 -105
  119. package/tests/router/routeMetadata.test.ts +233 -0
  120. package/tests/router/routeTree.test.ts +144 -836
  121. package/tests/router/rsc.test.tsx +131 -166
  122. package/tests/router/serverPlugin.test.ts +193 -1
  123. package/tests/router/tanstackTypes.test.ts +218 -963
  124. package/dist/esm/rslib-runtime~1.mjs +0 -18
  125. package/dist/esm-node/rslib-runtime~0.mjs +0 -19
  126. package/tests/realm-isolation.test.ts +0 -114
  127. package/tests/router/fastDefaults.test.ts +0 -32
  128. package/tests/router/fixtures/legacyRuntimeRouter.ts +0 -10
  129. package/tests/router/fixtures/rscDecoderSecurityChild.cjs +0 -162
  130. package/tests/router/hooks.test.ts +0 -40
  131. package/tests/router/hydrationBoundary.test.tsx +0 -23
  132. package/tests/router/packageSurface.test.ts +0 -24
  133. package/tests/router/preloadRedirect.test.ts +0 -64
  134. package/tests/router/register.test.ts +0 -8
  135. package/tests/router/routerCompatibility.test.ts +0 -239
  136. package/tests/router/rscDecoderSecurity.test.ts +0 -113
  137. package/tests/router/rscPayloadRouterMatrix.test.ts +0 -197
@@ -1,4 +1,5 @@
1
1
  import "node:module";
2
+ import { describeRouteTree } from "../../shared/routeDescriptor.mjs";
2
3
  import { toTanstackPath } from "./shared.mjs";
3
4
  const LOCALE_PARAM_SEGMENTS = new Set([
4
5
  ':lang',
@@ -45,11 +46,13 @@ function collectCanonicalRoutesForEntry(routes, options = {}) {
45
46
  const key = toTanstackPath(normalized);
46
47
  if (!canonicalParams.has(key)) canonicalParams.set(key, paramsTypeForCanonicalPath(normalized));
47
48
  };
48
- const visit = (route, parentPath)=>{
49
+ const visit = (descriptor, parentPath)=>{
50
+ const { source: route, children } = descriptor;
49
51
  let currentPath = parentPath;
50
- if ('string' == typeof route.modernCanonicalPath) {
52
+ const canonicalPath = route.modernLocalisedRoute?.canonicalPath ?? route.modernCanonicalPath;
53
+ if ('string' == typeof canonicalPath) {
51
54
  hasI18nSurface = true;
52
- currentPath = normalizeJoined(route.modernCanonicalPath);
55
+ currentPath = canonicalPath ? normalizeJoined(canonicalPath) : '';
53
56
  } else if ('string' == typeof route.path && route.path.length > 0) {
54
57
  const segments = route.path.replace(/\[(.+?)\]/g, ':$1').split('/').filter(Boolean);
55
58
  if (localeParamHeuristic && '' === parentPath && LOCALE_PARAM_SEGMENTS.has(segments[0])) {
@@ -58,16 +61,13 @@ function collectCanonicalRoutesForEntry(routes, options = {}) {
58
61
  }
59
62
  currentPath = segments.length ? normalizeJoined(`${parentPath}/${segments.join('/')}`) : parentPath;
60
63
  }
61
- const children = route.children;
62
64
  if (children && children.length > 0) {
63
65
  for (const child of children)visit(child, currentPath);
64
66
  return;
65
67
  }
66
68
  record(currentPath || '/');
67
69
  };
68
- const rootModern = routes.find((route)=>route.isRoot);
69
- const topLevel = rootModern ? rootModern.children ?? [] : routes;
70
- for (const route of topLevel)visit(route, '');
70
+ for (const route of describeRouteTree(routes).children)visit(route, '');
71
71
  if (!hasI18nSurface || 0 === canonicalParams.size) return null;
72
72
  return Object.fromEntries([
73
73
  ...canonicalParams.entries()
@@ -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,16 +1,12 @@
1
1
  import "node:module";
2
2
  import { makeLegalIdentifier } from "@modern-js/runtime/cli";
3
3
  import { upath } from "@modern-js/utils";
4
- 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";
5
6
  async function generateTanstackRouterTypesSourceForEntry(opts) {
6
7
  const { appContext, entryName, generatedDirName = 'modern-tanstack', routes } = opts;
7
8
  const outDir = upath.join(appContext.srcDirectory, generatedDirName, entryName);
8
- const rootModern = routes.find((r)=>{
9
- const extras = r;
10
- return r && 'nested' === extras.type && extras.isRoot;
11
- });
12
- const rootExtras = rootModern;
13
- const topLevel = rootModern ? rootExtras?.children || [] : routes;
9
+ const { root: rootModern, children: topLevel } = describeRouteTree(routes);
14
10
  const imports = [];
15
11
  const statements = [];
16
12
  const loaderImportMap = new Map();
@@ -27,9 +23,8 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
27
23
  let pendingImportName = componentImportMap.get(componentPath);
28
24
  if (!pendingImportName) {
29
25
  pendingImportName = (async ()=>{
30
- const resolvedNoExt = await resolveRouteModuleNoExt(componentPath);
31
- if (!resolvedNoExt) return null;
32
- const relImport = normalizeRelativeImport(upath.relative(outDir, resolvedNoExt));
26
+ const relImport = await resolveRouteImport(componentPath);
27
+ if (!relImport) return null;
33
28
  const componentName = `component_${componentIndex++}`;
34
29
  imports.push(`import ${componentName} from ${quote(relImport)};`);
35
30
  return componentName;
@@ -38,14 +33,15 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
38
33
  }
39
34
  return pendingImportName;
40
35
  };
41
- const resolveRouteModuleNoExt = async (aliasedNoExtPath)=>{
36
+ const resolveRouteImport = async (aliasedNoExtPath)=>{
42
37
  const prefix = `${appContext.internalSrcAlias}/`;
43
38
  let absNoExt;
44
39
  if (aliasedNoExtPath.startsWith(prefix)) {
45
40
  const rel = aliasedNoExtPath.slice(prefix.length);
46
41
  absNoExt = upath.join(appContext.srcDirectory, rel);
47
42
  } else absNoExt = upath.isAbsolute(aliasedNoExtPath) ? aliasedNoExtPath : upath.join(appContext.srcDirectory, aliasedNoExtPath);
48
- return resolveFileNoExt(absNoExt);
43
+ const resolvedNoExt = await resolveFileNoExt(absNoExt);
44
+ return resolvedNoExt ? normalizeRelativeImport(upath.relative(outDir, resolvedNoExt)) : null;
49
45
  };
50
46
  const getImportNamesForLoader = async (aliasedNoExtPath, inline, hasAction)=>{
51
47
  const key = `${inline ? 'inline' : 'default'}:${hasAction ? 'action' : 'loader'}:${aliasedNoExtPath}`;
@@ -54,9 +50,8 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
54
50
  loaderName: existing,
55
51
  actionName: hasAction ? existing.replace(/^loader_/, 'action_') : null
56
52
  };
57
- const resolvedNoExt = await resolveRouteModuleNoExt(aliasedNoExtPath);
58
- if (!resolvedNoExt) return null;
59
- const relImport = normalizeRelativeImport(upath.relative(outDir, resolvedNoExt));
53
+ const relImport = await resolveRouteImport(aliasedNoExtPath);
54
+ if (!relImport) return null;
60
55
  const importName = `loader_${loaderIndex++}`;
61
56
  const actionName = hasAction ? importName.replace(/^loader_/, 'action_') : null;
62
57
  if (inline) {
@@ -76,9 +71,8 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
76
71
  const key = `${exportName}:${aliasedNoExtPath}`;
77
72
  const existing = searchContractImportMap.get(key);
78
73
  if (existing) return existing;
79
- const resolvedNoExt = await resolveRouteModuleNoExt(aliasedNoExtPath);
80
- if (!resolvedNoExt) return null;
81
- const relImport = normalizeRelativeImport(upath.relative(outDir, resolvedNoExt));
74
+ const relImport = await resolveRouteImport(aliasedNoExtPath);
75
+ if (!relImport) return null;
82
76
  const importName = 'validateSearch' === exportName ? `validateSearch_${validateSearchIndex++}` : `loaderDeps_${loaderDepsIndex++}`;
83
77
  imports.push(`import { ${exportName} as ${importName} } from ${quote(relImport)};`);
84
78
  searchContractImportMap.set(key, importName);
@@ -96,9 +90,7 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
96
90
  const base = id ? makeLegalIdentifier(id) : `r_${routeIndex++}`;
97
91
  return reserveRouteVarName(`route_${base}`);
98
92
  };
99
- const buildRoute = async (opts)=>{
100
- const { parentVar, route } = opts;
101
- const varName = createRouteVarName(route);
93
+ const createRouteOptions = async (route, hasSplat)=>{
102
94
  const routeExtras = route;
103
95
  const loaderInfo = pickModernLoaderModule(route);
104
96
  const routeAction = routeExtras.action;
@@ -108,20 +100,9 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
108
100
  const searchContractInfo = pickRouteSearchContractModules(route);
109
101
  const validateSearchName = searchContractInfo.validateSearchPath ? await getImportNameForSearchContract(searchContractInfo.validateSearchPath, 'validateSearch') : null;
110
102
  const loaderDepsName = searchContractInfo.loaderDepsPath ? await getImportNameForSearchContract(searchContractInfo.loaderDepsPath, 'loaderDeps') : null;
111
- const rawPath = routeExtras.path;
112
- const hasSplat = 'string' == typeof rawPath && rawPath.includes('*');
113
- const routeOpts = [
114
- `getParentRoute: () => ${parentVar},`
115
- ];
103
+ const routeOpts = [];
116
104
  const componentName = await getImportNameForComponent(routeExtras._component);
117
105
  if (componentName) routeOpts.push(`component: ${componentName},`);
118
- if (isPathlessLayout(route)) {
119
- const id = routeExtras.id;
120
- routeOpts.push(`id: ${quote(id || 'pathless')},`);
121
- } else {
122
- const p = isIndexRoute(route) ? '/' : toTanstackPath(rawPath || '');
123
- routeOpts.push(`path: ${quote(p)},`);
124
- }
125
106
  if (loaderName) routeOpts.push(`loader: modernLoaderToTanstack({ hasSplat: ${hasSplat} }, ${loaderName}),`);
126
107
  if (validateSearchName) routeOpts.push(`validateSearch: ${validateSearchName},`);
127
108
  if (loaderDepsName) routeOpts.push(`loaderDeps: ${loaderDepsName},`);
@@ -131,39 +112,29 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
131
112
  actionName
132
113
  });
133
114
  if (staticDataSnippet) routeOpts.push(staticDataSnippet);
134
- 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
+ ];
135
125
  const hasChildren = Boolean(children && children.length > 0);
136
126
  const routeCtorVarName = hasChildren ? reserveRouteVarName(`${varName}__base`) : varName;
137
127
  statements.push(`const ${routeCtorVarName} = createRoute({\n ${routeOpts.join('\n ')}\n});`);
138
128
  if (children && children.length > 0) {
139
129
  const childVars = [];
140
- for (const child of children)childVars.push(await buildRoute({
141
- parentVar: routeCtorVarName,
142
- route: child
143
- }));
130
+ for (const child of children)childVars.push(await buildRoute(child, routeCtorVarName));
144
131
  statements.push(`const ${varName} = ${routeCtorVarName}.addChildren([${childVars.join(', ')}]);`);
145
132
  }
146
133
  return varName;
147
134
  };
148
- const rootLoaderInfo = rootModern ? pickModernLoaderModule(rootModern) : null;
149
- const rootAction = rootExtras?.action;
150
- const rootLoaderImports = rootLoaderInfo?.loaderPath ? await getImportNamesForLoader(rootLoaderInfo.loaderPath, rootLoaderInfo.inline, Boolean(rootLoaderInfo.inline && rootAction === rootLoaderInfo.loaderPath)) : null;
151
- const rootLoaderName = rootLoaderImports?.loaderName || null;
152
- const rootActionName = rootLoaderImports?.actionName || null;
153
- const rootSearchContractInfo = rootModern ? pickRouteSearchContractModules(rootModern) : null;
154
- const rootValidateSearchName = rootSearchContractInfo?.validateSearchPath ? await getImportNameForSearchContract(rootSearchContractInfo.validateSearchPath, 'validateSearch') : null;
155
- const rootLoaderDepsName = rootSearchContractInfo?.loaderDepsPath ? await getImportNameForSearchContract(rootSearchContractInfo.loaderDepsPath, 'loaderDeps') : null;
156
135
  const topLevelVars = [];
157
- for (const route of topLevel)topLevelVars.push(await buildRoute({
158
- parentVar: 'rootRoute',
159
- route
160
- }));
161
- const rootOpts = [];
162
- const rootComponentName = await getImportNameForComponent(rootExtras?._component);
163
- if (rootComponentName) rootOpts.push(`component: ${rootComponentName},`);
164
- if (rootLoaderName) rootOpts.push(`loader: modernLoaderToTanstack({ hasSplat: false }, ${rootLoaderName}),`);
165
- if (rootValidateSearchName) rootOpts.push(`validateSearch: ${rootValidateSearchName},`);
166
- 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) : [];
167
138
  const routerGenTs = `// This file is auto-generated by Modern.js. Do not edit manually.
168
139
 
169
140
  import {
@@ -180,11 +151,7 @@ ${imports.join('\n')}
180
151
 
181
152
  export const rootRoute = createRootRouteWithContext<ModernRouterContext>()({
182
153
  ${rootOpts.join('\n ')}
183
- ${createRouteStaticDataSnippet({
184
- modernRouteId: rootExtras?.id,
185
- loaderName: rootLoaderName,
186
- actionName: rootActionName
187
- }) || ''}
154
+
188
155
  });
189
156
 
190
157
  ${statements.join('\n\n')}
@@ -1,17 +1,20 @@
1
1
  import "node:module";
2
+ import { createLocalisedRouteRewrite } from "@modern-js/i18n-runtime-extensions";
3
+ import { composeRewrites } from "@tanstack/react-router";
2
4
  function normalizeBasepath(basepath) {
3
5
  if (!basepath) return '/';
4
6
  let normalized = basepath.startsWith('/') ? basepath : `/${basepath}`;
5
7
  if (normalized.length > 1 && normalized.endsWith('/')) normalized = normalized.slice(0, -1);
6
8
  return normalized || '/';
7
9
  }
8
- function createModernBasepathRewrite(basepath, caseSensitive = false) {
10
+ function createModernBasepathRewrite(basepath, caseSensitive = false, routes = []) {
11
+ const localisedRewrite = createLocalisedRouteRewrite(routes);
9
12
  const normalizedBasepath = normalizeBasepath(basepath);
10
- if ('/' === normalizedBasepath) return;
13
+ if ('/' === normalizedBasepath) return localisedRewrite;
11
14
  const normalizedBasepathWithSlash = `${normalizedBasepath}/`;
12
15
  const checkBasepath = caseSensitive ? normalizedBasepath : normalizedBasepath.toLowerCase();
13
16
  const checkBasepathWithSlash = caseSensitive ? normalizedBasepathWithSlash : normalizedBasepathWithSlash.toLowerCase();
14
- return {
17
+ const basepathRewrite = {
15
18
  input: ({ url })=>{
16
19
  const pathname = caseSensitive ? url.pathname : url.pathname.toLowerCase();
17
20
  if (pathname === checkBasepath) url.pathname = '/';
@@ -25,5 +28,9 @@ function createModernBasepathRewrite(basepath, caseSensitive = false) {
25
28
  return url;
26
29
  }
27
30
  };
31
+ return localisedRewrite ? composeRewrites([
32
+ basepathRewrite,
33
+ localisedRewrite
34
+ ]) : basepathRewrite;
28
35
  }
29
36
  export { createModernBasepathRewrite };
@@ -1,2 +1,2 @@
1
1
  import "node:module";
2
- export { applyRouterRuntimeState, applyRouterServerPrepareResult, createRouterRuntimeState, createRouterServerSnapshot, getRouterRuntimeState, getRouterServerSnapshot } from "@modern-js/runtime/context";
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 };
@@ -11,6 +11,7 @@ import { ModernRouterClient, getTanstackSsrHydrationPromise, hasTanstackSsrHydra
11
11
  import { routerProviderRegistryHooks } from "./hooks.mjs";
12
12
  import { wrapTanstackSsrHydrationBoundary } from "./hydrationBoundary.mjs";
13
13
  import { applyRouterRuntimeState } from "./lifecycle.mjs";
14
+ import { createTanstackNavigation } from "./navigation.mjs";
14
15
  import { createTanstackRouteObjects, getMergedRouterConfig, getTanstackRouteConfig, joinBasename } from "./pluginShared.mjs";
15
16
  import { Link } from "./prefetchLink.mjs";
16
17
  import { useMatches } from "./routeHooks.mjs";
@@ -78,7 +79,7 @@ const tanstackRouterPlugin = (userConfig = {})=>{
78
79
  const mergedConfig = getMergedConfig();
79
80
  const { supportHtml5History = true } = mergedConfig;
80
81
  const history = supportHtml5History ? createBrowserHistory() : createHashHistory();
81
- const rewrite = createModernBasepathRewrite(_basename);
82
+ const rewrite = createModernBasepathRewrite(_basename, false, getRouteObjects());
82
83
  const serializationAdapters = getGlobalEnableRsc() ? getTanstackRscSerializationAdapters() : void 0;
83
84
  const routerFastDefaults = getModernTanstackRouterFastDefaults(mergedConfig);
84
85
  cachedRouter = createRouter({
@@ -143,10 +144,14 @@ const tanstackRouterPlugin = (userConfig = {})=>{
143
144
  runtimeContext
144
145
  ]);
145
146
  if (!router) return App ? /*#__PURE__*/ jsx(App, {}) : null;
147
+ const navigation = useMemo(()=>createTanstackNavigation(router), [
148
+ router
149
+ ]);
146
150
  const runtimeState = applyRouterRuntimeState(runtimeContext, {
147
151
  framework: 'tanstack',
148
152
  basename: _basename,
149
- instance: router
153
+ instance: router,
154
+ navigation
150
155
  });
151
156
  const lifecycleContext = {
152
157
  framework: 'tanstack',
@@ -9,12 +9,14 @@ import { useContext } from "react";
9
9
  import { createModernBasepathRewrite } from "./basepathRewrite.mjs";
10
10
  import { routerProviderRegistryHooks } from "./hooks.mjs";
11
11
  import { wrapTanstackSsrHydrationBoundary } from "./hydrationBoundary.mjs";
12
- import { applyRouterServerPrepareResult, createRouterServerSnapshot, getRouterRuntimeState } from "./lifecycle.mjs";
12
+ import { applyRouterRuntimeState, applyRouterServerPrepareResult, getRouterRuntimeState, getRouterServerSnapshot } from "./lifecycle.mjs";
13
+ import { createTanstackNavigation } from "./navigation.mjs";
13
14
  import { createTanstackRouteObjects, getTanstackRouteConfig, joinBasename } from "./pluginShared.mjs";
15
+ import { Link } from "./prefetchLink.mjs";
14
16
  import { createRouteTreeFromRouteObjects, getModernRouteIdsFromMatches } from "./routeTree/index.mjs";
15
17
  import { createTanstackRscServerPayload, handleTanstackRscRedirect } from "./rsc/payloadRouter.mjs";
16
18
  import { getTanstackRscRouter, setTanstackRscRouter, setTanstackRscServerPayload } from "./rsc/payloadStorage.mjs";
17
- import { attachServerSsrUtils, collectRouterErrors, createGetSsrHref, routerManagedTagsToHtml, waitForRouterSerialization } from "./ssrManagedTags.mjs";
19
+ import { attachServerSsrUtils, collectRouterErrors, createGetSsrHref, routerManagedTagsToHtml } from "./ssrManagedTags.mjs";
18
20
  import { preloadMatchedRouteComponents } from "./ssrPreload.mjs";
19
21
  import { getModernTanstackRouterFastDefaults } from "./types.mjs";
20
22
  const tanstackRouterPlugin = (userConfig = {})=>{
@@ -36,6 +38,9 @@ const tanstackRouterPlugin = (userConfig = {})=>{
36
38
  routeConfig
37
39
  });
38
40
  if (!modifiedRouteObjects.length) return;
41
+ context.router = {
42
+ Link: Link
43
+ };
39
44
  const { request, nonce, baseUrl, loaderFailureMode = 'errorBoundary' } = context.ssrContext;
40
45
  const _basename = '/' === baseUrl ? joinBasename(baseUrl, basename || '') : baseUrl;
41
46
  const rawRequest = request.raw;
@@ -58,7 +63,7 @@ const tanstackRouterPlugin = (userConfig = {})=>{
58
63
  initialHref
59
64
  ]
60
65
  });
61
- const rewrite = createModernBasepathRewrite(_basename);
66
+ const rewrite = createModernBasepathRewrite(_basename, false, modifiedRouteObjects);
62
67
  const routerLifecycleContext = {
63
68
  framework: 'tanstack',
64
69
  phase: 'ssr-prepare',
@@ -84,76 +89,93 @@ const tanstackRouterPlugin = (userConfig = {})=>{
84
89
  });
85
90
  const serverRouter = tanstackRouter;
86
91
  if (enableRsc) setTanstackRscRouter(serverRouter);
87
- await attachServerSsrUtils(serverRouter);
88
- const end = time();
92
+ let disposed = false;
93
+ let prepared = false;
94
+ const onAbort = ()=>controller.abort(rawRequest.signal.reason);
95
+ const cleanup = ()=>{
96
+ if (disposed) return;
97
+ disposed = true;
98
+ rawRequest.signal.removeEventListener('abort', onAbort);
99
+ controller.abort();
100
+ serverRouter.serverSsr?.cleanup();
101
+ };
102
+ applyRouterRuntimeState(context, {
103
+ framework: 'tanstack',
104
+ basename: _basename,
105
+ instance: serverRouter,
106
+ cleanup
107
+ });
108
+ rawRequest.signal.addEventListener('abort', onAbort, {
109
+ once: true
110
+ });
89
111
  try {
90
- await tanstackRouter.load({
91
- sync: true
92
- });
93
- } finally{
94
- const cost = end();
95
- context.ssrContext?.onTiming?.(LOADER_REPORTER_NAME, cost);
96
- }
97
- const serverLoadResult = serverRouter._serverResult;
98
- if (!serverLoadResult) {
112
+ rawRequest.signal.throwIfAborted();
113
+ await attachServerSsrUtils(serverRouter);
114
+ rawRequest.signal.throwIfAborted();
115
+ const end = time();
99
116
  try {
100
- serverRouter.serverSsr?.cleanup?.();
101
- } catch {}
102
- throw new Error('TanStack Router completed an SSR load without a server result.');
103
- }
104
- if ('redirect' === serverLoadResult.type) {
105
- const { redirect } = serverLoadResult;
106
- try {
107
- serverRouter.serverSsr?.cleanup?.();
108
- } catch {}
109
- return interrupt(isRSCNavigation ? handleTanstackRscRedirect(redirect.headers, _basename, redirect.status) : redirect);
110
- }
111
- const routerErrors = collectRouterErrors(tanstackRouter);
112
- if (routerErrors && 'clientRender' === loaderFailureMode) {
113
- context.ssrContext?.response?.status(200);
114
- try {
115
- serverRouter.serverSsr?.cleanup?.();
116
- } catch {}
117
- throw Object.values(routerErrors)[0];
118
- }
119
- await preloadMatchedRouteComponents(serverRouter);
120
- context.ssrContext?.response?.status(serverLoadResult.status);
121
- await serverRouter.serverSsr?.dehydrate?.();
122
- if (enableRsc) {
123
- if (isRSCNavigation) await waitForRouterSerialization(serverRouter);
124
- setTanstackRscServerPayload(createTanstackRscServerPayload(serverRouter, {
117
+ await tanstackRouter.load({
118
+ sync: true
119
+ });
120
+ } finally{
121
+ const cost = end();
122
+ context.ssrContext?.onTiming?.(LOADER_REPORTER_NAME, cost);
123
+ }
124
+ rawRequest.signal.throwIfAborted();
125
+ const serverLoadResult = serverRouter._serverResult;
126
+ if (!serverLoadResult) throw new Error('TanStack Router completed an SSR load without a server result.');
127
+ if ('redirect' === serverLoadResult.type) {
128
+ const { redirect } = serverLoadResult;
129
+ return interrupt(isRSCNavigation ? handleTanstackRscRedirect(redirect.headers, _basename, redirect.status) : redirect);
130
+ }
131
+ const routerErrors = collectRouterErrors(tanstackRouter);
132
+ if (routerErrors && 'clientRender' === loaderFailureMode) {
133
+ context.ssrContext?.response?.status(200);
134
+ throw Object.values(routerErrors)[0];
135
+ }
136
+ await preloadMatchedRouteComponents(serverRouter);
137
+ context.ssrContext?.response?.status(serverLoadResult.status);
138
+ await serverRouter.serverSsr?.dehydrate();
139
+ if (enableRsc) setTanstackRscServerPayload(createTanstackRscServerPayload(serverRouter, {
125
140
  omitClientLoaderData: isRSCNavigation
126
141
  }));
127
- }
128
- const ssrScriptTags = serverRouter.serverSsr?.takeBufferedScripts?.();
129
- const hydrationScripts = routerManagedTagsToHtml(ssrScriptTags);
130
- const matchedRouteIds = getModernRouteIdsFromMatches(serverRouter);
131
- const routerServerSnapshot = createRouterServerSnapshot({
132
- framework: 'tanstack',
133
- basename: _basename,
134
- statusCode: serverLoadResult.status,
135
- errors: routerErrors,
136
- matchedRouteIds,
137
- hydrationScripts
138
- });
139
- const runtimeContext = applyRouterServerPrepareResult(context, {
140
- snapshot: routerServerSnapshot,
141
- cleanup: ()=>serverRouter.serverSsr?.cleanup?.(),
142
- state: {
142
+ const initialScriptTags = serverRouter.serverSsr?.takeInitialHydrationScriptTags();
143
+ const hydrationScripts = initialScriptTags ? routerManagedTagsToHtml([
144
+ ...initialScriptTags.before,
145
+ initialScriptTags.boundary
146
+ ]) : [];
147
+ const matchedRouteIds = getModernRouteIdsFromMatches(serverRouter);
148
+ const routerServerSnapshot = {
143
149
  framework: 'tanstack',
144
150
  basename: _basename,
145
- instance: serverRouter,
146
- hydrationScripts,
151
+ statusCode: serverLoadResult.status,
152
+ errors: routerErrors,
147
153
  matchedRouteIds,
148
- serverSnapshot: routerServerSnapshot
149
- }
150
- });
151
- hooks.onAfterCreateRouter.call({
152
- ...routerLifecycleContext,
153
- router: serverRouter,
154
- serverSnapshot: routerServerSnapshot,
155
- runtimeContext
156
- });
154
+ hydrationScripts
155
+ };
156
+ const runtimeContext = applyRouterServerPrepareResult(context, {
157
+ snapshot: routerServerSnapshot,
158
+ cleanup,
159
+ state: {
160
+ framework: 'tanstack',
161
+ basename: _basename,
162
+ instance: serverRouter,
163
+ navigation: createTanstackNavigation(tanstackRouter)
164
+ }
165
+ });
166
+ rawRequest.signal.throwIfAborted();
167
+ hooks.onAfterCreateRouter.call({
168
+ ...routerLifecycleContext,
169
+ router: serverRouter,
170
+ serverSnapshot: getRouterServerSnapshot(runtimeContext),
171
+ runtimeContext
172
+ });
173
+ prepared = true;
174
+ } finally{
175
+ if (!prepared) try {
176
+ cleanup();
177
+ } catch {}
178
+ }
157
179
  });
158
180
  api.wrapRoot((App)=>{
159
181
  const getRouteApp = ()=>{
@@ -1,12 +1,7 @@
1
1
  import "node:module";
2
2
  import { jsx } from "react/jsx-runtime";
3
+ import { resolvePreloadFromPrefetch } from "@modern-js/runtime-extensions";
3
4
  import { useLinkProps } from "@tanstack/react-router";
4
- function resolvePreloadFromPrefetch(prefetch, preload) {
5
- if (void 0 !== preload) return preload;
6
- if ('none' === prefetch) return false;
7
- if ('intent' === prefetch || 'render' === prefetch || 'viewport' === prefetch) return prefetch;
8
- return 'viewport';
9
- }
10
5
  const LinkComponentImpl = (props)=>{
11
6
  const { prefetch, preload, 'aria-current': ariaCurrentProp, ref, children, ...rest } = props;
12
7
  const hasAriaCurrentOverride = 'aria-current' in props;
@@ -1,6 +1,4 @@
1
1
  import "node:module";
2
- import { registerRouterProvider } from "@modern-js/runtime/context";
3
2
  import { tanstackRouterPlugin } from "./plugin.mjs";
4
3
  const tanstackRouterProviderFactory = (userConfig)=>tanstackRouterPlugin(userConfig);
5
- registerRouterProvider('tanstack', tanstackRouterProviderFactory);
6
4
  export { tanstackRouterProviderFactory };