UNPKG

webpack-chunk-report-plugin

Version:

Webpack Chunk Report Plugin

185 lines (184 loc) 7.92 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.generateGraphFromChunkIdVsChunkMap = generateGraphFromChunkIdVsChunkMap; const uniq_1 = __importDefault(require("lodash/uniq")); const uniqBy_1 = __importDefault(require("lodash/uniqBy")); const getImportersOfConcatenatedModule = (module, allModules, seen = new Set()) => { // Base case: already processed this module if (seen.has(module.fileName)) { return new Set(); } seen.add(module.fileName); const importers = new Set(); for (const reason of module.reasons) { const parentModuleId = reason.from; if (!parentModuleId) continue; const parentModule = allModules[parentModuleId]; if (!parentModule) continue; if (parentModule.type === "Concatenated") { // Recursively get importers of concatenated parent const parentImporters = getImportersOfConcatenatedModule(parentModule, allModules, seen); parentImporters.forEach(imp => importers.add(imp)); } else { // Regular module - add directly importers.add(parentModuleId); } } return importers; }; function generateGraphFromChunkIdVsChunkMap(chunkIdVsChunkMap) { const nodes = []; const links = []; const moduleMap = {}; const chunkMap = {}; const allModules = {}; // First pass: collect all modules across chunks Object.values(chunkIdVsChunkMap).forEach(chunk => { const collectModules = (module) => { if (module.fileName in allModules) { return; } if (module.fileName) { allModules[module.fileName] = module; } if (module.type === "Concatenated" && module.subModules) { module.subModules.forEach(subModule => collectModules(subModule)); } }; chunk.modules.forEach(collectModules); }); // Process chunks Object.entries(chunkIdVsChunkMap).forEach(([chunkId, chunk]) => { if (!chunkId) return; const chunkNode = { id: chunkId, type: "chunk", data: chunk, dependencies: [], }; chunkMap[chunkId] = chunkNode; nodes.push(chunkNode); }); // Process modules const processModule = (module, parentChunkId) => { if (!module.fileName) return; moduleMap[parentChunkId] = moduleMap[parentChunkId] ?? {}; if (module.type !== "Concatenated" && !moduleMap[parentChunkId][module.fileName]) { const moduleNode = { id: module.fileName, type: "module", data: module, dependencies: [], }; moduleMap[parentChunkId][module.fileName] = moduleNode; nodes.push(moduleNode); } if (module.type !== "Concatenated") { links.push({ source: parentChunkId, target: module.fileName, }); chunkMap[parentChunkId]?.dependencies.push(module.fileName); } // Process concatenated submodules else if (module.subModules) { module.subModules.forEach(subModule => { processModule(subModule, parentChunkId); links.push({ source: parentChunkId, target: subModule.fileName, }); chunkMap[parentChunkId]?.dependencies.push(subModule.fileName); }); } }; // First process all modules to ensure they exist in the graph Object.entries(chunkIdVsChunkMap).forEach(([chunkId, chunk]) => { if (!chunkId) return; chunk.modules.forEach(module => processModule(module, chunkId)); }); // Then process all reasons to create dependencies Object.entries(moduleMap).forEach(([chunkId, moduleIdVsGraphNode]) => { Object.entries(moduleIdVsGraphNode).forEach(([moduleId, graphNode]) => { const module = graphNode.data; const seen = new Set(); const processReasons = (mod) => { mod.reasons.forEach(reason => { const shouldAvoidAdding = !reason.from || reason.from === mod.fileName || seen.has(reason.from); if (shouldAvoidAdding) { return; } seen.add(reason.from); // Find the source module (could be in any chunk) const sourceModule = allModules[reason.from]; if (!sourceModule) return; switch (module.type) { case "Concatenated": { mod.subModules.forEach(processReasons); break; } default: { switch (sourceModule.type) { case "Concatenated": { const importers = getImportersOfConcatenatedModule(sourceModule, allModules); importers.forEach(importer => { links.push({ source: importer, target: module.fileName, reason: { from: importer, explanation: `Via concatenated source module: ${sourceModule.fileName}`, type: reason.type, }, }); const importerNode = nodes.find(node => node.id === importer); if (importerNode) { importerNode.dependencies ??= []; importerNode.dependencies.push(module.fileName); } }); break; } default: { // External Module or Normal Module links.push({ source: reason.from, target: module.fileName, reason, }); // Find the source module's node to add dependency Object.values(moduleMap).forEach(modIdVsModMap => { if (modIdVsModMap[reason.from]) { modIdVsModMap[reason.from].dependencies.push(module.fileName); } }); } } } } }); }; processReasons(module); }); }); // Deduplicate dependencies const graphNodes = (0, uniqBy_1.default)(nodes, node => node.id).map(node => ({ ...node, dependencies: (0, uniq_1.default)(node.dependencies), })); const graphLinks = (0, uniqBy_1.default)(links, link => `${link.source}-${link.target}`); return { nodes: graphNodes, links: graphLinks }; }