@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,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) {
@@ -1,5 +1,5 @@
1
1
  import "node:module";
2
- import { getLoaderHref, handleModernLoaderError, handleModernLoaderResult, mapSplatParamsForModernLoader } from "../loaderBridge.mjs";
2
+ import { getLoaderHref, getLoaderSignal, handleModernLoaderError, handleModernLoaderResult, mapSplatParamsForModernLoader } from "../loaderBridge.mjs";
3
3
  import { isTanstackRscPayloadNavigationEnabled, loadTanstackRscRouteData } from "../rsc/payloadRouter.mjs";
4
4
  function isModernDeferredData(value) {
5
5
  if (!value || 'object' != typeof value) return false;
@@ -50,7 +50,7 @@ function wrapRouteObjectLoader(route, revalidationState, options = {}) {
50
50
  if ('function' == typeof modernRoute.lazyImport) try {
51
51
  await modernRoute.lazyImport();
52
52
  } catch {}
53
- const signal = ctx?.abortController?.signal || ctx?.signal || new AbortController().signal;
53
+ const signal = getLoaderSignal(ctx);
54
54
  const baseRequest = ctx?.context?.request instanceof Request ? ctx.context.request : void 0;
55
55
  const href = getLoaderHref(ctx);
56
56
  const request = void 0 !== baseRequest ? new Request(baseRequest, {
@@ -11,7 +11,4 @@ function toTanstackPath(pathname) {
11
11
  return segment;
12
12
  }).join('/');
13
13
  }
14
- function isRouteObjectPathlessLayout(route) {
15
- return !route.path && !route.index;
16
- }
17
- export { isRouteObjectPathlessLayout, toTanstackPath };
14
+ export { toTanstackPath };
@@ -1,11 +1,4 @@
1
1
  import "node:module";
2
- async function waitForRouterSerialization(tanstackRouter) {
3
- const serverSsr = tanstackRouter.serverSsr;
4
- if (!serverSsr || 'function' != typeof serverSsr.onSerializationFinished || serverSsr.isSerializationFinished?.()) return;
5
- await new Promise((resolve)=>{
6
- serverSsr.onSerializationFinished?.(resolve);
7
- });
8
- }
9
2
  function htmlEscapeAttr(value) {
10
3
  return value.replace(/&/g, '&').replace(/"/g, '"');
11
4
  }
@@ -51,4 +44,4 @@ async function attachServerSsrUtils(router) {
51
44
  manifest: void 0
52
45
  });
53
46
  }
54
- export { attachServerSsrUtils, collectRouterErrors, createGetSsrHref, routerManagedTagsToHtml, waitForRouterSerialization };
47
+ export { attachServerSsrUtils, collectRouterErrors, createGetSsrHref, routerManagedTagsToHtml };
@@ -15,6 +15,7 @@ function withGlobalLayout(Component, globalApp) {
15
15
  function toTanstackRouteObject(route, globalApp) {
16
16
  if ('nested' === route.type) return {
17
17
  modernLocalisedRoute: Reflect.get(route, 'modernLocalisedRoute'),
18
+ isRoot: Reflect.get(route, 'isRoot'),
18
19
  path: route.path,
19
20
  id: route.id,
20
21
  loader: route.loader,
@@ -0,0 +1,51 @@
1
+ import "node:module";
2
+ import { toTanstackPath } from "../runtime/routeTree/paths.mjs";
3
+ const isPathless = (route)=>0 === (route.path ?? '').length && true !== route.index;
4
+ const toLocationPath = (route)=>true === route.index ? '/' : toTanstackPath(route.path || '');
5
+ const joinPath = (parent, path)=>`${parent}/${path}`.replace(/\/{2,}/g, '/');
6
+ function collectFullPaths(routes, parentPath, fullPaths) {
7
+ for (const route of routes){
8
+ let fullPath = parentPath;
9
+ if (!isPathless(route)) {
10
+ fullPath = joinPath(parentPath, toLocationPath(route));
11
+ fullPaths.push(fullPath);
12
+ }
13
+ collectFullPaths(route.children ?? [], fullPath, fullPaths);
14
+ }
15
+ }
16
+ const isPrefixIndex = (fullPath, fullPaths)=>!fullPath.endsWith('/') && fullPaths.some((other)=>other.startsWith(`${fullPath}/`) && other.length > fullPath.length + 1);
17
+ function describeRouteTree(routes) {
18
+ const root = routes.find((route)=>true === route.isRoot || '/' === route.path && true !== route.index);
19
+ const topLevel = void 0 !== root ? [
20
+ ...root.children ?? [],
21
+ ...routes.filter((route)=>route !== root)
22
+ ] : routes;
23
+ const fullPaths = [];
24
+ collectFullPaths(topLevel, '', fullPaths);
25
+ const describe = (parentPath)=>(route, index)=>{
26
+ const children = route.children ?? [];
27
+ if (isPathless(route)) return {
28
+ source: route,
29
+ location: {
30
+ id: route.id || route.file || `pathless-${index}`
31
+ },
32
+ hasSplat: false,
33
+ children: children.map(describe(parentPath))
34
+ };
35
+ const path = toLocationPath(route);
36
+ const fullPath = joinPath(parentPath, path);
37
+ return {
38
+ source: route,
39
+ location: {
40
+ path: 0 === children.length && isPrefixIndex(fullPath, fullPaths) ? `${path}/` : path
41
+ },
42
+ hasSplat: Boolean(route.path?.includes('*')),
43
+ children: children.map(describe(fullPath))
44
+ };
45
+ };
46
+ return {
47
+ root,
48
+ children: topLevel.map(describe(''))
49
+ };
50
+ }
51
+ export { describeRouteTree };
@@ -1,4 +1,5 @@
1
- import type { AppToolsContext } from '@modern-js/app-tools';
1
+ import type { AppTools } from '@modern-js/app-tools/cli-config';
2
+ import type { CLIPluginAPI } from '@modern-js/plugin';
2
3
  import type { NestedRouteForCli, PageRoute } from '@modern-js/types';
3
4
  export declare function writeTanstackRegisterFile(opts: {
4
5
  entries: string[];
@@ -9,7 +10,7 @@ export declare function writeTanstackRegisterFile(opts: {
9
10
  i18nRuntimeModule?: string;
10
11
  }): Promise<void>;
11
12
  export declare function writeTanstackRouterTypesForEntries(opts: {
12
- appContext: AppToolsContext;
13
+ appContext: Pick<ReturnType<CLIPluginAPI<AppTools>['getAppContext']>, 'srcDirectory' | 'internalSrcAlias' | 'entrypoints'>;
13
14
  generatedDirName?: string;
14
15
  routesByEntry: Record<string, (NestedRouteForCli | PageRoute)[]>;
15
16
  /**
@@ -21,11 +22,19 @@ export declare function writeTanstackRouterTypesForEntries(opts: {
21
22
  i18nPluginInstalled?: boolean;
22
23
  }): Promise<void>;
23
24
  /**
24
- * Headless regeneration of `router.gen.ts` / `register.gen.d.ts` for a generated
25
- * app, without running a full dev/build. Drives the app-tools analyze `onPrepare`
26
- * (which fires `generateEntryCode`) and returns before any bundling — the app's
27
- * own registered plugin-tanstack writes the artifacts via its `generateEntryCode`
28
- * subscriber. Reuses the exact hook dev/build use, so there is no scanning drift.
25
+ * Writes `src/routes/ultramodern-route-metadata.ts` from the `route.meta.ts`
26
+ * files below `src/routes`. Reads only the file system, never the app config.
27
+ */
28
+ export declare function writeRouteMetadataManifest(opts: {
29
+ appDirectory: string;
30
+ }): Promise<void>;
31
+ /**
32
+ * Headless regeneration of the route metadata manifest and of `router.gen.ts` /
33
+ * `register.gen.d.ts` for a generated app, without running a full dev/build.
34
+ * Drives the app-tools analyze `onPrepare` (which fires `generateEntryCode`)
35
+ * and returns before any bundling — the app's own registered plugin-tanstack
36
+ * writes the router artifacts via its `generateEntryCode` subscriber. Reuses
37
+ * the exact hook dev/build use, so there is no scanning drift.
29
38
  */
30
39
  export declare function generateTanstackRouteArtifacts(opts: {
31
40
  appDirectory: string;
@@ -1,6 +1,6 @@
1
1
  import type { AppTools, CliPlugin } from '@modern-js/app-tools';
2
2
  import { type TanstackRouteCodeSplittingOption } from './routeSplitting.js';
3
- export { generateTanstackRouteArtifacts, writeTanstackRegisterFile, writeTanstackRouterTypesForEntries, } from './artifacts.js';
3
+ export { generateTanstackRouteArtifacts, writeRouteMetadataManifest, writeTanstackRegisterFile, writeTanstackRouterTypesForEntries, } from './artifacts.js';
4
4
  export type { TanstackRouteCodeSplittingOption, TanstackRsbuildRouteSplittingProfile, } from './routeSplitting.js';
5
5
  export { createTanstackRsbuildRouteSplittingProfile, resolveTanstackRouteCodeSplittingEnabled, } from './routeSplitting.js';
6
6
  export { type CollectCanonicalRoutesOptions, collectCanonicalRoutesForEntry, generateTanstackRouterTypesSourceForEntry, } from './tanstackTypes.js';
@@ -0,0 +1,18 @@
1
+ export declare const ROUTE_META_FILE_NAME = "route.meta.ts";
2
+ export declare const ROUTE_METADATA_MANIFEST_FILE_NAME = "ultramodern-route-metadata.ts";
3
+ export declare const ROUTE_METADATA_MANIFEST_HEADER = "// @generated by @modern-js/plugin-tanstack from route.meta.ts files. Run routes-generate; do not edit.";
4
+ /**
5
+ * Renders the route metadata manifest for the `route.meta.ts` files of one
6
+ * routes directory. Each path is relative to that directory (either separator).
7
+ * Every `route.meta.ts` must export `routeMeta`; the manifest imports it
8
+ * statically, so the metadata is never evaluated while generating. Returns
9
+ * `null` when there are no route metadata files. Output depends only on the
10
+ * set of paths, never on their order or on the file system.
11
+ */
12
+ export declare function renderRouteMetadataManifest(routeMetaFiles: readonly string[]): string | null;
13
+ /**
14
+ * Lists the `route.meta.ts` files below `routesDirectory`, relative to it.
15
+ * Symbolic links are not followed: linked metadata belongs to the tree it
16
+ * lives in. A missing directory has no route metadata.
17
+ */
18
+ export declare function findRouteMetaFiles(routesDirectory: string): Promise<string[]>;
@@ -0,0 +1,11 @@
1
+ import type { AppNormalizedConfig } from '@modern-js/app-tools/cli-config';
2
+ type BuilderPlugin = NonNullable<AppNormalizedConfig['builderPlugins']>[number];
3
+ export declare const TANSTACK_SERVER_ROUTES_FILE = "tanstack-routes.server.js";
4
+ type RouteLayerContext = {
5
+ entryNames: readonly string[];
6
+ internalDirectory: string;
7
+ rsc: AppNormalizedConfig['server']['rsc'];
8
+ };
9
+ /** The HTML router consumes executable components, while route data stays in Flight. */
10
+ export declare function createTanstackRscRouteLayerPlugin(getContext: () => RouteLayerContext): BuilderPlugin;
11
+ export {};
@@ -11,8 +11,6 @@ export declare function pickRouteSearchContractModules(route: NestedRouteForCli
11
11
  validateSearchPath: string | null;
12
12
  loaderDepsPath: string | null;
13
13
  };
14
- export declare function isPathlessLayout(route: NestedRouteForCli | PageRoute): boolean;
15
- export declare function isIndexRoute(route: NestedRouteForCli | PageRoute): boolean;
16
14
  export declare function createRouteStaticDataSnippet(opts: {
17
15
  modernRouteId?: string;
18
16
  loaderName?: string | null;
@@ -1,8 +1,9 @@
1
- import type { AppToolsContext } from '@modern-js/app-tools';
1
+ import type { AppTools } from '@modern-js/app-tools/cli-config';
2
+ import type { CLIPluginAPI } from '@modern-js/plugin';
2
3
  import type { NestedRouteForCli, PageRoute } from '@modern-js/types';
3
4
  type RouteForCli = NestedRouteForCli | PageRoute;
4
5
  export declare function generateTanstackRouterTypesSourceForEntry(opts: {
5
- appContext: AppToolsContext;
6
+ appContext: Pick<ReturnType<CLIPluginAPI<AppTools>['getAppContext']>, 'srcDirectory' | 'internalSrcAlias'>;
6
7
  entryName: string;
7
8
  generatedDirName?: string;
8
9
  routes: RouteForCli[];
@@ -11,4 +11,4 @@ export type RouterLifecycleContext = NativeRouterLifecycleContext & {
11
11
  cleanup?: () => void | Promise<void>;
12
12
  serverSnapshot?: InternalRouterServerSnapshot;
13
13
  };
14
- export { applyRouterRuntimeState, applyRouterServerPrepareResult, createRouterRuntimeState, createRouterServerSnapshot, getRouterRuntimeState, getRouterServerSnapshot, type RouterLifecyclePhase, } from '@modern-js/runtime-extensions/router-state';
14
+ export { applyRouterRuntimeState, applyRouterServerPrepareResult, createRouterServerSnapshot, getRouterRuntimeState, getRouterServerSnapshot, type RouterLifecyclePhase, } from '@modern-js/runtime-extensions/router-state';
@@ -39,6 +39,7 @@ type LoaderLikeContext = {
39
39
  };
40
40
  params?: Record<string, string>;
41
41
  };
42
+ export declare function getLoaderSignal(ctx: LoaderLikeContext | undefined): AbortSignal;
42
43
  export declare function getLoaderHref(ctx: LoaderLikeContext | undefined): string;
43
44
  export declare function handleModernLoaderResult<LoaderResult>(result: LoaderResult): LoaderResult;
44
45
  export declare function handleModernLoaderError(err: unknown): never;
@@ -0,0 +1,4 @@
1
+ import type { RouterNavigationCapability } from '@modern-js/runtime-extensions/router-state';
2
+ import type { AnyRouter } from '@tanstack/react-router';
3
+ /** Native TanStack navigation, exposed through the provider contract. */
4
+ export declare function createTanstackNavigation(router: AnyRouter): RouterNavigationCapability;
@@ -1,6 +1,8 @@
1
+ import type { OnRenderPreparedFn, OnRequestEndFn } from '@modern-js/plugin/runtime';
1
2
  import { type TInternalRuntimeContext } from '@modern-js/runtime/context';
2
3
  import type { RouteObject } from '@modern-js/runtime-utils/router';
3
4
  import type { RouterExtendsHooks } from './hooks.js';
5
+ import type { RouterLifecycleContext } from './lifecycle.js';
4
6
  import type { RouterConfig } from './types.js';
5
7
  type TanstackRouterRuntimeConfig = {
6
8
  plugins?: TanstackRouterRuntimePlugin[];
@@ -12,6 +14,9 @@ export type TanstackRouterPluginAPI = {
12
14
  getRuntimeConfig: () => TanstackRouterRuntimeConfig;
13
15
  getHooks: () => RouterExtendsHooks;
14
16
  onBeforeRender: (listener: (context: TInternalRuntimeContext, interrupt: RuntimeInterrupt) => unknown) => void;
17
+ onRenderPrepared: (listener: OnRenderPreparedFn<TInternalRuntimeContext>) => void;
18
+ onRequestEnd: (listener: OnRequestEndFn<TInternalRuntimeContext>) => void;
19
+ onAfterCreateRouter: (listener: (event: RouterLifecycleContext) => void) => void;
15
20
  wrapRoot: (listener: (App: any) => any) => void;
16
21
  [key: string]: any;
17
22
  };
@@ -1,3 +1 @@
1
- import type { RouteObject } from '@modern-js/runtime-utils/router';
2
1
  export declare function toTanstackPath(pathname: string): string;
3
- export declare function isRouteObjectPathlessLayout(route: RouteObject): boolean;
@@ -1,6 +1,5 @@
1
1
  import type { AnyRouter } from '@tanstack/react-router';
2
2
  import type { TanstackRouterWithServerSsr } from './ssrTypes.js';
3
- export declare function waitForRouterSerialization(tanstackRouter: TanstackRouterWithServerSsr): Promise<void>;
4
3
  export declare function routerManagedTagsToHtml(tags: unknown): string[];
5
4
  export declare function createGetSsrHref(request: Request): string;
6
5
  export declare function collectRouterErrors(tanstackRouter: AnyRouter): Record<string, unknown> | undefined;
@@ -28,13 +28,6 @@ type RouterRouteWithOptions = {
28
28
  };
29
29
  export type TanstackRouterWithServerSsr = AnyRouter & {
30
30
  routesById?: Record<string, RouterRouteWithOptions>;
31
- serverSsr?: {
32
- cleanup?: () => void;
33
- dehydrate?: () => Promise<void> | void;
34
- isSerializationFinished?: () => boolean;
35
- onSerializationFinished?: (listener: () => void) => void;
36
- takeBufferedScripts?: () => unknown;
37
- };
38
31
  state: AnyRouter['state'] & {
39
32
  matches?: unknown;
40
33
  };
@@ -0,0 +1,26 @@
1
+ type RouteShape = {
2
+ id?: string;
3
+ file?: string;
4
+ path?: string;
5
+ index?: boolean;
6
+ isRoot?: boolean;
7
+ children?: RouteShape[];
8
+ };
9
+ /** Browser-safe structure shared by the executable tree and concrete TS emitter. */
10
+ export type RouteDescriptor<T extends RouteShape> = {
11
+ source: T;
12
+ location: {
13
+ id: string;
14
+ path?: never;
15
+ } | {
16
+ path: string;
17
+ id?: never;
18
+ };
19
+ hasSplat: boolean;
20
+ children: RouteDescriptor<T>[];
21
+ };
22
+ export declare function describeRouteTree<T extends RouteShape>(routes: T[]): {
23
+ root: T | undefined;
24
+ children: RouteDescriptor<T>[];
25
+ };
26
+ export {};
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@bleedingdev/modern-js-plugin-tanstack",
3
3
  "description": "TanStack Router integration for Modern.js.",
4
- "homepage": "https://github.com/BleedingDev/ultramodern.js#readme",
4
+ "homepage": "https://github.com/ultramodernjs/ultramodern.js#readme",
5
5
  "bugs": {
6
- "url": "https://github.com/BleedingDev/ultramodern.js/issues"
6
+ "url": "https://github.com/ultramodernjs/ultramodern.js/issues"
7
7
  },
8
8
  "repository": {
9
9
  "type": "git",
10
- "url": "git+https://github.com/BleedingDev/ultramodern.js.git",
10
+ "url": "git+https://github.com/ultramodernjs/ultramodern.js.git",
11
11
  "directory": "packages/runtime/plugin-tanstack"
12
12
  },
13
13
  "license": "MIT",
@@ -18,7 +18,7 @@
18
18
  "modern.js",
19
19
  "tanstack-router"
20
20
  ],
21
- "version": "3.9.0-ultramodern.9",
21
+ "version": "3.9.3-ultramodern.10",
22
22
  "engines": {
23
23
  "node": ">=20"
24
24
  },
@@ -94,21 +94,21 @@
94
94
  }
95
95
  },
96
96
  "dependencies": {
97
- "@modern-js/i18n-runtime-extensions": "npm:@bleedingdev/modern-js-i18n-runtime-extensions@3.9.0-ultramodern.9",
98
- "@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.9.0-ultramodern.9",
99
- "@modern-js/runtime-extensions": "npm:@bleedingdev/modern-js-runtime-extensions@3.9.0-ultramodern.9",
100
- "@modern-js/runtime-utils": "npm:@bleedingdev/modern-js-runtime-utils@3.9.0-ultramodern.9",
101
- "@modern-js/types": "npm:@bleedingdev/modern-js-types@3.9.0-ultramodern.9",
102
- "@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.9.0-ultramodern.9",
97
+ "@modern-js/i18n-runtime-extensions": "npm:@bleedingdev/modern-js-i18n-runtime-extensions@3.9.3-ultramodern.10",
98
+ "@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.9.3-ultramodern.10",
99
+ "@modern-js/runtime-extensions": "npm:@bleedingdev/modern-js-runtime-extensions@3.9.3-ultramodern.10",
100
+ "@modern-js/runtime-utils": "npm:@bleedingdev/modern-js-runtime-utils@3.9.3-ultramodern.10",
101
+ "@modern-js/types": "npm:@bleedingdev/modern-js-types@3.9.3-ultramodern.10",
102
+ "@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.9.3-ultramodern.10",
103
103
  "@swc/helpers": "^0.5.23",
104
- "@tanstack/react-router": "1.170.33",
105
- "@tanstack/router-core": "1.171.28"
104
+ "@tanstack/react-router": "1.170.44",
105
+ "@tanstack/router-core": "1.171.37"
106
106
  },
107
107
  "peerDependencies": {
108
- "@modern-js/app-tools": "3.9.0-ultramodern.9",
109
- "@modern-js/runtime": "3.9.0-ultramodern.9",
110
- "react": "^19.2.8",
111
- "react-dom": "^19.2.8",
108
+ "@modern-js/app-tools": "3.9.3-ultramodern.10",
109
+ "@modern-js/runtime": "3.9.3-ultramodern.10",
110
+ "react": "^19.3.1",
111
+ "react-dom": "^19.3.1",
112
112
  "react-server-dom-rspack": "0.1.0"
113
113
  },
114
114
  "peerDependenciesMeta": {
@@ -117,20 +117,19 @@
117
117
  }
118
118
  },
119
119
  "devDependencies": {
120
- "@modern-js/app-tools": "npm:@bleedingdev/modern-js-app-tools@3.9.0-ultramodern.9",
121
- "@modern-js/runtime": "npm:@bleedingdev/modern-js-runtime@3.9.0-ultramodern.9",
122
- "@modern-js/server-utils": "npm:@bleedingdev/modern-js-server-utils@3.9.0-ultramodern.9",
123
- "@rslib/core": "1.0.0",
120
+ "@modern-js/app-tools": "npm:@bleedingdev/modern-js-app-tools@3.9.3-ultramodern.10",
121
+ "@modern-js/runtime": "npm:@bleedingdev/modern-js-runtime@3.9.3-ultramodern.10",
122
+ "@modern-js/server-utils": "npm:@bleedingdev/modern-js-server-utils@3.9.3-ultramodern.10",
123
+ "@rslib/core": "1.0.3",
124
124
  "@scripts/rstest-config": "2.66.0",
125
- "@tanstack/history": "1.162.2",
125
+ "@tanstack/history": "1.162.5",
126
126
  "@testing-library/react": "^16.3.3",
127
127
  "@tsconfig/strictest": "2.0.8",
128
- "@types/node": "^26.4.1",
129
- "@types/react": "^19.2.18",
130
- "@types/react-dom": "^19.2.7",
131
- "@typescript/native-preview": "7.0.0-dev.20260707.2",
132
- "react": "^19.2.8",
133
- "react-dom": "^19.2.8",
128
+ "@types/node": "^26.6.5",
129
+ "@types/react": "^19.3.0",
130
+ "@types/react-dom": "^19.3.0",
131
+ "react": "^19.3.1",
132
+ "react-dom": "^19.3.1",
134
133
  "react-server-dom-rspack": "0.1.0"
135
134
  },
136
135
  "sideEffects": [
@@ -150,9 +149,16 @@
150
149
  "publishConfig": {
151
150
  "access": "public"
152
151
  },
152
+ "files": [
153
+ "dist",
154
+ "src"
155
+ ],
153
156
  "scripts": {
154
157
  "dev": "rslib build --watch",
155
158
  "build": "rslib build",
156
- "test": "rstest --passWithNoTests"
159
+ "test": "rstest"
160
+ },
161
+ "ultramodern": {
162
+ "sourceName": "@modern-js/plugin-tanstack"
157
163
  }
158
164
  }
@@ -1,10 +1,17 @@
1
1
  // @effect-diagnostics asyncFunction:off nodeBuiltinImport:off strictBooleanExpressions:off processEnv:off processEnvInEffect:off
2
2
 
3
3
  import path from 'node:path';
4
- import type { AppToolsContext } from '@modern-js/app-tools';
4
+ import type { AppTools } from '@modern-js/app-tools/cli-config';
5
+ import type { CLIPluginAPI } from '@modern-js/plugin';
5
6
  import type { NestedRouteForCli, PageRoute } from '@modern-js/types';
6
7
  import { fs } from '@modern-js/utils';
7
8
 
9
+ import {
10
+ findRouteMetaFiles,
11
+ ROUTE_METADATA_MANIFEST_FILE_NAME,
12
+ ROUTE_METADATA_MANIFEST_HEADER,
13
+ renderRouteMetadataManifest,
14
+ } from './routeMetadata';
8
15
  import {
9
16
  collectCanonicalRoutesForEntry,
10
17
  generateTanstackRouterTypesSourceForEntry,
@@ -108,7 +115,10 @@ export async function writeTanstackRegisterFile(opts: {
108
115
  }
109
116
 
110
117
  export async function writeTanstackRouterTypesForEntries(opts: {
111
- appContext: AppToolsContext;
118
+ appContext: Pick<
119
+ ReturnType<CLIPluginAPI<AppTools>['getAppContext']>,
120
+ 'srcDirectory' | 'internalSrcAlias' | 'entrypoints'
121
+ >;
112
122
  generatedDirName?: string;
113
123
  routesByEntry: Record<string, (NestedRouteForCli | PageRoute)[]>;
114
124
  /**
@@ -193,16 +203,52 @@ export async function writeTanstackRouterTypesForEntries(opts: {
193
203
  }
194
204
 
195
205
  /**
196
- * Headless regeneration of `router.gen.ts` / `register.gen.d.ts` for a generated
197
- * app, without running a full dev/build. Drives the app-tools analyze `onPrepare`
198
- * (which fires `generateEntryCode`) and returns before any bundling — the app's
199
- * own registered plugin-tanstack writes the artifacts via its `generateEntryCode`
200
- * subscriber. Reuses the exact hook dev/build use, so there is no scanning drift.
206
+ * Writes `src/routes/ultramodern-route-metadata.ts` from the `route.meta.ts`
207
+ * files below `src/routes`. Reads only the file system, never the app config.
208
+ */
209
+ export async function writeRouteMetadataManifest(opts: {
210
+ appDirectory: string;
211
+ }): Promise<void> {
212
+ const routesDirectory = path.join(opts.appDirectory, 'src', 'routes');
213
+ const manifestPath = path.join(
214
+ routesDirectory,
215
+ ROUTE_METADATA_MANIFEST_FILE_NAME,
216
+ );
217
+ const manifest = renderRouteMetadataManifest(
218
+ await findRouteMetaFiles(routesDirectory),
219
+ );
220
+ if (manifest !== null) {
221
+ await writeFileIfChanged(manifestPath, manifest);
222
+ return;
223
+ }
224
+ const previous = (await fs.pathExists(manifestPath))
225
+ ? await fs.readFile(manifestPath, 'utf-8')
226
+ : null;
227
+ // No valid manifest exists without routes (its namespace comes from them),
228
+ // and the app config imports it, so the app cannot load in this state.
229
+ if (previous?.startsWith(`${ROUTE_METADATA_MANIFEST_HEADER}\n`)) {
230
+ throw new Error(
231
+ `[plugin-tanstack] ${manifestPath} is generated from route.meta.ts files, but none remain below ${routesDirectory}. ` +
232
+ 'Restore at least one route.meta.ts that exports routeMeta, or delete the manifest together with its imports.',
233
+ );
234
+ }
235
+ }
236
+
237
+ /**
238
+ * Headless regeneration of the route metadata manifest and of `router.gen.ts` /
239
+ * `register.gen.d.ts` for a generated app, without running a full dev/build.
240
+ * Drives the app-tools analyze `onPrepare` (which fires `generateEntryCode`)
241
+ * and returns before any bundling — the app's own registered plugin-tanstack
242
+ * writes the router artifacts via its `generateEntryCode` subscriber. Reuses
243
+ * the exact hook dev/build use, so there is no scanning drift.
201
244
  */
202
245
  export async function generateTanstackRouteArtifacts(opts: {
203
246
  appDirectory: string;
204
247
  version?: string;
205
248
  }): Promise<void> {
249
+ // The app config imports the manifest. Writing it first means a manifest
250
+ // that still imports a deleted route.meta.ts cannot fail the config load.
251
+ await writeRouteMetadataManifest(opts);
206
252
  const [{ createRunOptions }, { cli }, utils] = await Promise.all([
207
253
  import('@modern-js/app-tools/cli/run'),
208
254
  import('@modern-js/plugin/cli'),
@@ -251,5 +297,10 @@ export async function generateTanstackRouteArtifacts(opts: {
251
297
  internalPlugins: (utils as { INTERNAL_RUNTIME_PLUGINS: unknown })
252
298
  .INTERNAL_RUNTIME_PLUGINS as never,
253
299
  });
254
- await cli.init({ ...runOptions, command: 'build' });
300
+ const { appContext } = await cli.init({ ...runOptions, command: 'build' });
301
+ // Preparation owns the artifact writes; there is no dev server or bundler
302
+ // phase after it. Release plugin resources before the headless caller exits.
303
+ // Failed initialization propagates unchanged: it has not returned a context
304
+ // whose lifecycle this caller can close.
305
+ await appContext.hooks.onBeforeExit.call();
255
306
  }