@bleedingdev/modern-js-plugin-tanstack 3.2.0-ultramodern.120 → 3.2.0-ultramodern.121

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 (94) hide show
  1. package/dist/cjs/cli/index.js +47 -27
  2. package/dist/cjs/cli/routeSplitting.js +0 -32
  3. package/dist/cjs/cli/tanstackTypes.js +34 -199
  4. package/dist/cjs/runtime/hooks.js +11 -14
  5. package/dist/cjs/runtime/index.js +107 -319
  6. package/dist/cjs/runtime/lifecycle.js +12 -86
  7. package/dist/cjs/runtime/loaderBridge.js +173 -0
  8. package/dist/cjs/runtime/plugin.js +6 -30
  9. package/dist/cjs/runtime/plugin.node.js +7 -29
  10. package/dist/cjs/runtime/pluginCore.js +55 -0
  11. package/dist/cjs/runtime/register.js +56 -0
  12. package/dist/cjs/runtime/routeTree.js +10 -207
  13. package/dist/cjs/runtime/{DefaultNotFound.js → router.js} +5 -15
  14. package/dist/cjs/runtime/rsc/payloadRouter.js +35 -1
  15. package/dist/cjs/runtime/state.js +45 -0
  16. package/dist/cjs/runtime/utils.js +0 -5
  17. package/dist/esm/cli/index.mjs +52 -26
  18. package/dist/esm/cli/routeSplitting.mjs +1 -30
  19. package/dist/esm/cli/tanstackTypes.mjs +32 -194
  20. package/dist/esm/runtime/hooks.mjs +1 -8
  21. package/dist/esm/runtime/index.mjs +4 -2
  22. package/dist/esm/runtime/lifecycle.mjs +1 -82
  23. package/dist/esm/runtime/loaderBridge.mjs +114 -0
  24. package/dist/esm/runtime/plugin.mjs +8 -32
  25. package/dist/esm/runtime/plugin.node.mjs +10 -32
  26. package/dist/esm/runtime/pluginCore.mjs +14 -0
  27. package/dist/esm/runtime/register.mjs +18 -0
  28. package/dist/esm/runtime/routeTree.mjs +4 -198
  29. package/dist/esm/runtime/router.mjs +2 -0
  30. package/dist/esm/runtime/rsc/payloadRouter.mjs +35 -1
  31. package/dist/esm/runtime/state.mjs +7 -0
  32. package/dist/esm/runtime/utils.mjs +0 -5
  33. package/dist/esm-node/cli/index.mjs +52 -26
  34. package/dist/esm-node/cli/routeSplitting.mjs +1 -30
  35. package/dist/esm-node/cli/tanstackTypes.mjs +32 -194
  36. package/dist/esm-node/runtime/hooks.mjs +1 -8
  37. package/dist/esm-node/runtime/index.mjs +4 -2
  38. package/dist/esm-node/runtime/lifecycle.mjs +1 -82
  39. package/dist/esm-node/runtime/loaderBridge.mjs +115 -0
  40. package/dist/esm-node/runtime/plugin.mjs +8 -32
  41. package/dist/esm-node/runtime/plugin.node.mjs +10 -32
  42. package/dist/esm-node/runtime/pluginCore.mjs +15 -0
  43. package/dist/esm-node/runtime/register.mjs +19 -0
  44. package/dist/esm-node/runtime/routeTree.mjs +4 -198
  45. package/dist/esm-node/runtime/router.mjs +3 -0
  46. package/dist/esm-node/runtime/rsc/payloadRouter.mjs +35 -1
  47. package/dist/esm-node/runtime/state.mjs +8 -0
  48. package/dist/esm-node/runtime/utils.mjs +0 -5
  49. package/dist/types/cli/index.d.ts +9 -2
  50. package/dist/types/cli/routeSplitting.d.ts +6 -15
  51. package/dist/types/cli/tanstackTypes.d.ts +13 -2
  52. package/dist/types/runtime/hooks.d.ts +8 -18
  53. package/dist/types/runtime/index.d.ts +6 -4
  54. package/dist/types/runtime/lifecycle.d.ts +7 -22
  55. package/dist/types/runtime/loaderBridge.d.ts +48 -0
  56. package/dist/types/runtime/plugin.d.ts +1 -14
  57. package/dist/types/runtime/plugin.node.d.ts +1 -14
  58. package/dist/types/runtime/pluginCore.d.ts +21 -0
  59. package/dist/types/runtime/register.d.ts +9 -0
  60. package/dist/types/runtime/routeTree.d.ts +0 -2
  61. package/dist/types/runtime/router.d.ts +14 -0
  62. package/dist/types/runtime/state.d.ts +16 -0
  63. package/dist/types/runtime/types.d.ts +7 -53
  64. package/package.json +30 -28
  65. package/rstest.config.mts +6 -0
  66. package/src/cli/index.ts +111 -29
  67. package/src/cli/routeSplitting.ts +6 -44
  68. package/src/cli/tanstackTypes.ts +78 -214
  69. package/src/runtime/hooks.ts +10 -27
  70. package/src/runtime/index.tsx +12 -107
  71. package/src/runtime/lifecycle.ts +16 -151
  72. package/src/runtime/loaderBridge.ts +257 -0
  73. package/src/runtime/plugin.node.tsx +14 -77
  74. package/src/runtime/plugin.tsx +12 -72
  75. package/src/runtime/pluginCore.ts +48 -0
  76. package/src/runtime/register.ts +58 -0
  77. package/src/runtime/routeTree.ts +8 -370
  78. package/src/runtime/router.ts +15 -0
  79. package/src/runtime/rsc/payloadRouter.ts +45 -2
  80. package/src/runtime/state.ts +29 -0
  81. package/src/runtime/types.ts +20 -67
  82. package/src/runtime/utils.tsx +3 -6
  83. package/tests/router/cli.test.ts +297 -31
  84. package/tests/router/hooks.test.ts +26 -0
  85. package/tests/router/loaderBridge.test.ts +211 -0
  86. package/tests/router/packageSurface.test.ts +24 -0
  87. package/tests/router/register.test.ts +46 -0
  88. package/tests/router/routeTree.test.ts +65 -180
  89. package/tests/router/rsc.test.tsx +70 -0
  90. package/tests/router/tanstackTypes.test.ts +164 -6
  91. package/dist/esm/runtime/DefaultNotFound.mjs +0 -13
  92. package/dist/esm-node/runtime/DefaultNotFound.mjs +0 -14
  93. package/dist/types/runtime/DefaultNotFound.d.ts +0 -2
  94. package/src/runtime/DefaultNotFound.tsx +0 -15
@@ -0,0 +1,48 @@
1
+ /** Router context shape used by the generated TanStack router types. */
2
+ export type ModernRouterContext = {
3
+ request?: Request;
4
+ requestContext?: unknown;
5
+ };
6
+ export declare function isResponse(value: unknown): value is Response;
7
+ export declare function isRedirectResponse(res: Response): boolean;
8
+ /**
9
+ * TanStack redirects are Response objects carrying the original redirect
10
+ * `options`. They must be re-thrown untouched — re-translating them through
11
+ * the Modern Response handling would lose `to`-based (internal) targets.
12
+ */
13
+ export declare function isTanstackRedirect(value: unknown): boolean;
14
+ export declare function isAbsoluteUrl(value: string): boolean;
15
+ /**
16
+ * Re-throw a Modern.js `Location` redirect as a TanStack redirect.
17
+ *
18
+ * Prefers `to` for internal/relative redirects so the basepath rewrite can be
19
+ * applied; absolute (external) URLs go through `href` untouched.
20
+ */
21
+ export declare function throwTanstackRedirect(location: string): never;
22
+ /**
23
+ * React Router uses `*` for splat params, TanStack Router uses `_splat`.
24
+ * Modern loaders expect the React Router spelling.
25
+ */
26
+ export declare function mapSplatParamsForModernLoader(params: Record<string, string>, hasSplat: boolean): Record<string, string>;
27
+ /**
28
+ * Static-data factory used by the generated router files: drops empty fields
29
+ * so route static data stays minimal.
30
+ */
31
+ export declare function createRouteStaticData(opts: {
32
+ modernRouteId?: string;
33
+ modernRouteAction?: unknown;
34
+ modernRouteLoader?: unknown;
35
+ }): {
36
+ modernRouteId?: string;
37
+ modernRouteAction?: unknown;
38
+ modernRouteLoader?: unknown;
39
+ };
40
+ /**
41
+ * Wrap a Modern.js data loader (`page.data.ts` loader/action style) into a
42
+ * TanStack Router loader: builds a `Request` from the loader context, maps
43
+ * splat params, and translates Response redirects/404s into TanStack
44
+ * `redirect()`/`notFound()`.
45
+ */
46
+ export declare function modernLoaderToTanstack<TLoader extends (args: any) => any>(opts: {
47
+ hasSplat: boolean;
48
+ }, modernLoader: TLoader): (ctx: unknown) => Promise<Awaited<ReturnType<TLoader>>>;
@@ -1,17 +1,4 @@
1
- import type { Plugin, RuntimePluginExtends } from '@modern-js/plugin';
2
- import type { RuntimePluginAPI } from '@modern-js/plugin/runtime';
3
- import { type TInternalRuntimeContext } from '@modern-js/runtime/context';
4
- import { type RouterExtendsHooks } from './hooks';
1
+ import { type TanstackRouterRuntimePlugin } from './pluginCore';
5
2
  import { type RouterConfig } from './types';
6
- type TanstackRouterRuntimeConfig = {
7
- plugins?: TanstackRouterRuntimePlugin[];
8
- router?: Partial<RouterConfig>;
9
- [key: string]: unknown;
10
- };
11
- type TanstackRouterRuntimeExtends = Required<RuntimePluginExtends<TanstackRouterRuntimeConfig, TInternalRuntimeContext>> & {
12
- extendHooks: RouterExtendsHooks;
13
- };
14
- type TanstackRouterPluginAPI = RuntimePluginAPI<TanstackRouterRuntimeExtends>;
15
- type TanstackRouterRuntimePlugin = Plugin<TanstackRouterPluginAPI, TInternalRuntimeContext>;
16
3
  export declare const tanstackRouterPlugin: (userConfig?: Partial<RouterConfig>) => TanstackRouterRuntimePlugin;
17
4
  export default tanstackRouterPlugin;
@@ -1,17 +1,4 @@
1
- import type { Plugin, RuntimePluginExtends } from '@modern-js/plugin';
2
- import type { RuntimePluginAPI } from '@modern-js/plugin/runtime';
3
- import { type TInternalRuntimeContext } from '@modern-js/runtime/context';
4
- import { type RouterExtendsHooks } from './hooks';
1
+ import { type TanstackRouterRuntimePlugin } from './pluginCore';
5
2
  import { type RouterConfig } from './types';
6
- type TanstackRouterRuntimeConfig = {
7
- plugins?: TanstackRouterRuntimePlugin[];
8
- router?: Partial<RouterConfig>;
9
- [key: string]: unknown;
10
- };
11
- type TanstackRouterRuntimeExtends = Required<RuntimePluginExtends<TanstackRouterRuntimeConfig, TInternalRuntimeContext>> & {
12
- extendHooks: RouterExtendsHooks;
13
- };
14
- type TanstackRouterPluginAPI = RuntimePluginAPI<TanstackRouterRuntimeExtends>;
15
- type TanstackRouterRuntimePlugin = Plugin<TanstackRouterPluginAPI, TInternalRuntimeContext>;
16
3
  export declare const tanstackRouterPlugin: (userConfig?: Partial<RouterConfig>) => TanstackRouterRuntimePlugin;
17
4
  export default tanstackRouterPlugin;
@@ -0,0 +1,21 @@
1
+ import type { Plugin, RuntimePluginExtends } from '@modern-js/plugin';
2
+ import type { RuntimePluginAPI } from '@modern-js/plugin/runtime';
3
+ import { type TInternalRuntimeContext } from '@modern-js/runtime/context';
4
+ import type { RouterExtendsHooks } from './hooks';
5
+ import type { RouterConfig } from './types';
6
+ export type TanstackRouterRuntimeConfig = {
7
+ plugins?: TanstackRouterRuntimePlugin[];
8
+ router?: Partial<RouterConfig>;
9
+ [key: string]: unknown;
10
+ };
11
+ type TanstackRouterRuntimeExtends = Required<RuntimePluginExtends<TanstackRouterRuntimeConfig, TInternalRuntimeContext>> & {
12
+ extendHooks: RouterExtendsHooks;
13
+ };
14
+ export type TanstackRouterPluginAPI = RuntimePluginAPI<TanstackRouterRuntimeExtends>;
15
+ export type TanstackRouterRuntimePlugin = Plugin<TanstackRouterPluginAPI, TInternalRuntimeContext>;
16
+ export declare function getMergedRouterConfig(api: TanstackRouterPluginAPI, userConfig: Partial<RouterConfig>): RouterConfig;
17
+ export declare function getFinalRouteConfig(mergedConfig: RouterConfig): {
18
+ routes: (import("@modern-js/types").NestedRoute<string | (() => JSX.Element)> | import("@modern-js/types").PageRoute)[] | undefined;
19
+ globalApp: import("react").ComponentClass<{}, any> | import("react").FunctionComponent<{}> | undefined;
20
+ };
21
+ export {};
@@ -0,0 +1,9 @@
1
+ import { Form, RouteActionResponseError, useFetcher } from './dataMutation';
2
+ export declare const tanstackRouterCompatBindings: {
3
+ Form: typeof Form;
4
+ Link: <TRouter extends import("@tanstack/router-core").AnyRouter = import("@tanstack/router-core").AnyRouter, const TFrom extends string = string, const TTo extends string | undefined = undefined, const TMaskFrom extends string = TFrom, const TMaskTo extends string = "">(props: import("./prefetchLink").LinkProps<TRouter, TFrom, TTo, TMaskFrom, TMaskTo>) => import("react").ReactElement;
5
+ NavLink: <TRouter extends import("@tanstack/router-core").AnyRouter = import("@tanstack/router-core").AnyRouter, const TFrom extends string = string, const TTo extends string | undefined = undefined, const TMaskFrom extends string = TFrom, const TMaskTo extends string = "">(props: import("./prefetchLink").LinkProps<TRouter, TFrom, TTo, TMaskFrom, TMaskTo>) => import("react").ReactElement;
6
+ Outlet: import("react").MemoExoticComponent<() => import("react").JSX.Element>;
7
+ RouteActionResponseError: typeof RouteActionResponseError;
8
+ useFetcher: typeof useFetcher;
9
+ };
@@ -1,11 +1,9 @@
1
1
  import type { RouteObject } from '@modern-js/runtime-utils/router';
2
- import type { NestedRoute, PageRoute } from '@modern-js/types';
3
2
  import type { AnyRouter, RootRoute as TanstackRootRoute } from '@tanstack/react-router';
4
3
  type ModernTanstackRootRoute = TanstackRootRoute;
5
4
  type RouteTreeOptions = {
6
5
  rscPayloadRouter?: boolean;
7
6
  };
8
- export declare function createRouteTreeFromModernRoutes(routes: Array<NestedRoute | PageRoute>, options?: RouteTreeOptions): ModernTanstackRootRoute;
9
7
  export declare function createRouteTreeFromRouteObjects(routes: RouteObject[], options?: RouteTreeOptions): ModernTanstackRootRoute;
10
8
  export declare function getModernRouteIdsFromMatches(router: AnyRouter): string[];
11
9
  export {};
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Entry-injected router module for entrypoints that are NOT TanStack
3
+ * file-route entrypoints (e.g. apps configuring
4
+ * `router: { framework: 'tanstack', createRoutes }` in modern.runtime.ts).
5
+ *
6
+ * The CLI plugin injects `{ name: 'router', path: '<pkg>/runtime/router' }`
7
+ * for those entries, so the generated entry code value-imports `routerPlugin`
8
+ * from here. That single import both installs the framework-resolving router
9
+ * plugin of @modern-js/runtime AND registers the TanStack router provider
10
+ * (the './register' side effect) — the registration can therefore never be
11
+ * tree-shaken away from the entry that needs it.
12
+ */
13
+ import './register';
14
+ export { routerPlugin } from '@modern-js/runtime/router/internal';
@@ -0,0 +1,16 @@
1
+ import type { AnyRouter } from '@tanstack/react-router';
2
+ import type { InternalRouterRuntimeState } from './types';
3
+ /**
4
+ * Router runtime state as published by the TanStack router provider into the
5
+ * runtime-context extension slot.
6
+ */
7
+ export interface TanstackRouterState extends Omit<InternalRouterRuntimeState, 'framework' | 'instance'> {
8
+ framework: 'tanstack';
9
+ instance?: AnyRouter;
10
+ }
11
+ /**
12
+ * Typed accessor for the TanStack router state stored on a Modern.js runtime
13
+ * context. Returns `undefined` when the active router provider is not
14
+ * TanStack (e.g. react-router) or no router has been created yet.
15
+ */
16
+ export declare function getTanstackRouterState(context: object): TanstackRouterState | undefined;
@@ -1,23 +1,23 @@
1
- import type { RequestContext } from '@modern-js/runtime-utils/node';
1
+ import type { RouterFramework } from '@modern-js/runtime/context';
2
2
  import type { RouteObject } from '@modern-js/runtime-utils/router';
3
3
  import type { NestedRoute, PageRoute } from '@modern-js/types';
4
4
  import type React from 'react';
5
- export type BuiltInRouterFramework = 'react-router' | 'tanstack';
6
- export type RouterFramework = BuiltInRouterFramework | (string & {});
5
+ export type { BuiltInRouterFramework, InternalRouterRuntimeState, InternalRouterServerSnapshot, RouterFramework, RouterRouteMatchSnapshot, RouterServerPrepareResult, } from '@modern-js/runtime/context';
6
+ /**
7
+ * TanStack-specific router config. Unlike the react-router provider config,
8
+ * this intentionally has no `oldVersion`/`future` fields — those are
9
+ * react-router-only knobs with no meaning here.
10
+ */
7
11
  export type RouterConfig = {
8
12
  framework?: RouterFramework;
9
13
  routesConfig: {
10
14
  globalApp?: React.ComponentType<any>;
11
15
  routes?: (NestedRoute | PageRoute)[];
12
16
  };
13
- oldVersion?: boolean;
14
17
  serverBase?: string[];
15
18
  supportHtml5History?: boolean;
16
19
  basename?: string;
17
20
  createRoutes?: () => RouteObject[];
18
- future?: Partial<{
19
- v7_startTransition: boolean;
20
- }>;
21
21
  defaultStructuralSharing?: boolean;
22
22
  unstable_reloadOnURLMismatch?: boolean;
23
23
  };
@@ -27,49 +27,3 @@ export declare const modernTanstackRouterFastDefaults: {
27
27
  export declare const getModernTanstackRouterFastDefaults: (config?: Partial<Pick<RouterConfig, 'defaultStructuralSharing'>>) => {
28
28
  defaultStructuralSharing: boolean;
29
29
  };
30
- export interface RouterRouteMatchSnapshot {
31
- routeId: string;
32
- assetRouteId?: string;
33
- pathname?: string;
34
- params?: Record<string, string>;
35
- }
36
- export interface InternalRouterServerSnapshot {
37
- framework?: RouterFramework;
38
- basename?: string;
39
- statusCode?: number;
40
- errors?: Record<string, unknown>;
41
- routerData?: {
42
- loaderData?: Record<string, unknown>;
43
- errors?: Record<string, unknown>;
44
- };
45
- hydrationScript?: string;
46
- hydrationScripts?: string[];
47
- matchedRouteIds?: string[];
48
- matches?: RouterRouteMatchSnapshot[];
49
- }
50
- export interface InternalRouterRuntimeState {
51
- framework: RouterFramework;
52
- basename?: string;
53
- instance?: unknown;
54
- hydrationScript?: string;
55
- hydrationScripts?: string[];
56
- matchedRouteIds?: string[];
57
- matches?: RouterRouteMatchSnapshot[];
58
- serverSnapshot?: InternalRouterServerSnapshot;
59
- cleanup?: () => void | Promise<void>;
60
- }
61
- export interface RouterServerPrepareResult {
62
- state: InternalRouterRuntimeState;
63
- snapshot?: InternalRouterServerSnapshot;
64
- redirect?: Response;
65
- cleanup?: () => void | Promise<void>;
66
- }
67
- interface DataFunctionArgs<D = any> {
68
- request: Request;
69
- params: Record<string, string>;
70
- context?: D;
71
- }
72
- export type LoaderFunctionArgs<P extends Record<string, unknown> = Record<string, unknown>> = DataFunctionArgs<RequestContext<P>>;
73
- type DataFunctionValue = Response | NonNullable<unknown> | null;
74
- export type LoaderFunction = <P extends Record<string, unknown> = Record<string, unknown>>(args: LoaderFunctionArgs<P>) => Promise<DataFunctionValue> | DataFunctionValue;
75
- export {};
package/package.json CHANGED
@@ -18,7 +18,7 @@
18
18
  "modern.js",
19
19
  "tanstack-router"
20
20
  ],
21
- "version": "3.2.0-ultramodern.120",
21
+ "version": "3.2.0-ultramodern.121",
22
22
  "engines": {
23
23
  "node": ">=20"
24
24
  },
@@ -49,17 +49,12 @@
49
49
  },
50
50
  "default": "./dist/esm/runtime/index.mjs"
51
51
  },
52
- "./runtime/rsc": {
53
- "types": "./dist/types/runtime/rsc/index.d.ts",
54
- "default": "./dist/esm/runtime/rsc/index.mjs"
55
- },
56
- "./runtime/rsc/client": {
57
- "types": "./dist/types/runtime/rsc/client.d.ts",
58
- "default": "./dist/esm/runtime/rsc/client.mjs"
59
- },
60
- "./runtime/rsc/server": {
61
- "types": "./dist/types/runtime/rsc/server.d.ts",
62
- "default": "./dist/esm/runtime/rsc/server.mjs"
52
+ "./runtime/router": {
53
+ "types": "./dist/types/runtime/router.d.ts",
54
+ "node": {
55
+ "module": "./dist/esm/runtime/router.mjs"
56
+ },
57
+ "default": "./dist/esm/runtime/router.mjs"
63
58
  }
64
59
  },
65
60
  "typesVersions": {
@@ -73,14 +68,8 @@
73
68
  "runtime": [
74
69
  "./dist/types/runtime/index.d.ts"
75
70
  ],
76
- "runtime/rsc": [
77
- "./dist/types/runtime/rsc/index.d.ts"
78
- ],
79
- "runtime/rsc/client": [
80
- "./dist/types/runtime/rsc/client.d.ts"
81
- ],
82
- "runtime/rsc/server": [
83
- "./dist/types/runtime/rsc/server.d.ts"
71
+ "runtime/router": [
72
+ "./dist/types/runtime/router.d.ts"
84
73
  ]
85
74
  }
86
75
  },
@@ -88,13 +77,13 @@
88
77
  "@swc/helpers": "^0.5.23",
89
78
  "@tanstack/react-router": "1.170.15",
90
79
  "@tanstack/router-core": "1.171.13",
91
- "@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.2.0-ultramodern.120",
92
- "@modern-js/runtime-utils": "npm:@bleedingdev/modern-js-runtime-utils@3.2.0-ultramodern.120",
93
- "@modern-js/types": "npm:@bleedingdev/modern-js-types@3.2.0-ultramodern.120",
94
- "@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.2.0-ultramodern.120"
80
+ "@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.2.0-ultramodern.121",
81
+ "@modern-js/runtime-utils": "npm:@bleedingdev/modern-js-runtime-utils@3.2.0-ultramodern.121",
82
+ "@modern-js/types": "npm:@bleedingdev/modern-js-types@3.2.0-ultramodern.121",
83
+ "@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.2.0-ultramodern.121"
95
84
  },
96
85
  "peerDependencies": {
97
- "@modern-js/runtime": "3.2.0-ultramodern.120",
86
+ "@modern-js/runtime": "3.2.0-ultramodern.121",
98
87
  "react": "^19.2.7",
99
88
  "react-dom": "^19.2.7"
100
89
  },
@@ -109,11 +98,24 @@
109
98
  "@typescript/native-preview": "7.0.0-dev.20260610.1",
110
99
  "react": "^19.2.7",
111
100
  "react-dom": "^19.2.7",
112
- "@modern-js/app-tools": "npm:@bleedingdev/modern-js-app-tools@3.2.0-ultramodern.120",
113
- "@modern-js/runtime": "npm:@bleedingdev/modern-js-runtime@3.2.0-ultramodern.120",
101
+ "@modern-js/app-tools": "npm:@bleedingdev/modern-js-app-tools@3.2.0-ultramodern.121",
102
+ "@modern-js/runtime": "npm:@bleedingdev/modern-js-runtime@3.2.0-ultramodern.121",
114
103
  "@scripts/rstest-config": "2.66.0"
115
104
  },
116
- "sideEffects": false,
105
+ "sideEffects": [
106
+ "./src/runtime/register.ts",
107
+ "./src/runtime/index.tsx",
108
+ "./src/runtime/router.ts",
109
+ "./dist/esm/runtime/register.mjs",
110
+ "./dist/esm/runtime/index.mjs",
111
+ "./dist/esm/runtime/router.mjs",
112
+ "./dist/esm-node/runtime/register.mjs",
113
+ "./dist/esm-node/runtime/index.mjs",
114
+ "./dist/esm-node/runtime/router.mjs",
115
+ "./dist/cjs/runtime/register.js",
116
+ "./dist/cjs/runtime/index.js",
117
+ "./dist/cjs/runtime/router.js"
118
+ ],
117
119
  "publishConfig": {
118
120
  "registry": "https://registry.npmjs.org/",
119
121
  "access": "public"
package/rstest.config.mts CHANGED
@@ -24,6 +24,11 @@ export default {
24
24
  testEnvironment: 'node',
25
25
  include: [
26
26
  'tests/router/cli.test.ts',
27
+ 'tests/router/fastDefaults.test.ts',
28
+ 'tests/router/hooks.test.ts',
29
+ 'tests/router/loaderBridge.test.ts',
30
+ 'tests/router/packageSurface.test.ts',
31
+ 'tests/router/register.test.ts',
27
32
  'tests/router/rsc.test.tsx',
28
33
  'tests/router/tanstackTypes.test.ts',
29
34
  'tests/router/routeTree.test.ts',
@@ -35,6 +40,7 @@ export default {
35
40
  testEnvironment: 'happy-dom',
36
41
  include: [
37
42
  'tests/router/dataMutation.test.tsx',
43
+ 'tests/router/hydrationBoundary.test.tsx',
38
44
  'tests/router/prefetchLink.test.tsx',
39
45
  ],
40
46
  extends: commonConfig,
package/src/cli/index.ts CHANGED
@@ -25,7 +25,6 @@ import {
25
25
  import {
26
26
  collectCanonicalRoutesForEntry,
27
27
  generateTanstackRouterTypesSourceForEntry,
28
- isTanstackRouterFrameworkEnabled,
29
28
  } from './tanstackTypes';
30
29
 
31
30
  export type {
@@ -34,13 +33,12 @@ export type {
34
33
  } from './routeSplitting';
35
34
  export {
36
35
  createTanstackRsbuildRouteSplittingProfile,
37
- isTanstackStartRouteModuleSource,
38
36
  resolveTanstackRouteCodeSplittingEnabled,
39
37
  } from './routeSplitting';
40
38
  export {
39
+ type CollectCanonicalRoutesOptions,
41
40
  collectCanonicalRoutesForEntry,
42
41
  generateTanstackRouterTypesSourceForEntry,
43
- isTanstackRouterFrameworkEnabled,
44
42
  } from './tanstackTypes';
45
43
 
46
44
  const DEFAULT_ROUTES_DIR = 'routes';
@@ -55,6 +53,7 @@ export type TanstackRouterPluginOptions = {
55
53
 
56
54
  type RuntimeRouterCliHelpers = {
57
55
  getEntrypointRoutesDir: (entrypoint: Entrypoint) => string | null;
56
+ getEntrypointRoutesOwner: (entrypoint: Entrypoint) => string | null;
58
57
  handleFileChange: (
59
58
  api: CLIPluginAPI<AppTools>,
60
59
  event: unknown,
@@ -74,9 +73,6 @@ type RuntimeRouterCliHelpers = {
74
73
  entrypoints: Entrypoint[],
75
74
  options?: {
76
75
  entrypointsKey?: string;
77
- generateCodeOptions?: {
78
- enableTanstackTypes?: boolean;
79
- };
80
76
  },
81
77
  ) => Promise<Record<string, (NestedRouteForCli | PageRoute)[]>>;
82
78
  handleModifyEntrypoints: (
@@ -98,7 +94,11 @@ function getRuntimeRouterCli(): RuntimeRouterCliHelpers {
98
94
 
99
95
  const cli =
100
96
  require('@modern-js/runtime/cli') as Partial<RuntimeRouterCliHelpers>;
101
- if (cli.handleGeneratorEntryCode && cli.getEntrypointRoutesDir) {
97
+ if (
98
+ cli.handleGeneratorEntryCode &&
99
+ cli.getEntrypointRoutesDir &&
100
+ cli.getEntrypointRoutesOwner
101
+ ) {
102
102
  runtimeRouterCli = cli as RuntimeRouterCliHelpers;
103
103
  return runtimeRouterCli;
104
104
  }
@@ -207,11 +207,19 @@ export async function writeTanstackRouterTypesForEntries(opts: {
207
207
  appContext: AppToolsContext;
208
208
  generatedDirName?: string;
209
209
  routesByEntry: Record<string, (NestedRouteForCli | PageRoute)[]>;
210
+ /**
211
+ * Whether `@modern-js/plugin-i18n` is actually installed in the app. The
212
+ * `register.gen.d.ts` augmentation of '@modern-js/plugin-i18n/runtime' is
213
+ * only emitted when this is true — otherwise apps with a hand-rolled
214
+ * `/:lang/` param would fail typechecking on an unresolvable module.
215
+ */
216
+ i18nPluginInstalled?: boolean;
210
217
  }) {
211
218
  const {
212
219
  appContext,
213
220
  generatedDirName = DEFAULT_GENERATED_DIR_NAME,
214
221
  routesByEntry,
222
+ i18nPluginInstalled = false,
215
223
  } = opts;
216
224
 
217
225
  const entryNames = Object.keys(routesByEntry);
@@ -253,14 +261,22 @@ export async function writeTanstackRouterTypesForEntries(opts: {
253
261
  });
254
262
 
255
263
  // Merge the canonical (language-agnostic) route maps of every entry so the
256
- // typed i18n Link covers all routes the app can navigate to.
264
+ // typed i18n Link covers all routes the app can navigate to. Only relevant
265
+ // when plugin-i18n is actually installed — without it the emitted module
266
+ // augmentation would reference an unresolvable module.
257
267
  let canonicalRoutes: Record<string, string> | null = null;
258
- for (const entryName of registerEntries) {
259
- const entryCanonicalRoutes = collectCanonicalRoutesForEntry(
260
- routesByEntry[entryName],
261
- );
262
- if (entryCanonicalRoutes) {
263
- canonicalRoutes = { ...entryCanonicalRoutes, ...(canonicalRoutes ?? {}) };
268
+ if (i18nPluginInstalled) {
269
+ for (const entryName of registerEntries) {
270
+ const entryCanonicalRoutes = collectCanonicalRoutesForEntry(
271
+ routesByEntry[entryName],
272
+ { localeParamHeuristic: true },
273
+ );
274
+ if (entryCanonicalRoutes) {
275
+ canonicalRoutes = {
276
+ ...entryCanonicalRoutes,
277
+ ...(canonicalRoutes ?? {}),
278
+ };
279
+ }
264
280
  }
265
281
  }
266
282
 
@@ -292,11 +308,38 @@ export function tanstackRouterPlugin(
292
308
  return getEntrypointRoutesDir(entrypoint) === routesDir;
293
309
  };
294
310
 
295
- api._internalRuntimePlugins(({ entrypoint, plugins }) => {
296
- if (!isTanstackEntrypoint(entrypoint as Entrypoint)) {
297
- return { entrypoint, plugins };
311
+ // Entrypoints claimed by another file-route convention — the built-in
312
+ // pages/ or routes/ entries of @modern-js/runtime's router plugin, or
313
+ // an entry tagged by a different routes-owner plugin. Their router
314
+ // runtime plugin must be left untouched: redirecting it through our
315
+ // wrapper would value-import the TanStack runtime into bundles that
316
+ // never use it, and pushing a second `router` plugin can install two
317
+ // routers for one entry.
318
+ const isForeignRouteEntrypoint = (entrypoint: Entrypoint) => {
319
+ const { getEntrypointRoutesDir, getEntrypointRoutesOwner } =
320
+ getRuntimeRouterCli();
321
+ if (getEntrypointRoutesOwner(entrypoint)) {
322
+ // Owned by some routes-owner plugin. TanStack-owned entries were
323
+ // already claimed by the isTanstackEntrypoint branch, so any owner
324
+ // seen here is foreign.
325
+ return true;
326
+ }
327
+ if (entrypoint.pageRoutesEntry) {
328
+ return true;
298
329
  }
330
+ return getEntrypointRoutesDir(entrypoint) !== null;
331
+ };
332
+
333
+ const isI18nPluginInstalled = () => {
334
+ const { plugins } = api.getAppContext() as {
335
+ plugins?: Array<{ name?: string }>;
336
+ };
337
+ return Boolean(
338
+ plugins?.some(plugin => plugin?.name === '@modern-js/plugin-i18n'),
339
+ );
340
+ };
299
341
 
342
+ api._internalRuntimePlugins(({ entrypoint, plugins }) => {
300
343
  const { metaName, serverRoutes } = api.getAppContext();
301
344
  const serverBase = serverRoutes
302
345
  .filter(
@@ -305,11 +348,44 @@ export function tanstackRouterPlugin(
305
348
  .map(route => route.urlPath)
306
349
  .sort((a, b) => (a.length - b.length > 0 ? -1 : 1));
307
350
 
308
- plugins.push({
309
- name: 'tanstackRouter',
310
- path: `@${metaName}/plugin-tanstack/runtime`,
311
- config: { serverBase },
312
- });
351
+ if (isTanstackEntrypoint(entrypoint as Entrypoint)) {
352
+ plugins.push({
353
+ name: 'tanstackRouter',
354
+ path: `@${metaName}/plugin-tanstack/runtime`,
355
+ config: { serverBase },
356
+ });
357
+
358
+ return { entrypoint, plugins };
359
+ }
360
+
361
+ // Entries owned by the built-in router (classic routes/ or pages/
362
+ // conventions) or by another routes-owner plugin keep their own
363
+ // router runtime plugin untouched.
364
+ if (isForeignRouteEntrypoint(entrypoint as Entrypoint)) {
365
+ return { entrypoint, plugins };
366
+ }
367
+
368
+ // True custom entry without any file-route convention (`createRoutes`
369
+ // apps, hand-rolled App entries, ...): having this plugin installed
370
+ // is the explicit signal that the app routes through the
371
+ // router-provider registry — no source sniffing of modern.runtime.ts.
372
+ // Inject the framework-resolving router plugin of @modern-js/runtime
373
+ // through our own runtime/router module, so the TanStack provider
374
+ // registration is value-imported together with it and can never be
375
+ // tree-shaken away from the entry.
376
+ const routerWrapperPath = `@${metaName}/plugin-tanstack/runtime/router`;
377
+ const existingRouterPlugin = plugins.find(
378
+ plugin => plugin.name === 'router',
379
+ );
380
+ if (existingRouterPlugin) {
381
+ existingRouterPlugin.path = routerWrapperPath;
382
+ } else {
383
+ plugins.push({
384
+ name: 'router',
385
+ path: routerWrapperPath,
386
+ config: { serverBase },
387
+ });
388
+ }
313
389
 
314
390
  return { entrypoint, plugins };
315
391
  });
@@ -324,8 +400,18 @@ export function tanstackRouterPlugin(
324
400
  ...routeSplittingProfile.defaultConfig,
325
401
  source: {
326
402
  include: [
403
+ // TanStack Router and its runtime deps ship modern syntax and
404
+ // must be down-leveled for the app's browser targets.
327
405
  /[\\/]node_modules[\\/]@tanstack[\\/]react-router[\\/]/,
328
- path.resolve(__dirname, '../runtime').replace('cjs', 'esm'),
406
+ /[\\/]node_modules[\\/]@tanstack[\\/]router-core[\\/]/,
407
+ /[\\/]node_modules[\\/]@tanstack[\\/]react-store[\\/]/,
408
+ // This package's own dist runtime, too. `__dirname` is
409
+ // dist/{cjs,esm-node}/cli (or src/cli in tests), so the package
410
+ // dist root is two levels up. Resolution-based — no string
411
+ // surgery: the old `.replace('cjs', 'esm')` corrupted workspace
412
+ // paths containing 'cjs' and, under the ESM CLI condition, never
413
+ // matched the dist/esm runtime that browsers actually bundle.
414
+ path.resolve(__dirname, '..', '..'),
329
415
  ],
330
416
  },
331
417
  }));
@@ -352,9 +438,6 @@ export function tanstackRouterPlugin(
352
438
  tanstackEntrypoints,
353
439
  {
354
440
  entrypointsKey: ENTRYPOINTS_KEY,
355
- generateCodeOptions: {
356
- enableTanstackTypes: false,
357
- },
358
441
  },
359
442
  );
360
443
 
@@ -362,6 +445,7 @@ export function tanstackRouterPlugin(
362
445
  appContext: api.getAppContext(),
363
446
  generatedDirName,
364
447
  routesByEntry,
448
+ i18nPluginInstalled: isI18nPluginInstalled(),
365
449
  });
366
450
  });
367
451
 
@@ -380,9 +464,6 @@ export function tanstackRouterPlugin(
380
464
  entrypoints,
381
465
  {
382
466
  entrypointsKey: ENTRYPOINTS_KEY,
383
- generateCodeOptions: {
384
- enableTanstackTypes: false,
385
- },
386
467
  },
387
468
  );
388
469
 
@@ -390,6 +471,7 @@ export function tanstackRouterPlugin(
390
471
  appContext: api.getAppContext(),
391
472
  generatedDirName,
392
473
  routesByEntry,
474
+ i18nPluginInstalled: isI18nPluginInstalled(),
393
475
  });
394
476
  },
395
477
  });