@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
@@ -1,384 +0,0 @@
1
- import { execFile } from 'node:child_process';
2
- import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises';
3
- import { tmpdir } from 'node:os';
4
- import path from 'node:path';
5
- import { promisify } from 'node:util';
6
-
7
- import { writeTanstackRegisterFile } from '../../src/cli/artifacts';
8
- import {
9
- collectCanonicalRoutesForEntry,
10
- generateTanstackRouterTypesSourceForEntry,
11
- } from '../../src/cli/tanstackTypes';
12
-
13
- const execFileAsync = promisify(execFile);
14
- const strictestTsconfigPath = path.resolve(
15
- __dirname,
16
- '../../node_modules/@tsconfig/strictest/tsconfig.json',
17
- );
18
- const installedReactRouterPath = path.resolve(
19
- __dirname,
20
- '../../node_modules/@tanstack/react-router',
21
- );
22
-
23
- async function writeTsconfig(
24
- projectDirectory: string,
25
- compilerOptions: Record<string, unknown>,
26
- ) {
27
- await writeFile(
28
- path.join(projectDirectory, 'tsconfig.json'),
29
- JSON.stringify(
30
- {
31
- extends: strictestTsconfigPath,
32
- compilerOptions: {
33
- jsx: 'react-jsx',
34
- lib: ['ESNext', 'DOM'],
35
- noEmit: true,
36
- target: 'ESNext',
37
- types: [],
38
- ...compilerOptions,
39
- },
40
- include: ['src/**/*.ts', 'src/**/*.tsx', 'src/**/*.d.ts'],
41
- },
42
- null,
43
- 2,
44
- ),
45
- );
46
- }
47
-
48
- /** Runs the same compiler a consumer build runs; surfaces its diagnostics. */
49
- async function runTsgo(projectDirectory: string) {
50
- try {
51
- await execFileAsync(
52
- process.platform === 'win32' ? 'tsgo.cmd' : 'tsgo',
53
- ['-p', 'tsconfig.json'],
54
- { cwd: projectDirectory, shell: process.platform === 'win32' },
55
- );
56
- } catch (error) {
57
- const { stdout, stderr } = (error ?? {}) as {
58
- stdout?: string;
59
- stderr?: string;
60
- };
61
- if (stdout || stderr) {
62
- throw new Error([stdout, stderr].filter(Boolean).join('\n'), {
63
- cause: error,
64
- });
65
- }
66
- throw error;
67
- }
68
- }
69
-
70
- async function compileGeneratedRouterAgainstInstalledDeclarations(options: {
71
- projectDirectory: string;
72
- routerGenTs: string;
73
- }) {
74
- const { projectDirectory, routerGenTs } = options;
75
- const generatedDirectory = path.join(
76
- projectDirectory,
77
- 'src',
78
- 'modern-tanstack',
79
- 'golden',
80
- );
81
- await mkdir(generatedDirectory, { recursive: true });
82
- await writeFile(path.join(generatedDirectory, 'router.gen.ts'), routerGenTs);
83
-
84
- // The runtime package re-exports the *installed* @tanstack/react-router
85
- // declarations, so the generated file is checked against the real router
86
- // types a consumer resolves — not a stand-in.
87
- const runtimePackageDirectory = path.join(
88
- projectDirectory,
89
- 'node_modules',
90
- '@modern-js',
91
- 'plugin-tanstack',
92
- );
93
- await mkdir(runtimePackageDirectory, { recursive: true });
94
- await writeFile(
95
- path.join(runtimePackageDirectory, 'package.json'),
96
- JSON.stringify({
97
- name: '@modern-js/plugin-tanstack',
98
- type: 'commonjs',
99
- exports: {
100
- './runtime': { types: './runtime.d.ts', default: './runtime.js' },
101
- },
102
- }),
103
- );
104
- await writeFile(
105
- path.join(runtimePackageDirectory, 'runtime.d.ts'),
106
- [
107
- "export { createMemoryHistory, createRootRouteWithContext, createRoute, createRouter } from '@tanstack/react-router';",
108
- 'export type ModernRouterContext = { request?: Request; requestContext?: unknown };',
109
- 'export const modernTanstackRouterFastDefaults: { readonly defaultStructuralSharing: true };',
110
- 'export function createRouteStaticData<TData extends { modernRouteId?: string; modernRouteAction?: unknown; modernRouteLoader?: unknown }>(data: TData): TData;',
111
- 'export function modernLoaderToTanstack<TLoader extends (...args: any[]) => any>(options: { hasSplat: boolean }, modernLoader: TLoader): (context: unknown) => Promise<Awaited<ReturnType<TLoader>>>;',
112
- ].join('\n'),
113
- );
114
- await writeFile(
115
- path.join(projectDirectory, 'package.json'),
116
- JSON.stringify({ private: true, type: 'commonjs' }),
117
- );
118
- await writeTsconfig(projectDirectory, {
119
- module: 'Node16',
120
- moduleResolution: 'Node16',
121
- paths: { '@tanstack/react-router': [installedReactRouterPath] },
122
- verbatimModuleSyntax: false,
123
- });
124
- await runTsgo(projectDirectory);
125
- }
126
-
127
- async function typecheckCanonicalRegisterContract(options: {
128
- canonicalRoutes: Record<string, string>;
129
- contractLines: string[];
130
- projectDirectory: string;
131
- }) {
132
- const { canonicalRoutes, contractLines, projectDirectory } = options;
133
- const srcDirectory = path.join(projectDirectory, 'src');
134
- await mkdir(path.join(srcDirectory, 'modern-tanstack', 'index'), {
135
- recursive: true,
136
- });
137
- await writeFile(
138
- path.join(srcDirectory, 'modern-tanstack', 'index', 'router.gen.ts'),
139
- 'export const router = { context: {} };',
140
- );
141
- await writeTanstackRegisterFile({
142
- canonicalRoutes,
143
- entries: ['index'],
144
- srcDirectory,
145
- });
146
- await writeFile(
147
- path.join(srcDirectory, 'runtime-shims.d.ts'),
148
- [
149
- "declare module '@modern-js/plugin-tanstack/runtime' { export interface Register {} }",
150
- "declare module '@modern-js/plugin-i18n/runtime' { export interface UltramodernCanonicalRoutes {} }",
151
- ].join('\n'),
152
- );
153
- await writeFile(
154
- path.join(srcDirectory, 'canonical-routes-contract.ts'),
155
- [
156
- "import type { UltramodernCanonicalRoutes } from '@modern-js/plugin-i18n/runtime';",
157
- ...contractLines,
158
- ].join('\n'),
159
- );
160
- await writeTsconfig(projectDirectory, {
161
- module: 'Preserve',
162
- moduleResolution: 'Bundler',
163
- });
164
- await runTsgo(projectDirectory);
165
- }
166
-
167
- async function generateComprehensiveRouterGen(srcDirectory: string) {
168
- const files = new Map([
169
- [
170
- 'routes/(app)/layout.tsx',
171
- 'export default function AppLayout() { return null; }',
172
- ],
173
- [
174
- 'routes/(app)/users/(userId)/page.tsx',
175
- 'export default function UserPage() { return null; }',
176
- ],
177
- [
178
- 'routes/(app)/users/(userId)/page.data.ts',
179
- [
180
- 'export const loader = () => ({ userId: "42" });',
181
- 'export const action = () => Response.json({ ok: true });',
182
- ].join('\n'),
183
- ],
184
- [
185
- 'routes/(app)/docs/splat.tsx',
186
- 'export default function DocsSplatPage() { return null; }',
187
- ],
188
- [
189
- 'routes/search.contract.ts',
190
- [
191
- 'export const validateSearch = (search: { tab?: string }) => ({ tab: search.tab ?? "overview" });',
192
- 'export const loaderDeps = ({ search }: { search: { tab: string } }) => ({ tab: search.tab });',
193
- ].join('\n'),
194
- ],
195
- ]);
196
- for (const [relativePath, contents] of files) {
197
- const filePath = path.join(srcDirectory, relativePath);
198
- await mkdir(path.dirname(filePath), { recursive: true });
199
- await writeFile(filePath, contents);
200
- }
201
-
202
- const { routerGenTs } = await generateTanstackRouterTypesSourceForEntry({
203
- appContext: { srcDirectory, internalSrcAlias: '@/_' } as any,
204
- entryName: 'golden',
205
- routes: [
206
- {
207
- type: 'nested',
208
- id: 'layout',
209
- isRoot: true,
210
- children: [
211
- {
212
- type: 'nested',
213
- id: '(app)/layout',
214
- _component: '@/_/routes/(app)/layout',
215
- children: [
216
- {
217
- type: 'nested',
218
- id: '(app)/users/(userId)/page',
219
- path: 'users/:userId',
220
- _component: '@/_/routes/(app)/users/(userId)/page',
221
- data: '@/_/routes/(app)/users/(userId)/page.data',
222
- action: '@/_/routes/(app)/users/(userId)/page.data',
223
- validateSearch: '@/_/routes/search.contract',
224
- loaderDeps: '@/_/routes/search.contract',
225
- },
226
- {
227
- type: 'nested',
228
- id: '(app)/docs/splat/page',
229
- path: 'docs/*',
230
- _component: '@/_/routes/(app)/docs/splat',
231
- },
232
- ],
233
- },
234
- ],
235
- },
236
- ] as any,
237
- });
238
-
239
- return routerGenTs;
240
- }
241
-
242
- describe('tanstack router type generation', () => {
243
- let tempDir: string | undefined;
244
-
245
- afterEach(async () => {
246
- if (tempDir) {
247
- await rm(tempDir, { recursive: true, force: true });
248
- tempDir = undefined;
249
- }
250
- });
251
-
252
- test('strictly compiles generated routes against installed router declarations', async () => {
253
- tempDir = await mkdtemp(path.join(tmpdir(), 'modern-tanstack-types-'));
254
- const routerGenTs = await generateComprehensiveRouterGen(
255
- path.join(tempDir, 'src'),
256
- );
257
-
258
- await compileGeneratedRouterAgainstInstalledDeclarations({
259
- projectDirectory: tempDir,
260
- routerGenTs,
261
- });
262
- });
263
-
264
- test('maps a locale-prefixed route tree to the canonical paths consumers navigate to', () => {
265
- const routes = [
266
- {
267
- type: 'nested',
268
- id: 'layout',
269
- isRoot: true,
270
- children: [
271
- {
272
- type: 'nested',
273
- id: '(lang)/layout',
274
- path: ':lang',
275
- children: [
276
- { type: 'nested', id: '(lang)/page', index: true },
277
- { type: 'nested', id: '(lang)/about/page', path: 'about' },
278
- {
279
- type: 'nested',
280
- id: '(lang)/optional/(slug$)/page',
281
- path: 'optional/:slug?',
282
- },
283
- { type: 'nested', id: '(lang)/files/page', path: 'files/*' },
284
- ],
285
- },
286
- ],
287
- },
288
- ] as any;
289
-
290
- // Without plugin-i18n installed, a hand-rolled `/:lang/` param must NOT
291
- // produce a canonical surface — the emitted module augmentation would
292
- // break typechecking in an app that never opted into i18n.
293
- expect(
294
- collectCanonicalRoutesForEntry(routes, { localeParamHeuristic: false }),
295
- ).toBeNull();
296
-
297
- const result = collectCanonicalRoutesForEntry(routes);
298
- expect(result).not.toBeNull();
299
- expect(Object.keys(result!)).toEqual([
300
- '/',
301
- '/about',
302
- '/files/$',
303
- '/optional/{-$slug}',
304
- ]);
305
- });
306
-
307
- test('collapses localized variants to one canonical key with typed params', async () => {
308
- const result = collectCanonicalRoutesForEntry([
309
- {
310
- type: 'nested',
311
- id: 'layout',
312
- isRoot: true,
313
- children: [
314
- {
315
- type: 'nested',
316
- id: '(lang)/layout',
317
- path: ':lang',
318
- children: [
319
- {
320
- type: 'nested',
321
- id: '(lang)/products/(slug)/page',
322
- path: 'products/:slug',
323
- modernCanonicalPath: '/products/:slug',
324
- },
325
- {
326
- type: 'nested',
327
- id: '(lang)/products/(slug)/page__localised_produkty_slug',
328
- path: 'produkty/:slug',
329
- modernCanonicalPath: '/products/:slug',
330
- },
331
- {
332
- type: 'nested',
333
- id: '(lang)/optional/(slug$)/page__localised_volitelne_slug',
334
- path: 'volitelne/:slug?',
335
- modernCanonicalPath: '/optional/:slug?',
336
- },
337
- ],
338
- },
339
- ],
340
- },
341
- ] as any);
342
-
343
- expect(result).not.toBeNull();
344
- expect(Object.keys(result!)).toEqual([
345
- '/optional/{-$slug}',
346
- '/products/$slug',
347
- ]);
348
- expect('/produkty/$slug' in result!).toBe(false);
349
-
350
- tempDir = await mkdtemp(path.join(tmpdir(), 'modern-tanstack-canonical-'));
351
- await typecheckCanonicalRegisterContract({
352
- canonicalRoutes: {
353
- '/': 'Record<string, never>',
354
- '/files/$': '{ _splat?: string }',
355
- ...result!,
356
- },
357
- contractLines: [
358
- "const rootParams: UltramodernCanonicalRoutes['/'] = {};",
359
- '// @ts-expect-error index canonical routes reject unexpected params',
360
- "const invalidRootParams: UltramodernCanonicalRoutes['/'] = { slug: 'unexpected' };",
361
- "const productParams: UltramodernCanonicalRoutes['/products/$slug'] = { slug: 'tractor' };",
362
- "const optionalParams: UltramodernCanonicalRoutes['/optional/{-$slug}'] = {};",
363
- "const presentOptionalParams: UltramodernCanonicalRoutes['/optional/{-$slug}'] = { slug: 'tractor' };",
364
- '// @ts-expect-error optional canonical params still retain their string type',
365
- "const invalidOptionalParams: UltramodernCanonicalRoutes['/optional/{-$slug}'] = { slug: 42 };",
366
- "const splatParams: UltramodernCanonicalRoutes['/files/$'] = { _splat: 'guides/intro' };",
367
- '// @ts-expect-error localized physical variants do not become canonical keys',
368
- "declare const localizedParams: UltramodernCanonicalRoutes['/produkty/$slug'];",
369
- '// @ts-expect-error the deduplicated product route still requires its param',
370
- "const missingProductParams: UltramodernCanonicalRoutes['/products/$slug'] = {};",
371
- 'void rootParams;',
372
- 'void productParams;',
373
- 'void optionalParams;',
374
- 'void presentOptionalParams;',
375
- 'void splatParams;',
376
- 'void invalidRootParams;',
377
- 'void invalidOptionalParams;',
378
- 'void localizedParams;',
379
- 'void missingProductParams;',
380
- ],
381
- projectDirectory: tempDir,
382
- });
383
- });
384
- });
package/tsconfig.json DELETED
@@ -1,12 +0,0 @@
1
- {
2
- "extends": "@modern-js/tsconfig/base",
3
- "compilerOptions": {
4
- "declaration": false,
5
- "lib": ["DOM", "ESNext", "dom.iterable"],
6
- "jsx": "preserve",
7
- "isolatedModules": true,
8
- "paths": {},
9
- "rootDir": "./src"
10
- },
11
- "include": ["src"]
12
- }
@@ -1,6 +0,0 @@
1
- {
2
- "extends": "./tsconfig.json",
3
- "compilerOptions": {
4
- "moduleResolution": "Bundler"
5
- }
6
- }