rsbuild-plugin-react-router 0.3.0 → 0.3.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/README.md +55 -24
- package/dist/451.js +36 -20
- package/dist/build-manifest.d.ts +6 -3
- package/dist/concurrency.d.ts +2 -1
- package/dist/config-imports.d.ts +7 -0
- package/dist/dev-background-resources.d.ts +38 -0
- package/dist/dev-generation.d.ts +2 -2
- package/dist/dev-runtime-artifacts.d.ts +9 -1
- package/dist/dev-runtime-compilation.d.ts +17 -2
- package/dist/dev-server.d.ts +5 -0
- package/dist/effect-runtime.d.ts +18 -0
- package/dist/export-utils.d.ts +2 -2
- package/dist/index.cjs +11448 -891
- package/dist/index.d.ts +4 -0
- package/dist/index.js +7950 -758
- package/dist/lazy-compilation-prewarm.d.ts +25 -0
- package/dist/lazy-compilation.d.ts +2 -1
- package/dist/manifest.d.ts +19 -4
- package/dist/parallel-route-transforms.d.ts +17 -2
- package/dist/prerender-build.d.ts +4 -0
- package/dist/prerender.d.ts +0 -1
- package/dist/react-router-config.d.ts +8 -5
- package/dist/server-build-resolution.d.ts +3 -0
- package/dist/ssr-externals.d.ts +1 -0
- package/dist/typegen.d.ts +14 -1
- package/dist/types.d.ts +15 -6
- package/package.json +11 -9
- package/src/build-manifest.ts +110 -73
- package/src/concurrency.ts +3 -22
- package/src/config-imports.ts +38 -0
- package/src/dev-background-resources.ts +255 -0
- package/src/dev-generation.ts +43 -53
- package/src/dev-runtime-artifacts.ts +50 -18
- package/src/dev-runtime-compilation.ts +80 -1
- package/src/dev-runtime-controller.ts +111 -31
- package/src/dev-runtime-session.ts +18 -11
- package/src/dev-server.ts +31 -1
- package/src/effect-runtime.ts +130 -0
- package/src/export-utils.ts +82 -23
- package/src/index.ts +118 -153
- package/src/lazy-compilation-prewarm.ts +279 -0
- package/src/lazy-compilation.ts +12 -5
- package/src/manifest.ts +366 -255
- package/src/modify-browser-manifest.ts +2 -1
- package/src/parallel-route-transforms.ts +195 -69
- package/src/prerender-build.ts +125 -61
- package/src/prerender.ts +0 -18
- package/src/react-router-config.ts +98 -73
- package/src/route-artifacts.ts +2 -2
- package/src/route-export-resolution.ts +3 -3
- package/src/route-watch.ts +119 -84
- package/src/server-build-resolution.ts +131 -0
- package/src/server-utils.ts +7 -106
- package/src/ssr-externals.ts +1 -1
- package/src/typegen.ts +162 -33
- package/src/types.ts +16 -6
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import * as Effect from 'effect/Effect';
|
|
2
|
+
import type { ReactRouterManifestForDev } from './manifest.js';
|
|
3
|
+
type LazyCompilationPrewarmConfig = {
|
|
4
|
+
entry: boolean;
|
|
5
|
+
routeLimit: number;
|
|
6
|
+
delayMs: number;
|
|
7
|
+
};
|
|
8
|
+
type LazyCompilationPrewarmController = {
|
|
9
|
+
setServerOrigin(origin: string): void;
|
|
10
|
+
setManifest(manifest: ReactRouterManifestForDev | null): void;
|
|
11
|
+
schedule(): void;
|
|
12
|
+
cancelEffect(): Effect.Effect<void, Error, never>;
|
|
13
|
+
};
|
|
14
|
+
export type RspackLazyCompilationTriggerClient = {
|
|
15
|
+
extractModuleKeys(source: string): string[];
|
|
16
|
+
trigger(origin: string, keys: readonly string[]): Effect.Effect<void, Error, never>;
|
|
17
|
+
};
|
|
18
|
+
export declare const normalizeLazyCompilationPrewarmOptions: (options: boolean | undefined) => LazyCompilationPrewarmConfig | null;
|
|
19
|
+
export declare const collectLazyCompilationPrewarmAssets: (manifest: ReactRouterManifestForDev, config: LazyCompilationPrewarmConfig) => string[];
|
|
20
|
+
export declare const createRspackLazyCompilationTriggerClient: (triggerPrefix?: string) => RspackLazyCompilationTriggerClient;
|
|
21
|
+
export declare const createLazyCompilationPrewarmController: ({ config, onError, }: {
|
|
22
|
+
config: LazyCompilationPrewarmConfig;
|
|
23
|
+
onError: (error: Error) => void;
|
|
24
|
+
}) => LazyCompilationPrewarmController;
|
|
25
|
+
export {};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { PluginOptions } from './types.js';
|
|
2
|
-
export declare const guardReactRouterLazyCompilation: ({ lazyCompilation, entryClientPath, }: {
|
|
2
|
+
export declare const guardReactRouterLazyCompilation: ({ lazyCompilation, entryClientPath, prewarmReactRouterModules, }: {
|
|
3
3
|
lazyCompilation: PluginOptions["lazyCompilation"] | undefined;
|
|
4
4
|
entryClientPath: string;
|
|
5
|
+
prewarmReactRouterModules?: boolean;
|
|
5
6
|
}) => PluginOptions["lazyCompilation"] | undefined;
|
package/dist/manifest.d.ts
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
import type { Route, PluginOptions, RouteManifestItem } from './types.js';
|
|
2
|
-
import type { RouteConfigEntry } from '@react-router/dev/routes';
|
|
3
2
|
import { type RouteChunkCache } from './route-chunks.js';
|
|
3
|
+
/**
|
|
4
|
+
* Structural equivalent of `RouteConfigEntry` from `@react-router/dev/routes`,
|
|
5
|
+
* inlined so the public declaration files do not reference a devDependency
|
|
6
|
+
* (which would fail to resolve for consumers with `skipLibCheck: false`).
|
|
7
|
+
*/
|
|
8
|
+
export interface RouteConfigEntry {
|
|
9
|
+
id?: string;
|
|
10
|
+
path?: string;
|
|
11
|
+
index?: boolean;
|
|
12
|
+
caseSensitive?: boolean;
|
|
13
|
+
file: string;
|
|
14
|
+
children?: RouteConfigEntry[];
|
|
15
|
+
}
|
|
4
16
|
export declare function configRoutesToRouteManifest(appDirectory: string, routes: RouteConfigEntry[], rootId?: string): Record<string, Route>;
|
|
5
17
|
export declare function configRoutesToRouteManifestEntries(appDirectory: string, routes: RouteConfigEntry[], rootId?: string): Array<[string, Route]>;
|
|
6
18
|
type RouteChunkManifestOptions = {
|
|
@@ -33,9 +45,12 @@ type ReactRouterManifestStatsChunk = {
|
|
|
33
45
|
type ReactRouterManifestStatsEntrypoint = {
|
|
34
46
|
getFiles?: () => Iterable<string>;
|
|
35
47
|
};
|
|
48
|
+
type ReactRouterManifestStatsLookup<T> = Iterable<[string, T]> & {
|
|
49
|
+
get?: (name: string) => T | undefined;
|
|
50
|
+
};
|
|
36
51
|
type ReactRouterManifestStatsCompilation = {
|
|
37
|
-
namedChunks:
|
|
38
|
-
entrypoints?:
|
|
52
|
+
namedChunks: ReactRouterManifestStatsLookup<ReactRouterManifestStatsChunk>;
|
|
53
|
+
entrypoints?: ReactRouterManifestStatsLookup<ReactRouterManifestStatsEntrypoint>;
|
|
39
54
|
};
|
|
40
55
|
export declare const createReactRouterManifestStats: (compilation: ReactRouterManifestStatsCompilation | undefined, chunkNames?: ReadonlySet<string>) => ReactRouterManifestStats | undefined;
|
|
41
56
|
export type RouteManifestModuleExports = Record<string, readonly string[]>;
|
|
@@ -49,6 +64,6 @@ export declare const getReactRouterManifestPath: ({ version, isBuild, entryModul
|
|
|
49
64
|
entryModulePath?: string;
|
|
50
65
|
}) => string;
|
|
51
66
|
export declare const getReactRouterManifestChunkNames: (routes: Record<string, Route>, splitRouteModules?: boolean | "enforce") => Set<string>;
|
|
52
|
-
export declare function generateReactRouterManifestForDev(routes: Record<string, Route>,
|
|
67
|
+
export declare function generateReactRouterManifestForDev(routes: Record<string, Route>, options: PluginOptions, clientStats: ReactRouterManifestStats | undefined, context: string, assetPrefix?: string, routeChunkOptions?: RouteChunkManifestOptions): Promise<ReactRouterManifestGenerationResult>;
|
|
53
68
|
export declare function getReactRouterManifestForDev(...args: Parameters<typeof generateReactRouterManifestForDev>): Promise<ReactRouterManifestForDev>;
|
|
54
69
|
export {};
|
|
@@ -1,14 +1,29 @@
|
|
|
1
1
|
import { type RouteTransformResult, type RouteTransformTask, type RouteTransformTaskOptions } from './route-transform-tasks.js';
|
|
2
2
|
import type { PluginOptions } from './types.js';
|
|
3
|
+
import type { WorkerRequest, WorkerResponse } from './parallel-route-transform-protocol.js';
|
|
3
4
|
export type ParallelRouteTransformConfig = NonNullable<PluginOptions['parallelRouteTransform']> extends infer Config ? Exclude<Config, false> : never;
|
|
4
5
|
export type RouteTransformExecutorOptions = RouteTransformTaskOptions & {
|
|
5
6
|
parallelRouteTransform?: PluginOptions['parallelRouteTransform'];
|
|
6
7
|
splitRouteModules?: boolean;
|
|
8
|
+
isBuild?: boolean;
|
|
7
9
|
};
|
|
8
10
|
export type RouteTransformExecutor = {
|
|
9
11
|
run: (task: RouteTransformTask) => Promise<RouteTransformResult>;
|
|
12
|
+
prewarm: () => void;
|
|
10
13
|
close: () => Promise<void>;
|
|
11
14
|
};
|
|
12
|
-
|
|
15
|
+
type RouteTransformWorker = {
|
|
16
|
+
on(event: 'message', handler: (response: WorkerResponse) => void): unknown;
|
|
17
|
+
on(event: 'error', handler: (error: Error) => void): unknown;
|
|
18
|
+
on(event: 'exit', handler: (code: number) => void): unknown;
|
|
19
|
+
postMessage(message: WorkerRequest): void;
|
|
20
|
+
terminate(): Promise<number> | number;
|
|
21
|
+
};
|
|
22
|
+
type RouteTransformWorkerFactory = () => RouteTransformWorker;
|
|
23
|
+
export declare const getDefaultWorkerCount: (cpuCount?: number, { isBuild }?: {
|
|
24
|
+
isBuild?: boolean;
|
|
25
|
+
}) => number;
|
|
13
26
|
export declare const shouldParallelizeRouteTransforms: (routeCount: number) => boolean;
|
|
14
|
-
export declare const createRouteTransformExecutor: ({ parallelRouteTransform, routeChunkCache, splitRouteModules, }?: RouteTransformExecutorOptions) => RouteTransformExecutor;
|
|
27
|
+
export declare const createRouteTransformExecutor: ({ parallelRouteTransform, routeChunkCache, splitRouteModules, isBuild, }?: RouteTransformExecutorOptions) => RouteTransformExecutor;
|
|
28
|
+
export declare const createRouteTransformExecutorForTesting: (options: RouteTransformExecutorOptions, createWorker: RouteTransformWorkerFactory) => RouteTransformExecutor;
|
|
29
|
+
export {};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import * as Effect from 'effect/Effect';
|
|
1
2
|
import type { RsbuildPluginAPI } from '@rsbuild/core';
|
|
2
3
|
import { getBuildManifest } from './build-manifest.js';
|
|
3
4
|
import { getReactRouterManifestForDev, type ReactRouterManifestStats, type RouteManifestModuleExports } from './manifest.js';
|
|
@@ -28,5 +29,8 @@ type RunReactRouterPrerenderBuildOptions = {
|
|
|
28
29
|
resolvedConfigWithRoutes: ResolvedReactRouterConfig;
|
|
29
30
|
buildEnd: Config['buildEnd'];
|
|
30
31
|
};
|
|
32
|
+
export declare const createBuildRequestEffect: <T>(input: string | URL, init: RequestInit | undefined, handle: (request: Request) => Promise<T>) => Effect.Effect<T, Error, never>;
|
|
33
|
+
export declare const withBuildRequest: <T>(input: string | URL, init: RequestInit | undefined, handle: (request: Request) => Promise<T>) => Promise<T>;
|
|
34
|
+
export declare const createBoundedPrerenderTasksEffect: (prerenderPaths: string[], concurrency: number, renderPath: (path: string) => Effect.Effect<void, Error, never>) => Effect.Effect<void, Error, never>;
|
|
31
35
|
export declare const runReactRouterPrerenderBuild: (options: RunReactRouterPrerenderBuildOptions) => Promise<void>;
|
|
32
36
|
export {};
|
package/dist/prerender.d.ts
CHANGED
|
@@ -33,7 +33,6 @@ type SsrFalsePrerenderExportOptions = {
|
|
|
33
33
|
};
|
|
34
34
|
export declare const createPrerenderRoutes: (manifest: Record<string, any>, parentId?: string, grouped?: Record<string, any[]>) => MatchRouteObject[];
|
|
35
35
|
export declare const normalizePrerenderMatchPath: (path: string) => string;
|
|
36
|
-
export declare const withBuildRequest: <T>(input: string | URL, init: RequestInit | undefined, handle: (request: Request) => Promise<T>) => Promise<T>;
|
|
37
36
|
export declare const getSsrFalsePrerenderExportErrors: ({ routes, manifestRoutes, routeExports, prerenderPaths, }: SsrFalsePrerenderExportOptions) => string[];
|
|
38
37
|
export declare const getStaticPrerenderPaths: (routes: RouteConfigEntry[]) => StaticPrerenderPaths;
|
|
39
38
|
export declare const resolvePrerenderPaths: (prerender: PrerenderConfig, ssr: boolean, routes: RouteConfigEntry[], options?: PrerenderResolveOptions) => Promise<string[]>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { BuildManifest as ReactRouterBuildManifest, Config as ReactRouterConfig } from '@react-router/dev/config';
|
|
2
2
|
import type { NormalizedConfig } from '@rsbuild/core';
|
|
3
3
|
import type { RouteConfigEntry } from '@react-router/dev/routes';
|
|
4
|
+
import * as Effect from 'effect/Effect';
|
|
4
5
|
export type BuildEndHook = {
|
|
5
6
|
bivarianceHack(args: {
|
|
6
7
|
buildManifest: ReactRouterBuildManifest | undefined;
|
|
@@ -32,6 +33,11 @@ type RouteManifestEntry = {
|
|
|
32
33
|
file: string;
|
|
33
34
|
};
|
|
34
35
|
type RouteManifest = Record<string, RouteManifestEntry>;
|
|
36
|
+
type ResolveReactRouterConfigResult = {
|
|
37
|
+
resolved: ResolvedReactRouterConfig;
|
|
38
|
+
presets: NonNullable<Config['presets']>;
|
|
39
|
+
hasConfiguredServerModuleFormat: boolean;
|
|
40
|
+
};
|
|
35
41
|
export type ResolvedReactRouterConfig = Readonly<{
|
|
36
42
|
appDirectory: string;
|
|
37
43
|
basename: string;
|
|
@@ -50,9 +56,6 @@ export type ResolvedReactRouterConfig = Readonly<{
|
|
|
50
56
|
allowedActionOrigins: string[] | false;
|
|
51
57
|
unstable_routeConfig: RouteConfigEntry[];
|
|
52
58
|
}>;
|
|
53
|
-
export declare const
|
|
54
|
-
|
|
55
|
-
presets: NonNullable<Config["presets"]>;
|
|
56
|
-
hasConfiguredServerModuleFormat: boolean;
|
|
57
|
-
}>;
|
|
59
|
+
export declare const resolveReactRouterConfigEffect: (reactRouterUserConfig: Config) => Effect.Effect<ResolveReactRouterConfigResult, Error, never>;
|
|
60
|
+
export declare const resolveReactRouterConfig: (reactRouterUserConfig: Config) => Promise<ResolveReactRouterConfigResult>;
|
|
58
61
|
export {};
|
package/dist/ssr-externals.d.ts
CHANGED
package/dist/typegen.d.ts
CHANGED
|
@@ -1,2 +1,15 @@
|
|
|
1
1
|
import type { RsbuildPluginAPI } from '@rsbuild/core';
|
|
2
|
-
|
|
2
|
+
type Execa = typeof import('execa').execa;
|
|
3
|
+
type LoadExeca = () => Promise<Execa>;
|
|
4
|
+
export type ReactRouterTypegenRunner = {
|
|
5
|
+
startWatch(): Promise<void>;
|
|
6
|
+
closeWatch(): Promise<void>;
|
|
7
|
+
runBuild(): Promise<void>;
|
|
8
|
+
};
|
|
9
|
+
export declare const createReactRouterTypegenRunner: (loadExeca?: LoadExeca, appDirectory?: string) => ReactRouterTypegenRunner;
|
|
10
|
+
export declare const registerReactRouterTypegen: (api: RsbuildPluginAPI, { runner, devWatchDelayMs, appDirectory, }?: {
|
|
11
|
+
runner?: ReactRouterTypegenRunner;
|
|
12
|
+
devWatchDelayMs?: number;
|
|
13
|
+
appDirectory?: string;
|
|
14
|
+
}) => void;
|
|
15
|
+
export {};
|
package/dist/types.d.ts
CHANGED
|
@@ -17,8 +17,8 @@ export type PluginOptions = {
|
|
|
17
17
|
customServer?: boolean;
|
|
18
18
|
/**
|
|
19
19
|
* The output format for server builds.
|
|
20
|
-
* When
|
|
21
|
-
*
|
|
20
|
+
* When omitted, React Router's `serverModuleFormat` selects the emitted
|
|
21
|
+
* Rsbuild format (`"esm"` -> `"module"`, `"cjs"` -> `"commonjs"`).
|
|
22
22
|
*/
|
|
23
23
|
serverOutput?: 'module' | 'commonjs';
|
|
24
24
|
/**
|
|
@@ -26,14 +26,23 @@ export type PluginOptions = {
|
|
|
26
26
|
*/
|
|
27
27
|
federation?: boolean;
|
|
28
28
|
/**
|
|
29
|
-
*
|
|
29
|
+
* Rsbuild dev-only lazy compilation behavior.
|
|
30
30
|
*
|
|
31
|
-
* React Router
|
|
32
|
-
*
|
|
31
|
+
* React Router's browser manifest remains eager so initial dev requests can
|
|
32
|
+
* discover browser assets without lazy proxy delays.
|
|
33
33
|
*
|
|
34
|
-
*
|
|
34
|
+
* Pass `false` to disable.
|
|
35
|
+
* @default true
|
|
35
36
|
*/
|
|
36
37
|
lazyCompilation?: NonNullable<RsbuildConfig['dev']>['lazyCompilation'];
|
|
38
|
+
/**
|
|
39
|
+
* Prewarm Rspack lazy-compilation proxy modules after dev compiles.
|
|
40
|
+
* This depends on Rspack's generated lazy-compilation client shape and should
|
|
41
|
+
* be treated as experimental.
|
|
42
|
+
*
|
|
43
|
+
* @default false
|
|
44
|
+
*/
|
|
45
|
+
unstableLazyCompilationPrewarm?: boolean;
|
|
37
46
|
/**
|
|
38
47
|
* Emit structured React Router plugin timing logs.
|
|
39
48
|
* @default false
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rsbuild-plugin-react-router",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "React Router plugin for Rsbuild",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -78,6 +78,7 @@
|
|
|
78
78
|
"@types/node": "^25.0.10",
|
|
79
79
|
"@types/react": "^19.2.10",
|
|
80
80
|
"@types/react-dom": "^19.2.3",
|
|
81
|
+
"effect": "^3.21.4",
|
|
81
82
|
"es-module-lexer": "1.7.0",
|
|
82
83
|
"kill-port": "^2.0.1",
|
|
83
84
|
"pkg-pr-new": "^0.0.75",
|
|
@@ -100,13 +101,14 @@
|
|
|
100
101
|
},
|
|
101
102
|
"scripts": {
|
|
102
103
|
"build": "rslib build",
|
|
103
|
-
"bench": "node scripts/bench-builds.
|
|
104
|
-
"bench:ci-report": "node scripts/report-benchmark-ci.
|
|
105
|
-
"bench:compare": "node scripts/compare-benchmarks.
|
|
106
|
-
"bench:smoke": "node scripts/bench-builds.
|
|
107
|
-
"bench:baseline": "node scripts/bench-builds.
|
|
108
|
-
"bench:full": "node scripts/bench-builds.
|
|
109
|
-
"bench:large": "node scripts/bench-builds.
|
|
104
|
+
"bench": "node scripts/bench-builds.mts",
|
|
105
|
+
"bench:ci-report": "node scripts/report-benchmark-ci.mts",
|
|
106
|
+
"bench:compare": "node scripts/compare-benchmarks.mts",
|
|
107
|
+
"bench:smoke": "node scripts/bench-builds.mts --profile smoke --iterations 1 --warmup 0 --format both --out .benchmark/results/smoke",
|
|
108
|
+
"bench:baseline": "node scripts/bench-builds.mts --profile default --iterations 5 --warmup 1 --clean build --format both --out .benchmark/results/baseline",
|
|
109
|
+
"bench:full": "node scripts/bench-builds.mts --profile full --iterations 5 --warmup 1 --clean build --format both --out .benchmark/results/full",
|
|
110
|
+
"bench:large": "node scripts/bench-builds.mts --profile large --iterations 1 --warmup 0 --clean cold --format both --out .benchmark/results/large",
|
|
111
|
+
"bench:synthetic-app": "node scripts/bench-synthetic-app.mjs",
|
|
110
112
|
"e2e": "pnpm build && pnpm test:package-interop && pnpm --filter './examples/{default-template,spa-mode,prerender,custom-node-server,cloudflare,client-only}' test:e2e",
|
|
111
113
|
"dev": "rslib build --watch",
|
|
112
114
|
"test": "rstest run",
|
|
@@ -114,7 +116,7 @@
|
|
|
114
116
|
"test:coverage": "rstest run --coverage",
|
|
115
117
|
"test:core": "rstest run -c ./rstest.config.ts",
|
|
116
118
|
"test:core:watch": "rstest watch -c ./rstest.config.ts",
|
|
117
|
-
"test:package-interop": "node scripts/test-package-interop.
|
|
119
|
+
"test:package-interop": "node scripts/test-package-interop.mts",
|
|
118
120
|
"format": "prettier --write \"src/**/*.{js,jsx,ts,tsx}\"",
|
|
119
121
|
"format:check": "prettier --check \"src/**/*.{js,jsx,ts,tsx}\"",
|
|
120
122
|
"changeset": "changeset",
|
package/src/build-manifest.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { relative, resolve } from 'pathe';
|
|
2
|
+
import * as Effect from 'effect/Effect';
|
|
3
|
+
import { getCappedPluginConcurrency } from './concurrency.js';
|
|
4
|
+
import { runPluginEffect, tryPluginPromise } from './effect-runtime.js';
|
|
2
5
|
import type { Config } from './react-router-config.js';
|
|
3
6
|
import type { Route } from './types.js';
|
|
4
7
|
|
|
@@ -57,11 +60,26 @@ const configRouteToBranchRoute = (route: Route) => ({
|
|
|
57
60
|
index: route.index,
|
|
58
61
|
});
|
|
59
62
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
63
|
+
const validateServerBundleId = (
|
|
64
|
+
serverBundleId: string,
|
|
65
|
+
future: Required<Config>['future']
|
|
66
|
+
): Error | undefined => {
|
|
67
|
+
if (future?.v8_viteEnvironmentApi) {
|
|
68
|
+
return /^[a-zA-Z0-9_]+$/.test(serverBundleId)
|
|
69
|
+
? undefined
|
|
70
|
+
: new Error(
|
|
71
|
+
'The "serverBundles" function must only return strings containing alphanumeric characters and underscores.'
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return /^[a-zA-Z0-9-_]+$/.test(serverBundleId)
|
|
76
|
+
? undefined
|
|
77
|
+
: new Error(
|
|
78
|
+
'The "serverBundles" function must only return strings containing alphanumeric characters, hyphens and underscores.'
|
|
79
|
+
);
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
type GetBuildManifestOptions = {
|
|
65
83
|
reactRouterConfig: Required<
|
|
66
84
|
Pick<
|
|
67
85
|
Config,
|
|
@@ -71,80 +89,99 @@ export const getBuildManifest = async ({
|
|
|
71
89
|
Pick<Config, 'serverBundles'>;
|
|
72
90
|
routes: Record<string, Route>;
|
|
73
91
|
rootDirectory: string;
|
|
74
|
-
}
|
|
75
|
-
const {
|
|
76
|
-
serverBundles,
|
|
77
|
-
appDirectory,
|
|
78
|
-
buildDirectory,
|
|
79
|
-
serverBuildFile,
|
|
80
|
-
future,
|
|
81
|
-
} = reactRouterConfig;
|
|
82
|
-
|
|
83
|
-
if (!serverBundles) {
|
|
84
|
-
return { routes };
|
|
85
|
-
}
|
|
92
|
+
};
|
|
86
93
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
94
|
+
export const getBuildManifestEffect = ({
|
|
95
|
+
reactRouterConfig,
|
|
96
|
+
routes,
|
|
97
|
+
rootDirectory,
|
|
98
|
+
}: GetBuildManifestOptions): Effect.Effect<
|
|
99
|
+
BuildManifest | undefined,
|
|
100
|
+
Error,
|
|
101
|
+
never
|
|
102
|
+
> =>
|
|
103
|
+
Effect.gen(function* () {
|
|
104
|
+
const {
|
|
105
|
+
serverBundles,
|
|
106
|
+
appDirectory,
|
|
107
|
+
buildDirectory,
|
|
108
|
+
serverBuildFile,
|
|
109
|
+
future,
|
|
110
|
+
} = reactRouterConfig;
|
|
111
|
+
|
|
112
|
+
if (!serverBundles) {
|
|
113
|
+
return { routes };
|
|
114
|
+
}
|
|
96
115
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
),
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
if (typeof serverBundleId !== 'string') {
|
|
118
|
-
throw new Error('The "serverBundles" function must return a string');
|
|
119
|
-
}
|
|
116
|
+
const rootRelativeRoutes = Object.fromEntries(
|
|
117
|
+
Object.entries(routes).map(([id, route]) => {
|
|
118
|
+
const filePath = resolve(appDirectory, route.file);
|
|
119
|
+
return [
|
|
120
|
+
id,
|
|
121
|
+
{ ...route, file: normalizePath(relative(rootDirectory, filePath)) },
|
|
122
|
+
];
|
|
123
|
+
})
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
const serverBuildDirectory = resolve(buildDirectory, 'server');
|
|
127
|
+
|
|
128
|
+
const buildManifest: BuildManifest = {
|
|
129
|
+
routes: rootRelativeRoutes,
|
|
130
|
+
serverBundles: {},
|
|
131
|
+
routeIdToServerBundleId: {},
|
|
132
|
+
};
|
|
120
133
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
134
|
+
yield* Effect.forEach(
|
|
135
|
+
getAddressableRoutes(routes),
|
|
136
|
+
route =>
|
|
137
|
+
Effect.gen(function* () {
|
|
138
|
+
const branch = getRouteBranch(routes, route.id);
|
|
139
|
+
const serverBundleId = yield* tryPluginPromise(() =>
|
|
140
|
+
serverBundles({
|
|
141
|
+
branch: branch.map(branchRoute =>
|
|
142
|
+
configRouteToBranchRoute({
|
|
143
|
+
...branchRoute,
|
|
144
|
+
file: resolve(appDirectory, branchRoute.file),
|
|
145
|
+
})
|
|
146
|
+
),
|
|
147
|
+
})
|
|
125
148
|
);
|
|
126
|
-
}
|
|
127
|
-
} else if (!/^[a-zA-Z0-9-_]+$/.test(serverBundleId)) {
|
|
128
|
-
throw new Error(
|
|
129
|
-
'The "serverBundles" function must only return strings containing alphanumeric characters, hyphens and underscores.'
|
|
130
|
-
);
|
|
131
|
-
}
|
|
132
149
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
rootDirectory,
|
|
139
|
-
resolve(serverBuildDirectory, serverBundleId, serverBuildFile)
|
|
140
|
-
)
|
|
141
|
-
),
|
|
142
|
-
};
|
|
143
|
-
})
|
|
144
|
-
);
|
|
150
|
+
if (typeof serverBundleId !== 'string') {
|
|
151
|
+
return yield* Effect.fail(
|
|
152
|
+
new Error('The "serverBundles" function must return a string')
|
|
153
|
+
);
|
|
154
|
+
}
|
|
145
155
|
|
|
146
|
-
|
|
147
|
-
|
|
156
|
+
const validationError = validateServerBundleId(
|
|
157
|
+
serverBundleId,
|
|
158
|
+
future
|
|
159
|
+
);
|
|
160
|
+
if (validationError) {
|
|
161
|
+
return yield* Effect.fail(validationError);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
buildManifest.routeIdToServerBundleId![route.id] = serverBundleId;
|
|
165
|
+
buildManifest.serverBundles![serverBundleId] ??= {
|
|
166
|
+
id: serverBundleId,
|
|
167
|
+
file: normalizePath(
|
|
168
|
+
relative(
|
|
169
|
+
rootDirectory,
|
|
170
|
+
resolve(serverBuildDirectory, serverBundleId, serverBuildFile)
|
|
171
|
+
)
|
|
172
|
+
),
|
|
173
|
+
};
|
|
174
|
+
}),
|
|
175
|
+
{ concurrency: getCappedPluginConcurrency(), discard: true }
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
return buildManifest;
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
export const getBuildManifest = (
|
|
182
|
+
options: GetBuildManifestOptions
|
|
183
|
+
): Promise<BuildManifest | undefined> =>
|
|
184
|
+
runPluginEffect(getBuildManifestEffect(options));
|
|
148
185
|
|
|
149
186
|
export const getRoutesByServerBundleId = (
|
|
150
187
|
buildManifest: BuildManifest | undefined,
|
package/src/concurrency.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { availableParallelism, cpus } from 'node:os';
|
|
|
2
2
|
|
|
3
3
|
const DEFAULT_RESERVED_CORES = 2;
|
|
4
4
|
|
|
5
|
-
const getAvailableCpuCount = (): number =>
|
|
5
|
+
export const getAvailableCpuCount = (): number =>
|
|
6
6
|
typeof availableParallelism === 'function'
|
|
7
7
|
? availableParallelism()
|
|
8
8
|
: cpus().length;
|
|
@@ -11,24 +11,5 @@ export const getDefaultConcurrency = (
|
|
|
11
11
|
cpuCount: number = getAvailableCpuCount()
|
|
12
12
|
): number => Math.max(0, Math.floor(cpuCount) - DEFAULT_RESERVED_CORES);
|
|
13
13
|
|
|
14
|
-
export const
|
|
15
|
-
|
|
16
|
-
concurrency: number,
|
|
17
|
-
worker: (item: Item, index: number) => Promise<Result>
|
|
18
|
-
): Promise<Result[]> => {
|
|
19
|
-
const results = new Array<Result>(items.length);
|
|
20
|
-
let nextIndex = 0;
|
|
21
|
-
const workerCount = Math.max(1, Math.min(concurrency, items.length));
|
|
22
|
-
await Promise.all(
|
|
23
|
-
Array.from({ length: workerCount }, async () => {
|
|
24
|
-
while (true) {
|
|
25
|
-
const index = nextIndex++;
|
|
26
|
-
if (index >= items.length) {
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
results[index] = await worker(items[index], index);
|
|
30
|
-
}
|
|
31
|
-
})
|
|
32
|
-
);
|
|
33
|
-
return results;
|
|
34
|
-
};
|
|
14
|
+
export const getCappedPluginConcurrency = (cap = 16): number =>
|
|
15
|
+
Math.max(1, Math.min(cap, getDefaultConcurrency() || 1));
|
package/src/config-imports.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type { ModuleCache } from 'jiti';
|
|
2
|
+
import { createJiti } from 'jiti';
|
|
2
3
|
import { resolve } from 'pathe';
|
|
3
4
|
|
|
5
|
+
type ConfigImporter = Pick<ReturnType<typeof createJiti>, 'import'>;
|
|
6
|
+
|
|
4
7
|
const normalizePath = (filePath: string): string => resolve(filePath);
|
|
5
8
|
|
|
6
9
|
const isNodeModulePath = (filePath: string): boolean =>
|
|
@@ -43,3 +46,38 @@ export const clearConfigImportCache = (
|
|
|
43
46
|
}
|
|
44
47
|
}
|
|
45
48
|
};
|
|
49
|
+
|
|
50
|
+
export const importConfigWithWatchPaths = async <T>(
|
|
51
|
+
configPath: string,
|
|
52
|
+
load: (importer: ConfigImporter) => PromiseLike<T> | T = async importer =>
|
|
53
|
+
importer.import<T>(configPath, { default: true })
|
|
54
|
+
): Promise<{ value: Awaited<T>; watchPaths: string | string[] }> => {
|
|
55
|
+
const jiti = createJiti(process.cwd(), {
|
|
56
|
+
moduleCache: true,
|
|
57
|
+
});
|
|
58
|
+
const previousCacheKeys = new Set(Object.keys(jiti.cache));
|
|
59
|
+
let importPaths: string[] = [];
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
const value = await load(jiti);
|
|
63
|
+
importPaths = collectConfigImportWatchPaths(
|
|
64
|
+
configPath,
|
|
65
|
+
jiti.cache,
|
|
66
|
+
previousCacheKeys
|
|
67
|
+
);
|
|
68
|
+
return {
|
|
69
|
+
value,
|
|
70
|
+
watchPaths:
|
|
71
|
+
importPaths.length > 0 ? [configPath, ...importPaths] : configPath,
|
|
72
|
+
};
|
|
73
|
+
} finally {
|
|
74
|
+
if (importPaths.length === 0) {
|
|
75
|
+
importPaths = collectConfigImportWatchPaths(
|
|
76
|
+
configPath,
|
|
77
|
+
jiti.cache,
|
|
78
|
+
previousCacheKeys
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
clearConfigImportCache(jiti.cache, [configPath, ...importPaths]);
|
|
82
|
+
}
|
|
83
|
+
};
|