2013-01-31 01:49:25 +08:00
|
|
|
/*
|
|
|
|
MIT License http://www.opensource.org/licenses/mit-license.php
|
|
|
|
Author Tobias Koppers @sokra
|
|
|
|
*/
|
2017-01-05 00:17:49 +08:00
|
|
|
"use strict";
|
|
|
|
|
|
|
|
const compareLocations = require("./compareLocations");
|
|
|
|
let debugId = 1000;
|
|
|
|
const removeAndDo = require("./removeAndDo");
|
|
|
|
|
|
|
|
const byId = (a, b) => {
|
|
|
|
if(a.id < b.id) return -1;
|
|
|
|
if(b.id < a.id) return 1;
|
|
|
|
return 0;
|
2017-01-11 17:51:58 +08:00
|
|
|
};
|
2013-01-31 01:49:25 +08:00
|
|
|
|
2017-01-05 00:17:49 +08:00
|
|
|
class Chunk {
|
|
|
|
|
|
|
|
constructor(name, module, loc) {
|
|
|
|
this.id = null;
|
|
|
|
this.ids = null;
|
|
|
|
this.debugId = debugId++;
|
|
|
|
this.name = name;
|
|
|
|
this.modules = [];
|
|
|
|
this.entrypoints = [];
|
|
|
|
this.chunks = [];
|
|
|
|
this.parents = [];
|
|
|
|
this.blocks = [];
|
|
|
|
this.origins = [];
|
|
|
|
this.files = [];
|
|
|
|
this.rendered = false;
|
|
|
|
if(module) {
|
|
|
|
this.origins.push({
|
|
|
|
module,
|
|
|
|
loc,
|
|
|
|
name
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
get entry() {
|
2016-07-13 17:03:14 +08:00
|
|
|
throw new Error("Chunk.entry was removed. Use hasRuntime()");
|
2017-01-05 00:17:49 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
set entry(data) {
|
2016-07-13 17:03:14 +08:00
|
|
|
throw new Error("Chunk.entry was removed. Use hasRuntime()");
|
|
|
|
}
|
|
|
|
|
2017-01-05 00:17:49 +08:00
|
|
|
get initial() {
|
2016-07-13 17:03:14 +08:00
|
|
|
throw new Error("Chunk.initial was removed. Use isInitial()");
|
|
|
|
}
|
|
|
|
|
2017-01-05 00:17:49 +08:00
|
|
|
set initial(data) {
|
|
|
|
throw new Error("Chunk.initial was removed. Use isInitial()");
|
|
|
|
}
|
2016-07-13 17:03:14 +08:00
|
|
|
|
2017-01-05 00:17:49 +08:00
|
|
|
hasRuntime() {
|
|
|
|
if(this.entrypoints.length === 0) return false;
|
|
|
|
return this.entrypoints[0].chunks[0] === this;
|
|
|
|
}
|
2016-07-13 17:03:14 +08:00
|
|
|
|
2017-01-05 00:17:49 +08:00
|
|
|
isInitial() {
|
|
|
|
return this.entrypoints.length > 0;
|
|
|
|
}
|
2016-07-13 17:03:14 +08:00
|
|
|
|
2017-01-05 00:17:49 +08:00
|
|
|
hasEntryModule() {
|
|
|
|
return !!this.entryModule;
|
2014-06-04 03:03:21 +08:00
|
|
|
}
|
2013-01-31 01:49:25 +08:00
|
|
|
|
2017-01-26 13:55:20 +08:00
|
|
|
addToCollection(collection, item) {
|
|
|
|
if(item === this) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(collection.indexOf(item) > -1) {
|
2017-01-05 00:17:49 +08:00
|
|
|
return false;
|
|
|
|
}
|
2017-01-26 13:55:20 +08:00
|
|
|
|
|
|
|
collection.push(item);
|
2017-01-05 00:17:49 +08:00
|
|
|
return true;
|
|
|
|
}
|
2013-01-31 01:49:25 +08:00
|
|
|
|
2017-01-26 13:55:20 +08:00
|
|
|
addChunk(chunk) {
|
|
|
|
return this.addToCollection(this.chunks, chunk);
|
|
|
|
}
|
|
|
|
|
|
|
|
addParent(parent) {
|
|
|
|
return this.addToCollection(this.parents, parent);
|
|
|
|
}
|
|
|
|
|
|
|
|
addModule(module) {
|
|
|
|
return this.addToCollection(this.modules, module);
|
|
|
|
}
|
|
|
|
|
|
|
|
addBlock(block) {
|
|
|
|
return this.addToCollection(this.blocks, block);
|
|
|
|
}
|
|
|
|
|
2017-01-05 00:17:49 +08:00
|
|
|
removeModule(module) {
|
2017-01-26 12:30:58 +08:00
|
|
|
removeAndDo(this.modules, module, "removeChunk", this);
|
2017-01-05 00:17:49 +08:00
|
|
|
}
|
2013-01-31 01:49:25 +08:00
|
|
|
|
2017-01-05 00:17:49 +08:00
|
|
|
removeChunk(chunk) {
|
2017-01-26 12:30:58 +08:00
|
|
|
removeAndDo(this.chunks, chunk, "removeParent", this);
|
2017-01-05 00:17:49 +08:00
|
|
|
}
|
2013-01-31 01:49:25 +08:00
|
|
|
|
2017-01-05 00:17:49 +08:00
|
|
|
removeParent(chunk) {
|
2017-01-26 12:30:58 +08:00
|
|
|
removeAndDo(this.parents, chunk, "removeChunk", this);
|
2017-01-05 00:17:49 +08:00
|
|
|
}
|
2013-01-31 01:49:25 +08:00
|
|
|
|
2017-01-05 00:17:49 +08:00
|
|
|
addOrigin(module, loc) {
|
|
|
|
this.origins.push({
|
|
|
|
module,
|
|
|
|
loc,
|
|
|
|
name: this.name
|
|
|
|
});
|
|
|
|
}
|
2014-06-25 00:53:32 +08:00
|
|
|
|
2017-01-05 00:17:49 +08:00
|
|
|
remove(reason) {
|
2017-01-26 12:47:31 +08:00
|
|
|
// cleanup modules
|
2017-01-26 12:42:23 +08:00
|
|
|
this.modules.slice().forEach(module => {
|
|
|
|
module.removeChunk(this);
|
2017-01-26 12:31:37 +08:00
|
|
|
});
|
2017-01-26 12:47:31 +08:00
|
|
|
|
|
|
|
// cleanup parents
|
2017-01-26 12:42:23 +08:00
|
|
|
this.parents.forEach(parent => {
|
2017-01-26 12:47:31 +08:00
|
|
|
// remove this chunk from its parents
|
2017-01-26 12:42:23 +08:00
|
|
|
const idx = parent.chunks.indexOf(this);
|
2017-01-05 00:17:49 +08:00
|
|
|
if(idx >= 0) {
|
2017-01-26 12:42:23 +08:00
|
|
|
parent.chunks.splice(idx, 1);
|
2017-01-05 00:17:49 +08:00
|
|
|
}
|
2017-01-26 12:47:31 +08:00
|
|
|
|
|
|
|
// cleanup "sub chunks"
|
2017-01-26 12:42:23 +08:00
|
|
|
this.chunks.forEach(chunk => {
|
2017-01-26 12:47:31 +08:00
|
|
|
/**
|
|
|
|
* remove this chunk as "intermediary" and connect
|
|
|
|
* it "sub chunks" and parents directly
|
|
|
|
*/
|
|
|
|
// add parent to each "sub chunk"
|
2017-01-26 12:42:23 +08:00
|
|
|
chunk.addParent(parent);
|
2017-01-26 12:47:31 +08:00
|
|
|
// add "sub chunk" to parent
|
2017-01-26 12:42:23 +08:00
|
|
|
parent.addChunk(chunk);
|
2017-01-26 12:47:31 +08:00
|
|
|
|
|
|
|
// remove this as parent of every "sub chunk"
|
|
|
|
const idx = chunk.parents.indexOf(this);
|
|
|
|
if(idx >= 0) {
|
|
|
|
chunk.parents.splice(idx, 1);
|
|
|
|
}
|
2017-01-05 00:17:49 +08:00
|
|
|
});
|
2017-01-26 12:31:37 +08:00
|
|
|
});
|
2017-01-26 12:47:31 +08:00
|
|
|
|
|
|
|
// cleanup blocks
|
2017-01-26 12:42:23 +08:00
|
|
|
this.blocks.forEach(block => {
|
|
|
|
const idx = block.chunks.indexOf(this);
|
2017-01-05 00:17:49 +08:00
|
|
|
if(idx >= 0) {
|
2017-01-26 12:42:23 +08:00
|
|
|
block.chunks.splice(idx, 1);
|
|
|
|
if(block.chunks.length === 0) {
|
|
|
|
block.chunks = null;
|
|
|
|
block.chunkReason = reason;
|
2017-01-05 00:17:49 +08:00
|
|
|
}
|
|
|
|
}
|
2017-01-26 12:31:37 +08:00
|
|
|
});
|
2017-01-05 00:17:49 +08:00
|
|
|
}
|
2014-06-25 00:53:32 +08:00
|
|
|
|
2017-01-26 14:08:35 +08:00
|
|
|
moveModule(module, otherChunk) {
|
2017-01-05 00:17:49 +08:00
|
|
|
module.removeChunk(this);
|
2017-01-26 14:08:35 +08:00
|
|
|
module.addChunk(otherChunk);
|
|
|
|
otherChunk.addModule(module);
|
|
|
|
module.rewriteChunkInReasons(this, [otherChunk]);
|
2014-06-04 03:03:21 +08:00
|
|
|
}
|
2017-01-05 00:17:49 +08:00
|
|
|
|
2017-01-26 14:53:23 +08:00
|
|
|
replaceChunk(oldChunk, newChunk) {
|
|
|
|
const idx = this.chunks.indexOf(oldChunk);
|
|
|
|
if(idx >= 0) {
|
|
|
|
this.chunks.splice(idx, 1);
|
|
|
|
}
|
|
|
|
if(this !== newChunk && newChunk.addParent(this)) {
|
|
|
|
this.addChunk(newChunk);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
replaceParent(oldParent, newParent) {
|
|
|
|
const idx = this.parents.indexOf(oldParent);
|
|
|
|
if(idx >= 0) {
|
|
|
|
this.parents.splice(idx, 1);
|
|
|
|
}
|
|
|
|
if(this !== newParent && newParent.addChunk(this)) {
|
|
|
|
this.addParent(newParent);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-05 00:17:49 +08:00
|
|
|
integrate(other, reason) {
|
|
|
|
if(!this.canBeIntegrated(other)) {
|
|
|
|
return false;
|
2014-06-04 03:03:21 +08:00
|
|
|
}
|
2017-01-05 00:17:49 +08:00
|
|
|
|
|
|
|
const otherModules = other.modules.slice();
|
2017-01-26 14:10:46 +08:00
|
|
|
otherModules.forEach(m => other.moveModule(m, this));
|
2017-01-05 00:17:49 +08:00
|
|
|
other.modules.length = 0;
|
|
|
|
|
2017-01-26 14:56:42 +08:00
|
|
|
other.parents.forEach(parent => parent.replaceChunk(other, this));
|
2017-01-05 00:17:49 +08:00
|
|
|
other.parents.length = 0;
|
2017-01-26 14:56:42 +08:00
|
|
|
|
|
|
|
other.chunks.forEach(chunk => chunk.replaceParent(other, this));
|
2017-01-05 00:17:49 +08:00
|
|
|
other.chunks.length = 0;
|
2017-01-26 14:56:42 +08:00
|
|
|
|
2017-01-05 00:17:49 +08:00
|
|
|
other.blocks.forEach(b => {
|
|
|
|
b.chunks = (b.chunks || [this]).map(c => {
|
|
|
|
return c === other ? this : c;
|
|
|
|
}, this);
|
|
|
|
b.chunkReason = reason;
|
|
|
|
this.addBlock(b);
|
|
|
|
}, this);
|
|
|
|
other.blocks.length = 0;
|
|
|
|
other.origins.forEach(origin => {
|
|
|
|
this.origins.push(origin);
|
|
|
|
}, this);
|
|
|
|
this.origins.forEach(origin => {
|
|
|
|
if(!origin.reasons) {
|
|
|
|
origin.reasons = [reason];
|
|
|
|
} else if(origin.reasons[0] !== reason) {
|
|
|
|
origin.reasons.unshift(reason);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
this.chunks = this.chunks.filter(c => {
|
|
|
|
return c !== other && c !== this;
|
|
|
|
});
|
|
|
|
this.parents = this.parents.filter(c => {
|
|
|
|
return c !== other && c !== this;
|
|
|
|
});
|
|
|
|
return true;
|
2014-06-04 03:03:21 +08:00
|
|
|
}
|
2014-02-04 01:12:19 +08:00
|
|
|
|
2017-01-05 00:17:49 +08:00
|
|
|
split(newChunk) {
|
|
|
|
this.blocks.forEach(b => {
|
|
|
|
newChunk.blocks.push(b);
|
|
|
|
b.chunks.push(newChunk);
|
|
|
|
});
|
|
|
|
this.chunks.forEach(c => {
|
|
|
|
newChunk.chunks.push(c);
|
|
|
|
c.parents.push(newChunk);
|
|
|
|
});
|
|
|
|
this.parents.forEach(p => {
|
|
|
|
p.chunks.push(newChunk);
|
|
|
|
newChunk.parents.push(p);
|
|
|
|
});
|
|
|
|
this.entrypoints.forEach(e => {
|
2017-01-26 12:32:03 +08:00
|
|
|
e.insertChunk(newChunk, this);
|
2014-06-25 00:53:32 +08:00
|
|
|
});
|
|
|
|
}
|
2017-01-05 00:17:49 +08:00
|
|
|
|
|
|
|
isEmpty() {
|
|
|
|
return this.modules.length === 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
updateHash(hash) {
|
|
|
|
hash.update(`${this.id} `);
|
|
|
|
hash.update(this.ids ? this.ids.join(",") : "");
|
|
|
|
hash.update(`${this.name || ""} `);
|
|
|
|
this.modules.forEach(m => m.updateHash(hash));
|
|
|
|
}
|
|
|
|
|
|
|
|
size(options) {
|
|
|
|
const CHUNK_OVERHEAD = typeof options.chunkOverhead === "number" ? options.chunkOverhead : 10000;
|
|
|
|
const ENTRY_CHUNK_MULTIPLICATOR = options.entryChunkMultiplicator || 10;
|
|
|
|
|
|
|
|
const modulesSize = this.modules.reduce((a, b) => {
|
|
|
|
return a + b.size();
|
|
|
|
}, 0);
|
|
|
|
return modulesSize * (this.isInitial() ? ENTRY_CHUNK_MULTIPLICATOR : 1) + CHUNK_OVERHEAD;
|
|
|
|
}
|
|
|
|
|
|
|
|
canBeIntegrated(other) {
|
|
|
|
if(other.isInitial()) {
|
|
|
|
return false;
|
2014-08-22 19:51:24 +08:00
|
|
|
}
|
2017-01-05 00:17:49 +08:00
|
|
|
if(this.isInitial()) {
|
|
|
|
if(other.parents.length !== 1 || other.parents[0] !== this) {
|
|
|
|
return false;
|
|
|
|
}
|
2014-06-04 03:03:21 +08:00
|
|
|
}
|
2017-01-05 00:17:49 +08:00
|
|
|
return true;
|
2014-06-04 03:03:21 +08:00
|
|
|
}
|
2017-01-05 00:17:49 +08:00
|
|
|
|
|
|
|
integratedSize(other, options) {
|
|
|
|
// Chunk if it's possible to integrate this chunk
|
|
|
|
if(!this.canBeIntegrated(other)) {
|
2014-06-04 03:03:21 +08:00
|
|
|
return false;
|
|
|
|
}
|
2017-01-05 00:17:49 +08:00
|
|
|
|
|
|
|
const CHUNK_OVERHEAD = typeof options.chunkOverhead === "number" ? options.chunkOverhead : 10000;
|
|
|
|
const ENTRY_CHUNK_MULTIPLICATOR = options.entryChunkMultiplicator || 10;
|
|
|
|
|
|
|
|
const mergedModules = this.modules.slice();
|
|
|
|
other.modules.forEach(m => {
|
|
|
|
if(this.modules.indexOf(m) < 0) {
|
|
|
|
mergedModules.push(m);
|
|
|
|
}
|
|
|
|
}, this);
|
|
|
|
|
|
|
|
const modulesSize = mergedModules.reduce((a, m) => {
|
|
|
|
return a + m.size();
|
|
|
|
}, 0);
|
|
|
|
return modulesSize * (this.isInitial() || other.isInitial() ? ENTRY_CHUNK_MULTIPLICATOR : 1) + CHUNK_OVERHEAD;
|
2014-02-04 01:12:19 +08:00
|
|
|
}
|
|
|
|
|
2017-01-05 00:17:49 +08:00
|
|
|
getChunkMaps(includeEntries, realHash) {
|
|
|
|
const chunksProcessed = [];
|
|
|
|
const chunkHashMap = {};
|
|
|
|
const chunkNameMap = {};
|
|
|
|
(function addChunk(c) {
|
|
|
|
if(chunksProcessed.indexOf(c) >= 0) return;
|
|
|
|
chunksProcessed.push(c);
|
|
|
|
if(!c.hasRuntime() || includeEntries) {
|
|
|
|
chunkHashMap[c.id] = realHash ? c.hash : c.renderedHash;
|
|
|
|
if(c.name)
|
|
|
|
chunkNameMap[c.id] = c.name;
|
|
|
|
}
|
|
|
|
c.chunks.forEach(addChunk);
|
|
|
|
}(this));
|
|
|
|
return {
|
|
|
|
hash: chunkHashMap,
|
|
|
|
name: chunkNameMap
|
|
|
|
};
|
2014-06-04 03:03:21 +08:00
|
|
|
}
|
2014-02-04 01:12:19 +08:00
|
|
|
|
2017-01-05 00:17:49 +08:00
|
|
|
sortItems() {
|
|
|
|
this.modules.sort(byId);
|
|
|
|
this.origins.sort((a, b) => {
|
|
|
|
const aIdent = a.module.identifier();
|
|
|
|
const bIdent = b.module.identifier();
|
|
|
|
if(aIdent < bIdent) return -1;
|
|
|
|
if(aIdent > bIdent) return 1;
|
|
|
|
return compareLocations(a.loc, b.loc);
|
|
|
|
});
|
|
|
|
this.origins.forEach(origin => {
|
|
|
|
if(origin.reasons)
|
|
|
|
origin.reasons.sort();
|
|
|
|
});
|
|
|
|
this.parents.sort(byId);
|
|
|
|
this.chunks.sort(byId);
|
|
|
|
}
|
2013-06-18 00:55:11 +08:00
|
|
|
|
2017-01-05 00:17:49 +08:00
|
|
|
toString() {
|
|
|
|
return `Chunk[${this.modules.join()}]`;
|
|
|
|
}
|
|
|
|
|
|
|
|
checkConstraints() {
|
|
|
|
const chunk = this;
|
|
|
|
chunk.chunks.forEach((child, idx) => {
|
|
|
|
if(chunk.chunks.indexOf(child) !== idx)
|
|
|
|
throw new Error(`checkConstraints: duplicate child in chunk ${chunk.debugId} ${child.debugId}`);
|
|
|
|
if(child.parents.indexOf(chunk) < 0)
|
|
|
|
throw new Error(`checkConstraints: child missing parent ${chunk.debugId} -> ${child.debugId}`);
|
|
|
|
});
|
|
|
|
chunk.parents.forEach((parent, idx) => {
|
|
|
|
if(chunk.parents.indexOf(parent) !== idx)
|
|
|
|
throw new Error(`checkConstraints: duplicate parent in chunk ${chunk.debugId} ${parent.debugId}`);
|
|
|
|
if(parent.chunks.indexOf(chunk) < 0)
|
|
|
|
throw new Error(`checkConstraints: parent missing child ${parent.debugId} <- ${chunk.debugId}`);
|
|
|
|
});
|
|
|
|
}
|
2016-07-13 17:03:14 +08:00
|
|
|
}
|
|
|
|
|
2017-01-05 00:17:49 +08:00
|
|
|
module.exports = Chunk;
|