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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/dist/cjs/cli/artifacts.js +15 -1
  2. package/dist/cjs/cli/index.js +13 -2
  3. package/dist/cjs/cli/routeMetadata.js +131 -0
  4. package/dist/cjs/cli/rscRouteLayer.js +86 -0
  5. package/dist/cjs/cli/tanstackTypes/canonicalRoutes.js +7 -7
  6. package/dist/cjs/cli/tanstackTypes/shared.js +0 -14
  7. package/dist/cjs/cli/tanstackTypes/source.js +28 -63
  8. package/dist/cjs/runtime/lifecycle.js +0 -3
  9. package/dist/cjs/runtime/loaderBridge.js +10 -4
  10. package/dist/cjs/runtime/navigation.js +102 -0
  11. package/dist/cjs/runtime/plugin.js +10 -1
  12. package/dist/cjs/runtime/plugin.node.js +89 -64
  13. package/dist/cjs/runtime/routeTree/construction.js +19 -61
  14. package/dist/cjs/runtime/routeTree/loaders.js +1 -1
  15. package/dist/cjs/runtime/routeTree/paths.js +0 -6
  16. package/dist/cjs/runtime/ssrManagedTags.js +2 -12
  17. package/dist/cjs/runtime/utils.js +1 -0
  18. package/dist/cjs/shared/routeDescriptor.js +60 -0
  19. package/dist/esm/cli/artifacts.mjs +13 -2
  20. package/dist/esm/cli/index.mjs +13 -11
  21. package/dist/esm/cli/routeMetadata.mjs +71 -0
  22. package/dist/esm/cli/rscRouteLayer.mjs +35 -0
  23. package/dist/esm/cli/tanstackTypes/canonicalRoutes.mjs +7 -7
  24. package/dist/esm/cli/tanstackTypes/shared.mjs +1 -9
  25. package/dist/esm/cli/tanstackTypes/source.mjs +29 -64
  26. package/dist/esm/runtime/lifecycle.mjs +1 -1
  27. package/dist/esm/runtime/loaderBridge.mjs +8 -5
  28. package/dist/esm/runtime/navigation.mjs +64 -0
  29. package/dist/esm/runtime/plugin.mjs +10 -1
  30. package/dist/esm/runtime/plugin.node.mjs +91 -66
  31. package/dist/esm/runtime/routeTree/construction.mjs +19 -61
  32. package/dist/esm/runtime/routeTree/loaders.mjs +2 -2
  33. package/dist/esm/runtime/routeTree/paths.mjs +1 -4
  34. package/dist/esm/runtime/ssrManagedTags.mjs +1 -8
  35. package/dist/esm/runtime/utils.mjs +1 -0
  36. package/dist/esm/shared/routeDescriptor.mjs +22 -0
  37. package/dist/esm-node/cli/artifacts.mjs +13 -2
  38. package/dist/esm-node/cli/index.mjs +13 -11
  39. package/dist/esm-node/cli/routeMetadata.mjs +72 -0
  40. package/dist/esm-node/cli/rscRouteLayer.mjs +36 -0
  41. package/dist/esm-node/cli/tanstackTypes/canonicalRoutes.mjs +7 -7
  42. package/dist/esm-node/cli/tanstackTypes/shared.mjs +1 -9
  43. package/dist/esm-node/cli/tanstackTypes/source.mjs +29 -64
  44. package/dist/esm-node/runtime/lifecycle.mjs +1 -1
  45. package/dist/esm-node/runtime/loaderBridge.mjs +8 -5
  46. package/dist/esm-node/runtime/navigation.mjs +65 -0
  47. package/dist/esm-node/runtime/plugin.mjs +10 -1
  48. package/dist/esm-node/runtime/plugin.node.mjs +91 -66
  49. package/dist/esm-node/runtime/routeTree/construction.mjs +19 -61
  50. package/dist/esm-node/runtime/routeTree/loaders.mjs +2 -2
  51. package/dist/esm-node/runtime/routeTree/paths.mjs +1 -4
  52. package/dist/esm-node/runtime/ssrManagedTags.mjs +1 -8
  53. package/dist/esm-node/runtime/utils.mjs +1 -0
  54. package/dist/esm-node/shared/routeDescriptor.mjs +23 -0
  55. package/dist/types/cli/artifacts.d.ts +16 -7
  56. package/dist/types/cli/index.d.ts +1 -1
  57. package/dist/types/cli/routeMetadata.d.ts +18 -0
  58. package/dist/types/cli/rscRouteLayer.d.ts +11 -0
  59. package/dist/types/cli/tanstackTypes/shared.d.ts +0 -2
  60. package/dist/types/cli/tanstackTypes/source.d.ts +3 -2
  61. package/dist/types/runtime/lifecycle.d.ts +1 -1
  62. package/dist/types/runtime/loaderBridge.d.ts +1 -0
  63. package/dist/types/runtime/navigation.d.ts +4 -0
  64. package/dist/types/runtime/pluginShared.d.ts +5 -0
  65. package/dist/types/runtime/routeTree/paths.d.ts +0 -2
  66. package/dist/types/runtime/ssrManagedTags.d.ts +0 -1
  67. package/dist/types/runtime/ssrTypes.d.ts +0 -7
  68. package/dist/types/shared/routeDescriptor.d.ts +26 -0
  69. package/package.json +32 -26
  70. package/src/cli/artifacts.ts +59 -8
  71. package/src/cli/index.ts +17 -4
  72. package/src/cli/routeMetadata.ts +121 -0
  73. package/src/cli/rscRouteLayer.ts +93 -0
  74. package/src/cli/tanstackTypes/canonicalRoutes.ts +17 -12
  75. package/src/cli/tanstackTypes/shared.ts +0 -14
  76. package/src/cli/tanstackTypes/source.ts +48 -120
  77. package/src/runtime/lifecycle.ts +0 -1
  78. package/src/runtime/loaderBridge.ts +9 -9
  79. package/src/runtime/navigation.ts +78 -0
  80. package/src/runtime/plugin.node.tsx +121 -91
  81. package/src/runtime/plugin.tsx +10 -0
  82. package/src/runtime/pluginShared.ts +12 -0
  83. package/src/runtime/routeTree/construction.ts +32 -112
  84. package/src/runtime/routeTree/loaders.ts +2 -4
  85. package/src/runtime/routeTree/paths.ts +0 -6
  86. package/src/runtime/ssrManagedTags.ts +0 -17
  87. package/src/runtime/ssrTypes.ts +0 -7
  88. package/src/runtime/utils.tsx +1 -0
  89. package/src/shared/routeDescriptor.ts +46 -0
  90. package/dist/esm/rslib-runtime~1.mjs +0 -18
  91. package/dist/esm-node/rslib-runtime~0.mjs +0 -19
  92. package/rslib.config.mts +0 -4
  93. package/rstest.config.mts +0 -53
  94. package/tests/router/cli.test.ts +0 -420
  95. package/tests/router/clientHydration.test.tsx +0 -200
  96. package/tests/router/dataMutation.test.tsx +0 -428
  97. package/tests/router/fixtures/router-gen.oxlint.json +0 -5
  98. package/tests/router/flightSerialization.roundtrip.test.tsx +0 -140
  99. package/tests/router/generateRouteArtifacts.test.ts +0 -146
  100. package/tests/router/loaderBridge.test.ts +0 -207
  101. package/tests/router/localisedIdentity.test.tsx +0 -95
  102. package/tests/router/prefetchLink.test.tsx +0 -122
  103. package/tests/router/prefetchLinkPreload.test.tsx +0 -65
  104. package/tests/router/routeHooks.test.ts +0 -49
  105. package/tests/router/routeTree.test.ts +0 -240
  106. package/tests/router/rsc.test.tsx +0 -480
  107. package/tests/router/serverPlugin.test.ts +0 -157
  108. package/tests/router/slotUsageSanitizer.test.ts +0 -23
  109. package/tests/router/ssrPreload.test.ts +0 -30
  110. package/tests/router/tanstackTypes.test.ts +0 -384
  111. package/tsconfig.json +0 -12
  112. package/tsconfig.tsgo.json +0 -6
@@ -6,23 +6,6 @@ import type {
6
6
  TanstackRouterWithServerSsr,
7
7
  } from './ssrTypes';
8
8
 
9
- export async function waitForRouterSerialization(
10
- tanstackRouter: TanstackRouterWithServerSsr,
11
- ) {
12
- const serverSsr = tanstackRouter.serverSsr;
13
- if (
14
- !serverSsr ||
15
- typeof serverSsr.onSerializationFinished !== 'function' ||
16
- serverSsr.isSerializationFinished?.()
17
- ) {
18
- return;
19
- }
20
-
21
- await new Promise<void>(resolve => {
22
- serverSsr.onSerializationFinished?.(resolve);
23
- });
24
- }
25
-
26
9
  function htmlEscapeAttr(value: string) {
27
10
  return value.replace(/&/g, '&amp;').replace(/"/g, '&quot;');
28
11
  }
@@ -34,13 +34,6 @@ type RouterRouteWithOptions = {
34
34
 
35
35
  export type TanstackRouterWithServerSsr = AnyRouter & {
36
36
  routesById?: Record<string, RouterRouteWithOptions>;
37
- serverSsr?: {
38
- cleanup?: () => void;
39
- dehydrate?: () => Promise<void> | void;
40
- isSerializationFinished?: () => boolean;
41
- onSerializationFinished?: (listener: () => void) => void;
42
- takeBufferedScripts?: () => unknown;
43
- };
44
37
  state: AnyRouter['state'] & {
45
38
  matches?: unknown;
46
39
  };
@@ -55,6 +55,7 @@ function toTanstackRouteObject(
55
55
  if (route.type === 'nested') {
56
56
  return {
57
57
  modernLocalisedRoute: Reflect.get(route, 'modernLocalisedRoute'),
58
+ isRoot: Reflect.get(route, 'isRoot'),
58
59
  path: route.path,
59
60
  id: route.id,
60
61
  loader: route.loader,
@@ -0,0 +1,46 @@
1
+ import { toTanstackPath } from '../runtime/routeTree/paths';
2
+
3
+ type RouteShape = {
4
+ id?: string;
5
+ file?: string;
6
+ path?: string;
7
+ index?: boolean;
8
+ isRoot?: boolean;
9
+ children?: RouteShape[];
10
+ };
11
+
12
+ /** Browser-safe structure shared by the executable tree and concrete TS emitter. */
13
+ export type RouteDescriptor<T extends RouteShape> = {
14
+ source: T;
15
+ location: { id: string; path?: never } | { path: string; id?: never };
16
+ hasSplat: boolean;
17
+ children: RouteDescriptor<T>[];
18
+ };
19
+
20
+ export function describeRouteTree<T extends RouteShape>(routes: T[]) {
21
+ const root = routes.find(
22
+ route =>
23
+ route.isRoot === true || (route.path === '/' && route.index !== true),
24
+ );
25
+ const describe = (route: T, index: number): RouteDescriptor<T> => ({
26
+ source: route,
27
+ location:
28
+ (route.path ?? '').length === 0 && route.index !== true
29
+ ? { id: route.id || route.file || `pathless-${index}` }
30
+ : {
31
+ path: route.index === true ? '/' : toTanstackPath(route.path || ''),
32
+ },
33
+ hasSplat: Boolean(route.path?.includes('*')),
34
+ children: ((route.children ?? []) as T[]).map(describe),
35
+ });
36
+ return {
37
+ root,
38
+ children: (root !== undefined
39
+ ? [
40
+ ...((root.children ?? []) as T[]),
41
+ ...routes.filter(route => route !== root),
42
+ ]
43
+ : routes
44
+ ).map(describe),
45
+ };
46
+ }
@@ -1,18 +0,0 @@
1
- var __webpack_modules__ = {};
2
- var __webpack_module_cache__ = {};
3
- function __webpack_require__(moduleId) {
4
- var cachedModule = __webpack_module_cache__[moduleId];
5
- if (void 0 !== cachedModule) return cachedModule.exports;
6
- var module = __webpack_module_cache__[moduleId] = {
7
- exports: {}
8
- };
9
- __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
10
- return module.exports;
11
- }
12
- __webpack_require__.m = __webpack_modules__;
13
- (()=>{
14
- __webpack_require__.add = function(modules) {
15
- Object.assign(__webpack_require__.m, modules);
16
- };
17
- })();
18
- export { __webpack_require__ };
@@ -1,19 +0,0 @@
1
- import "node:module";
2
- var __webpack_modules__ = {};
3
- var __webpack_module_cache__ = {};
4
- function __webpack_require__(moduleId) {
5
- var cachedModule = __webpack_module_cache__[moduleId];
6
- if (void 0 !== cachedModule) return cachedModule.exports;
7
- var module = __webpack_module_cache__[moduleId] = {
8
- exports: {}
9
- };
10
- __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
11
- return module.exports;
12
- }
13
- __webpack_require__.m = __webpack_modules__;
14
- (()=>{
15
- __webpack_require__.add = function(modules) {
16
- Object.assign(__webpack_require__.m, modules);
17
- };
18
- })();
19
- export { __webpack_require__ };
package/rslib.config.mts DELETED
@@ -1,4 +0,0 @@
1
- import { rslibConfig } from '@modern-js/rslib';
2
- import { defineConfig } from '@rslib/core';
3
-
4
- export default defineConfig(rslibConfig);
package/rstest.config.mts DELETED
@@ -1,53 +0,0 @@
1
- import type { ProjectConfig } from '@rstest/core';
2
- import { withTestPreset } from '@scripts/rstest-config';
3
-
4
- const commonConfig: ProjectConfig = {
5
- setupFiles: ['@scripts/rstest-config/setup.ts'],
6
- globals: true,
7
- tools: {
8
- swc: {
9
- jsc: {
10
- transform: {
11
- react: {
12
- runtime: 'automatic',
13
- },
14
- },
15
- },
16
- },
17
- },
18
- };
19
-
20
- export default {
21
- projects: [
22
- withTestPreset({
23
- name: 'plugin-tanstack-node',
24
- testEnvironment: 'node',
25
- include: [
26
- 'tests/router/cli.test.ts',
27
- 'tests/router/flightSerialization.roundtrip.test.tsx',
28
- 'tests/router/generateRouteArtifacts.test.ts',
29
- 'tests/router/loaderBridge.test.ts',
30
- 'tests/router/localisedIdentity.test.tsx',
31
- 'tests/router/routeHooks.test.ts',
32
- 'tests/router/serverPlugin.test.ts',
33
- 'tests/router/rsc.test.tsx',
34
- 'tests/router/slotUsageSanitizer.test.ts',
35
- 'tests/router/ssrPreload.test.ts',
36
- 'tests/router/tanstackTypes.test.ts',
37
- 'tests/router/routeTree.test.ts',
38
- ],
39
- extends: commonConfig,
40
- }),
41
- withTestPreset({
42
- name: 'plugin-tanstack-client',
43
- testEnvironment: 'happy-dom',
44
- include: [
45
- 'tests/router/clientHydration.test.tsx',
46
- 'tests/router/dataMutation.test.tsx',
47
- 'tests/router/prefetchLink.test.tsx',
48
- 'tests/router/prefetchLinkPreload.test.tsx',
49
- ],
50
- extends: commonConfig,
51
- }),
52
- ],
53
- };
@@ -1,420 +0,0 @@
1
- import { execFile } from 'node:child_process';
2
- import { mkdtemp, rm } from 'node:fs/promises';
3
- import { tmpdir } from 'node:os';
4
- import path from 'node:path';
5
- import { promisify } from 'node:util';
6
- import { compile } from '@modern-js/server-utils';
7
- import type { Entrypoint } from '@modern-js/types';
8
- import { fs } from '@modern-js/utils';
9
- import { tanstackRouterPlugin, writeTanstackRegisterFile } from '../../src/cli';
10
-
11
- const execFileAsync = promisify(execFile);
12
- const strictestTsconfigPath = path.resolve(
13
- __dirname,
14
- '../../node_modules/@tsconfig/strictest/tsconfig.json',
15
- );
16
-
17
- function createAppContextApi<T extends object>(initial: T) {
18
- let context: T & { serverCompileExcludedFiles?: string[] } = { ...initial };
19
- return {
20
- getAppContext: () => context,
21
- updateAppContext: (update: Partial<typeof context>) => {
22
- context = { ...context, ...update };
23
- },
24
- };
25
- }
26
-
27
- /**
28
- * Compiles the generated registration output with `tsgo` under
29
- * @tsconfig/strictest — the way a consumer's own type check would see it.
30
- */
31
- async function typecheckGeneratedRegistration(options: {
32
- entries: string[];
33
- generatedDirName: string;
34
- srcDirectory: string;
35
- i18nRuntimeModule?: string;
36
- canonicalTypeChecks?: string[];
37
- }) {
38
- const {
39
- entries,
40
- generatedDirName,
41
- srcDirectory,
42
- i18nRuntimeModule,
43
- canonicalTypeChecks = [],
44
- } = options;
45
- const runtimeModule = '@modern-js/plugin-tanstack/runtime';
46
- const projectDirectory = path.dirname(srcDirectory);
47
-
48
- for (const entry of entries) {
49
- const routerPath = path.join(
50
- srcDirectory,
51
- generatedDirName,
52
- entry,
53
- 'router.gen.ts',
54
- );
55
- if (!(await fs.pathExists(routerPath))) {
56
- await fs.outputFile(routerPath, 'export const router = { context: {} };');
57
- }
58
- }
59
-
60
- const runtimeDeclaration = [
61
- `declare module '${runtimeModule}' {`,
62
- ' export interface Register {}',
63
- ' export type ModernRouterContext = { request?: Request; requestContext?: unknown };',
64
- ' type RouteOptions = { getParentRoute?: (...args: never[]) => unknown; id?: string; loader?: (...args: never[]) => unknown; path?: string; staticData?: unknown };',
65
- ' type Route<TOptions extends RouteOptions = RouteOptions> = { options: TOptions; addChildren<const TChildren extends readonly unknown[]>(children: TChildren): Route<TOptions> & { children: TChildren } };',
66
- ' export function createMemoryHistory<TOptions>(options: TOptions): TOptions;',
67
- ' export function createRootRouteWithContext<TContext extends ModernRouterContext>(): <const TOptions extends RouteOptions>(options: TOptions) => Route<TOptions>;',
68
- ' export function createRoute<const TOptions extends RouteOptions>(options: TOptions): Route<TOptions>;',
69
- ' export function createRouter<const TOptions extends { context: ModernRouterContext; routeTree: unknown }>(options: TOptions): TOptions;',
70
- ' export function createRouteStaticData<const TData extends Record<string, unknown>>(data: TData): TData;',
71
- ' export function modernLoaderToTanstack<TLoader extends (...args: never[]) => unknown>(options: { hasSplat: boolean }, loader: TLoader): (context: unknown) => Promise<Awaited<ReturnType<TLoader>>>;',
72
- ' export const modernTanstackRouterFastDefaults: Record<string, unknown>;',
73
- '}',
74
- ...(i18nRuntimeModule
75
- ? [
76
- `declare module '${i18nRuntimeModule}' { export interface UltramodernCanonicalRoutes {} }`,
77
- ]
78
- : []),
79
- ];
80
- await fs.outputFile(
81
- path.join(srcDirectory, 'generated-runtime-shim.d.ts'),
82
- runtimeDeclaration.join('\n'),
83
- );
84
-
85
- await fs.outputFile(
86
- path.join(srcDirectory, 'registration-contract.ts'),
87
- [
88
- `import type { Register } from '${runtimeModule}';`,
89
- ...entries.flatMap((entry, index) => [
90
- `import { router as router${index} } from './${generatedDirName}/${entry}/router.gen';`,
91
- `const registeredRouter${index}: Register['router'] = router${index};`,
92
- `void registeredRouter${index};`,
93
- ]),
94
- ...canonicalTypeChecks,
95
- ].join('\n'),
96
- );
97
- await fs.outputJSON(path.join(projectDirectory, 'tsconfig.json'), {
98
- extends: strictestTsconfigPath,
99
- compilerOptions: {
100
- lib: ['ESNext', 'DOM'],
101
- module: 'Preserve',
102
- moduleResolution: 'Bundler',
103
- noEmit: true,
104
- target: 'ESNext',
105
- types: [],
106
- },
107
- include: ['src/**/*.ts', 'src/**/*.d.ts'],
108
- });
109
-
110
- try {
111
- await execFileAsync(
112
- process.platform === 'win32' ? 'tsgo.cmd' : 'tsgo',
113
- ['-p', 'tsconfig.json'],
114
- { cwd: projectDirectory, shell: process.platform === 'win32' },
115
- );
116
- } catch (error: any) {
117
- throw typeof error?.stdout === 'string'
118
- ? new Error(error.stdout, { cause: error })
119
- : error;
120
- }
121
- }
122
-
123
- const runtimeCliMocks = {
124
- handleGeneratorEntryCode: rstest.fn(),
125
- };
126
-
127
- rstest.mock('@modern-js/runtime/cli', () => {
128
- const routesDirMetaKey = '__modernRoutesDir';
129
- // The codegen helpers are pure — forward to the real implementations.
130
- const actualCli = rstest.requireActual('@modern-js/runtime/cli') as {
131
- getPathWithoutExt: (filename: string) => string;
132
- makeLegalIdentifier: (value: string) => string;
133
- };
134
-
135
- return {
136
- __esModule: true,
137
- getPathWithoutExt: actualCli.getPathWithoutExt,
138
- makeLegalIdentifier: actualCli.makeLegalIdentifier,
139
- getEntrypointRoutesDir: (entrypoint: any) =>
140
- entrypoint[routesDirMetaKey] ||
141
- (entrypoint.nestedRoutesEntry
142
- ? path.basename(entrypoint.nestedRoutesEntry)
143
- : null),
144
- getEntrypointRoutesOwner: (entrypoint: any) =>
145
- entrypoint.__modernRoutesOwner || null,
146
- // Forward through an arrow: the mock factory is hoisted above the
147
- // `runtimeCliMocks` initializer, so it must not dereference it eagerly.
148
- handleGeneratorEntryCode: (...args: unknown[]) =>
149
- runtimeCliMocks.handleGeneratorEntryCode(...args),
150
- handleFileChange: async () => {},
151
- handleModifyEntrypoints: async (entrypoints: Entrypoint[]) => entrypoints,
152
- isRouteEntry: () => false,
153
- updateNestedRoutesSpec: async () => {},
154
- };
155
- });
156
-
157
- describe('tanstack router cli plugin', () => {
158
- let tempDir: string | undefined;
159
-
160
- afterEach(async () => {
161
- runtimeCliMocks.handleGeneratorEntryCode.mockReset();
162
- if (tempDir) {
163
- await rm(tempDir, { recursive: true, force: true });
164
- tempDir = undefined;
165
- }
166
- });
167
-
168
- test('excludes its generated declaration from server roots only when installed', async () => {
169
- tempDir = await fs.realpath(
170
- await mkdtemp(path.join(tmpdir(), 'tanstack-server-roots-')),
171
- );
172
- const srcDirectory = path.join(tempDir, 'src');
173
- const declaration = path.join(
174
- srcDirectory,
175
- 'modern-tanstack',
176
- 'register.gen.d.ts',
177
- );
178
- const apiDirectory = path.join(tempDir, 'api');
179
- const compileOptions = {
180
- sourceDirs: [apiDirectory],
181
- distDir: path.join(tempDir, 'dist'),
182
- tsconfigPath: path.join(tempDir, 'tsconfig.json'),
183
- throwErrorInsteadOfExit: true,
184
- };
185
- await fs.outputJSON(compileOptions.tsconfigPath, {
186
- compilerOptions: {
187
- module: 'commonjs',
188
- target: 'ES2022',
189
- types: [],
190
- noEmitOnError: true,
191
- skipLibCheck: false,
192
- },
193
- include: ['api', 'src'],
194
- });
195
- await fs.outputFile(
196
- path.join(apiDirectory, 'index.ts'),
197
- 'export const server = true;\n',
198
- );
199
- await fs.outputFile(
200
- path.join(srcDirectory, 'routes/page.tsx'),
201
- "import type { Missing } from 'missing-browser-only-types';\nexport const page: Missing = undefined;\n",
202
- );
203
- await fs.outputFile(
204
- path.join(srcDirectory, 'modern-tanstack', 'main/router.gen.ts'),
205
- "import { page } from '../../routes/page';\nexport const router = { page };\n",
206
- );
207
- await writeTanstackRegisterFile({ entries: ['main'], srcDirectory });
208
-
209
- await expect(compile(tempDir, {}, compileOptions)).rejects.toThrow(
210
- /missing-browser-only-types/,
211
- );
212
-
213
- const previousExclusions = [path.join(tempDir, 'other-client.d.ts')];
214
- const api = {
215
- ...createAppContextApi({
216
- srcDirectory,
217
- serverCompileExcludedFiles: previousExclusions,
218
- }),
219
- _internalRuntimePlugins: () => {},
220
- checkEntryPoint: () => {},
221
- config: () => {},
222
- modifyEntrypoints: () => {},
223
- generateEntryCode: () => {},
224
- onFileChanged: () => {},
225
- modifyFileSystemRoutes: () => {},
226
- onBeforeGenerateRoutes: () => {},
227
- };
228
- tanstackRouterPlugin().setup!(api as never);
229
- const excludeFiles = api.getAppContext().serverCompileExcludedFiles;
230
- expect(excludeFiles).toEqual([...previousExclusions, declaration]);
231
- await compile(tempDir, {}, { ...compileOptions, excludeFiles });
232
- expect(
233
- await fs.pathExists(path.join(compileOptions.distDir, 'api/index.js')),
234
- ).toBe(true);
235
- expect(await fs.pathExists(path.join(compileOptions.distDir, 'src'))).toBe(
236
- false,
237
- );
238
- });
239
-
240
- test('installs the TanStack router only for entrypoints it owns', () => {
241
- const taps: Record<string, any> = {};
242
- const api = {
243
- ...createAppContextApi({
244
- srcDirectory: '/tmp/app/src',
245
- metaName: 'modern-js',
246
- serverRoutes: [
247
- { entryName: 'custom', urlPath: '/' },
248
- { entryName: 'home', urlPath: '/' },
249
- ],
250
- }),
251
- _internalRuntimePlugins: (tap: any) => {
252
- taps.internalRuntimePlugins = tap;
253
- },
254
- checkEntryPoint: () => {},
255
- config: () => {},
256
- modifyEntrypoints: () => {},
257
- generateEntryCode: () => {},
258
- onFileChanged: () => {},
259
- modifyFileSystemRoutes: () => {},
260
- onBeforeGenerateRoutes: () => {},
261
- };
262
-
263
- tanstackRouterPlugin({ routesDir: 'ts-routes' }).setup!(api as any);
264
-
265
- // Custom entry without a routes dir (e.g. `createRoutes` in
266
- // modern.runtime.ts): installing the plugin is the explicit opt-in, so
267
- // the wrapper plus the provider registration is injected through the
268
- // package's own runtime/router module.
269
- const customEntrypoint = {
270
- entryName: 'custom',
271
- isAutoMount: true,
272
- } as Entrypoint;
273
- const tanstackRouterEntry = {
274
- name: 'router',
275
- path: '@modern-js/plugin-tanstack/runtime/router',
276
- config: { serverBase: ['/'] },
277
- };
278
- expect(
279
- taps.internalRuntimePlugins({ entrypoint: customEntrypoint, plugins: [] })
280
- .plugins,
281
- ).toEqual([tanstackRouterEntry]);
282
-
283
- // If the built-in router CLI already installed the internal router for
284
- // this custom entry (explicit `runtime.router` config), only the module
285
- // path is redirected — pushing a second entry installs two routers.
286
- const builtInPlugin = {
287
- name: 'router',
288
- path: '@modern-js/runtime/router/internal',
289
- config: { serverBase: ['/'] },
290
- };
291
- expect(
292
- taps.internalRuntimePlugins({
293
- entrypoint: customEntrypoint,
294
- plugins: [builtInPlugin],
295
- }).plugins,
296
- ).toEqual([tanstackRouterEntry]);
297
-
298
- // A classic react-router file-route entry (src/<entry>/routes) living
299
- // next to the TanStack entries: its internal router plugin must be left
300
- // untouched — redirecting it would pull @tanstack/react-router into a
301
- // pure react-router bundle.
302
- expect(
303
- taps.internalRuntimePlugins({
304
- entrypoint: {
305
- entryName: 'home',
306
- isAutoMount: true,
307
- nestedRoutesEntry: '/tmp/app/src/home/routes',
308
- __modernRoutesDir: 'routes',
309
- } as Entrypoint,
310
- plugins: [builtInPlugin],
311
- }).plugins,
312
- ).toEqual([builtInPlugin]);
313
-
314
- // An entry tagged by another routes-owner plugin: nothing is pushed.
315
- expect(
316
- taps.internalRuntimePlugins({
317
- entrypoint: {
318
- entryName: 'home',
319
- isAutoMount: true,
320
- nestedRoutesEntry: '/tmp/app/src/home/acme-routes',
321
- __modernRoutesDir: 'acme-routes',
322
- __modernRoutesOwner: '@acme/plugin-file-router',
323
- } as Entrypoint,
324
- plugins: [],
325
- }).plugins,
326
- ).toEqual([]);
327
- });
328
-
329
- test('emits the plugin-i18n augmentation only when plugin-i18n is registered', async () => {
330
- const langRoutes = [
331
- {
332
- type: 'nested',
333
- id: 'layout',
334
- isRoot: true,
335
- children: [
336
- {
337
- type: 'nested',
338
- id: '(lang)/layout',
339
- path: ':lang',
340
- children: [
341
- {
342
- type: 'nested',
343
- id: '(lang)/about/page',
344
- path: 'about',
345
- },
346
- ],
347
- },
348
- ],
349
- },
350
- ];
351
-
352
- const runGenerate = async (registeredPlugins: Array<{ name: string }>) => {
353
- const dir = await mkdtemp(path.join(tmpdir(), 'modern-tanstack-cli-'));
354
- const srcDirectory = path.join(dir, 'src');
355
- const entrypoint = {
356
- entryName: 'main',
357
- isAutoMount: true,
358
- isMainEntry: true,
359
- nestedRoutesEntry: path.join(srcDirectory, 'routes'),
360
- __modernRoutesDir: 'routes',
361
- } as Entrypoint;
362
- runtimeCliMocks.handleGeneratorEntryCode.mockResolvedValueOnce({
363
- main: langRoutes,
364
- });
365
-
366
- const taps: Record<string, any> = {};
367
- const api = {
368
- ...createAppContextApi({
369
- srcDirectory,
370
- internalSrcAlias: '@/_',
371
- entrypoints: [entrypoint],
372
- plugins: registeredPlugins,
373
- }),
374
- _internalRuntimePlugins: () => {},
375
- checkEntryPoint: () => {},
376
- config: () => {},
377
- modifyEntrypoints: () => {},
378
- generateEntryCode: (tap: any) => {
379
- taps.generateEntryCode = tap;
380
- },
381
- onFileChanged: () => {},
382
- modifyFileSystemRoutes: () => {},
383
- onBeforeGenerateRoutes: () => {},
384
- };
385
-
386
- tanstackRouterPlugin().setup!(api as any);
387
- await taps.generateEntryCode({ entrypoints: [entrypoint] });
388
-
389
- const hasI18n = registeredPlugins.some(
390
- plugin => plugin.name === '@modern-js/plugin-i18n',
391
- );
392
- await typecheckGeneratedRegistration({
393
- entries: ['main'],
394
- generatedDirName: 'modern-tanstack',
395
- srcDirectory,
396
- ...(hasI18n && {
397
- i18nRuntimeModule: '@modern-js/plugin-i18n/runtime',
398
- }),
399
- canonicalTypeChecks: hasI18n
400
- ? [
401
- "import type { UltramodernCanonicalRoutes } from '@modern-js/plugin-i18n/runtime';",
402
- "const aboutParams: UltramodernCanonicalRoutes['/about'] = {};",
403
- 'void aboutParams;',
404
- ]
405
- : [],
406
- });
407
- await rm(dir, { recursive: true, force: true });
408
- };
409
-
410
- // A hand-rolled `/:lang/` app WITHOUT plugin-i18n must not get the
411
- // augmentation — it would reference an unresolvable module (TS2664).
412
- await runGenerate([{ name: '@modern-js/app-tools' }]);
413
-
414
- // With plugin-i18n registered the canonical route map is emitted.
415
- await runGenerate([
416
- { name: '@modern-js/app-tools' },
417
- { name: '@modern-js/plugin-i18n' },
418
- ]);
419
- });
420
- });