Spaces:
Running
Running
| ; | |
| Object.defineProperty(exports, "__esModule", { | |
| value: true | |
| }); | |
| Object.defineProperty(exports, "CssChunkingPlugin", { | |
| enumerable: true, | |
| get: function() { | |
| return CssChunkingPlugin; | |
| } | |
| }); | |
| const PLUGIN_NAME = 'CssChunkingPlugin'; | |
| /** | |
| * Merge chunks until they are bigger than the target size. | |
| */ const MIN_CSS_CHUNK_SIZE = 30 * 1024; | |
| /** | |
| * Avoid merging chunks when they would be bigger than this size. | |
| */ const MAX_CSS_CHUNK_SIZE = 100 * 1024; | |
| function isGlobalCss(module) { | |
| return !/\.module\.(css|scss|sass)$/.test(module.nameForCondition() || ''); | |
| } | |
| class CssChunkingPlugin { | |
| constructor(strict){ | |
| this.strict = strict; | |
| } | |
| apply(compiler) { | |
| const strict = this.strict; | |
| const summary = !!process.env.CSS_CHUNKING_SUMMARY; | |
| compiler.hooks.thisCompilation.tap(PLUGIN_NAME, (compilation)=>{ | |
| let once = false; | |
| compilation.hooks.optimizeChunks.tap({ | |
| name: PLUGIN_NAME, | |
| stage: 5 | |
| }, ()=>{ | |
| if (once) { | |
| return; | |
| } | |
| once = true; | |
| const chunkGraph = compilation.chunkGraph; | |
| let changed = undefined; | |
| const chunkStates = new Map(); | |
| const chunkStatesByModule = new Map(); | |
| // Collect all css modules in chunks and the execpted order of them | |
| for (const chunk of compilation.chunks){ | |
| var _chunk_name; | |
| if ((_chunk_name = chunk.name) == null ? void 0 : _chunk_name.startsWith('pages/')) continue; | |
| const modules = []; | |
| for (const module of chunkGraph.getChunkModulesIterable(chunk)){ | |
| var _module_type; | |
| if (!((_module_type = module.type) == null ? void 0 : _module_type.startsWith('css'))) continue; | |
| modules.push(module); | |
| } | |
| if (!modules.length) continue; | |
| const chunkState = { | |
| chunk, | |
| modules, | |
| order: 0, | |
| requests: modules.length | |
| }; | |
| chunkStates.set(chunk, chunkState); | |
| for(let i = 0; i < modules.length; i++){ | |
| const module = modules[i]; | |
| let moduleChunkStates = chunkStatesByModule.get(module); | |
| if (!moduleChunkStates) { | |
| moduleChunkStates = new Map(); | |
| chunkStatesByModule.set(module, moduleChunkStates); | |
| } | |
| moduleChunkStates.set(chunkState, i); | |
| chunkStatesByModule.set(module, moduleChunkStates); | |
| } | |
| } | |
| // Sort modules by their index sum | |
| const orderedModules = []; | |
| for (const [module, moduleChunkStates] of chunkStatesByModule){ | |
| let sum = 0; | |
| for (const i of moduleChunkStates.values()){ | |
| sum += i; | |
| } | |
| orderedModules.push({ | |
| module, | |
| sum | |
| }); | |
| } | |
| orderedModules.sort((a, b)=>a.sum - b.sum); | |
| // A queue of modules that still need to be processed | |
| const remainingModules = new Set(orderedModules.map(({ module })=>module)); | |
| // In loose mode we guess the dependents of modules from the order | |
| // assuming that when a module is a dependency of another module | |
| // it will always appear before it in every chunk. | |
| const allDependents = new Map(); | |
| if (!this.strict) { | |
| for (const b of remainingModules){ | |
| const dependent = new Set(); | |
| loop: for (const a of remainingModules){ | |
| if (a === b) continue; | |
| // check if a depends on b | |
| for (const [chunkState, ia] of chunkStatesByModule.get(a)){ | |
| const bChunkStates = chunkStatesByModule.get(b); | |
| const ib = bChunkStates.get(chunkState); | |
| if (ib === undefined) { | |
| continue loop; | |
| } | |
| if (ib > ia) { | |
| continue loop; | |
| } | |
| } | |
| dependent.add(a); | |
| } | |
| if (dependent.size > 0) allDependents.set(b, dependent); | |
| } | |
| } | |
| // Stores the new chunk for every module | |
| const newChunksByModule = new Map(); | |
| // Process through all modules | |
| for (const startModule of remainingModules){ | |
| let globalCssMode = isGlobalCss(startModule); | |
| // The current position of processing in all selected chunks | |
| let allChunkStates = new Map(chunkStatesByModule.get(startModule)); | |
| // The list of modules that goes into the new chunk | |
| const newChunkModules = new Set([ | |
| startModule | |
| ]); | |
| // The current size of the new chunk | |
| let currentSize = startModule.size(); | |
| // A pool of potential modules where the next module is selected from. | |
| // It's filled from the next module of the selected modules in every chunk. | |
| // It also keeps some metadata to improve performance [size, chunkStates]. | |
| const potentialNextModules = new Map(); | |
| for (const [chunkState, i] of allChunkStates){ | |
| const nextModule = chunkState.modules[i + 1]; | |
| if (nextModule && remainingModules.has(nextModule)) { | |
| potentialNextModules.set(nextModule, [ | |
| nextModule.size(), | |
| chunkStatesByModule.get(nextModule) | |
| ]); | |
| } | |
| } | |
| // Try to add modules to the chunk until a break condition is met | |
| let cont; | |
| do { | |
| cont = false; | |
| // We try to select a module that reduces request count and | |
| // has the highest number of requests | |
| const orderedPotentialNextModules = []; | |
| for (const [nextModule, [size, nextChunkStates]] of potentialNextModules){ | |
| let maxRequests = 0; | |
| for (const chunkState of nextChunkStates.keys()){ | |
| // There is always some overlap | |
| if (allChunkStates.has(chunkState)) { | |
| maxRequests = Math.max(maxRequests, chunkState.requests); | |
| } | |
| } | |
| orderedPotentialNextModules.push([ | |
| nextModule, | |
| size, | |
| nextChunkStates, | |
| maxRequests | |
| ]); | |
| } | |
| orderedPotentialNextModules.sort((a, b)=>b[3] - a[3] || (a[0].identifier() < b[0].identifier() ? -1 : 1)); | |
| // Try every potential module | |
| loop: for (const [nextModule, size, nextChunkStates] of orderedPotentialNextModules){ | |
| if (currentSize + size > MAX_CSS_CHUNK_SIZE) { | |
| continue; | |
| } | |
| if (!strict) { | |
| // In loose mode we only check if the dependencies are not violated | |
| const dependent = allDependents.get(nextModule); | |
| if (dependent) { | |
| for (const dep of dependent){ | |
| if (newChunkModules.has(dep)) { | |
| continue loop; | |
| } | |
| } | |
| } | |
| } else { | |
| // In strict mode we check that none of the order in any chunk is changed by adding the module | |
| for (const [chunkState, i] of nextChunkStates){ | |
| const prevState = allChunkStates.get(chunkState); | |
| if (prevState === undefined) { | |
| // New chunk group, can add it, but should we? | |
| // We only add that if below min size | |
| if (currentSize < MIN_CSS_CHUNK_SIZE) { | |
| continue; | |
| } else { | |
| continue loop; | |
| } | |
| } else if (prevState + 1 === i) { | |
| continue; | |
| } else { | |
| continue loop; | |
| } | |
| } | |
| } | |
| // Global CSS must not leak into unrelated chunks | |
| const nextIsGlobalCss = isGlobalCss(nextModule); | |
| if (nextIsGlobalCss && globalCssMode) { | |
| if (allChunkStates.size !== nextChunkStates.size) { | |
| continue; | |
| } | |
| } | |
| if (globalCssMode) { | |
| for (const chunkState of nextChunkStates.keys()){ | |
| if (!allChunkStates.has(chunkState)) { | |
| continue loop; | |
| } | |
| } | |
| } | |
| if (nextIsGlobalCss) { | |
| for (const chunkState of allChunkStates.keys()){ | |
| if (!nextChunkStates.has(chunkState)) { | |
| continue loop; | |
| } | |
| } | |
| } | |
| potentialNextModules.delete(nextModule); | |
| currentSize += size; | |
| if (nextIsGlobalCss) { | |
| globalCssMode = true; | |
| } | |
| for (const [chunkState, i] of nextChunkStates){ | |
| if (allChunkStates.has(chunkState)) { | |
| // This reduces the request count of the chunk group | |
| chunkState.requests--; | |
| } | |
| allChunkStates.set(chunkState, i); | |
| const newNextModule = chunkState.modules[i + 1]; | |
| if (newNextModule && remainingModules.has(newNextModule) && !newChunkModules.has(newNextModule)) { | |
| potentialNextModules.set(newNextModule, [ | |
| newNextModule.size(), | |
| chunkStatesByModule.get(newNextModule) | |
| ]); | |
| } | |
| } | |
| newChunkModules.add(nextModule); | |
| cont = true; | |
| break; | |
| } | |
| }while (cont); | |
| const newChunk = compilation.addChunk(); | |
| newChunk.preventIntegration = true; | |
| newChunk.idNameHints.add('css'); | |
| for (const module of newChunkModules){ | |
| remainingModules.delete(module); | |
| chunkGraph.connectChunkAndModule(newChunk, module); | |
| newChunksByModule.set(module, newChunk); | |
| } | |
| changed = true; | |
| } | |
| for (const { chunk, modules } of chunkStates.values()){ | |
| const chunks = new Set(); | |
| for (const module of modules){ | |
| const newChunk = newChunksByModule.get(module); | |
| if (newChunk) { | |
| chunkGraph.disconnectChunkAndModule(chunk, module); | |
| if (chunks.has(newChunk)) continue; | |
| chunks.add(newChunk); | |
| chunk.split(newChunk); | |
| } | |
| } | |
| } | |
| if (summary) { | |
| console.log('Top 20 chunks by request count:'); | |
| const orderedChunkStates = [ | |
| ...chunkStates.values() | |
| ]; | |
| orderedChunkStates.sort((a, b)=>b.requests - a.requests); | |
| for (const { chunk, modules, requests } of orderedChunkStates.slice(0, 20)){ | |
| console.log(`- ${requests} requests for ${chunk.name} (has ${modules.length} modules)`); | |
| } | |
| } | |
| return changed; | |
| }); | |
| }); | |
| } | |
| } | |
| //# sourceMappingURL=css-chunking-plugin.js.map |