rsbuild-plugin-react-router 0.2.0 → 0.3.0
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 +185 -204
- package/dist/451.js +1087 -0
- package/dist/bounded-cache.d.ts +1 -0
- package/dist/build-manifest.d.ts +1 -1
- package/dist/build-output-transforms.d.ts +30 -0
- package/dist/concurrency.d.ts +2 -0
- package/dist/config-imports.d.ts +3 -0
- package/dist/constants.d.ts +3 -0
- package/dist/dev-generation.d.ts +25 -0
- package/dist/dev-runtime-artifacts.d.ts +39 -0
- package/dist/dev-runtime-compilation.d.ts +32 -0
- package/dist/dev-runtime-controller.d.ts +14 -0
- package/dist/dev-runtime-session.d.ts +34 -0
- package/dist/dev-server.d.ts +11 -2
- package/dist/export-utils.d.ts +15 -7
- package/dist/index.cjs +3050 -1244
- package/dist/index.d.ts +4 -1
- package/dist/index.js +2015 -1279
- package/dist/lazy-compilation.d.ts +5 -0
- package/dist/manifest.d.ts +32 -9
- package/dist/modify-browser-manifest.d.ts +30 -13
- package/dist/parallel-route-transform-protocol.d.ts +25 -0
- package/dist/parallel-route-transform-worker.d.ts +1 -0
- package/dist/parallel-route-transform-worker.js +38 -0
- package/dist/parallel-route-transforms.d.ts +14 -0
- package/dist/performance.d.ts +26 -0
- package/dist/plugin-utils.d.ts +2 -12
- package/dist/prerender-build.d.ts +32 -0
- package/dist/prerender.d.ts +28 -2
- package/dist/react-router-config.d.ts +8 -1
- package/dist/route-artifacts.d.ts +33 -0
- package/dist/route-ast.d.ts +21 -0
- package/dist/route-chunks.d.ts +9 -1
- package/dist/route-component-transform.d.ts +5 -0
- package/dist/route-export-pruning.d.ts +6 -0
- package/dist/route-export-resolution.d.ts +5 -0
- package/dist/route-transform-tasks.d.ts +47 -0
- package/dist/route-watch.d.ts +27 -0
- package/dist/server-build-plan.d.ts +22 -0
- package/dist/server-utils.d.ts +3 -2
- package/dist/templates/entry.server.cjs +3 -3
- package/dist/templates/entry.server.js +3 -3
- package/dist/typegen.d.ts +2 -0
- package/dist/types.d.ts +30 -12
- package/dist/virtual-modules.d.ts +2 -0
- package/dist/warnings/warn-on-client-source-maps.d.ts +1 -0
- package/dist/yuku.d.ts +15 -0
- package/package.json +22 -19
- package/src/bounded-cache.ts +18 -0
- package/src/build-manifest.ts +168 -0
- package/src/build-output-transforms.ts +273 -0
- package/src/concurrency.ts +34 -0
- package/src/config-imports.ts +45 -0
- package/src/constants.ts +91 -0
- package/src/dev-generation.ts +700 -0
- package/src/dev-runtime-artifacts.ts +207 -0
- package/src/dev-runtime-compilation.ts +92 -0
- package/src/dev-runtime-controller.ts +462 -0
- package/src/dev-runtime-session.ts +184 -0
- package/src/dev-server.ts +58 -0
- package/src/export-utils.ts +219 -0
- package/src/index.ts +1025 -0
- package/src/lazy-compilation.ts +94 -0
- package/src/manifest.ts +480 -0
- package/src/modify-browser-manifest.ts +245 -0
- package/src/parallel-route-transform-protocol.ts +35 -0
- package/src/parallel-route-transform-worker.ts +82 -0
- package/src/parallel-route-transforms.ts +332 -0
- package/src/performance.ts +254 -0
- package/src/plugin-utils.ts +82 -0
- package/src/prerender-build.ts +623 -0
- package/src/prerender.ts +367 -0
- package/src/react-router-config.ts +220 -0
- package/src/route-artifacts.ts +155 -0
- package/src/route-ast.ts +163 -0
- package/src/route-chunks.ts +857 -0
- package/src/route-component-transform.ts +314 -0
- package/src/route-config.ts +106 -0
- package/src/route-export-pruning.ts +668 -0
- package/src/route-export-resolution.ts +329 -0
- package/src/route-transform-tasks.ts +249 -0
- package/src/route-watch.ts +322 -0
- package/src/server-build-plan.ts +91 -0
- package/src/server-utils.ts +210 -0
- package/src/ssr-externals.ts +59 -0
- package/src/templates/context.ts +12 -0
- package/src/templates/entry.client.tsx +12 -0
- package/src/templates/entry.server.tsx +76 -0
- package/src/typegen.ts +49 -0
- package/src/types.ts +82 -0
- package/src/validation/validate-plugin-order.ts +76 -0
- package/src/virtual-modules.ts +30 -0
- package/src/warnings/warn-on-client-source-maps.ts +96 -0
- package/src/yuku.ts +67 -0
- package/dist/0~rslib-runtime.js +0 -16
- package/dist/babel.d.ts +0 -8
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { BUILD_CLIENT_ROUTE_QUERY_STRING } from './constants.js';
|
|
2
|
+
import type { PluginOptions } from './types.js';
|
|
3
|
+
|
|
4
|
+
type LazyCompilationOptions = Exclude<
|
|
5
|
+
NonNullable<PluginOptions['lazyCompilation']>,
|
|
6
|
+
boolean
|
|
7
|
+
>;
|
|
8
|
+
|
|
9
|
+
type LazyCompilationModule = {
|
|
10
|
+
request?: string;
|
|
11
|
+
userRequest?: string;
|
|
12
|
+
rawRequest?: string;
|
|
13
|
+
resource?: string;
|
|
14
|
+
identifier?: () => string;
|
|
15
|
+
nameForCondition?: () => string | null;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const normalizeSlashes = (value: string): string => value.replace(/\\/g, '/');
|
|
19
|
+
|
|
20
|
+
const getLazyCompilationModuleValues = (
|
|
21
|
+
module: LazyCompilationModule
|
|
22
|
+
): string[] =>
|
|
23
|
+
[
|
|
24
|
+
module.request,
|
|
25
|
+
module.userRequest,
|
|
26
|
+
module.rawRequest,
|
|
27
|
+
module.resource,
|
|
28
|
+
module.identifier?.(),
|
|
29
|
+
module.nameForCondition?.(),
|
|
30
|
+
].filter((value): value is string => Boolean(value));
|
|
31
|
+
|
|
32
|
+
const matchesLazyCompilationTest = (
|
|
33
|
+
test: LazyCompilationOptions['test'] | undefined,
|
|
34
|
+
module: LazyCompilationModule
|
|
35
|
+
): boolean => {
|
|
36
|
+
if (!test) {
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
if (typeof test === 'function') {
|
|
40
|
+
return test(module as Parameters<typeof test>[0]);
|
|
41
|
+
}
|
|
42
|
+
const conditionName = module.nameForCondition?.();
|
|
43
|
+
if (!conditionName) {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
test.lastIndex = 0;
|
|
47
|
+
return test.test(conditionName);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const createReactRouterHydrationModuleTest = (entryClientPath: string) => {
|
|
51
|
+
const eagerPatterns = [
|
|
52
|
+
normalizeSlashes(entryClientPath),
|
|
53
|
+
'virtual/react-router/browser-manifest',
|
|
54
|
+
BUILD_CLIENT_ROUTE_QUERY_STRING,
|
|
55
|
+
'?react-router-route',
|
|
56
|
+
];
|
|
57
|
+
|
|
58
|
+
return (module: LazyCompilationModule): boolean =>
|
|
59
|
+
getLazyCompilationModuleValues(module).some(value => {
|
|
60
|
+
const normalizedValue = normalizeSlashes(value);
|
|
61
|
+
return eagerPatterns.some(pattern => normalizedValue.includes(pattern));
|
|
62
|
+
});
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export const guardReactRouterLazyCompilation = ({
|
|
66
|
+
lazyCompilation,
|
|
67
|
+
entryClientPath,
|
|
68
|
+
}: {
|
|
69
|
+
lazyCompilation: PluginOptions['lazyCompilation'] | undefined;
|
|
70
|
+
entryClientPath: string;
|
|
71
|
+
}): PluginOptions['lazyCompilation'] | undefined => {
|
|
72
|
+
if (lazyCompilation === undefined || lazyCompilation === false) {
|
|
73
|
+
return lazyCompilation;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const options: LazyCompilationOptions =
|
|
77
|
+
lazyCompilation === true
|
|
78
|
+
? { entries: true, imports: true }
|
|
79
|
+
: lazyCompilation;
|
|
80
|
+
const userTest = options.test;
|
|
81
|
+
const isReactRouterHydrationModule =
|
|
82
|
+
createReactRouterHydrationModuleTest(entryClientPath);
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
...options,
|
|
86
|
+
test(module) {
|
|
87
|
+
const lazyModule = module as LazyCompilationModule;
|
|
88
|
+
if (isReactRouterHydrationModule(lazyModule)) {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
return matchesLazyCompilationTest(userTest, lazyModule);
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
};
|
package/src/manifest.ts
ADDED
|
@@ -0,0 +1,480 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { dirname, isAbsolute, relative, resolve } from 'pathe';
|
|
3
|
+
import type { Route, PluginOptions, RouteManifestItem } from './types.js';
|
|
4
|
+
import type { RouteConfigEntry } from '@react-router/dev/routes';
|
|
5
|
+
import { combineURLs, createRouteId } from './plugin-utils.js';
|
|
6
|
+
import { SERVER_EXPORTS, CLIENT_EXPORTS } from './constants.js';
|
|
7
|
+
import {
|
|
8
|
+
buildManifestChunkValidity,
|
|
9
|
+
createEmptyRouteChunkByExportName,
|
|
10
|
+
detectRouteChunksIfEnabled,
|
|
11
|
+
getRouteChunkEntryName,
|
|
12
|
+
routeChunkExportNames,
|
|
13
|
+
validateRouteChunks,
|
|
14
|
+
type RouteChunkCache,
|
|
15
|
+
type RouteChunkConfig,
|
|
16
|
+
} from './route-chunks.js';
|
|
17
|
+
import { getRouteModuleAnalysis } from './export-utils.js';
|
|
18
|
+
import { getDefaultConcurrency, mapWithConcurrency } from './concurrency.js';
|
|
19
|
+
|
|
20
|
+
const ROUTE_ANALYSIS_CONCURRENCY = Math.max(
|
|
21
|
+
1,
|
|
22
|
+
Math.min(16, getDefaultConcurrency() || 1)
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
export function configRoutesToRouteManifest(
|
|
26
|
+
appDirectory: string,
|
|
27
|
+
routes: RouteConfigEntry[],
|
|
28
|
+
rootId = 'root'
|
|
29
|
+
): Record<string, Route> {
|
|
30
|
+
return Object.fromEntries(
|
|
31
|
+
configRoutesToRouteManifestEntries(appDirectory, routes, rootId)
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function configRoutesToRouteManifestEntries(
|
|
36
|
+
appDirectory: string,
|
|
37
|
+
routes: RouteConfigEntry[],
|
|
38
|
+
rootId = 'root'
|
|
39
|
+
): Array<[string, Route]> {
|
|
40
|
+
const routeManifestEntries: Array<[string, Route]> = [];
|
|
41
|
+
const routeIds = new Set<string>();
|
|
42
|
+
|
|
43
|
+
function walk(route: RouteConfigEntry, parentId: string) {
|
|
44
|
+
const id = route.id || createRouteId(route.file);
|
|
45
|
+
const manifestItem = {
|
|
46
|
+
id,
|
|
47
|
+
parentId,
|
|
48
|
+
file: isAbsolute(route.file)
|
|
49
|
+
? relative(appDirectory, route.file)
|
|
50
|
+
: route.file,
|
|
51
|
+
path: route.path,
|
|
52
|
+
index: route.index,
|
|
53
|
+
caseSensitive: route.caseSensitive,
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
if (routeIds.has(id)) {
|
|
57
|
+
throw new Error(
|
|
58
|
+
`Unable to define routes with duplicate route id: "${id}"`
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
routeIds.add(id);
|
|
62
|
+
routeManifestEntries.push([id, manifestItem]);
|
|
63
|
+
|
|
64
|
+
if (route.children) {
|
|
65
|
+
for (const child of route.children) {
|
|
66
|
+
walk(child, id);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
for (const route of routes) {
|
|
72
|
+
walk(route, rootId);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return routeManifestEntries;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
type RouteChunkManifestOptions = {
|
|
79
|
+
splitRouteModules?: boolean | 'enforce';
|
|
80
|
+
rootRouteFile?: string;
|
|
81
|
+
isBuild?: boolean;
|
|
82
|
+
cache?: RouteChunkCache;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export type ReactRouterManifestForDev = {
|
|
86
|
+
version: string;
|
|
87
|
+
url: string;
|
|
88
|
+
hmr?: {
|
|
89
|
+
runtime: string;
|
|
90
|
+
};
|
|
91
|
+
entry: {
|
|
92
|
+
module: string;
|
|
93
|
+
imports: string[];
|
|
94
|
+
css: string[];
|
|
95
|
+
};
|
|
96
|
+
sri?: Record<string, string> | true;
|
|
97
|
+
routes: Record<string, RouteManifestItem>;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export type ReactRouterManifestStats = {
|
|
101
|
+
assetsByChunkName?: Record<string, string[]>;
|
|
102
|
+
entrypointFilesByName?: Record<string, string[]>;
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
type ReactRouterManifestStatsChunk = {
|
|
106
|
+
files?: Iterable<string>;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
type ReactRouterManifestStatsEntrypoint = {
|
|
110
|
+
getFiles?: () => Iterable<string>;
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
type ReactRouterManifestStatsCompilation = {
|
|
114
|
+
namedChunks: Iterable<[string, ReactRouterManifestStatsChunk]>;
|
|
115
|
+
entrypoints?: Iterable<[string, ReactRouterManifestStatsEntrypoint]>;
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
type ReactRouterManifestStatsNamedChunks =
|
|
119
|
+
ReactRouterManifestStatsCompilation['namedChunks'] & {
|
|
120
|
+
get?: (chunkName: string) => ReactRouterManifestStatsChunk | undefined;
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
type ReactRouterManifestStatsEntrypoints = NonNullable<
|
|
124
|
+
ReactRouterManifestStatsCompilation['entrypoints']
|
|
125
|
+
> & {
|
|
126
|
+
get?: (
|
|
127
|
+
entrypointName: string
|
|
128
|
+
) => ReactRouterManifestStatsEntrypoint | undefined;
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const orderChunkFiles = (chunkName: string, files: string[]): string[] => {
|
|
132
|
+
const ownChunkAsset = `${chunkName}.js`;
|
|
133
|
+
const ownFileIndex = files.findIndex(file => file.endsWith(ownChunkAsset));
|
|
134
|
+
if (ownFileIndex <= 0) {
|
|
135
|
+
return files;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return [
|
|
139
|
+
files[ownFileIndex],
|
|
140
|
+
...files.slice(0, ownFileIndex),
|
|
141
|
+
...files.slice(ownFileIndex + 1),
|
|
142
|
+
];
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
export const createReactRouterManifestStats = (
|
|
146
|
+
compilation: ReactRouterManifestStatsCompilation | undefined,
|
|
147
|
+
chunkNames?: ReadonlySet<string>
|
|
148
|
+
): ReactRouterManifestStats | undefined => {
|
|
149
|
+
if (!compilation) {
|
|
150
|
+
return undefined;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const assetsByChunkName: Record<string, string[]> = {};
|
|
154
|
+
const entrypointFilesByName: Record<string, string[]> = {};
|
|
155
|
+
const namedChunks =
|
|
156
|
+
compilation.namedChunks as ReactRouterManifestStatsNamedChunks;
|
|
157
|
+
const entrypoints = compilation.entrypoints as
|
|
158
|
+
| ReactRouterManifestStatsEntrypoints
|
|
159
|
+
| undefined;
|
|
160
|
+
|
|
161
|
+
if (chunkNames && typeof namedChunks.get === 'function') {
|
|
162
|
+
for (const chunkName of chunkNames) {
|
|
163
|
+
const chunk = namedChunks.get(chunkName);
|
|
164
|
+
if (!chunk) {
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
const files = Array.from(chunk.files ?? []);
|
|
168
|
+
assetsByChunkName[chunkName] = orderChunkFiles(chunkName, files);
|
|
169
|
+
}
|
|
170
|
+
} else {
|
|
171
|
+
for (const [chunkName, chunk] of namedChunks) {
|
|
172
|
+
if (chunkNames && !chunkNames.has(chunkName)) {
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
const files = Array.from(chunk.files ?? []);
|
|
176
|
+
assetsByChunkName[chunkName] = orderChunkFiles(chunkName, files);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (entrypoints) {
|
|
181
|
+
if (chunkNames && typeof entrypoints.get === 'function') {
|
|
182
|
+
for (const entrypointName of chunkNames) {
|
|
183
|
+
const entrypoint = entrypoints.get(entrypointName);
|
|
184
|
+
if (!entrypoint) {
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
entrypointFilesByName[entrypointName] = Array.from(
|
|
188
|
+
entrypoint.getFiles?.() ?? []
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
} else {
|
|
192
|
+
for (const [entrypointName, entrypoint] of entrypoints) {
|
|
193
|
+
if (chunkNames && !chunkNames.has(entrypointName)) {
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
entrypointFilesByName[entrypointName] = Array.from(
|
|
197
|
+
entrypoint.getFiles?.() ?? []
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return Object.keys(entrypointFilesByName).length > 0
|
|
204
|
+
? { assetsByChunkName, entrypointFilesByName }
|
|
205
|
+
: { assetsByChunkName };
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
export type RouteManifestModuleExports = Record<string, readonly string[]>;
|
|
209
|
+
|
|
210
|
+
export type ReactRouterManifestGenerationResult = {
|
|
211
|
+
manifest: ReactRouterManifestForDev;
|
|
212
|
+
moduleExportsByRouteId: RouteManifestModuleExports;
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
const DEFAULT_MANIFEST_DIR = 'static/js';
|
|
216
|
+
|
|
217
|
+
const getManifestDirFromEntryAsset = (entryModulePath?: string): string => {
|
|
218
|
+
if (!entryModulePath) {
|
|
219
|
+
return DEFAULT_MANIFEST_DIR;
|
|
220
|
+
}
|
|
221
|
+
const dir = dirname(entryModulePath);
|
|
222
|
+
return dir === '.' ? DEFAULT_MANIFEST_DIR : dir;
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
export const getReactRouterManifestPath = ({
|
|
226
|
+
version,
|
|
227
|
+
isBuild,
|
|
228
|
+
entryModulePath,
|
|
229
|
+
}: {
|
|
230
|
+
version: string;
|
|
231
|
+
isBuild: boolean;
|
|
232
|
+
entryModulePath?: string;
|
|
233
|
+
}): string => {
|
|
234
|
+
if (!isBuild) {
|
|
235
|
+
return 'static/js/virtual/react-router/browser-manifest.js';
|
|
236
|
+
}
|
|
237
|
+
const dir = getManifestDirFromEntryAsset(entryModulePath);
|
|
238
|
+
return `${dir}/manifest-${version}.js`;
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
const getManifestVersion = (
|
|
242
|
+
fingerprintedValues: { entry: unknown; routes: unknown },
|
|
243
|
+
isBuild: boolean
|
|
244
|
+
): string => {
|
|
245
|
+
if (!isBuild) {
|
|
246
|
+
return String(Math.random());
|
|
247
|
+
}
|
|
248
|
+
return createHash('md5')
|
|
249
|
+
.update(JSON.stringify(fingerprintedValues))
|
|
250
|
+
.digest('hex')
|
|
251
|
+
.slice(0, 8);
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
const getRouteEntryName = (route: Route): string => {
|
|
255
|
+
const extensionIndex = route.file.lastIndexOf('.');
|
|
256
|
+
return extensionIndex >= 0 ? route.file.slice(0, extensionIndex) : route.file;
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
export const getReactRouterManifestChunkNames = (
|
|
260
|
+
routes: Record<string, Route>,
|
|
261
|
+
splitRouteModules: boolean | 'enforce' = false
|
|
262
|
+
): Set<string> => {
|
|
263
|
+
const chunkNames = new Set<string>(['entry.client']);
|
|
264
|
+
for (const route of Object.values(routes)) {
|
|
265
|
+
chunkNames.add(getRouteEntryName(route));
|
|
266
|
+
if (!splitRouteModules || route.id === 'root') {
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
269
|
+
for (const exportName of routeChunkExportNames) {
|
|
270
|
+
chunkNames.add(getRouteChunkEntryName(route.id, exportName));
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return chunkNames;
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
export async function generateReactRouterManifestForDev(
|
|
277
|
+
routes: Record<string, Route>,
|
|
278
|
+
_options: PluginOptions,
|
|
279
|
+
clientStats: ReactRouterManifestStats | undefined,
|
|
280
|
+
context: string,
|
|
281
|
+
assetPrefix = '/',
|
|
282
|
+
routeChunkOptions?: RouteChunkManifestOptions
|
|
283
|
+
): Promise<ReactRouterManifestGenerationResult> {
|
|
284
|
+
const result: Record<string, RouteManifestItem> = {};
|
|
285
|
+
const splitRouteModules = routeChunkOptions?.splitRouteModules ?? false;
|
|
286
|
+
const enforceSplitRouteModules = splitRouteModules === 'enforce';
|
|
287
|
+
const isBuild = routeChunkOptions?.isBuild ?? false;
|
|
288
|
+
const routeChunkConfig: RouteChunkConfig | null =
|
|
289
|
+
splitRouteModules && routeChunkOptions?.rootRouteFile
|
|
290
|
+
? {
|
|
291
|
+
splitRouteModules,
|
|
292
|
+
appDirectory: context,
|
|
293
|
+
rootRouteFile: routeChunkOptions.rootRouteFile,
|
|
294
|
+
}
|
|
295
|
+
: null;
|
|
296
|
+
|
|
297
|
+
const getAssetsForChunk = (chunkName: string): string[] => {
|
|
298
|
+
const assets = clientStats?.assetsByChunkName?.[chunkName];
|
|
299
|
+
if (!assets) {
|
|
300
|
+
return [`${DEFAULT_MANIFEST_DIR}/${chunkName}.js`];
|
|
301
|
+
}
|
|
302
|
+
const entrypointCssAssets =
|
|
303
|
+
clientStats?.entrypointFilesByName?.[chunkName]?.filter(asset =>
|
|
304
|
+
asset.endsWith('.css')
|
|
305
|
+
) ?? [];
|
|
306
|
+
const cssAssets = [
|
|
307
|
+
...assets.filter(asset => asset.endsWith('.css')),
|
|
308
|
+
...entrypointCssAssets,
|
|
309
|
+
].filter((asset, index, all) => all.indexOf(asset) === index);
|
|
310
|
+
const nonCssAssets = assets.filter(asset => !asset.endsWith('.css'));
|
|
311
|
+
|
|
312
|
+
if (!nonCssAssets.some(asset => asset.endsWith('.js'))) {
|
|
313
|
+
return [
|
|
314
|
+
`${DEFAULT_MANIFEST_DIR}/${chunkName}.js`,
|
|
315
|
+
...nonCssAssets,
|
|
316
|
+
...cssAssets,
|
|
317
|
+
];
|
|
318
|
+
}
|
|
319
|
+
return [...nonCssAssets, ...cssAssets];
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
const getModulePathForChunk = (chunkName: string): string | undefined => {
|
|
323
|
+
const assets = getAssetsForChunk(chunkName);
|
|
324
|
+
const jsAssets = assets.filter(asset => asset.endsWith('.js'));
|
|
325
|
+
return jsAssets[0] ? combineURLs(assetPrefix, jsAssets[0]) : undefined;
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
const manifestEntries = await mapWithConcurrency(
|
|
329
|
+
Object.entries(routes),
|
|
330
|
+
ROUTE_ANALYSIS_CONCURRENCY,
|
|
331
|
+
async ([key, route]) => {
|
|
332
|
+
const routeEntryName = getRouteEntryName(route);
|
|
333
|
+
const assets = getAssetsForChunk(routeEntryName);
|
|
334
|
+
const jsAssets = assets.filter(asset => asset.endsWith('.js'));
|
|
335
|
+
let cssAssets = assets.filter(asset => asset.endsWith('.css'));
|
|
336
|
+
const routeFilePath = resolve(context, route.file);
|
|
337
|
+
let exports = new Set<string>();
|
|
338
|
+
let routeModuleExports: readonly string[] = [];
|
|
339
|
+
let hasRouteChunkByExportName: ReturnType<
|
|
340
|
+
typeof createEmptyRouteChunkByExportName
|
|
341
|
+
> | null = null;
|
|
342
|
+
|
|
343
|
+
try {
|
|
344
|
+
const { code, exports: exportNames } =
|
|
345
|
+
await getRouteModuleAnalysis(routeFilePath);
|
|
346
|
+
if (
|
|
347
|
+
!isBuild &&
|
|
348
|
+
cssAssets.length === 0 &&
|
|
349
|
+
/\.(?:css|less|sass|scss)(?:\?[^'"`]+)?['"`]/.test(code)
|
|
350
|
+
) {
|
|
351
|
+
cssAssets = [
|
|
352
|
+
`${DEFAULT_MANIFEST_DIR.replace('/js', '/css')}/${routeEntryName}.css`,
|
|
353
|
+
];
|
|
354
|
+
}
|
|
355
|
+
routeModuleExports = exportNames;
|
|
356
|
+
exports = new Set(exportNames);
|
|
357
|
+
|
|
358
|
+
if (isBuild && routeChunkConfig) {
|
|
359
|
+
const { hasRouteChunkByExportName: chunkInfo } =
|
|
360
|
+
await detectRouteChunksIfEnabled(
|
|
361
|
+
routeChunkOptions?.cache,
|
|
362
|
+
routeChunkConfig,
|
|
363
|
+
routeFilePath,
|
|
364
|
+
code
|
|
365
|
+
);
|
|
366
|
+
hasRouteChunkByExportName = chunkInfo;
|
|
367
|
+
}
|
|
368
|
+
} catch (error) {
|
|
369
|
+
if (isBuild) {
|
|
370
|
+
throw error;
|
|
371
|
+
}
|
|
372
|
+
console.error(`Failed to analyze route file ${routeFilePath}:`, error);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
const hasClientAction = exports.has(CLIENT_EXPORTS.clientAction);
|
|
376
|
+
const hasClientLoader = exports.has(CLIENT_EXPORTS.clientLoader);
|
|
377
|
+
const hasClientMiddleware = exports.has(CLIENT_EXPORTS.clientMiddleware);
|
|
378
|
+
const hasDefaultExport = exports.has('default');
|
|
379
|
+
const routeChunkMap = hasRouteChunkByExportName;
|
|
380
|
+
|
|
381
|
+
if (isBuild && enforceSplitRouteModules && routeChunkConfig) {
|
|
382
|
+
validateRouteChunks({
|
|
383
|
+
config: routeChunkConfig,
|
|
384
|
+
id: routeFilePath,
|
|
385
|
+
valid: buildManifestChunkValidity(
|
|
386
|
+
exports,
|
|
387
|
+
routeChunkMap ?? createEmptyRouteChunkByExportName()
|
|
388
|
+
),
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
return [
|
|
393
|
+
key,
|
|
394
|
+
{
|
|
395
|
+
id: route.id,
|
|
396
|
+
parentId: route.parentId,
|
|
397
|
+
path: route.path,
|
|
398
|
+
index: route.index,
|
|
399
|
+
caseSensitive: route.caseSensitive,
|
|
400
|
+
module: combineURLs(assetPrefix, jsAssets[0] || ''),
|
|
401
|
+
clientActionModule: routeChunkMap?.clientAction
|
|
402
|
+
? getModulePathForChunk(
|
|
403
|
+
getRouteChunkEntryName(route.id, 'clientAction')
|
|
404
|
+
)
|
|
405
|
+
: undefined,
|
|
406
|
+
clientLoaderModule: routeChunkMap?.clientLoader
|
|
407
|
+
? getModulePathForChunk(
|
|
408
|
+
getRouteChunkEntryName(route.id, 'clientLoader')
|
|
409
|
+
)
|
|
410
|
+
: undefined,
|
|
411
|
+
clientMiddlewareModule: routeChunkMap?.clientMiddleware
|
|
412
|
+
? getModulePathForChunk(
|
|
413
|
+
getRouteChunkEntryName(route.id, 'clientMiddleware')
|
|
414
|
+
)
|
|
415
|
+
: undefined,
|
|
416
|
+
hydrateFallbackModule: routeChunkMap?.HydrateFallback
|
|
417
|
+
? getModulePathForChunk(
|
|
418
|
+
getRouteChunkEntryName(route.id, 'HydrateFallback')
|
|
419
|
+
)
|
|
420
|
+
: undefined,
|
|
421
|
+
hasAction: exports.has(SERVER_EXPORTS.action),
|
|
422
|
+
hasLoader: exports.has(SERVER_EXPORTS.loader),
|
|
423
|
+
hasClientAction,
|
|
424
|
+
hasClientLoader,
|
|
425
|
+
hasClientMiddleware,
|
|
426
|
+
hasDefaultExport,
|
|
427
|
+
hasErrorBoundary: exports.has(CLIENT_EXPORTS.ErrorBoundary),
|
|
428
|
+
imports: jsAssets.map(asset => combineURLs(assetPrefix, asset)),
|
|
429
|
+
css: cssAssets.map(asset => combineURLs(assetPrefix, asset)),
|
|
430
|
+
},
|
|
431
|
+
routeModuleExports,
|
|
432
|
+
] as const;
|
|
433
|
+
}
|
|
434
|
+
);
|
|
435
|
+
|
|
436
|
+
const routeModuleExportsByRouteId: RouteManifestModuleExports = {};
|
|
437
|
+
for (const [key, routeManifestItem, routeModuleExports] of manifestEntries) {
|
|
438
|
+
result[key] = routeManifestItem;
|
|
439
|
+
routeModuleExportsByRouteId[key] = routeModuleExports;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
const entryAssets = getAssetsForChunk('entry.client');
|
|
443
|
+
const entryJsAssets = entryAssets.filter(asset => asset.endsWith('.js'));
|
|
444
|
+
const entryCssAssets = entryAssets.filter(asset => asset.endsWith('.css'));
|
|
445
|
+
|
|
446
|
+
const fingerprintedValues = {
|
|
447
|
+
entry: {
|
|
448
|
+
module: combineURLs(assetPrefix, entryJsAssets[0] || ''),
|
|
449
|
+
imports: entryJsAssets.map(asset => combineURLs(assetPrefix, asset)),
|
|
450
|
+
css: entryCssAssets.map(asset => combineURLs(assetPrefix, asset)),
|
|
451
|
+
},
|
|
452
|
+
routes: result,
|
|
453
|
+
};
|
|
454
|
+
const version = getManifestVersion(fingerprintedValues, isBuild);
|
|
455
|
+
const manifestPath = getReactRouterManifestPath({
|
|
456
|
+
version,
|
|
457
|
+
isBuild,
|
|
458
|
+
entryModulePath: entryJsAssets[0],
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
const manifest = {
|
|
462
|
+
version,
|
|
463
|
+
url: combineURLs(assetPrefix, manifestPath),
|
|
464
|
+
hmr: undefined,
|
|
465
|
+
entry: fingerprintedValues.entry,
|
|
466
|
+
sri: undefined,
|
|
467
|
+
routes: result,
|
|
468
|
+
};
|
|
469
|
+
|
|
470
|
+
return {
|
|
471
|
+
manifest,
|
|
472
|
+
moduleExportsByRouteId: routeModuleExportsByRouteId,
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
export async function getReactRouterManifestForDev(
|
|
477
|
+
...args: Parameters<typeof generateReactRouterManifestForDev>
|
|
478
|
+
): Promise<ReactRouterManifestForDev> {
|
|
479
|
+
return (await generateReactRouterManifestForDev(...args)).manifest;
|
|
480
|
+
}
|