| | |
| | | compareChunks, |
| | | compareModulesByIdentifier |
| | | } = require("../util/comparators"); |
| | | const createSchemaValidation = require("../util/create-schema-validation"); |
| | | const identifierUtils = require("../util/identifier"); |
| | | |
| | | /** @typedef {import("../../declarations/plugins/optimize/AggressiveSplittingPlugin").AggressiveSplittingPluginOptions} AggressiveSplittingPluginOptions */ |
| | |
| | | /** @typedef {import("../Compiler")} Compiler */ |
| | | /** @typedef {import("../Module")} Module */ |
| | | |
| | | const validate = createSchemaValidation( |
| | | require("../../schemas/plugins/optimize/AggressiveSplittingPlugin.check"), |
| | | () => |
| | | require("../../schemas/plugins/optimize/AggressiveSplittingPlugin.json"), |
| | | { |
| | | name: "Aggressive Splitting Plugin", |
| | | baseDataPath: "options" |
| | | } |
| | | ); |
| | | |
| | | /** |
| | | * Move module between. |
| | | * @param {ChunkGraph} chunkGraph the chunk graph |
| | | * @param {Chunk} oldChunk the old chunk |
| | | * @param {Chunk} newChunk the new chunk |
| | |
| | | }; |
| | | |
| | | /** |
| | | * Checks whether this object is not a entry module. |
| | | * @param {ChunkGraph} chunkGraph the chunk graph |
| | | * @param {Chunk} chunk the chunk |
| | | * @returns {(module: Module) => boolean} filter for entry module |
| | |
| | | const isNotAEntryModule = (chunkGraph, chunk) => (module) => |
| | | !chunkGraph.isEntryModuleInChunk(module, chunk); |
| | | |
| | | /** @typedef {{ id?: NonNullable<Chunk["id"]>, hash?: NonNullable<Chunk["hash"]>, modules: Module[], size: number }} SplitData */ |
| | | /** @typedef {{ id?: NonNullable<Chunk["id"]>, hash?: NonNullable<Chunk["hash"]>, modules: string[], size: number }} SplitData */ |
| | | |
| | | /** @type {WeakSet<Chunk>} */ |
| | | const recordedChunks = new WeakSet(); |
| | |
| | | |
| | | class AggressiveSplittingPlugin { |
| | | /** |
| | | * Creates an instance of AggressiveSplittingPlugin. |
| | | * @param {AggressiveSplittingPluginOptions=} options options object |
| | | */ |
| | | constructor(options = {}) { |
| | | validate(options); |
| | | |
| | | /** @type {AggressiveSplittingPluginOptions} */ |
| | | this.options = options; |
| | | if (typeof this.options.minSize !== "number") { |
| | | this.options.minSize = 30 * 1024; |
| | | } |
| | | if (typeof this.options.maxSize !== "number") { |
| | | this.options.maxSize = 50 * 1024; |
| | | } |
| | | if (typeof this.options.chunkOverhead !== "number") { |
| | | this.options.chunkOverhead = 0; |
| | | } |
| | | if (typeof this.options.entryChunkMultiplicator !== "number") { |
| | | this.options.entryChunkMultiplicator = 1; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Was chunk recorded. |
| | | * @param {Chunk} chunk the chunk to test |
| | | * @returns {boolean} true if the chunk was recorded |
| | | */ |
| | |
| | | } |
| | | |
| | | /** |
| | | * Apply the plugin |
| | | * Applies the plugin by registering its hooks on the compiler. |
| | | * @param {Compiler} compiler the compiler instance |
| | | * @returns {void} |
| | | */ |
| | | apply(compiler) { |
| | | compiler.hooks.validate.tap(PLUGIN_NAME, () => { |
| | | compiler.validate( |
| | | () => |
| | | require("../../schemas/plugins/optimize/AggressiveSplittingPlugin.json"), |
| | | this.options, |
| | | { |
| | | name: "Aggressive Splitting Plugin", |
| | | baseDataPath: "options" |
| | | }, |
| | | (options) => |
| | | require("../../schemas/plugins/optimize/AggressiveSplittingPlugin.check")( |
| | | options |
| | | ) |
| | | ); |
| | | }); |
| | | |
| | | compiler.hooks.thisCompilation.tap(PLUGIN_NAME, (compilation) => { |
| | | let needAdditionalSeal = false; |
| | | /** @type {SplitData[]} */ |
| | |
| | | (chunks) => { |
| | | const chunkGraph = compilation.chunkGraph; |
| | | // Precompute stuff |
| | | /** @type {Map<string, Module>} */ |
| | | const nameToModuleMap = new Map(); |
| | | /** @type {Map<Module, string>} */ |
| | | const moduleToNameMap = new Map(); |
| | | const makePathsRelative = |
| | | identifierUtils.makePathsRelative.bindContextCache( |
| | |
| | | } |
| | | |
| | | // Check used chunk ids |
| | | /** @typedef {Set<ChunkId>} */ |
| | | /** @type {Set<ChunkId>} */ |
| | | const usedIds = new Set(); |
| | | for (const chunk of chunks) { |
| | | usedIds.add(chunk.id); |
| | | usedIds.add(/** @type {ChunkId} */ (chunk.id)); |
| | | } |
| | | |
| | | const recordedSplits = |
| | |
| | | ? [...recordedSplits, ...newSplits] |
| | | : recordedSplits; |
| | | |
| | | const minSize = /** @type {number} */ (this.options.minSize); |
| | | const maxSize = /** @type {number} */ (this.options.maxSize); |
| | | const minSize = this.options.minSize || 30 * 1024; |
| | | const maxSize = this.options.maxSize || 50 * 1024; |
| | | |
| | | /** |
| | | * Returns true when applied, otherwise false. |
| | | * @param {SplitData} splitData split data |
| | | * @returns {boolean} true when applied, otherwise false |
| | | */ |
| | |
| | | } |
| | | |
| | | // Get module objects from names |
| | | const selectedModules = splitData.modules.map((name) => |
| | | nameToModuleMap.get(name) |
| | | const selectedModules = splitData.modules.map( |
| | | (name) => /** @type {Module} */ (nameToModuleMap.get(name)) |
| | | ); |
| | | |
| | | // Does the modules exist at all? |
| | |
| | | const modules = chunkGraph |
| | | .getOrderedChunkModules(chunk, compareModulesByIdentifier) |
| | | .filter(isNotAEntryModule(chunkGraph, chunk)); |
| | | /** @type {Module[]} */ |
| | | const selectedModules = []; |
| | | let selectedModulesSize = 0; |
| | | for (let k = 0; k < modules.length; k++) { |
| | |
| | | /** @type {SplitData} */ |
| | | const splitData = { |
| | | modules: selectedModules |
| | | .map((m) => moduleToNameMap.get(m)) |
| | | .map((m) => /** @type {string} */ (moduleToNameMap.get(m))) |
| | | .sort(), |
| | | size: selectedModulesSize |
| | | }; |
| | |
| | | ); |
| | | compilation.hooks.recordHash.tap(PLUGIN_NAME, (records) => { |
| | | // 4. save made splittings to records |
| | | /** @type {Set<SplitData>} */ |
| | | const allSplits = new Set(); |
| | | /** @type {Set<SplitData>} */ |
| | | const invalidSplits = new Set(); |