@bleedingdev/modern-js-plugin-tanstack 3.9.0-ultramodern.8 → 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.
- package/dist/cjs/cli/artifacts.js +15 -1
- package/dist/cjs/cli/index.js +13 -2
- package/dist/cjs/cli/routeMetadata.js +131 -0
- package/dist/cjs/cli/rscRouteLayer.js +86 -0
- package/dist/cjs/cli/tanstackTypes/canonicalRoutes.js +7 -7
- package/dist/cjs/cli/tanstackTypes/shared.js +0 -14
- package/dist/cjs/cli/tanstackTypes/source.js +28 -63
- package/dist/cjs/runtime/lifecycle.js +0 -3
- package/dist/cjs/runtime/loaderBridge.js +10 -4
- package/dist/cjs/runtime/navigation.js +102 -0
- package/dist/cjs/runtime/plugin.js +10 -1
- package/dist/cjs/runtime/plugin.node.js +89 -64
- package/dist/cjs/runtime/routeTree/construction.js +19 -61
- package/dist/cjs/runtime/routeTree/loaders.js +1 -1
- package/dist/cjs/runtime/routeTree/paths.js +0 -6
- package/dist/cjs/runtime/ssrManagedTags.js +2 -12
- package/dist/cjs/runtime/utils.js +1 -0
- package/dist/cjs/shared/routeDescriptor.js +60 -0
- package/dist/esm/cli/artifacts.mjs +13 -2
- package/dist/esm/cli/index.mjs +13 -11
- package/dist/esm/cli/routeMetadata.mjs +71 -0
- package/dist/esm/cli/rscRouteLayer.mjs +35 -0
- package/dist/esm/cli/tanstackTypes/canonicalRoutes.mjs +7 -7
- package/dist/esm/cli/tanstackTypes/shared.mjs +1 -9
- package/dist/esm/cli/tanstackTypes/source.mjs +29 -64
- package/dist/esm/runtime/lifecycle.mjs +1 -1
- package/dist/esm/runtime/loaderBridge.mjs +8 -5
- package/dist/esm/runtime/navigation.mjs +64 -0
- package/dist/esm/runtime/plugin.mjs +10 -1
- package/dist/esm/runtime/plugin.node.mjs +91 -66
- package/dist/esm/runtime/routeTree/construction.mjs +19 -61
- package/dist/esm/runtime/routeTree/loaders.mjs +2 -2
- package/dist/esm/runtime/routeTree/paths.mjs +1 -4
- package/dist/esm/runtime/ssrManagedTags.mjs +1 -8
- package/dist/esm/runtime/utils.mjs +1 -0
- package/dist/esm/shared/routeDescriptor.mjs +22 -0
- package/dist/esm-node/cli/artifacts.mjs +13 -2
- package/dist/esm-node/cli/index.mjs +13 -11
- package/dist/esm-node/cli/routeMetadata.mjs +72 -0
- package/dist/esm-node/cli/rscRouteLayer.mjs +36 -0
- package/dist/esm-node/cli/tanstackTypes/canonicalRoutes.mjs +7 -7
- package/dist/esm-node/cli/tanstackTypes/shared.mjs +1 -9
- package/dist/esm-node/cli/tanstackTypes/source.mjs +29 -64
- package/dist/esm-node/runtime/lifecycle.mjs +1 -1
- package/dist/esm-node/runtime/loaderBridge.mjs +8 -5
- package/dist/esm-node/runtime/navigation.mjs +65 -0
- package/dist/esm-node/runtime/plugin.mjs +10 -1
- package/dist/esm-node/runtime/plugin.node.mjs +91 -66
- package/dist/esm-node/runtime/routeTree/construction.mjs +19 -61
- package/dist/esm-node/runtime/routeTree/loaders.mjs +2 -2
- package/dist/esm-node/runtime/routeTree/paths.mjs +1 -4
- package/dist/esm-node/runtime/ssrManagedTags.mjs +1 -8
- package/dist/esm-node/runtime/utils.mjs +1 -0
- package/dist/esm-node/shared/routeDescriptor.mjs +23 -0
- package/dist/types/cli/artifacts.d.ts +16 -7
- package/dist/types/cli/index.d.ts +1 -1
- package/dist/types/cli/routeMetadata.d.ts +18 -0
- package/dist/types/cli/rscRouteLayer.d.ts +11 -0
- package/dist/types/cli/tanstackTypes/shared.d.ts +0 -2
- package/dist/types/cli/tanstackTypes/source.d.ts +3 -2
- package/dist/types/runtime/lifecycle.d.ts +1 -1
- package/dist/types/runtime/loaderBridge.d.ts +1 -0
- package/dist/types/runtime/navigation.d.ts +4 -0
- package/dist/types/runtime/pluginShared.d.ts +5 -0
- package/dist/types/runtime/routeTree/paths.d.ts +0 -2
- package/dist/types/runtime/ssrManagedTags.d.ts +0 -1
- package/dist/types/runtime/ssrTypes.d.ts +0 -7
- package/dist/types/shared/routeDescriptor.d.ts +26 -0
- package/package.json +32 -26
- package/src/cli/artifacts.ts +59 -8
- package/src/cli/index.ts +17 -4
- package/src/cli/routeMetadata.ts +121 -0
- package/src/cli/rscRouteLayer.ts +93 -0
- package/src/cli/tanstackTypes/canonicalRoutes.ts +17 -12
- package/src/cli/tanstackTypes/shared.ts +0 -14
- package/src/cli/tanstackTypes/source.ts +48 -120
- package/src/runtime/lifecycle.ts +0 -1
- package/src/runtime/loaderBridge.ts +9 -9
- package/src/runtime/navigation.ts +78 -0
- package/src/runtime/plugin.node.tsx +121 -91
- package/src/runtime/plugin.tsx +10 -0
- package/src/runtime/pluginShared.ts +12 -0
- package/src/runtime/routeTree/construction.ts +32 -112
- package/src/runtime/routeTree/loaders.ts +2 -4
- package/src/runtime/routeTree/paths.ts +0 -6
- package/src/runtime/ssrManagedTags.ts +0 -17
- package/src/runtime/ssrTypes.ts +0 -7
- package/src/runtime/utils.tsx +1 -0
- package/src/shared/routeDescriptor.ts +46 -0
- package/dist/esm/rslib-runtime~1.mjs +0 -18
- package/dist/esm-node/rslib-runtime~0.mjs +0 -19
- package/rslib.config.mts +0 -4
- package/rstest.config.mts +0 -53
- package/tests/router/cli.test.ts +0 -420
- package/tests/router/clientHydration.test.tsx +0 -200
- package/tests/router/dataMutation.test.tsx +0 -428
- package/tests/router/fixtures/router-gen.oxlint.json +0 -5
- package/tests/router/flightSerialization.roundtrip.test.tsx +0 -140
- package/tests/router/generateRouteArtifacts.test.ts +0 -146
- package/tests/router/loaderBridge.test.ts +0 -207
- package/tests/router/localisedIdentity.test.tsx +0 -95
- package/tests/router/prefetchLink.test.tsx +0 -122
- package/tests/router/prefetchLinkPreload.test.tsx +0 -65
- package/tests/router/routeHooks.test.ts +0 -49
- package/tests/router/routeTree.test.ts +0 -240
- package/tests/router/rsc.test.tsx +0 -480
- package/tests/router/serverPlugin.test.ts +0 -157
- package/tests/router/slotUsageSanitizer.test.ts +0 -23
- package/tests/router/ssrPreload.test.ts +0 -30
- package/tests/router/tanstackTypes.test.ts +0 -384
- package/tsconfig.json +0 -12
- 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, '&').replace(/"/g, '"');
|
|
28
11
|
}
|
package/src/runtime/ssrTypes.ts
CHANGED
|
@@ -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
|
};
|
package/src/runtime/utils.tsx
CHANGED
|
@@ -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
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
|
-
};
|
package/tests/router/cli.test.ts
DELETED
|
@@ -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
|
-
});
|