@bleedingdev/modern-js-plugin-tanstack 3.9.0-ultramodern.9 → 3.9.3-ultramodern.10

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 +88 -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 +50 -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 +51 -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 +34 -28
  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 +102 -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
@@ -1,5 +1,6 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
2
  import { InternalRuntimeContext, getGlobalEnableRsc } from "@modern-js/runtime/context";
3
+ import { createRouterStatePlugin } from "@modern-js/runtime-extensions/router-state-plugin";
3
4
  import { createRequestContext } from "@modern-js/runtime-utils/node";
4
5
  import { time } from "@modern-js/runtime-utils/time";
5
6
  import { LOADER_REPORTER_NAME } from "@modern-js/utils/universal/constants";
@@ -8,13 +9,14 @@ import { useContext } from "react";
8
9
  import { createModernBasepathRewrite } from "./basepathRewrite.mjs";
9
10
  import { routerProviderRegistryHooks } from "./hooks.mjs";
10
11
  import { wrapTanstackSsrHydrationBoundary } from "./hydrationBoundary.mjs";
11
- import { applyRouterServerPrepareResult, createRouterServerSnapshot, getRouterRuntimeState } from "./lifecycle.mjs";
12
+ import { applyRouterRuntimeState, applyRouterServerPrepareResult, getRouterRuntimeState, getRouterServerSnapshot } from "./lifecycle.mjs";
13
+ import { createTanstackNavigation } from "./navigation.mjs";
12
14
  import { createTanstackRouteObjects, getTanstackRouteConfig, joinBasename } from "./pluginShared.mjs";
13
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 = {})=>{
@@ -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
  api.onBeforeRender(async (context, interrupt)=>{
26
31
  const routeConfig = getTanstackRouteConfig(api, userConfig);
27
32
  const { mergedConfig } = routeConfig;
@@ -87,76 +92,96 @@ const tanstackRouterPlugin = (userConfig = {})=>{
87
92
  });
88
93
  const serverRouter = tanstackRouter;
89
94
  if (enableRsc) setTanstackRscRouter(serverRouter);
90
- await attachServerSsrUtils(serverRouter);
91
- const end = time();
92
- try {
93
- await tanstackRouter.load({
94
- sync: true
95
- });
96
- } finally{
97
- const cost = end();
98
- context.ssrContext?.onTiming?.(LOADER_REPORTER_NAME, cost);
99
- }
100
- const serverLoadResult = serverRouter._serverResult;
101
- if (!serverLoadResult) {
102
- try {
103
- serverRouter.serverSsr?.cleanup?.();
104
- } catch {}
105
- throw new Error('TanStack Router completed an SSR load without a server result.');
106
- }
107
- if ('redirect' === serverLoadResult.type) {
108
- const { redirect } = serverLoadResult;
109
- try {
110
- serverRouter.serverSsr?.cleanup?.();
111
- } catch {}
112
- return interrupt(isRSCNavigation ? handleTanstackRscRedirect(redirect.headers, _basename, redirect.status) : redirect);
113
- }
114
- const routerErrors = collectRouterErrors(tanstackRouter);
115
- if (routerErrors && 'clientRender' === loaderFailureMode) {
116
- context.ssrContext?.response?.status(200);
117
- try {
118
- serverRouter.serverSsr?.cleanup?.();
119
- } catch {}
120
- throw Object.values(routerErrors)[0];
121
- }
122
- await preloadMatchedRouteComponents(serverRouter);
123
- context.ssrContext?.response?.status(serverLoadResult.status);
124
- await serverRouter.serverSsr?.dehydrate?.();
125
- if (enableRsc) {
126
- if (isRSCNavigation) await waitForRouterSerialization(serverRouter);
127
- setTanstackRscServerPayload(createTanstackRscServerPayload(serverRouter, {
128
- omitClientLoaderData: isRSCNavigation
129
- }));
130
- }
131
- const ssrScriptTags = serverRouter.serverSsr?.takeBufferedScripts?.();
132
- const hydrationScripts = routerManagedTagsToHtml(ssrScriptTags);
133
- const matchedRouteIds = getModernRouteIdsFromMatches(serverRouter);
134
- const routerServerSnapshot = createRouterServerSnapshot({
95
+ let disposed = false;
96
+ let prepared = false;
97
+ const onAbort = ()=>controller.abort(rawRequest.signal.reason);
98
+ const cleanup = ()=>{
99
+ if (disposed) return;
100
+ disposed = true;
101
+ rawRequest.signal.removeEventListener('abort', onAbort);
102
+ controller.abort();
103
+ serverRouter.serverSsr?.cleanup();
104
+ };
105
+ applyRouterRuntimeState(context, {
135
106
  framework: 'tanstack',
136
107
  basename: _basename,
137
- statusCode: serverLoadResult.status,
138
- errors: routerErrors,
139
- matchedRouteIds,
140
- hydrationScripts
108
+ instance: serverRouter,
109
+ cleanup
110
+ });
111
+ rawRequest.signal.addEventListener('abort', onAbort, {
112
+ once: true
141
113
  });
142
- const runtimeContext = applyRouterServerPrepareResult(context, {
143
- snapshot: routerServerSnapshot,
144
- cleanup: ()=>serverRouter.serverSsr?.cleanup?.(),
145
- state: {
114
+ try {
115
+ rawRequest.signal.throwIfAborted();
116
+ await attachServerSsrUtils(serverRouter);
117
+ rawRequest.signal.throwIfAborted();
118
+ const end = time();
119
+ try {
120
+ await tanstackRouter.load({
121
+ sync: true
122
+ });
123
+ } finally{
124
+ const cost = end();
125
+ context.ssrContext?.onTiming?.(LOADER_REPORTER_NAME, cost);
126
+ }
127
+ rawRequest.signal.throwIfAborted();
128
+ const serverLoadResult = serverRouter._serverResult;
129
+ if (!serverLoadResult) throw new Error('TanStack Router completed an SSR load without a server result.');
130
+ if ('redirect' === serverLoadResult.type) {
131
+ const { redirect } = serverLoadResult;
132
+ return interrupt(isRSCNavigation ? handleTanstackRscRedirect(redirect.headers, _basename, redirect.status) : redirect);
133
+ }
134
+ const routerErrors = collectRouterErrors(tanstackRouter);
135
+ if (routerErrors && 'clientRender' === loaderFailureMode) {
136
+ context.ssrContext?.response?.status(200);
137
+ throw Object.values(routerErrors)[0];
138
+ }
139
+ await preloadMatchedRouteComponents(serverRouter);
140
+ context.ssrContext?.response?.status(serverLoadResult.status);
141
+ await serverRouter.serverSsr?.dehydrate();
142
+ if (enableRsc) {
143
+ setTanstackRscServerPayload(createTanstackRscServerPayload(serverRouter, {
144
+ omitClientLoaderData: isRSCNavigation
145
+ }));
146
+ if (isRSCNavigation) context.ssrContext.response.setHeader('Content-Type', 'text/x-component');
147
+ }
148
+ const initialScriptTags = serverRouter.serverSsr?.takeInitialHydrationScriptTags();
149
+ const hydrationScripts = initialScriptTags ? routerManagedTagsToHtml([
150
+ ...initialScriptTags.before,
151
+ initialScriptTags.boundary
152
+ ]) : [];
153
+ const matchedRouteIds = getModernRouteIdsFromMatches(serverRouter);
154
+ const routerServerSnapshot = {
146
155
  framework: 'tanstack',
147
156
  basename: _basename,
148
- instance: serverRouter,
149
- hydrationScripts,
157
+ statusCode: serverLoadResult.status,
158
+ errors: routerErrors,
150
159
  matchedRouteIds,
151
- serverSnapshot: routerServerSnapshot
152
- }
153
- });
154
- hooks.onAfterCreateRouter.call({
155
- ...routerLifecycleContext,
156
- router: serverRouter,
157
- serverSnapshot: routerServerSnapshot,
158
- runtimeContext
159
- });
160
+ hydrationScripts
161
+ };
162
+ const runtimeContext = applyRouterServerPrepareResult(context, {
163
+ snapshot: routerServerSnapshot,
164
+ cleanup,
165
+ state: {
166
+ framework: 'tanstack',
167
+ basename: _basename,
168
+ instance: serverRouter,
169
+ navigation: createTanstackNavigation(tanstackRouter)
170
+ }
171
+ });
172
+ rawRequest.signal.throwIfAborted();
173
+ hooks.onAfterCreateRouter.call({
174
+ ...routerLifecycleContext,
175
+ router: serverRouter,
176
+ serverSnapshot: getRouterServerSnapshot(runtimeContext),
177
+ runtimeContext
178
+ });
179
+ prepared = true;
180
+ } finally{
181
+ if (!prepared) try {
182
+ cleanup();
183
+ } catch {}
184
+ }
160
185
  });
161
186
  api.wrapRoot((App)=>{
162
187
  const getRouteApp = ()=>{
@@ -1,23 +1,19 @@
1
- import { canonicaliseLocalisedRoutes } from "@modern-js/i18n-runtime-extensions";
2
1
  import { DefaultNotFound } from "@modern-js/runtime/context";
3
2
  import { rootRouteId } from "@tanstack/react-router";
3
+ import { describeRouteTree } from "../../shared/routeDescriptor.mjs";
4
4
  import { createRouteStaticData } from "../../shared/routeStaticData.mjs";
5
5
  import { withModernRouteMatchContext } from "../outlet.mjs";
6
6
  import { toErrorComponent, toPendingComponent, toRouteComponent } from "./components.mjs";
7
7
  import { createModernShouldReload, wrapRouteObjectLoader } from "./loaders.mjs";
8
- import { isRouteObjectPathlessLayout, toTanstackPath } from "./paths.mjs";
9
8
  import { mergeModernRouteHandle } from "./staticData.mjs";
10
9
  import { createTanstackRootRoute, createTanstackRoute, wrapRouteComponentWithModernContext } from "./tanstackRoutes.mjs";
11
- function createRouteFromRouteObject(opts) {
12
- const { options = {}, parent, pathlessFallbackId = 'pathless', routeObject } = opts;
10
+ function createRouteOptions(routeObject, options) {
13
11
  const modernRouteObject = routeObject;
14
12
  const revalidationState = {};
15
13
  const shouldRevalidate = modernRouteObject.shouldRevalidate;
16
14
  const shouldReload = createModernShouldReload(shouldRevalidate, revalidationState);
17
- const stableFallbackId = routeObject.id || modernRouteObject.file || pathlessFallbackId;
18
15
  const component = toRouteComponent(routeObject);
19
16
  const base = {
20
- getParentRoute: ()=>parent,
21
17
  component,
22
18
  pendingComponent: toPendingComponent(routeObject),
23
19
  errorComponent: toErrorComponent(routeObject),
@@ -38,67 +34,29 @@ function createRouteFromRouteObject(opts) {
38
34
  };
39
35
  if (modernRouteObject.inValidSSRRoute) base.ssr = false;
40
36
  if (shouldReload) base.shouldReload = shouldReload;
41
- if (isRouteObjectPathlessLayout(routeObject)) base.id = stableFallbackId;
42
- else base.path = routeObject.index ? '/' : toTanstackPath(routeObject.path || '');
43
- const route = createTanstackRoute(base);
44
- wrapRouteComponentWithModernContext(route, component, routeObject.id);
45
- const children = routeObject.children;
46
- if (children && children.length > 0) {
47
- const childRoutes = children.map((child, index)=>createRouteFromRouteObject({
48
- options,
49
- parent: route,
50
- pathlessFallbackId: `pathless-${index}`,
51
- routeObject: child
52
- }));
53
- route.addChildren(childRoutes);
54
- }
55
- return route;
37
+ return base;
56
38
  }
57
- function getRootLikeRouteObject(routes) {
58
- return routes.find((route)=>'/' === route.path && !route.index);
39
+ function createRouteFromDescriptor(descriptor, parent, options) {
40
+ const { source, location, children } = descriptor;
41
+ const routeOptions = createRouteOptions(source, options);
42
+ const route = createTanstackRoute({
43
+ ...routeOptions,
44
+ ...location,
45
+ getParentRoute: ()=>parent
46
+ });
47
+ wrapRouteComponentWithModernContext(route, routeOptions.component, source.id);
48
+ if (children.length) route.addChildren(children.map((child)=>createRouteFromDescriptor(child, route, options)));
49
+ return route;
59
50
  }
60
51
  function createRouteTreeFromRouteObjects(routes, options = {}) {
61
- routes = canonicaliseLocalisedRoutes(routes);
62
- const rootLikeRoute = getRootLikeRouteObject(routes);
63
- const rootRevalidationState = {};
64
- const rootShouldRevalidate = rootLikeRoute?.shouldRevalidate;
65
- const rootShouldReload = createModernShouldReload(rootShouldRevalidate, rootRevalidationState);
66
- const rootComponent = rootLikeRoute ? toRouteComponent(rootLikeRoute) : void 0;
52
+ const { root, children } = describeRouteTree(routes);
67
53
  const rootRouteOptions = {
68
- component: rootComponent,
69
- pendingComponent: rootLikeRoute ? toPendingComponent(rootLikeRoute) : void 0,
70
- errorComponent: rootLikeRoute ? toErrorComponent(rootLikeRoute) : void 0,
71
- validateSearch: rootLikeRoute?.validateSearch,
72
- loaderDeps: rootLikeRoute?.loaderDeps,
73
- notFoundComponent: DefaultNotFound,
74
- staticData: createRouteStaticData({
75
- modernRouteId: rootLikeRoute?.id,
76
- modernRouteAction: rootLikeRoute?.action,
77
- modernRouteHandle: rootLikeRoute ? mergeModernRouteHandle(rootLikeRoute) : void 0,
78
- modernRouteHasAction: rootLikeRoute?.hasAction || Boolean(rootLikeRoute?.action),
79
- modernRouteHasClientLoader: rootLikeRoute?.hasClientLoader || void 0 !== rootLikeRoute?.clientData,
80
- modernRouteHasLoader: rootLikeRoute?.hasLoader || 'function' == typeof rootLikeRoute?.loader,
81
- modernRouteIsClientComponent: rootLikeRoute?.isClientComponent,
82
- modernRouteLoader: rootLikeRoute?.loader,
83
- modernRouteShouldRevalidate: rootShouldRevalidate
84
- }),
85
- loader: rootLikeRoute ? wrapRouteObjectLoader(rootLikeRoute, rootRevalidationState, options) : void 0
54
+ ...root ? createRouteOptions(root, options) : {},
55
+ notFoundComponent: DefaultNotFound
86
56
  };
87
- if (rootShouldReload) rootRouteOptions.shouldReload = rootShouldReload;
88
- if (rootLikeRoute?.inValidSSRRoute) rootRouteOptions.ssr = false;
89
57
  const rootRoute = createTanstackRootRoute(rootRouteOptions);
90
- if (rootComponent) rootRoute.options.component = withModernRouteMatchContext(rootComponent, rootRouteId);
91
- const topLevel = rootLikeRoute ? [
92
- ...rootLikeRoute.children || [],
93
- ...routes.filter((route)=>route !== rootLikeRoute)
94
- ] : routes;
95
- const childRoutes = topLevel.map((routeObject, index)=>createRouteFromRouteObject({
96
- options,
97
- parent: rootRoute,
98
- pathlessFallbackId: `pathless-${index}`,
99
- routeObject
100
- }));
101
- rootRoute.addChildren(childRoutes);
58
+ if (rootRouteOptions.component) rootRoute.options.component = withModernRouteMatchContext(rootRouteOptions.component, rootRouteId);
59
+ rootRoute.addChildren(children.map((child)=>createRouteFromDescriptor(child, rootRoute, options)));
102
60
  return rootRoute;
103
61
  }
104
62
  function getModernRouteIdsFromMatches(router) {
@@ -1,4 +1,4 @@
1
- import { getLoaderHref, handleModernLoaderError, handleModernLoaderResult, mapSplatParamsForModernLoader } from "../loaderBridge.mjs";
1
+ import { getLoaderHref, getLoaderSignal, handleModernLoaderError, handleModernLoaderResult, mapSplatParamsForModernLoader } from "../loaderBridge.mjs";
2
2
  import { isTanstackRscPayloadNavigationEnabled, loadTanstackRscRouteData } from "../rsc/payloadRouter.mjs";
3
3
  function isModernDeferredData(value) {
4
4
  if (!value || 'object' != typeof value) return false;
@@ -49,7 +49,7 @@ function wrapRouteObjectLoader(route, revalidationState, options = {}) {
49
49
  if ('function' == typeof modernRoute.lazyImport) try {
50
50
  await modernRoute.lazyImport();
51
51
  } catch {}
52
- const signal = ctx?.abortController?.signal || ctx?.signal || new AbortController().signal;
52
+ const signal = getLoaderSignal(ctx);
53
53
  const baseRequest = ctx?.context?.request instanceof Request ? ctx.context.request : void 0;
54
54
  const href = getLoaderHref(ctx);
55
55
  const request = void 0 !== baseRequest ? new Request(baseRequest, {
@@ -10,7 +10,4 @@ function toTanstackPath(pathname) {
10
10
  return segment;
11
11
  }).join('/');
12
12
  }
13
- function isRouteObjectPathlessLayout(route) {
14
- return !route.path && !route.index;
15
- }
16
- export { isRouteObjectPathlessLayout, toTanstackPath };
13
+ export { toTanstackPath };
@@ -1,10 +1,3 @@
1
- async function waitForRouterSerialization(tanstackRouter) {
2
- const serverSsr = tanstackRouter.serverSsr;
3
- if (!serverSsr || 'function' != typeof serverSsr.onSerializationFinished || serverSsr.isSerializationFinished?.()) return;
4
- await new Promise((resolve)=>{
5
- serverSsr.onSerializationFinished?.(resolve);
6
- });
7
- }
8
1
  function htmlEscapeAttr(value) {
9
2
  return value.replace(/&/g, '&').replace(/"/g, '"');
10
3
  }
@@ -50,4 +43,4 @@ async function attachServerSsrUtils(router) {
50
43
  manifest: void 0
51
44
  });
52
45
  }
53
- export { attachServerSsrUtils, collectRouterErrors, createGetSsrHref, routerManagedTagsToHtml, waitForRouterSerialization };
46
+ export { attachServerSsrUtils, collectRouterErrors, createGetSsrHref, routerManagedTagsToHtml };
@@ -14,6 +14,7 @@ function withGlobalLayout(Component, globalApp) {
14
14
  function toTanstackRouteObject(route, globalApp) {
15
15
  if ('nested' === route.type) return {
16
16
  modernLocalisedRoute: Reflect.get(route, 'modernLocalisedRoute'),
17
+ isRoot: Reflect.get(route, 'isRoot'),
17
18
  path: route.path,
18
19
  id: route.id,
19
20
  loader: route.loader,
@@ -0,0 +1,50 @@
1
+ import { toTanstackPath } from "../runtime/routeTree/paths.mjs";
2
+ const isPathless = (route)=>0 === (route.path ?? '').length && true !== route.index;
3
+ const toLocationPath = (route)=>true === route.index ? '/' : toTanstackPath(route.path || '');
4
+ const joinPath = (parent, path)=>`${parent}/${path}`.replace(/\/{2,}/g, '/');
5
+ function collectFullPaths(routes, parentPath, fullPaths) {
6
+ for (const route of routes){
7
+ let fullPath = parentPath;
8
+ if (!isPathless(route)) {
9
+ fullPath = joinPath(parentPath, toLocationPath(route));
10
+ fullPaths.push(fullPath);
11
+ }
12
+ collectFullPaths(route.children ?? [], fullPath, fullPaths);
13
+ }
14
+ }
15
+ const isPrefixIndex = (fullPath, fullPaths)=>!fullPath.endsWith('/') && fullPaths.some((other)=>other.startsWith(`${fullPath}/`) && other.length > fullPath.length + 1);
16
+ function describeRouteTree(routes) {
17
+ const root = routes.find((route)=>true === route.isRoot || '/' === route.path && true !== route.index);
18
+ const topLevel = void 0 !== root ? [
19
+ ...root.children ?? [],
20
+ ...routes.filter((route)=>route !== root)
21
+ ] : routes;
22
+ const fullPaths = [];
23
+ collectFullPaths(topLevel, '', fullPaths);
24
+ const describe = (parentPath)=>(route, index)=>{
25
+ const children = route.children ?? [];
26
+ if (isPathless(route)) return {
27
+ source: route,
28
+ location: {
29
+ id: route.id || route.file || `pathless-${index}`
30
+ },
31
+ hasSplat: false,
32
+ children: children.map(describe(parentPath))
33
+ };
34
+ const path = toLocationPath(route);
35
+ const fullPath = joinPath(parentPath, path);
36
+ return {
37
+ source: route,
38
+ location: {
39
+ path: 0 === children.length && isPrefixIndex(fullPath, fullPaths) ? `${path}/` : path
40
+ },
41
+ hasSplat: Boolean(route.path?.includes('*')),
42
+ children: children.map(describe(fullPath))
43
+ };
44
+ };
45
+ return {
46
+ root,
47
+ children: topLevel.map(describe(''))
48
+ };
49
+ }
50
+ export { describeRouteTree };
@@ -1,6 +1,7 @@
1
1
  import "node:module";
2
2
  import node_path from "node:path";
3
3
  import { fs } from "@modern-js/utils";
4
+ import { ROUTE_METADATA_MANIFEST_FILE_NAME, ROUTE_METADATA_MANIFEST_HEADER, findRouteMetaFiles, renderRouteMetadataManifest } from "./routeMetadata.mjs";
4
5
  import { collectCanonicalRoutesForEntry, generateTanstackRouterTypesSourceForEntry } from "./tanstackTypes.mjs";
5
6
  const DEFAULT_GENERATED_DIR_NAME = 'modern-tanstack';
6
7
  async function writeFileIfChanged(filePath, content) {
@@ -78,7 +79,16 @@ async function writeTanstackRouterTypesForEntries(opts) {
78
79
  canonicalRoutes
79
80
  });
80
81
  }
82
+ async function writeRouteMetadataManifest(opts) {
83
+ const routesDirectory = node_path.join(opts.appDirectory, 'src', 'routes');
84
+ const manifestPath = node_path.join(routesDirectory, ROUTE_METADATA_MANIFEST_FILE_NAME);
85
+ const manifest = renderRouteMetadataManifest(await findRouteMetaFiles(routesDirectory));
86
+ if (null !== manifest) return void await writeFileIfChanged(manifestPath, manifest);
87
+ const previous = await fs.pathExists(manifestPath) ? await fs.readFile(manifestPath, 'utf-8') : null;
88
+ if (previous?.startsWith(`${ROUTE_METADATA_MANIFEST_HEADER}\n`)) throw new Error(`[plugin-tanstack] ${manifestPath} is generated from route.meta.ts files, but none remain below ${routesDirectory}. Restore at least one route.meta.ts that exports routeMeta, or delete the manifest together with its imports.`);
89
+ }
81
90
  async function generateTanstackRouteArtifacts(opts) {
91
+ await writeRouteMetadataManifest(opts);
82
92
  const [{ createRunOptions }, { cli }, utils] = await Promise.all([
83
93
  import("@modern-js/app-tools/cli/run"),
84
94
  import("@modern-js/plugin/cli"),
@@ -96,9 +106,10 @@ async function generateTanstackRouteArtifacts(opts) {
96
106
  version: opts.version || process.env.MODERN_JS_VERSION || '0.0.0-routes-generate',
97
107
  internalPlugins: utils.INTERNAL_RUNTIME_PLUGINS
98
108
  });
99
- await cli.init({
109
+ const { appContext } = await cli.init({
100
110
  ...runOptions,
101
111
  command: 'build'
102
112
  });
113
+ await appContext.hooks.onBeforeExit.call();
103
114
  }
104
- export { generateTanstackRouteArtifacts, writeTanstackRegisterFile, writeTanstackRouterTypesForEntries };
115
+ export { generateTanstackRouteArtifacts, writeRouteMetadataManifest, writeTanstackRegisterFile, writeTanstackRouterTypesForEntries };
@@ -1,26 +1,21 @@
1
1
  import "node:module";
2
2
  import node_path from "node:path";
3
3
  import { NESTED_ROUTE_SPEC_FILE, filterRoutesForServer } from "@modern-js/utils";
4
- import { generateTanstackRouteArtifacts, writeTanstackRegisterFile, writeTanstackRouterTypesForEntries } from "./artifacts.mjs";
4
+ import { generateTanstackRouteArtifacts, writeRouteMetadataManifest, writeTanstackRegisterFile, writeTanstackRouterTypesForEntries } from "./artifacts.mjs";
5
5
  import { createTanstackRsbuildRouteSplittingProfile, resolveTanstackRouteCodeSplittingEnabled } from "./routeSplitting.mjs";
6
- import { __webpack_require__ } from "../rslib-runtime~0.mjs";
6
+ import { TANSTACK_SERVER_ROUTES_FILE, createTanstackRscRouteLayerPlugin } from "./rscRouteLayer.mjs";
7
7
  import { fileURLToPath as __rspack_fileURLToPath } from "node:url";
8
8
  import { dirname as __rspack_dirname } from "node:path";
9
9
  var __rspack_import_meta_dirname__ = __rspack_dirname(__rspack_fileURLToPath(import.meta.url));
10
10
  import { createRequire as __rspack_createRequire } from "node:module";
11
11
  const __rspack_createRequire_require = __rspack_createRequire(import.meta.url);
12
- __webpack_require__.add({
13
- "@modern-js/runtime/cli?667f" (module) {
14
- module.exports = __rspack_createRequire_require("@modern-js/runtime/cli");
15
- }
16
- });
17
12
  const DEFAULT_ROUTES_DIR = 'routes';
18
13
  const DEFAULT_GENERATED_DIR_NAME = 'modern-tanstack';
19
14
  const ENTRYPOINTS_KEY = '@modern-js/plugin-tanstack';
20
15
  let runtimeRouterCli;
21
16
  function getRuntimeRouterCli() {
22
17
  if (runtimeRouterCli) return runtimeRouterCli;
23
- const cli = __webpack_require__("@modern-js/runtime/cli?667f");
18
+ const cli = __rspack_createRequire_require("@modern-js/runtime/cli");
24
19
  if (cli.handleGeneratorEntryCode && cli.getEntrypointRoutesDir && cli.getEntrypointRoutesOwner && cli.updateNestedRoutesSpec) {
25
20
  runtimeRouterCli = cli;
26
21
  return runtimeRouterCli;
@@ -102,6 +97,13 @@ function tanstackRouterPlugin(options = {}) {
102
97
  }));
103
98
  api.config(()=>({
104
99
  ...routeSplittingProfile.defaultConfig,
100
+ builderPlugins: [
101
+ createTanstackRscRouteLayerPlugin(()=>({
102
+ entryNames: api.getAppContext().entrypoints.filter(isTanstackEntrypoint).map((entrypoint)=>entrypoint.entryName),
103
+ internalDirectory: api.getAppContext().internalDirectory,
104
+ rsc: api.getNormalizedConfig().server.rsc
105
+ }))
106
+ ],
105
107
  source: {
106
108
  include: [
107
109
  /[\\/]node_modules[\\/]@tanstack[\\/]react-router[\\/]/,
@@ -128,7 +130,7 @@ function tanstackRouterPlugin(options = {}) {
128
130
  hydrateRscClientRoutes: true,
129
131
  includeRouteServerLoadersInSsrEntry: false,
130
132
  isolateRouteDataInRscLayer: true,
131
- serverRoutesFileName: 'tanstack-routes.server.js'
133
+ serverRoutesFileName: TANSTACK_SERVER_ROUTES_FILE
132
134
  });
133
135
  await writeTanstackRouterTypesForEntries({
134
136
  appContext: api.getAppContext(),
@@ -152,7 +154,7 @@ function tanstackRouterPlugin(options = {}) {
152
154
  hydrateRscClientRoutes: true,
153
155
  includeRouteServerLoadersInSsrEntry: false,
154
156
  isolateRouteDataInRscLayer: true,
155
- serverRoutesFileName: 'tanstack-routes.server.js'
157
+ serverRoutesFileName: TANSTACK_SERVER_ROUTES_FILE
156
158
  });
157
159
  await writeTanstackRouterTypesForEntries({
158
160
  appContext: api.getAppContext(),
@@ -188,4 +190,4 @@ function tanstackRouterPlugin(options = {}) {
188
190
  const src_cli = tanstackRouterPlugin;
189
191
  export { collectCanonicalRoutesForEntry, generateTanstackRouterTypesSourceForEntry } from "./tanstackTypes.mjs";
190
192
  export default src_cli;
191
- export { createTanstackRsbuildRouteSplittingProfile, generateTanstackRouteArtifacts, resolveTanstackRouteCodeSplittingEnabled, tanstackRouterPlugin, writeTanstackRegisterFile, writeTanstackRouterTypesForEntries };
193
+ export { createTanstackRsbuildRouteSplittingProfile, generateTanstackRouteArtifacts, resolveTanstackRouteCodeSplittingEnabled, tanstackRouterPlugin, writeRouteMetadataManifest, writeTanstackRegisterFile, writeTanstackRouterTypesForEntries };
@@ -0,0 +1,72 @@
1
+ import "node:module";
2
+ import { readdir } from "node:fs/promises";
3
+ import node_path from "node:path";
4
+ const ROUTE_META_FILE_NAME = 'route.meta.ts';
5
+ const ROUTE_METADATA_MANIFEST_FILE_NAME = 'ultramodern-route-metadata.ts';
6
+ const ROUTE_METADATA_MANIFEST_HEADER = `// @generated by @modern-js/plugin-tanstack from ${ROUTE_META_FILE_NAME} files. Run routes-generate; do not edit.`;
7
+ const PRINT_WIDTH = 120;
8
+ const MANIFEST_BODY = `export const ultramodernLocalisedUrls = Object.fromEntries(
9
+ ultramodernRouteMetadata
10
+ .filter((route: { canonicalPath: string }) => route.canonicalPath !== '/')
11
+ .map((route) => [route.canonicalPath, route.localisedPaths]),
12
+ );
13
+ export const ultramodernPublicRoutes = ultramodernRouteMetadata
14
+ .filter((route) => route.public && route.indexable)
15
+ .map((route) => ({
16
+ canonicalPath: route.canonicalPath,
17
+ descriptionKey: route.descriptionKey,
18
+ id: route.id,
19
+ localisedPaths: route.localisedPaths,
20
+ namespace: route.namespace,
21
+ ownerAppId: route.ownerAppId,
22
+ titleKey: route.titleKey,
23
+ ...('jsonLd' in route ? { jsonLd: route.jsonLd } : {}),
24
+ }));
25
+ export const ultramodernRouteConfig = {
26
+ authoring: 'colocated-route-meta',
27
+ generatedManifest: true,
28
+ localisedUrls: ultramodernLocalisedUrls,
29
+ namespace: ultramodernRouteNamespace,
30
+ publicRoutes: ultramodernPublicRoutes,
31
+ routes: ultramodernRouteMetadata,
32
+ source: 'route-owned',
33
+ } as const;
34
+ `;
35
+ const compareCodeUnits = (left, right)=>left < right ? -1 : left > right ? 1 : 0;
36
+ const quoteSpecifier = (specifier)=>specifier.includes("'") ? JSON.stringify(specifier) : `'${specifier}'`;
37
+ function renderRouteMetadataManifest(routeMetaFiles) {
38
+ const specifiers = routeMetaFiles.map((file)=>`./${file.replaceAll('\\', '/').replace(/\.ts$/u, '')}`).sort(compareCodeUnits);
39
+ if (0 === specifiers.length) return null;
40
+ const bindings = specifiers.map((_, index)=>`route${index}`);
41
+ const imports = specifiers.map((specifier, index)=>`import { routeMeta as ${bindings[index]} } from ${quoteSpecifier(specifier)};`);
42
+ const inlineRoutes = `export const ultramodernRouteMetadata = [${bindings.join(', ')}] as const;`;
43
+ const routes = inlineRoutes.length <= PRINT_WIDTH ? inlineRoutes : `export const ultramodernRouteMetadata = [\n${bindings.map((binding)=>` ${binding},\n`).join('')}] as const;`;
44
+ return `${ROUTE_METADATA_MANIFEST_HEADER}
45
+ ${imports.join('\n')}
46
+
47
+ export const ultramodernRouteNamespace = route0.namespace;
48
+ ${routes}
49
+ ${MANIFEST_BODY}`;
50
+ }
51
+ async function findRouteMetaFiles(routesDirectory) {
52
+ const files = [];
53
+ const visit = async (relativeDirectory)=>{
54
+ const entries = await readdir(node_path.join(routesDirectory, relativeDirectory), {
55
+ withFileTypes: true
56
+ });
57
+ for (const entry of entries){
58
+ const relativePath = node_path.join(relativeDirectory, entry.name);
59
+ if (entry.isDirectory()) await visit(relativePath);
60
+ else if (entry.isFile() && entry.name === ROUTE_META_FILE_NAME) files.push(relativePath);
61
+ }
62
+ };
63
+ try {
64
+ await visit('');
65
+ } catch (error) {
66
+ const { code, path: missingPath } = error;
67
+ if ('ENOENT' === code && missingPath === routesDirectory) return [];
68
+ throw error;
69
+ }
70
+ return files;
71
+ }
72
+ export { ROUTE_METADATA_MANIFEST_FILE_NAME, ROUTE_METADATA_MANIFEST_HEADER, ROUTE_META_FILE_NAME, findRouteMetaFiles, renderRouteMetadataManifest };
@@ -0,0 +1,36 @@
1
+ import "node:module";
2
+ import node_path from "node:path";
3
+ const TANSTACK_SERVER_ROUTES_FILE = 'tanstack-routes.server.js';
4
+ function exactResource(...filenames) {
5
+ const escaped = filenames.map((filename)=>filename.replace(/[.*+?^${}()|[\]\\]/gu, '\\$&'));
6
+ return new RegExp(`^(?:${escaped.join('|')})(?:\\?.*)?$`, 'u');
7
+ }
8
+ function createTanstackRscRouteLayerPlugin(getContext) {
9
+ return {
10
+ name: 'modern:tanstack:rsc-route-layer',
11
+ setup (api) {
12
+ api.modifyRspackConfig({
13
+ order: 'post',
14
+ handler (config, { environment, rspack, target }) {
15
+ const { entryNames, internalDirectory, rsc } = getContext();
16
+ if (false === rsc || void 0 === rsc || 0 === entryNames.length) return;
17
+ const rscServerEnvironment = 'object' == typeof rsc ? rsc.environments?.server ?? 'server' : 'server';
18
+ if ('node' !== target && 'node' !== config.target && environment.name !== rscServerEnvironment) return;
19
+ config.module ??= {};
20
+ config.module.rules ??= [];
21
+ config.plugins ??= [];
22
+ for (const entryName of entryNames){
23
+ const routes = node_path.resolve(internalDirectory, entryName, 'routes.js');
24
+ const serverRoutes = node_path.resolve(internalDirectory, entryName, TANSTACK_SERVER_ROUTES_FILE);
25
+ config.plugins.push(new rspack.NormalModuleReplacementPlugin(exactResource(routes), serverRoutes));
26
+ config.module.rules.push({
27
+ resource: exactResource(routes, serverRoutes),
28
+ layer: 'server-side-rendering'
29
+ });
30
+ }
31
+ }
32
+ });
33
+ }
34
+ };
35
+ }
36
+ export { TANSTACK_SERVER_ROUTES_FILE, createTanstackRscRouteLayerPlugin };