2018-01-20 00:06:59 +08:00
|
|
|
/*
|
|
|
|
MIT License http://www.opensource.org/licenses/mit-license.php
|
|
|
|
Author Tobias Koppers @sokra
|
|
|
|
*/
|
|
|
|
"use strict";
|
|
|
|
|
2018-02-01 02:52:50 +08:00
|
|
|
const crypto = require("crypto");
|
2018-01-20 00:06:59 +08:00
|
|
|
const SortableSet = require("../util/SortableSet");
|
|
|
|
const GraphHelpers = require("../GraphHelpers");
|
2018-02-17 16:48:40 +08:00
|
|
|
const isSubset = require("../util/SetHelpers").isSubset;
|
2018-01-20 00:06:59 +08:00
|
|
|
|
2018-02-25 09:00:20 +08:00
|
|
|
const hashFilename = name => {
|
|
|
|
return crypto
|
|
|
|
.createHash("md4")
|
|
|
|
.update(name)
|
|
|
|
.digest("hex")
|
|
|
|
.slice(0, 8);
|
2018-02-01 02:52:50 +08:00
|
|
|
};
|
|
|
|
|
2018-01-20 00:06:59 +08:00
|
|
|
const sortByIdentifier = (a, b) => {
|
2018-02-25 09:00:20 +08:00
|
|
|
if (a.identifier() > b.identifier()) return 1;
|
|
|
|
if (a.identifier() < b.identifier()) return -1;
|
2018-01-20 00:06:59 +08:00
|
|
|
return 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
const getRequests = chunk => {
|
|
|
|
let requests = 0;
|
2018-02-25 09:00:20 +08:00
|
|
|
for (const chunkGroup of chunk.groupsIterable) {
|
2018-01-20 00:06:59 +08:00
|
|
|
requests = Math.max(requests, chunkGroup.chunks.length);
|
|
|
|
}
|
|
|
|
return requests;
|
|
|
|
};
|
|
|
|
|
|
|
|
const getModulesSize = modules => {
|
|
|
|
let sum = 0;
|
2018-02-25 09:00:20 +08:00
|
|
|
for (const m of modules) sum += m.size();
|
2018-01-20 00:06:59 +08:00
|
|
|
return sum;
|
|
|
|
};
|
|
|
|
|
|
|
|
const isOverlap = (a, b) => {
|
2018-02-25 09:00:20 +08:00
|
|
|
for (const item of a.keys()) {
|
|
|
|
if (b.has(item)) return true;
|
2018-01-20 00:06:59 +08:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
|
|
|
|
const compareEntries = (a, b) => {
|
2018-01-20 20:11:02 +08:00
|
|
|
// 1. by priority
|
|
|
|
const diffPriority = a.cacheGroup.priority - b.cacheGroup.priority;
|
2018-02-25 09:00:20 +08:00
|
|
|
if (diffPriority) return diffPriority;
|
2018-02-16 16:12:01 +08:00
|
|
|
// 2. by number of chunks
|
|
|
|
const diffCount = a.chunks.size - b.chunks.size;
|
2018-02-25 09:00:20 +08:00
|
|
|
if (diffCount) return diffCount;
|
2018-02-16 16:12:01 +08:00
|
|
|
// 3. by size reduction
|
|
|
|
const aSizeReduce = a.size * (a.chunks.size - 1);
|
|
|
|
const bSizeReduce = b.size * (b.chunks.size - 1);
|
|
|
|
const diffSizeReduce = aSizeReduce - bSizeReduce;
|
2018-02-25 09:00:20 +08:00
|
|
|
if (diffSizeReduce) return diffSizeReduce;
|
2018-02-16 16:12:01 +08:00
|
|
|
// 4. by number of modules (to be able to compare by identifier)
|
2018-01-20 00:06:59 +08:00
|
|
|
const modulesA = a.modules;
|
|
|
|
const modulesB = b.modules;
|
|
|
|
const diff = modulesA.size - modulesB.size;
|
2018-02-25 09:00:20 +08:00
|
|
|
if (diff) return diff;
|
2018-02-16 16:12:01 +08:00
|
|
|
// 5. by module identifiers
|
2018-01-20 00:06:59 +08:00
|
|
|
modulesA.sort();
|
|
|
|
modulesB.sort();
|
|
|
|
const aI = modulesA[Symbol.iterator]();
|
|
|
|
const bI = modulesB[Symbol.iterator]();
|
2018-02-25 09:15:37 +08:00
|
|
|
// eslint-disable-next-line no-constant-condition
|
2018-02-25 09:00:20 +08:00
|
|
|
while (true) {
|
2018-01-20 00:06:59 +08:00
|
|
|
const aItem = aI.next();
|
|
|
|
const bItem = bI.next();
|
2018-02-25 09:00:20 +08:00
|
|
|
if (aItem.done) return 0;
|
2018-01-20 00:06:59 +08:00
|
|
|
const aModuleIdentifier = aItem.value.identifier();
|
|
|
|
const bModuleIdentifier = bItem.value.identifier();
|
2018-02-25 09:00:20 +08:00
|
|
|
if (aModuleIdentifier > bModuleIdentifier) return -1;
|
|
|
|
if (aModuleIdentifier < bModuleIdentifier) return 1;
|
2018-01-20 00:06:59 +08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
module.exports = class SplitChunksPlugin {
|
|
|
|
constructor(options) {
|
|
|
|
this.options = SplitChunksPlugin.normalizeOptions(options);
|
|
|
|
}
|
|
|
|
|
|
|
|
static normalizeOptions(options) {
|
|
|
|
return {
|
|
|
|
chunks: options.chunks || "all",
|
|
|
|
minSize: options.minSize || 0,
|
|
|
|
minChunks: options.minChunks || 1,
|
|
|
|
maxAsyncRequests: options.maxAsyncRequests || 1,
|
|
|
|
maxInitialRequests: options.maxInitialRequests || 1,
|
|
|
|
getName: SplitChunksPlugin.normalizeName(options.name) || (() => {}),
|
2018-02-25 09:00:20 +08:00
|
|
|
getCacheGroups: SplitChunksPlugin.normalizeCacheGroups(
|
|
|
|
options.cacheGroups
|
|
|
|
)
|
2018-01-20 00:06:59 +08:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
static normalizeName(option) {
|
2018-02-25 09:00:20 +08:00
|
|
|
if (option === true) {
|
2018-01-20 00:06:59 +08:00
|
|
|
const fn = (module, chunks, cacheGroup) => {
|
|
|
|
const names = chunks.map(c => c.name);
|
2018-02-25 09:00:20 +08:00
|
|
|
if (!names.every(Boolean)) return;
|
2018-01-20 00:06:59 +08:00
|
|
|
names.sort();
|
2018-02-25 09:00:20 +08:00
|
|
|
let name =
|
|
|
|
(cacheGroup && cacheGroup !== "default" ? cacheGroup + "~" : "") +
|
|
|
|
names.join("~");
|
2018-02-01 02:52:50 +08:00
|
|
|
// Filenames and paths can't be too long otherwise an
|
|
|
|
// ENAMETOOLONG error is raised. If the generated name if too
|
|
|
|
// long, it is truncated and a hash is appended. The limit has
|
|
|
|
// been set to 100 to prevent `[name].[chunkhash].[ext]` from
|
|
|
|
// generating a 256+ character string.
|
2018-02-25 09:00:20 +08:00
|
|
|
if (name.length > 100) {
|
2018-02-01 02:52:50 +08:00
|
|
|
name = name.slice(0, 100) + "~" + hashFilename(name);
|
|
|
|
}
|
2018-01-20 00:06:59 +08:00
|
|
|
return name;
|
|
|
|
};
|
|
|
|
return fn;
|
|
|
|
}
|
2018-02-25 09:00:20 +08:00
|
|
|
if (typeof option === "string") {
|
2018-01-20 00:06:59 +08:00
|
|
|
const fn = () => {
|
|
|
|
return option;
|
|
|
|
};
|
|
|
|
return fn;
|
|
|
|
}
|
2018-02-25 09:00:20 +08:00
|
|
|
if (typeof option === "function") return option;
|
2018-01-20 00:06:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static normalizeCacheGroups(cacheGroups) {
|
2018-02-25 09:00:20 +08:00
|
|
|
if (typeof cacheGroups === "function") {
|
2018-01-20 00:06:59 +08:00
|
|
|
return cacheGroups;
|
|
|
|
}
|
2018-02-25 09:00:20 +08:00
|
|
|
if (cacheGroups && typeof cacheGroups === "object") {
|
2018-01-20 00:06:59 +08:00
|
|
|
const fn = (module, chunks) => {
|
|
|
|
let results;
|
2018-02-25 09:00:20 +08:00
|
|
|
for (const key of Object.keys(cacheGroups)) {
|
2018-01-20 00:06:59 +08:00
|
|
|
let option = cacheGroups[key];
|
2018-02-25 09:00:20 +08:00
|
|
|
if (option === false) continue;
|
|
|
|
if (option instanceof RegExp || typeof option === "string") {
|
2018-01-20 00:06:59 +08:00
|
|
|
option = {
|
|
|
|
test: option
|
|
|
|
};
|
|
|
|
}
|
2018-02-25 09:00:20 +08:00
|
|
|
if (typeof option === "function") {
|
2018-01-20 00:06:59 +08:00
|
|
|
let result = option(module);
|
2018-02-25 09:00:20 +08:00
|
|
|
if (result) {
|
|
|
|
if (results === undefined) results = [];
|
|
|
|
for (const r of Array.isArray(result) ? result : [result]) {
|
|
|
|
const result = Object.assign(
|
|
|
|
{
|
|
|
|
key
|
|
|
|
},
|
|
|
|
r
|
|
|
|
);
|
|
|
|
if (result.name) result.getName = () => result.name;
|
2018-01-20 00:06:59 +08:00
|
|
|
results.push(result);
|
|
|
|
}
|
|
|
|
}
|
2018-02-25 09:00:20 +08:00
|
|
|
} else if (SplitChunksPlugin.checkTest(option.test, module, chunks)) {
|
|
|
|
if (results === undefined) results = [];
|
2018-01-20 00:06:59 +08:00
|
|
|
results.push({
|
|
|
|
key: key,
|
2018-01-20 20:11:02 +08:00
|
|
|
priority: option.priority,
|
2018-01-20 00:06:59 +08:00
|
|
|
getName: SplitChunksPlugin.normalizeName(option.name),
|
|
|
|
chunks: option.chunks,
|
|
|
|
enforce: option.enforce,
|
|
|
|
minSize: option.minSize,
|
|
|
|
minChunks: option.minChunks,
|
|
|
|
maxAsyncRequests: option.maxAsyncRequests,
|
|
|
|
maxInitialRequests: option.maxInitialRequests,
|
|
|
|
reuseExistingChunk: option.reuseExistingChunk
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return results;
|
|
|
|
};
|
|
|
|
return fn;
|
|
|
|
}
|
|
|
|
const fn = () => {};
|
|
|
|
return fn;
|
|
|
|
}
|
|
|
|
|
|
|
|
static checkTest(test, module, chunks) {
|
2018-02-25 09:00:20 +08:00
|
|
|
if (test === undefined) return true;
|
|
|
|
if (typeof test === "function") return test(module, chunks);
|
|
|
|
if (typeof test === "boolean") return test;
|
|
|
|
const names = chunks
|
|
|
|
.map(c => c.name)
|
|
|
|
.concat(module.nameForCondition ? [module.nameForCondition()] : [])
|
|
|
|
.filter(Boolean);
|
|
|
|
if (typeof test === "string") {
|
|
|
|
for (const name of names) if (name.startsWith(test)) return true;
|
2018-01-20 00:06:59 +08:00
|
|
|
return false;
|
|
|
|
}
|
2018-02-25 09:00:20 +08:00
|
|
|
if (test instanceof RegExp) {
|
|
|
|
for (const name of names) if (test.test(name)) return true;
|
2018-01-20 00:06:59 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
apply(compiler) {
|
2018-01-24 19:00:50 +08:00
|
|
|
compiler.hooks.thisCompilation.tap("SplitChunksPlugin", compilation => {
|
2018-01-20 20:11:02 +08:00
|
|
|
let alreadyOptimized = false;
|
2018-01-20 00:06:59 +08:00
|
|
|
compilation.hooks.unseal.tap("SplitChunksPlugin", () => {
|
2018-01-20 20:11:02 +08:00
|
|
|
alreadyOptimized = false;
|
2018-01-20 00:06:59 +08:00
|
|
|
});
|
2018-02-25 09:00:20 +08:00
|
|
|
compilation.hooks.optimizeChunksAdvanced.tap(
|
|
|
|
"SplitChunksPlugin",
|
|
|
|
chunks => {
|
|
|
|
if (alreadyOptimized) return;
|
|
|
|
alreadyOptimized = true;
|
|
|
|
// Give each selected chunk an index (to create strings from chunks)
|
|
|
|
const indexMap = new Map();
|
|
|
|
let index = 1;
|
|
|
|
for (const chunk of chunks) {
|
|
|
|
indexMap.set(chunk, index++);
|
2018-02-17 16:48:40 +08:00
|
|
|
}
|
2018-02-25 09:00:20 +08:00
|
|
|
const getKey = chunks => {
|
|
|
|
return Array.from(chunks, c => indexMap.get(c))
|
|
|
|
.sort()
|
|
|
|
.join();
|
|
|
|
};
|
|
|
|
// Create a list of possible combinations
|
|
|
|
const chunkSetsInGraph = new Map(); // Map<string, Set<Chunk>>
|
|
|
|
for (const module of compilation.modules) {
|
|
|
|
const chunkIndices = getKey(module.chunksIterable);
|
|
|
|
chunkSetsInGraph.set(chunkIndices, new Set(module.chunksIterable));
|
|
|
|
}
|
|
|
|
const combinations = new Map(); // Map<string, Set<Chunk>[]>
|
|
|
|
for (const [key, chunksSet] of chunkSetsInGraph) {
|
|
|
|
var array = [];
|
|
|
|
for (const set of chunkSetsInGraph.values()) {
|
|
|
|
if (isSubset(chunksSet, set)) {
|
|
|
|
array.push(set);
|
2018-02-16 16:12:01 +08:00
|
|
|
}
|
2018-02-25 09:00:20 +08:00
|
|
|
}
|
|
|
|
combinations.set(key, array);
|
|
|
|
}
|
|
|
|
// Map a list of chunks to a list of modules
|
|
|
|
// For the key the chunk "index" is used, the value is a SortableSet of modules
|
|
|
|
const chunksInfoMap = new Map();
|
|
|
|
// Walk through all modules
|
|
|
|
for (const module of compilation.modules) {
|
|
|
|
// Get array of chunks
|
|
|
|
const chunks = module.getChunks();
|
|
|
|
// Get cache group
|
|
|
|
let cacheGroups = this.options.getCacheGroups(module, chunks);
|
|
|
|
if (!Array.isArray(cacheGroups)) continue;
|
|
|
|
for (const cacheGroupSource of cacheGroups) {
|
|
|
|
const cacheGroup = {
|
|
|
|
key: cacheGroupSource.key,
|
|
|
|
priority: cacheGroupSource.priority || 0,
|
|
|
|
chunks: cacheGroupSource.chunks || this.options.chunks,
|
|
|
|
minSize:
|
|
|
|
cacheGroupSource.minSize !== undefined
|
|
|
|
? cacheGroupSource.minSize
|
|
|
|
: cacheGroupSource.enforce ? 0 : this.options.minSize,
|
|
|
|
minChunks:
|
|
|
|
cacheGroupSource.minChunks !== undefined
|
|
|
|
? cacheGroupSource.minChunks
|
|
|
|
: cacheGroupSource.enforce ? 1 : this.options.minChunks,
|
|
|
|
maxAsyncRequests:
|
|
|
|
cacheGroupSource.maxAsyncRequests !== undefined
|
|
|
|
? cacheGroupSource.maxAsyncRequests
|
|
|
|
: cacheGroupSource.enforce
|
|
|
|
? Infinity
|
|
|
|
: this.options.maxAsyncRequests,
|
|
|
|
maxInitialRequests:
|
|
|
|
cacheGroupSource.maxInitialRequests !== undefined
|
|
|
|
? cacheGroupSource.maxInitialRequests
|
|
|
|
: cacheGroupSource.enforce
|
|
|
|
? Infinity
|
|
|
|
: this.options.maxInitialRequests,
|
|
|
|
getName:
|
|
|
|
cacheGroupSource.getName !== undefined
|
|
|
|
? cacheGroupSource.getName
|
|
|
|
: this.options.getName,
|
|
|
|
reuseExistingChunk: cacheGroupSource.reuseExistingChunk
|
|
|
|
};
|
|
|
|
// For all combination of chunk selection
|
|
|
|
for (const chunkCombination of combinations.get(getKey(chunks))) {
|
|
|
|
// Get indices of chunks in which this module occurs
|
|
|
|
const chunkIndices = Array.from(chunkCombination, chunk =>
|
|
|
|
indexMap.get(chunk)
|
|
|
|
);
|
|
|
|
// Break if minimum number of chunks is not reached
|
|
|
|
if (chunkIndices.length < cacheGroup.minChunks) continue;
|
|
|
|
// Select chunks by configuration
|
|
|
|
const selectedChunks =
|
|
|
|
cacheGroup.chunks === "initial"
|
|
|
|
? Array.from(chunkCombination).filter(chunk =>
|
|
|
|
chunk.canBeInitial()
|
|
|
|
)
|
|
|
|
: cacheGroup.chunks === "async"
|
|
|
|
? Array.from(chunkCombination).filter(
|
|
|
|
chunk => !chunk.canBeInitial()
|
|
|
|
)
|
|
|
|
: Array.from(chunkCombination);
|
|
|
|
// Determine name for split chunk
|
|
|
|
const name = cacheGroup.getName(
|
|
|
|
module,
|
|
|
|
selectedChunks,
|
|
|
|
cacheGroup.key
|
|
|
|
);
|
|
|
|
// Create key for maps
|
|
|
|
// When it has a name we use the name as key
|
|
|
|
// Elsewise we create the key from chunks and cache group key
|
|
|
|
// This automatically merges equal names
|
|
|
|
const chunksKey = getKey(selectedChunks);
|
|
|
|
const key =
|
|
|
|
(name && `name:${name}`) ||
|
|
|
|
`chunks:${chunksKey} key:${cacheGroup.key}`;
|
|
|
|
// Add module to maps
|
|
|
|
let info = chunksInfoMap.get(key);
|
|
|
|
if (info === undefined) {
|
|
|
|
chunksInfoMap.set(
|
|
|
|
key,
|
|
|
|
(info = {
|
|
|
|
modules: new SortableSet(undefined, sortByIdentifier),
|
|
|
|
cacheGroup,
|
|
|
|
name,
|
|
|
|
chunks: new Map(),
|
|
|
|
reusedableChunks: new Set(),
|
|
|
|
chunksKeys: new Set()
|
|
|
|
})
|
|
|
|
);
|
|
|
|
}
|
|
|
|
info.modules.add(module);
|
|
|
|
if (!info.chunksKeys.has(chunksKey)) {
|
|
|
|
info.chunksKeys.add(chunksKey);
|
|
|
|
for (const chunk of selectedChunks) {
|
|
|
|
info.chunks.set(chunk, chunk.getNumberOfModules());
|
|
|
|
}
|
2018-02-16 16:12:01 +08:00
|
|
|
}
|
2018-01-20 00:06:59 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-02-25 09:00:20 +08:00
|
|
|
for (const [key, info] of chunksInfoMap) {
|
|
|
|
// Get size of module lists
|
|
|
|
info.size = getModulesSize(info.modules);
|
|
|
|
if (info.size < info.cacheGroup.minSize) {
|
|
|
|
chunksInfoMap.delete(key);
|
2018-01-20 00:06:59 +08:00
|
|
|
}
|
|
|
|
}
|
2018-02-25 09:00:20 +08:00
|
|
|
let changed = false;
|
|
|
|
while (chunksInfoMap.size > 0) {
|
|
|
|
// Find best matching entry
|
|
|
|
let bestEntryKey;
|
|
|
|
let bestEntry;
|
|
|
|
for (const pair of chunksInfoMap) {
|
|
|
|
const key = pair[0];
|
|
|
|
const info = pair[1];
|
|
|
|
if (bestEntry === undefined) {
|
|
|
|
bestEntry = info;
|
|
|
|
bestEntryKey = key;
|
|
|
|
} else if (compareEntries(bestEntry, info) < 0) {
|
|
|
|
bestEntry = info;
|
|
|
|
bestEntryKey = key;
|
|
|
|
}
|
|
|
|
}
|
2018-01-20 00:06:59 +08:00
|
|
|
|
2018-02-25 09:00:20 +08:00
|
|
|
const item = bestEntry;
|
|
|
|
chunksInfoMap.delete(bestEntryKey);
|
2018-01-20 00:06:59 +08:00
|
|
|
|
2018-02-25 09:00:20 +08:00
|
|
|
let chunkName = item.name;
|
|
|
|
// Variable for the new chunk (lazy created)
|
|
|
|
let newChunk;
|
|
|
|
// When no chunk name, check if we can reuse a chunk instead of creating a new one
|
|
|
|
let isReused = false;
|
|
|
|
if (item.cacheGroup.reuseExistingChunk) {
|
|
|
|
for (const pair of item.chunks) {
|
|
|
|
if (pair[1] === item.modules.size) {
|
|
|
|
const chunk = pair[0];
|
|
|
|
if (chunk.hasEntryModule()) continue;
|
|
|
|
if (!newChunk || !newChunk.name) newChunk = chunk;
|
|
|
|
else if (
|
|
|
|
chunk.name &&
|
|
|
|
chunk.name.length < newChunk.name.length
|
|
|
|
)
|
|
|
|
newChunk = chunk;
|
|
|
|
else if (
|
|
|
|
chunk.name &&
|
|
|
|
chunk.name.length === newChunk.name.length &&
|
|
|
|
chunk.name < newChunk.name
|
|
|
|
)
|
|
|
|
newChunk = chunk;
|
|
|
|
chunkName = undefined;
|
|
|
|
isReused = true;
|
|
|
|
}
|
2018-01-20 00:06:59 +08:00
|
|
|
}
|
|
|
|
}
|
2018-02-25 09:00:20 +08:00
|
|
|
// Walk through all chunks
|
|
|
|
for (const chunk of item.chunks.keys()) {
|
|
|
|
// skip if we address ourself
|
|
|
|
if (chunk.name === chunkName || chunk === newChunk) continue;
|
|
|
|
// respect max requests when not enforced
|
|
|
|
const maxRequests = chunk.isOnlyInitial()
|
|
|
|
? item.cacheGroup.maxInitialRequests
|
|
|
|
: chunk.canBeInitial()
|
|
|
|
? Math.min(
|
|
|
|
item.cacheGroup.maxInitialRequests,
|
|
|
|
item.cacheGroup.maxAsyncRequests
|
|
|
|
)
|
|
|
|
: item.cacheGroup.maxAsyncRequests;
|
|
|
|
if (isFinite(maxRequests) && getRequests(chunk) >= maxRequests)
|
|
|
|
continue;
|
|
|
|
if (newChunk === undefined) {
|
|
|
|
// Create the new chunk
|
|
|
|
newChunk = compilation.addChunk(chunkName);
|
2018-01-20 00:06:59 +08:00
|
|
|
}
|
2018-02-25 09:00:20 +08:00
|
|
|
// Add graph connections for splitted chunk
|
|
|
|
chunk.split(newChunk);
|
|
|
|
// Remove all selected modules from the chunk
|
|
|
|
for (const module of item.modules) {
|
|
|
|
chunk.removeModule(module);
|
|
|
|
module.rewriteChunkInReasons(chunk, [newChunk]);
|
2018-01-20 00:06:59 +08:00
|
|
|
}
|
|
|
|
}
|
2018-02-25 09:00:20 +08:00
|
|
|
// If we successfully created a new chunk or reused one
|
|
|
|
if (newChunk) {
|
|
|
|
// Add a note to the chunk
|
|
|
|
newChunk.chunkReason = isReused
|
|
|
|
? "reused as split chunk"
|
|
|
|
: "split chunk";
|
|
|
|
if (item.cacheGroup.key) {
|
|
|
|
newChunk.chunkReason += ` (cache group: ${
|
|
|
|
item.cacheGroup.key
|
|
|
|
})`;
|
|
|
|
}
|
|
|
|
if (chunkName) {
|
|
|
|
newChunk.chunkReason += ` (name: ${chunkName})`;
|
2018-02-26 10:26:06 +08:00
|
|
|
// If the chosen name is already an entry point we remove the entry point
|
2018-02-25 09:00:20 +08:00
|
|
|
const entrypoint = compilation.entrypoints.get(chunkName);
|
|
|
|
if (entrypoint) {
|
|
|
|
compilation.entrypoints.delete(chunkName);
|
|
|
|
entrypoint.remove();
|
|
|
|
newChunk.entryModule = undefined;
|
2018-01-20 00:06:59 +08:00
|
|
|
}
|
2018-02-25 09:00:20 +08:00
|
|
|
}
|
|
|
|
if (!isReused) {
|
|
|
|
// Add all modules to the new chunk
|
|
|
|
for (const module of item.modules) {
|
|
|
|
GraphHelpers.connectChunkAndModule(newChunk, module);
|
2018-02-16 16:12:01 +08:00
|
|
|
}
|
2018-02-25 09:00:20 +08:00
|
|
|
}
|
|
|
|
// remove all modules from other entries and update size
|
|
|
|
for (const [key, info] of chunksInfoMap) {
|
|
|
|
if (isOverlap(info.chunks, item.chunks)) {
|
|
|
|
const oldSize = info.modules.size;
|
|
|
|
for (const module of item.modules) {
|
|
|
|
info.modules.delete(module);
|
|
|
|
}
|
|
|
|
if (info.modules.size === 0) {
|
2018-02-16 16:12:01 +08:00
|
|
|
chunksInfoMap.delete(key);
|
2018-02-25 09:00:20 +08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (info.modules.size !== oldSize) {
|
|
|
|
info.size = getModulesSize(info.modules);
|
|
|
|
if (info.size < info.cacheGroup.minSize)
|
|
|
|
chunksInfoMap.delete(key);
|
|
|
|
}
|
2018-01-20 00:06:59 +08:00
|
|
|
}
|
|
|
|
}
|
2018-02-25 09:00:20 +08:00
|
|
|
changed = true;
|
2018-01-20 00:06:59 +08:00
|
|
|
}
|
|
|
|
}
|
2018-02-25 09:00:20 +08:00
|
|
|
if (changed) return true;
|
2018-01-20 00:06:59 +08:00
|
|
|
}
|
2018-02-25 09:00:20 +08:00
|
|
|
);
|
2018-01-20 00:06:59 +08:00
|
|
|
});
|
|
|
|
}
|
|
|
|
};
|