rsbuild-plugin-react-router 0.3.0 → 0.4.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 +88 -35
- package/dist/451.js +164 -30
- package/dist/build-manifest.d.ts +6 -3
- package/dist/build-output-transforms.d.ts +2 -1
- 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-hmr.d.ts +45 -0
- package/dist/dev-runtime-artifacts.d.ts +9 -1
- package/dist/dev-runtime-compilation.d.ts +17 -2
- package/dist/dev-runtime-controller.d.ts +6 -1
- 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 +11852 -848
- package/dist/index.d.ts +5 -0
- package/dist/index.js +8289 -773
- 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/route-artifacts.d.ts +10 -2
- package/dist/route-transform-tasks.d.ts +3 -0
- 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 +18 -10
- package/src/build-manifest.ts +110 -73
- package/src/build-output-transforms.ts +5 -0
- 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-hmr.ts +431 -0
- package/src/dev-runtime-artifacts.ts +50 -18
- package/src/dev-runtime-compilation.ts +80 -1
- package/src/dev-runtime-controller.ts +155 -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 +166 -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 +147 -63
- package/src/prerender.ts +1 -19
- package/src/react-router-config.ts +98 -73
- package/src/route-artifacts.ts +122 -3
- package/src/route-export-resolution.ts +3 -3
- package/src/route-transform-tasks.ts +173 -2
- 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
package/src/manifest.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
2
|
import { dirname, isAbsolute, relative, resolve } from 'pathe';
|
|
3
|
+
import * as Effect from 'effect/Effect';
|
|
3
4
|
import type { Route, PluginOptions, RouteManifestItem } from './types.js';
|
|
4
|
-
import type { RouteConfigEntry } from '@react-router/dev/routes';
|
|
5
5
|
import { combineURLs, createRouteId } from './plugin-utils.js';
|
|
6
6
|
import { SERVER_EXPORTS, CLIENT_EXPORTS } from './constants.js';
|
|
7
7
|
import {
|
|
@@ -15,12 +15,24 @@ import {
|
|
|
15
15
|
type RouteChunkConfig,
|
|
16
16
|
} from './route-chunks.js';
|
|
17
17
|
import { getRouteModuleAnalysis } from './export-utils.js';
|
|
18
|
-
import {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
18
|
+
import { getCappedPluginConcurrency } from './concurrency.js';
|
|
19
|
+
import { runPluginEffect, tryPluginPromise } from './effect-runtime.js';
|
|
20
|
+
|
|
21
|
+
const ROUTE_ANALYSIS_CONCURRENCY = getCappedPluginConcurrency();
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Structural equivalent of `RouteConfigEntry` from `@react-router/dev/routes`,
|
|
25
|
+
* inlined so the public declaration files do not reference a devDependency
|
|
26
|
+
* (which would fail to resolve for consumers with `skipLibCheck: false`).
|
|
27
|
+
*/
|
|
28
|
+
export interface RouteConfigEntry {
|
|
29
|
+
id?: string;
|
|
30
|
+
path?: string;
|
|
31
|
+
index?: boolean;
|
|
32
|
+
caseSensitive?: boolean;
|
|
33
|
+
file: string;
|
|
34
|
+
children?: RouteConfigEntry[];
|
|
35
|
+
}
|
|
24
36
|
|
|
25
37
|
export function configRoutesToRouteManifest(
|
|
26
38
|
appDirectory: string,
|
|
@@ -110,22 +122,13 @@ type ReactRouterManifestStatsEntrypoint = {
|
|
|
110
122
|
getFiles?: () => Iterable<string>;
|
|
111
123
|
};
|
|
112
124
|
|
|
113
|
-
type
|
|
114
|
-
|
|
115
|
-
entrypoints?: Iterable<[string, ReactRouterManifestStatsEntrypoint]>;
|
|
125
|
+
type ReactRouterManifestStatsLookup<T> = Iterable<[string, T]> & {
|
|
126
|
+
get?: (name: string) => T | undefined;
|
|
116
127
|
};
|
|
117
128
|
|
|
118
|
-
type
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
type ReactRouterManifestStatsEntrypoints = NonNullable<
|
|
124
|
-
ReactRouterManifestStatsCompilation['entrypoints']
|
|
125
|
-
> & {
|
|
126
|
-
get?: (
|
|
127
|
-
entrypointName: string
|
|
128
|
-
) => ReactRouterManifestStatsEntrypoint | undefined;
|
|
129
|
+
type ReactRouterManifestStatsCompilation = {
|
|
130
|
+
namedChunks: ReactRouterManifestStatsLookup<ReactRouterManifestStatsChunk>;
|
|
131
|
+
entrypoints?: ReactRouterManifestStatsLookup<ReactRouterManifestStatsEntrypoint>;
|
|
129
132
|
};
|
|
130
133
|
|
|
131
134
|
const orderChunkFiles = (chunkName: string, files: string[]): string[] => {
|
|
@@ -142,64 +145,71 @@ const orderChunkFiles = (chunkName: string, files: string[]): string[] => {
|
|
|
142
145
|
];
|
|
143
146
|
};
|
|
144
147
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
148
|
+
const collectManifestFilesByName = <T>(
|
|
149
|
+
items: ReactRouterManifestStatsLookup<T>,
|
|
150
|
+
names: ReadonlySet<string> | undefined,
|
|
151
|
+
getFiles: (name: string, item: T) => string[]
|
|
152
|
+
): Record<string, string[]> => {
|
|
153
|
+
const filesByName: Record<string, string[]> = {};
|
|
154
|
+
if (!names) {
|
|
155
|
+
for (const [name, item] of items) {
|
|
156
|
+
filesByName[name] = getFiles(name, item);
|
|
157
|
+
}
|
|
158
|
+
return filesByName;
|
|
151
159
|
}
|
|
152
160
|
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
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) {
|
|
161
|
+
const missingNames = new Set(names);
|
|
162
|
+
if (typeof items.get === 'function') {
|
|
163
|
+
for (const name of names) {
|
|
164
|
+
const item = items.get(name);
|
|
165
|
+
if (!item) {
|
|
165
166
|
continue;
|
|
166
167
|
}
|
|
167
|
-
|
|
168
|
-
|
|
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);
|
|
168
|
+
filesByName[name] = getFiles(name, item);
|
|
169
|
+
missingNames.delete(name);
|
|
177
170
|
}
|
|
178
171
|
}
|
|
179
172
|
|
|
180
|
-
if (
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
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
|
-
}
|
|
173
|
+
if (missingNames.size === 0) {
|
|
174
|
+
return filesByName;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
for (const [name, item] of items) {
|
|
178
|
+
if (!missingNames.has(name)) {
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
filesByName[name] = getFiles(name, item);
|
|
182
|
+
missingNames.delete(name);
|
|
183
|
+
if (missingNames.size === 0) {
|
|
184
|
+
break;
|
|
200
185
|
}
|
|
201
186
|
}
|
|
202
187
|
|
|
188
|
+
return filesByName;
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
export const createReactRouterManifestStats = (
|
|
192
|
+
compilation: ReactRouterManifestStatsCompilation | undefined,
|
|
193
|
+
chunkNames?: ReadonlySet<string>
|
|
194
|
+
): ReactRouterManifestStats | undefined => {
|
|
195
|
+
if (!compilation) {
|
|
196
|
+
return undefined;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const assetsByChunkName = collectManifestFilesByName(
|
|
200
|
+
compilation.namedChunks,
|
|
201
|
+
chunkNames,
|
|
202
|
+
(chunkName, chunk) =>
|
|
203
|
+
orderChunkFiles(chunkName, Array.from(chunk.files ?? []))
|
|
204
|
+
);
|
|
205
|
+
const entrypointFilesByName = compilation.entrypoints
|
|
206
|
+
? collectManifestFilesByName(
|
|
207
|
+
compilation.entrypoints,
|
|
208
|
+
chunkNames,
|
|
209
|
+
(_name, entrypoint) => Array.from(entrypoint.getFiles?.() ?? [])
|
|
210
|
+
)
|
|
211
|
+
: {};
|
|
212
|
+
|
|
203
213
|
return Object.keys(entrypointFilesByName).length > 0
|
|
204
214
|
? { assetsByChunkName, entrypointFilesByName }
|
|
205
215
|
: { assetsByChunkName };
|
|
@@ -212,7 +222,122 @@ export type ReactRouterManifestGenerationResult = {
|
|
|
212
222
|
moduleExportsByRouteId: RouteManifestModuleExports;
|
|
213
223
|
};
|
|
214
224
|
|
|
225
|
+
type ChunkAssets = {
|
|
226
|
+
js: string[];
|
|
227
|
+
css: string[];
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
type RouteManifestAnalysis = {
|
|
231
|
+
cssAssets: string[];
|
|
232
|
+
exports: Set<string>;
|
|
233
|
+
routeModuleExports: readonly string[];
|
|
234
|
+
hasRouteChunkByExportName: ReturnType<
|
|
235
|
+
typeof createEmptyRouteChunkByExportName
|
|
236
|
+
> | null;
|
|
237
|
+
};
|
|
238
|
+
|
|
215
239
|
const DEFAULT_MANIFEST_DIR = 'static/js';
|
|
240
|
+
const CSS_IMPORT_RE = /\.(?:css|less|sass|scss)(?:\?[^'"`]+)?['"`]/;
|
|
241
|
+
|
|
242
|
+
const createChunkAssetResolver = (
|
|
243
|
+
clientStats: ReactRouterManifestStats | undefined
|
|
244
|
+
): ((chunkName: string) => ChunkAssets) => {
|
|
245
|
+
const chunkAssetsByName = new Map<string, ChunkAssets>();
|
|
246
|
+
|
|
247
|
+
return (chunkName: string): ChunkAssets => {
|
|
248
|
+
const cached = chunkAssetsByName.get(chunkName);
|
|
249
|
+
if (cached) {
|
|
250
|
+
return cached;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const assets = clientStats?.assetsByChunkName?.[chunkName];
|
|
254
|
+
if (!assets) {
|
|
255
|
+
const fallback = `${DEFAULT_MANIFEST_DIR}/${chunkName}.js`;
|
|
256
|
+
const result = { js: [fallback], css: [] };
|
|
257
|
+
chunkAssetsByName.set(chunkName, result);
|
|
258
|
+
return result;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const cssAssets = new Set<string>();
|
|
262
|
+
const jsAssets: string[] = [];
|
|
263
|
+
for (const asset of assets) {
|
|
264
|
+
if (asset.endsWith('.css')) {
|
|
265
|
+
cssAssets.add(asset);
|
|
266
|
+
} else if (asset.endsWith('.js')) {
|
|
267
|
+
jsAssets.push(asset);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
for (const asset of clientStats?.entrypointFilesByName?.[chunkName] ?? []) {
|
|
271
|
+
if (asset.endsWith('.css')) {
|
|
272
|
+
cssAssets.add(asset);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
if (jsAssets.length === 0) {
|
|
276
|
+
jsAssets.push(`${DEFAULT_MANIFEST_DIR}/${chunkName}.js`);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const result = { js: jsAssets, css: [...cssAssets] };
|
|
280
|
+
chunkAssetsByName.set(chunkName, result);
|
|
281
|
+
return result;
|
|
282
|
+
};
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
const analyzeRouteForManifestEffect = ({
|
|
286
|
+
discoveredCssAssets,
|
|
287
|
+
isBuild,
|
|
288
|
+
routeChunkCache,
|
|
289
|
+
routeChunkConfig,
|
|
290
|
+
routeEntryName,
|
|
291
|
+
routeFilePath,
|
|
292
|
+
}: {
|
|
293
|
+
discoveredCssAssets: string[];
|
|
294
|
+
isBuild: boolean;
|
|
295
|
+
routeChunkCache: RouteChunkCache | undefined;
|
|
296
|
+
routeChunkConfig: RouteChunkConfig | null;
|
|
297
|
+
routeEntryName: string;
|
|
298
|
+
routeFilePath: string;
|
|
299
|
+
}): Effect.Effect<RouteManifestAnalysis, Error, never> =>
|
|
300
|
+
tryPluginPromise(async () => {
|
|
301
|
+
const { code, exports: exportNames } =
|
|
302
|
+
await getRouteModuleAnalysis(routeFilePath);
|
|
303
|
+
const cssAssets =
|
|
304
|
+
!isBuild && discoveredCssAssets.length === 0 && CSS_IMPORT_RE.test(code)
|
|
305
|
+
? [
|
|
306
|
+
`${DEFAULT_MANIFEST_DIR.replace('/js', '/css')}/${routeEntryName}.css`,
|
|
307
|
+
]
|
|
308
|
+
: discoveredCssAssets;
|
|
309
|
+
const chunkInfo =
|
|
310
|
+
isBuild && routeChunkConfig
|
|
311
|
+
? await detectRouteChunksIfEnabled(
|
|
312
|
+
routeChunkCache,
|
|
313
|
+
routeChunkConfig,
|
|
314
|
+
routeFilePath,
|
|
315
|
+
code
|
|
316
|
+
)
|
|
317
|
+
: null;
|
|
318
|
+
|
|
319
|
+
return {
|
|
320
|
+
cssAssets,
|
|
321
|
+
exports: new Set(exportNames),
|
|
322
|
+
routeModuleExports: exportNames,
|
|
323
|
+
hasRouteChunkByExportName: chunkInfo?.hasRouteChunkByExportName ?? null,
|
|
324
|
+
};
|
|
325
|
+
}).pipe(
|
|
326
|
+
Effect.catchAll(error => {
|
|
327
|
+
if (isBuild) {
|
|
328
|
+
return Effect.fail(error);
|
|
329
|
+
}
|
|
330
|
+
return Effect.sync(() => {
|
|
331
|
+
console.error(`Failed to analyze route file ${routeFilePath}:`, error);
|
|
332
|
+
return {
|
|
333
|
+
cssAssets: discoveredCssAssets,
|
|
334
|
+
exports: new Set<string>(),
|
|
335
|
+
routeModuleExports: [],
|
|
336
|
+
hasRouteChunkByExportName: null,
|
|
337
|
+
};
|
|
338
|
+
});
|
|
339
|
+
})
|
|
340
|
+
);
|
|
216
341
|
|
|
217
342
|
const getManifestDirFromEntryAsset = (entryModulePath?: string): string => {
|
|
218
343
|
if (!entryModulePath) {
|
|
@@ -273,204 +398,190 @@ export const getReactRouterManifestChunkNames = (
|
|
|
273
398
|
return chunkNames;
|
|
274
399
|
};
|
|
275
400
|
|
|
276
|
-
|
|
401
|
+
function generateReactRouterManifestForDevEffect(
|
|
277
402
|
routes: Record<string, Route>,
|
|
278
403
|
_options: PluginOptions,
|
|
279
404
|
clientStats: ReactRouterManifestStats | undefined,
|
|
280
405
|
context: string,
|
|
281
|
-
assetPrefix
|
|
406
|
+
assetPrefix: string,
|
|
282
407
|
routeChunkOptions?: RouteChunkManifestOptions
|
|
283
|
-
):
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
const
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
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
|
-
}
|
|
408
|
+
): Effect.Effect<ReactRouterManifestGenerationResult, Error, never> {
|
|
409
|
+
return Effect.gen(function* () {
|
|
410
|
+
const result: Record<string, RouteManifestItem> = {};
|
|
411
|
+
const splitRouteModules = routeChunkOptions?.splitRouteModules ?? false;
|
|
412
|
+
const enforceSplitRouteModules = splitRouteModules === 'enforce';
|
|
413
|
+
const isBuild = routeChunkOptions?.isBuild ?? false;
|
|
414
|
+
const routeChunkConfig: RouteChunkConfig | null =
|
|
415
|
+
splitRouteModules && routeChunkOptions?.rootRouteFile
|
|
416
|
+
? {
|
|
417
|
+
splitRouteModules,
|
|
418
|
+
appDirectory: context,
|
|
419
|
+
rootRouteFile: routeChunkOptions.rootRouteFile,
|
|
420
|
+
}
|
|
421
|
+
: null;
|
|
422
|
+
|
|
423
|
+
const getAssetsForChunk = createChunkAssetResolver(clientStats);
|
|
424
|
+
const getModulePathForChunk = (chunkName: string): string | undefined => {
|
|
425
|
+
const { js: jsAssets } = getAssetsForChunk(chunkName);
|
|
426
|
+
return jsAssets[0] ? combineURLs(assetPrefix, jsAssets[0]) : undefined;
|
|
427
|
+
};
|
|
374
428
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
429
|
+
const manifestEntries = yield* Effect.forEach(
|
|
430
|
+
Object.entries(routes),
|
|
431
|
+
([key, route]) =>
|
|
432
|
+
Effect.gen(function* () {
|
|
433
|
+
const routeEntryName = getRouteEntryName(route);
|
|
434
|
+
const { js: jsAssets, css: discoveredCssAssets } =
|
|
435
|
+
getAssetsForChunk(routeEntryName);
|
|
436
|
+
const routeFilePath = resolve(context, route.file);
|
|
437
|
+
const routeAnalysis = yield* analyzeRouteForManifestEffect({
|
|
438
|
+
discoveredCssAssets,
|
|
439
|
+
isBuild,
|
|
440
|
+
routeChunkCache: routeChunkOptions?.cache,
|
|
441
|
+
routeChunkConfig,
|
|
442
|
+
routeEntryName,
|
|
443
|
+
routeFilePath,
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
const hasClientAction = routeAnalysis.exports.has(
|
|
447
|
+
CLIENT_EXPORTS.clientAction
|
|
448
|
+
);
|
|
449
|
+
const hasClientLoader = routeAnalysis.exports.has(
|
|
450
|
+
CLIENT_EXPORTS.clientLoader
|
|
451
|
+
);
|
|
452
|
+
const hasClientMiddleware = routeAnalysis.exports.has(
|
|
453
|
+
CLIENT_EXPORTS.clientMiddleware
|
|
454
|
+
);
|
|
455
|
+
const hasDefaultExport = routeAnalysis.exports.has('default');
|
|
456
|
+
const routeChunkMap = routeAnalysis.hasRouteChunkByExportName;
|
|
457
|
+
|
|
458
|
+
if (isBuild && enforceSplitRouteModules && routeChunkConfig) {
|
|
459
|
+
validateRouteChunks({
|
|
460
|
+
config: routeChunkConfig,
|
|
461
|
+
id: routeFilePath,
|
|
462
|
+
valid: buildManifestChunkValidity(
|
|
463
|
+
routeAnalysis.exports,
|
|
464
|
+
routeChunkMap ?? createEmptyRouteChunkByExportName()
|
|
465
|
+
),
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
return [
|
|
470
|
+
key,
|
|
471
|
+
{
|
|
472
|
+
id: route.id,
|
|
473
|
+
parentId: route.parentId,
|
|
474
|
+
path: route.path,
|
|
475
|
+
index: route.index,
|
|
476
|
+
caseSensitive: route.caseSensitive,
|
|
477
|
+
module: combineURLs(assetPrefix, jsAssets[0] || ''),
|
|
478
|
+
clientActionModule: routeChunkMap?.clientAction
|
|
479
|
+
? getModulePathForChunk(
|
|
480
|
+
getRouteChunkEntryName(route.id, 'clientAction')
|
|
481
|
+
)
|
|
482
|
+
: undefined,
|
|
483
|
+
clientLoaderModule: routeChunkMap?.clientLoader
|
|
484
|
+
? getModulePathForChunk(
|
|
485
|
+
getRouteChunkEntryName(route.id, 'clientLoader')
|
|
486
|
+
)
|
|
487
|
+
: undefined,
|
|
488
|
+
clientMiddlewareModule: routeChunkMap?.clientMiddleware
|
|
489
|
+
? getModulePathForChunk(
|
|
490
|
+
getRouteChunkEntryName(route.id, 'clientMiddleware')
|
|
491
|
+
)
|
|
492
|
+
: undefined,
|
|
493
|
+
hydrateFallbackModule: routeChunkMap?.HydrateFallback
|
|
494
|
+
? getModulePathForChunk(
|
|
495
|
+
getRouteChunkEntryName(route.id, 'HydrateFallback')
|
|
496
|
+
)
|
|
497
|
+
: undefined,
|
|
498
|
+
hasAction: routeAnalysis.exports.has(SERVER_EXPORTS.action),
|
|
499
|
+
hasLoader: routeAnalysis.exports.has(SERVER_EXPORTS.loader),
|
|
500
|
+
hasClientAction,
|
|
501
|
+
hasClientLoader,
|
|
502
|
+
hasClientMiddleware,
|
|
503
|
+
hasDefaultExport,
|
|
504
|
+
hasErrorBoundary: routeAnalysis.exports.has(
|
|
505
|
+
CLIENT_EXPORTS.ErrorBoundary
|
|
506
|
+
),
|
|
507
|
+
imports: jsAssets.map(asset => combineURLs(assetPrefix, asset)),
|
|
508
|
+
css: routeAnalysis.cssAssets.map(asset =>
|
|
509
|
+
combineURLs(assetPrefix, asset)
|
|
510
|
+
),
|
|
511
|
+
},
|
|
512
|
+
routeAnalysis.routeModuleExports,
|
|
513
|
+
] as const;
|
|
514
|
+
}),
|
|
515
|
+
{
|
|
516
|
+
concurrency: Math.max(
|
|
517
|
+
1,
|
|
518
|
+
Math.min(ROUTE_ANALYSIS_CONCURRENCY, Object.keys(routes).length)
|
|
519
|
+
),
|
|
390
520
|
}
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
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;
|
|
521
|
+
);
|
|
522
|
+
|
|
523
|
+
const routeModuleExportsByRouteId: RouteManifestModuleExports = {};
|
|
524
|
+
for (const [
|
|
525
|
+
key,
|
|
526
|
+
routeManifestItem,
|
|
527
|
+
routeModuleExports,
|
|
528
|
+
] of manifestEntries) {
|
|
529
|
+
result[key] = routeManifestItem;
|
|
530
|
+
routeModuleExportsByRouteId[key] = routeModuleExports;
|
|
433
531
|
}
|
|
434
|
-
);
|
|
435
532
|
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
result[key] = routeManifestItem;
|
|
439
|
-
routeModuleExportsByRouteId[key] = routeModuleExports;
|
|
440
|
-
}
|
|
533
|
+
const { js: entryJsAssets, css: entryCssAssets } =
|
|
534
|
+
getAssetsForChunk('entry.client');
|
|
441
535
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
536
|
+
const fingerprintedValues = {
|
|
537
|
+
entry: {
|
|
538
|
+
module: combineURLs(assetPrefix, entryJsAssets[0] || ''),
|
|
539
|
+
imports: entryJsAssets.map(asset => combineURLs(assetPrefix, asset)),
|
|
540
|
+
css: entryCssAssets.map(asset => combineURLs(assetPrefix, asset)),
|
|
541
|
+
},
|
|
542
|
+
routes: result,
|
|
543
|
+
};
|
|
544
|
+
const version = getManifestVersion(fingerprintedValues, isBuild);
|
|
545
|
+
const manifestPath = getReactRouterManifestPath({
|
|
546
|
+
version,
|
|
547
|
+
isBuild,
|
|
548
|
+
entryModulePath: entryJsAssets[0],
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
const manifest = {
|
|
552
|
+
version,
|
|
553
|
+
url: combineURLs(assetPrefix, manifestPath),
|
|
554
|
+
hmr: undefined,
|
|
555
|
+
entry: fingerprintedValues.entry,
|
|
556
|
+
sri: undefined,
|
|
557
|
+
routes: result,
|
|
558
|
+
};
|
|
460
559
|
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
routes: result,
|
|
468
|
-
};
|
|
560
|
+
return {
|
|
561
|
+
manifest,
|
|
562
|
+
moduleExportsByRouteId: routeModuleExportsByRouteId,
|
|
563
|
+
};
|
|
564
|
+
});
|
|
565
|
+
}
|
|
469
566
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
567
|
+
export async function generateReactRouterManifestForDev(
|
|
568
|
+
routes: Record<string, Route>,
|
|
569
|
+
options: PluginOptions,
|
|
570
|
+
clientStats: ReactRouterManifestStats | undefined,
|
|
571
|
+
context: string,
|
|
572
|
+
assetPrefix = '/',
|
|
573
|
+
routeChunkOptions?: RouteChunkManifestOptions
|
|
574
|
+
): Promise<ReactRouterManifestGenerationResult> {
|
|
575
|
+
return runPluginEffect(
|
|
576
|
+
generateReactRouterManifestForDevEffect(
|
|
577
|
+
routes,
|
|
578
|
+
options,
|
|
579
|
+
clientStats,
|
|
580
|
+
context,
|
|
581
|
+
assetPrefix,
|
|
582
|
+
routeChunkOptions
|
|
583
|
+
)
|
|
584
|
+
);
|
|
474
585
|
}
|
|
475
586
|
|
|
476
587
|
export async function getReactRouterManifestForDev(
|