UNPKG

fuse-box

Version:

Fuse-Box a bundler that does it right

209 lines (186 loc) • 7.81 kB
import { RequireStatement } from "../core/nodes/RequireStatement"; import { FileAbstraction } from "../core/FileAbstraction"; import { hashString, joinFuseBoxPath } from "../../Utils"; import { PackageAbstraction } from "../core/PackageAbstraction"; import { QuantumCore } from "./QuantumCore"; export class QuantumBit { public name: string; public core: QuantumCore; private candidates: Map<string, FileAbstraction> = new Map(); private modulesCanidates = new Map<string, PackageAbstraction>(); private isEntryModule = false; private modules2proccess : FileAbstraction[] = []; public files: Map<string, FileAbstraction> = new Map(); public modules = new Map<string, PackageAbstraction>(); constructor(public entry: FileAbstraction, public requireStatement: RequireStatement) { this.generateName(); this.core = this.entry.core; this.entry.quantumBitEntry = true; this.isEntryModule = !this.entry.belongsToProject(); this.requireStatement.setValue(this.name); } public isNodeModules() { return this.requireStatement.isNodeModule; } private generateName() { this.name = hashString(this.entry.getFuseBoxFullPath()) } public getBundleName() { const dest = this.core.context.quantumSplitConfig.getDest(); return joinFuseBoxPath(dest, this.name); } public isEligible() { return this.files.size > 0 || this.modules.size > 0; } private dealWithModule(file: FileAbstraction, origin = false) { // that's a node_module we need to move packages too (if possible) // For this purpose we need to register all entry entry points // and assign QuantumBit instance let pkg = file.packageAbstraction; if (!origin && file.quantumBitEntry) { return; } if (!this.modulesCanidates.has(pkg.name)) { this.modulesCanidates.set(pkg.name, pkg); pkg.fileAbstractions.forEach(dep => { if (dep.quantumBit) { if (dep.quantumBit !== this) { pkg.quantumBitBanned = true; } } else { dep.quantumBit = this; } dep.getDependencies().forEach((key, libDep) => { if (libDep.belongsToExternalModule()) { if (!libDep.quantumBitEntry) { this.dealWithModule(libDep); } } }) if( origin === false ){ dep.dependents.forEach(dependent => { if ( !dependent.quantumBit && dependent.packageAbstraction !== dep.packageAbstraction ) { pkg.quantumBitBanned = true; } }); } }) } return true; } private populateDependencies(file?: FileAbstraction) { const dependencies = file.getDependencies(); for (const item of dependencies) { const dependency = item[0]; if (dependency.belongsToProject()) { if (dependency.quantumBit && dependency.quantumBit !== this) { dependency.quantumBitBanned = true; } else { dependency.quantumBit = this; if (!this.candidates.has(dependency.getFuseBoxFullPath())) { this.candidates.set(dependency.getFuseBoxFullPath(), dependency); this.populateDependencies(dependency); } } } else { this.modules2proccess.push(dependency); } } } private findRootDependents(f: FileAbstraction, list: FileAbstraction[]): FileAbstraction[] { if (list.indexOf(f) === -1) { list.push(f); if (f !== this.entry) { f.dependents.forEach(dep => { this.findRootDependents(dep, list); }); } } return list; } public resolve(file?: FileAbstraction) { if (this.isEntryModule) { this.dealWithModule(this.entry, true); } else { this.files.set(this.entry.getFuseBoxFullPath(), this.entry) } this.populateDependencies(this.entry); this.modules2proccess.forEach(dep => this.dealWithModule(dep)) for (const p of this.candidates) { const file = p[1]; const rootDependents = this.findRootDependents(file, []); for (const root of rootDependents) { if (!root.quantumBit && root !== this.entry) { file.quantumBitBanned = true; } else { if (root.quantumBit && root.quantumBit !== this && root !== this.entry) { file.quantumBitBanned = true; } } } if (!file.quantumBit) { file.quantumBitBanned = true; }; } for (const item of this.modulesCanidates) { const moduleCandidate = item[1]; // a case where the same library is imported dynamically and through require statements // we need to ban it and all of it dependencies moduleCandidate.fileAbstractions.forEach(file => { let dynamicStatementUsed = false; let regularStatementUsed = false; file.referencedRequireStatements.forEach(ref => { if (ref.isDynamicImport) { dynamicStatementUsed = true; } else { regularStatementUsed = true; } }); if (dynamicStatementUsed && regularStatementUsed) { moduleCandidate.quantumBitBanned = true; } }); if (moduleCandidate.quantumBitBanned) { moduleCandidate.fileAbstractions.forEach(f => { f.getDependencies().forEach((key, dep) => { if (dep.belongsToExternalModule()) { const existingCandidate = this.modulesCanidates.get(dep.packageAbstraction.name); if (existingCandidate) { // demote MOFO existingCandidate.quantumBitBanned = true; } } }); }) } } this.modulesCanidates.forEach(pkg => { if (!pkg.quantumBitBanned) { pkg.fileAbstractions.forEach(f => { const dependents = this.findRootDependents(f, []); dependents.forEach(dep => { if (!dep.quantumBit && dep !== this.entry) { pkg.quantumBitBanned = true; } else { if (dep.quantumBit && dep.quantumBit !== this && dep !== this.entry) { pkg.quantumBitBanned = true; } } }); }); } }) } public populate() { this.candidates.forEach(candidate => { if (!candidate.quantumBitBanned) { this.files.set(candidate.getFuseBoxFullPath(), candidate) } }); this.modulesCanidates.forEach(moduleCandidate => { if (!moduleCandidate.quantumBitBanned) { this.modules.set(moduleCandidate.name, moduleCandidate) } }); } }