Skip to content

Commit 27319f6

Browse files
committed
feat: share routes info with other bundler plugins
1 parent 5668a2a commit 27319f6

File tree

1 file changed

+127
-1
lines changed
  • packages/solutions/app-tools/src/builder/shared/bundlerPlugins

1 file changed

+127
-1
lines changed

packages/solutions/app-tools/src/builder/shared/bundlerPlugins/RouterPlugin.ts

Lines changed: 127 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ import { merge, mergeWith } from '@modern-js/utils/lodash';
99
import { ROUTE_MANIFEST } from '@modern-js/utils/universal/constants';
1010
import type { ScriptLoading } from '@rsbuild/core';
1111

12-
const PLUGIN_NAME = 'ModernjsRoutePlugin';
12+
const PLUGIN_NAME = 'ModernjsRouterPlugin';
13+
export const ROUTE_DEPS_COMPILATION_KEY = Symbol.for(
14+
`${PLUGIN_NAME}RouteSources`,
15+
);
1316

1417
export interface RouteAssets {
1518
[routeId: string]: {
@@ -22,6 +25,8 @@ export interface RouteAssets {
2225
type Compiler = webpack.Compiler | Rspack.Compiler;
2326
type Compilation = webpack.Compilation | Rspack.Compilation;
2427
type Chunks = webpack.StatsChunk[];
28+
type StatsChunk = webpack.StatsChunk;
29+
type StatsModule = webpack.StatsModule;
2530

2631
type Options = {
2732
HtmlBundlerPlugin: typeof HtmlWebpackPlugin;
@@ -149,13 +154,16 @@ export class RouterPlugin {
149154
chunkGroups: true,
150155
chunks: true,
151156
ids: true,
157+
modules: true,
158+
chunkModules: true,
152159
});
153160
const {
154161
publicPath,
155162
chunks = [],
156163
namedChunkGroups,
157164
} = stats as webpack.StatsCompilation;
158165
const routeAssets: RouteAssets = {};
166+
const route2Sources: Record<string, string[]> = {};
159167

160168
if (!namedChunkGroups) {
161169
return;
@@ -222,6 +230,124 @@ export class RouterPlugin {
222230
routeAssets,
223231
};
224232

233+
// Build dependencies using compilation chunkGroups and modules for better coverage
234+
try {
235+
const getChunkModules = (chunk: StatsChunk): StatsModule[] => {
236+
if (!chunk) return [];
237+
if (typeof chunk.getModules === 'function') {
238+
return chunk.getModules() || [];
239+
}
240+
const cg = (compilation as any).chunkGraph;
241+
if (cg && typeof cg.getChunkModules === 'function') {
242+
return cg.getChunkModules(chunk) || [];
243+
}
244+
return [];
245+
};
246+
247+
const addModuleResources = (
248+
mod: StatsModule,
249+
bucket: Set<string>,
250+
) => {
251+
if (!mod) return;
252+
if (mod.resource) {
253+
if (!/[\\/]node_modules[\\/]/.test(mod.resource)) {
254+
bucket.add(mod.resource);
255+
}
256+
}
257+
if (Array.isArray(mod.modules)) {
258+
mod.modules.forEach(m => addModuleResources(m, bucket));
259+
}
260+
};
261+
262+
const depsByName = new Map<string, Set<string>>();
263+
264+
const namedGroups = compilation.namedChunkGroups;
265+
const groupNames =
266+
namedGroups && typeof namedGroups.forEach === 'function'
267+
? (() => {
268+
const names: string[] = [];
269+
namedGroups.forEach((_: any, key: string) =>
270+
names.push(key),
271+
);
272+
return names;
273+
})()
274+
: Object.keys(namedChunkGroups || {});
275+
276+
groupNames.forEach((name: string) => {
277+
const resourceSet = new Set<string>();
278+
const group =
279+
namedGroups && typeof namedGroups.get === 'function'
280+
? namedGroups.get(name)
281+
: undefined;
282+
283+
if (group && Array.isArray(group.chunks)) {
284+
group.chunks.forEach((chunk: any) => {
285+
const modules = getChunkModules(chunk);
286+
modules.forEach(m => addModuleResources(m, resourceSet));
287+
});
288+
} else {
289+
const matched: Chunks = Array.from(
290+
((compilation as any).chunks as unknown as Iterable<any>) ||
291+
[],
292+
).filter(c => c?.name === name || c?.names?.includes?.(name));
293+
matched.forEach(chunk => {
294+
const modules = getChunkModules(chunk);
295+
modules.forEach(m => addModuleResources(m, resourceSet));
296+
});
297+
}
298+
299+
depsByName.set(name, resourceSet);
300+
});
301+
302+
// Map to routeIds (keys of routeAssets)
303+
Object.keys(routeAssets).forEach(routeId => {
304+
const set = depsByName.get(routeId);
305+
if (set && set.size > 0) {
306+
route2Sources[routeId] = Array.from(set);
307+
}
308+
});
309+
310+
// Merge async-* into sync route names similar to assets merging
311+
if (asyncEntryNames.length > 0) {
312+
for (const asyncEntryName of asyncEntryNames) {
313+
const syncEntryName = asyncEntryName.replace('async-', '');
314+
const asyncSet = depsByName.get(asyncEntryName);
315+
if (asyncSet && asyncSet.size > 0) {
316+
const base = new Set<string>(
317+
route2Sources[syncEntryName] || [],
318+
);
319+
asyncSet.forEach(v => base.add(v));
320+
route2Sources[syncEntryName] = Array.from(base);
321+
}
322+
}
323+
}
324+
325+
(compilation as any)[ROUTE_DEPS_COMPILATION_KEY] = route2Sources;
326+
327+
// Also emit to dist for external consumption
328+
const ROUTE_SOURCE_MANIFEST_FILE = 'routes-source-manifest.json';
329+
const routeSourceManifest = { route2Sources: route2Sources };
330+
const existed = compilation.getAsset(ROUTE_SOURCE_MANIFEST_FILE);
331+
if (existed) {
332+
compilation.updateAsset(
333+
ROUTE_SOURCE_MANIFEST_FILE,
334+
new RawSource(
335+
JSON.stringify(routeSourceManifest, null, 2),
336+
) as any,
337+
undefined as any,
338+
);
339+
} else {
340+
compilation.emitAsset(
341+
ROUTE_SOURCE_MANIFEST_FILE,
342+
new RawSource(
343+
JSON.stringify(routeSourceManifest, null, 2),
344+
) as any,
345+
);
346+
}
347+
} catch {
348+
// swallow silently; do not block build if compilation shape is unexpected
349+
}
350+
225351
const entryNames = Array.from(compilation.entrypoints.keys());
226352
let entryChunks = [];
227353
if (isRspack) {

0 commit comments

Comments
 (0)