diff --git a/tools/isobuild/compiler-plugin.js b/tools/isobuild/compiler-plugin.js index 4646f3f76f..3ff6eb6463 100644 --- a/tools/isobuild/compiler-plugin.js +++ b/tools/isobuild/compiler-plugin.js @@ -670,46 +670,54 @@ export class PackageSourceBatch { scanner.addInputFiles(map.get(name)); if (batch.useMeteorInstall) { - scanner.scanImports().getOutputFiles().forEach(file => { - if (file.missingNodeModules) { - _.extend(allMissingNodeModules, file.missingNodeModules); - } - }); + scanner.scanImports(); + _.extend(allMissingNodeModules, scanner.allMissingNodeModules); } scannerMap.set(name, scanner); }); - const missingMap = new Map; + function handleMissing(missingNodeModules) { + const missingMap = new Map; - Object.keys(allMissingNodeModules).forEach(id => { - const parts = id.split("/"); - let name = null; + Object.keys(missingNodeModules).forEach(id => { + const parts = id.split("/"); + let name = null; - if (parts[0] === "meteor") { - if (parts.length > 2) { - name = parts[1]; - parts[1] = "."; - id = parts.slice(1).join("/"); - } else { + if (parts[0] === "meteor") { + if (parts.length > 2) { + name = parts[1]; + parts[1] = "."; + id = parts.slice(1).join("/"); + } else { + return; + } + } + + if (! scannerMap.has(name)) { return; } - } - if (! scannerMap.has(name)) { - return; - } + if (missingMap.has(name)) { + missingMap.get(name).push(id); + } else { + missingMap.set(name, [id]); + } + }); - if (missingMap.has(name)) { - missingMap.get(name).push(id); - } else { - missingMap.set(name, [id]); - } - }); + const nextMissingNodeModules = Object.create(null); - missingMap.forEach((ids, name) => { - scannerMap.get(name).addNodeModules(ids); - }); + missingMap.forEach((ids, name) => { + _.extend(nextMissingNodeModules, + scannerMap.get(name).addNodeModules(ids)); + }); + + if (! _.isEmpty(nextMissingNodeModules)) { + handleMissing(nextMissingNodeModules); + } + } + + handleMissing(allMissingNodeModules); scannerMap.forEach((scanner, name) => { const isApp = ! name; diff --git a/tools/isobuild/import-scanner.js b/tools/isobuild/import-scanner.js index 8108e62462..390a851893 100644 --- a/tools/isobuild/import-scanner.js +++ b/tools/isobuild/import-scanner.js @@ -63,7 +63,8 @@ export default class ImportScanner { this.usedPackageNames = usedPackageNames; this.nodeModulesPath = nodeModulesPath; this.watchSet = watchSet; - this.absPathToOutputIndex = {}; + this.absPathToOutputIndex = Object.create(null); + this.allMissingNodeModules = Object.create(null); this.outputFiles = []; this._statCache = new Map; @@ -104,6 +105,8 @@ export default class ImportScanner { } addNodeModules(identifiers) { + const newMissingNodeModules = Object.create(null); + if (identifiers) { if (typeof identifiers === "object" && ! Array.isArray(identifiers)) { @@ -111,17 +114,37 @@ export default class ImportScanner { } if (identifiers.length > 0) { - this._scanFile({ - sourcePath: "fake.js", - // By specifying the .deps property of this fake file ahead of - // time, we can avoid calling findImportedModuleIdentifiers in the - // _scanFile method. - deps: identifiers, - }); + const previousAllMissingNodeModules = this.allMissingNodeModules; + this.allMissingNodeModules = newMissingNodeModules; + + try { + this._scanFile({ + sourcePath: "fake.js", + // By specifying the .deps property of this fake file ahead of + // time, we can avoid calling findImportedModuleIdentifiers in the + // _scanFile method. + deps: identifiers, + }); + + } finally { + this.allMissingNodeModules = previousAllMissingNodeModules; + + // Remove previously seen missing module identifiers from + // newMissingNodeModules and merge the new identifiers back into + // this.allMissingNodeModules. + each(keys(newMissingNodeModules), key => { + if (has(previousAllMissingNodeModules, key)) { + delete newMissingNodeModules[key]; + } else { + previousAllMissingNodeModules[key] = + newMissingNodeModules[key]; + } + }); + } } } - return this; + return newMissingNodeModules; } getOutputFiles(options) { @@ -476,7 +499,7 @@ export default class ImportScanner { // the top-level node_modules directory, and we should record the // missing dependency so that we can include it in the app bundle. const missing = file.missingNodeModules || Object.create(null); - missing[id] = true; + this.allMissingNodeModules[id] = missing[id] = true; file.missingNodeModules = missing; }