/* MIT License http://www.opensource.org/licenses/mit-license.php Author Tobias Koppers @sokra */ "use strict"; const SortableSet = require("./util/SortableSet"); const compareLocations = require("./compareLocations"); let debugId = 5000; const getArray = set => Array.from(set); const sortById = (a, b) => { if(a.id < b.id) return -1; if(b.id < a.id) return 1; return 0; }; const sortOrigin = (a, b) => { const aIdent = a.module ? a.module.identifier() : ""; const bIdent = b.module ? b.module.identifier() : ""; if(aIdent < bIdent) return -1; if(aIdent > bIdent) return 1; return compareLocations(a.loc, b.loc); }; class ChunkGroup { constructor(name) { this.groupDebugId = debugId++; this.name = name; this._children = new SortableSet(undefined, sortById); this._parents = new SortableSet(undefined, sortById); this._blocks = new SortableSet(); this.chunks = []; this.origins = []; } /* istanbul ignore next */ get debugId() { return Array.from(this.chunks, x => x.debugId).join("+"); } get id() { return Array.from(this.chunks, x => x.id).join("+"); } unshiftChunk(chunk) { const oldIdx = this.chunks.indexOf(chunk); if(oldIdx > 0) { this.chunks.splice(oldIdx, 1); this.chunks.unshift(chunk); } else if(oldIdx < 0) { this.chunks.unshift(chunk); return true; } return false; } insertChunk(chunk, before) { const oldIdx = this.chunks.indexOf(chunk); const idx = this.chunks.indexOf(before); if(idx < 0) { throw new Error("before chunk not found"); } if(oldIdx >= 0 && oldIdx > idx) { this.chunks.splice(oldIdx, 1); this.chunks.splice(idx, 0, chunk); } else if(oldIdx < 0) { this.chunks.splice(idx, 0, chunk); return true; } return false; } pushChunk(chunk) { const oldIdx = this.chunks.indexOf(chunk); if(oldIdx >= 0) { return false; } this.chunks.push(chunk); return true; } replaceChunk(oldChunk, newChunk) { const oldIdx = this.chunks.indexOf(oldChunk); if(oldIdx < 0) return false; const newIdx = this.chunks.indexOf(newChunk); if(newIdx < 0) { this.chunks[oldIdx] = newChunk; return true; } if(newIdx < oldIdx) { this.chunks.splice(oldIdx, 1); return true; } else if(newIdx !== oldIdx) { this.chunks[oldIdx] = newChunk; this.chunks.splice(newIdx, 1); return true; } } removeChunk(chunk) { const idx = this.chunks.indexOf(chunk); if(idx >= 0) { this.chunks.splice(idx, 1); return true; } return false; } isInitial() { return false; } addChild(chunk) { if(this._children.has(chunk)) { return false; } this._children.add(chunk); return true; } getChildren() { return this._children.getFromCache(getArray); } getNumberOfChildren() { return this._children.size; } get childrenIterable() { return this._children; } removeChild(chunk) { if(!this._children.has(chunk)) { return false; } this._children.delete(chunk); chunk.removeParent(this); return true; } addParent(parentChunk) { if(!this._parents.has(parentChunk)) { this._parents.add(parentChunk); return true; } return false; } getParents() { return this._parents.getFromCache(getArray); } setParents(newParents) { this._parents.clear(); for(const p of newParents) this._parents.add(p); } getNumberOfParents() { return this._parents.size; } hasParent(parent) { return this._parents.has(parent); } get parentsIterable() { return this._parents; } removeParent(chunk) { if(this._parents.delete(chunk)) { chunk.removeChunk(this); return true; } return false; } /** * @return {Array} - an array containing the blocks */ getBlocks() { return this._blocks.getFromCache(getArray); } getNumberOfBlocks() { return this._blocks.size; } hasBlock(block) { return this._blocks.has(block); } get blocksIterable() { return this._blocks; } addBlock(block) { if(!this._blocks.has(block)) { this._blocks.add(block); return true; } return false; } addOrigin(module, loc, request) { this.origins.push({ module, loc, request }); } containsModule(module) { for(const chunk of this.chunks) { if(chunk.containsModule(module)) return true; } return false; } remove(reason) { // cleanup parents for(const parentChunkGroup of this._parents) { // remove this chunk from its parents parentChunkGroup._children.delete(this); // cleanup "sub chunks" for(const chunkGroup of this._children) { /** * remove this chunk as "intermediary" and connect * it "sub chunks" and parents directly */ // add parent to each "sub chunk" chunkGroup.addParent(parentChunkGroup); // add "sub chunk" to parent parentChunkGroup.addChild(chunkGroup); } } /** * we need to iterate again over the children * to remove this from the childs parents. * This can not be done in the above loop * as it is not garuanteed that `this._parents` contains anything. */ for(const chunkGroup of this._children) { // remove this as parent of every "sub chunk" chunkGroup._parents.delete(this); } // cleanup blocks for(const block of this._blocks) { block.chunkGroup = null; } // remove chunks for(const chunk of this.chunks) { chunk.removeGroup(this); } } sortItems() { this.origins.sort(sortOrigin); this._parents.sort(); this._children.sort(); } checkConstraints() { const chunk = this; for(const child of chunk._children) { if(!child._parents.has(chunk)) throw new Error(`checkConstraints: child missing parent ${chunk.debugId} -> ${child.debugId}`); } for(const parentChunk of chunk._parents) { if(!parentChunk._children.has(chunk)) throw new Error(`checkConstraints: parent missing child ${parentChunk.debugId} <- ${chunk.debugId}`); } } } module.exports = ChunkGroup;