diff --git a/lib/Compilation.js b/lib/Compilation.js index 0f6ccf9a7b5..96727cbdc96 100644 --- a/lib/Compilation.js +++ b/lib/Compilation.js @@ -1488,7 +1488,11 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si for (let i = 0; i < dependencies.length; i++) { const dependency = dependencies[i]; - moduleGraph.setResolvedModule(originModule, dependency, module); + moduleGraph.setResolvedModule( + recursive ? originModule : null, + dependency, + module + ); } moduleGraph.setIssuerIfUnset( diff --git a/lib/optimize/ModuleConcatenationPlugin.js b/lib/optimize/ModuleConcatenationPlugin.js index a00d4098c23..e602119ff83 100644 --- a/lib/optimize/ModuleConcatenationPlugin.js +++ b/lib/optimize/ModuleConcatenationPlugin.js @@ -8,8 +8,6 @@ const asyncLib = require("neo-async"); const ChunkGraph = require("../ChunkGraph"); const ModuleGraph = require("../ModuleGraph"); -const ModuleRestoreError = require("../ModuleRestoreError"); -const ModuleStoreError = require("../ModuleStoreError"); const { STAGE_DEFAULT } = require("../OptimizationStages"); const HarmonyImportDependency = require("../dependencies/HarmonyImportDependency"); const { compareModulesByIdentifier } = require("../util/comparators"); @@ -58,441 +56,404 @@ class ModuleConcatenationPlugin { * @returns {void} */ apply(compiler) { - compiler.hooks.compilation.tap( - "ModuleConcatenationPlugin", - (compilation, { normalModuleFactory }) => { - const moduleGraph = compilation.moduleGraph; - const bailoutReasonMap = new Map(); - const cache = compilation.getCache("ModuleConcatenationPlugin"); - - const setBailoutReason = (module, reason) => { - setInnerBailoutReason(module, reason); - moduleGraph - .getOptimizationBailout(module) - .push( - typeof reason === "function" - ? rs => formatBailoutReason(reason(rs)) - : formatBailoutReason(reason) - ); - }; - - const setInnerBailoutReason = (module, reason) => { - bailoutReasonMap.set(module, reason); - }; + compiler.hooks.compilation.tap("ModuleConcatenationPlugin", compilation => { + const moduleGraph = compilation.moduleGraph; + const bailoutReasonMap = new Map(); + + const setBailoutReason = (module, reason) => { + setInnerBailoutReason(module, reason); + moduleGraph + .getOptimizationBailout(module) + .push( + typeof reason === "function" + ? rs => formatBailoutReason(reason(rs)) + : formatBailoutReason(reason) + ); + }; - const getInnerBailoutReason = (module, requestShortener) => { - const reason = bailoutReasonMap.get(module); - if (typeof reason === "function") return reason(requestShortener); - return reason; - }; + const setInnerBailoutReason = (module, reason) => { + bailoutReasonMap.set(module, reason); + }; - const formatBailoutWarning = (module, problem) => requestShortener => { - if (typeof problem === "function") { - return formatBailoutReason( - `Cannot concat with ${module.readableIdentifier( - requestShortener - )}: ${problem(requestShortener)}` - ); - } - const reason = getInnerBailoutReason(module, requestShortener); - const reasonWithPrefix = reason ? `: ${reason}` : ""; - if (module === problem) { - return formatBailoutReason( - `Cannot concat with ${module.readableIdentifier( - requestShortener - )}${reasonWithPrefix}` - ); - } else { - return formatBailoutReason( - `Cannot concat with ${module.readableIdentifier( - requestShortener - )} because of ${problem.readableIdentifier( - requestShortener - )}${reasonWithPrefix}` - ); - } - }; + const getInnerBailoutReason = (module, requestShortener) => { + const reason = bailoutReasonMap.get(module); + if (typeof reason === "function") return reason(requestShortener); + return reason; + }; - compilation.hooks.optimizeChunkModules.tapAsync( - { - name: "ModuleConcatenationPlugin", - stage: STAGE_DEFAULT - }, - (allChunks, modules, callback) => { - const logger = compilation.getLogger( - "webpack.ModuleConcatenationPlugin" - ); - const { chunkGraph, moduleGraph } = compilation; - const relevantModules = []; - const possibleInners = new Set(); - const context = { - chunkGraph, - moduleGraph - }; - logger.time("select relevant modules"); - for (const module of modules) { - let canBeRoot = true; - let canBeInner = true; - - const bailoutReason = module.getConcatenationBailoutReason( - context - ); - if (bailoutReason) { - setBailoutReason(module, bailoutReason); - continue; - } + const formatBailoutWarning = (module, problem) => requestShortener => { + if (typeof problem === "function") { + return formatBailoutReason( + `Cannot concat with ${module.readableIdentifier( + requestShortener + )}: ${problem(requestShortener)}` + ); + } + const reason = getInnerBailoutReason(module, requestShortener); + const reasonWithPrefix = reason ? `: ${reason}` : ""; + if (module === problem) { + return formatBailoutReason( + `Cannot concat with ${module.readableIdentifier( + requestShortener + )}${reasonWithPrefix}` + ); + } else { + return formatBailoutReason( + `Cannot concat with ${module.readableIdentifier( + requestShortener + )} because of ${problem.readableIdentifier( + requestShortener + )}${reasonWithPrefix}` + ); + } + }; - // Must not be an async module - if (moduleGraph.isAsync(module)) { - setBailoutReason(module, `Module is async`); - continue; - } + compilation.hooks.optimizeChunkModules.tapAsync( + { + name: "ModuleConcatenationPlugin", + stage: STAGE_DEFAULT + }, + (allChunks, modules, callback) => { + const logger = compilation.getLogger( + "webpack.ModuleConcatenationPlugin" + ); + const { chunkGraph, moduleGraph } = compilation; + const relevantModules = []; + const possibleInners = new Set(); + const context = { + chunkGraph, + moduleGraph + }; + logger.time("select relevant modules"); + for (const module of modules) { + let canBeRoot = true; + let canBeInner = true; + + const bailoutReason = module.getConcatenationBailoutReason(context); + if (bailoutReason) { + setBailoutReason(module, bailoutReason); + continue; + } - // Must be in strict mode - if (!module.buildInfo.strict) { - setBailoutReason(module, `Module is not in strict mode`); - continue; - } + // Must not be an async module + if (moduleGraph.isAsync(module)) { + setBailoutReason(module, `Module is async`); + continue; + } - // Module must be in any chunk (we don't want to do useless work) - if (chunkGraph.getNumberOfModuleChunks(module) === 0) { - setBailoutReason(module, "Module is not in any chunk"); - continue; - } + // Must be in strict mode + if (!module.buildInfo.strict) { + setBailoutReason(module, `Module is not in strict mode`); + continue; + } - // Exports must be known (and not dynamic) - const exportsInfo = moduleGraph.getExportsInfo(module); - const relevantExports = exportsInfo.getRelevantExports(undefined); - const unknownReexports = relevantExports.filter(exportInfo => { - return ( - exportInfo.isReexport() && !exportInfo.getTarget(moduleGraph) - ); - }); - if (unknownReexports.length > 0) { - setBailoutReason( - module, - `Reexports in this module do not have a static target (${Array.from( - unknownReexports, - exportInfo => - `${ - exportInfo.name || "other exports" - }: ${exportInfo.getUsedInfo()}` - ).join(", ")})` - ); - continue; - } + // Module must be in any chunk (we don't want to do useless work) + if (chunkGraph.getNumberOfModuleChunks(module) === 0) { + setBailoutReason(module, "Module is not in any chunk"); + continue; + } - // Root modules must have a static list of exports - const unknownProvidedExports = relevantExports.filter( - exportInfo => { - return exportInfo.provided !== true; - } + // Exports must be known (and not dynamic) + const exportsInfo = moduleGraph.getExportsInfo(module); + const relevantExports = exportsInfo.getRelevantExports(undefined); + const unknownReexports = relevantExports.filter(exportInfo => { + return ( + exportInfo.isReexport() && !exportInfo.getTarget(moduleGraph) ); - if (unknownProvidedExports.length > 0) { - setBailoutReason( - module, - `List of module exports is dynamic (${Array.from( - unknownProvidedExports, - exportInfo => - `${ - exportInfo.name || "other exports" - }: ${exportInfo.getProvidedInfo()} and ${exportInfo.getUsedInfo()}` - ).join(", ")})` - ); - canBeRoot = false; - } + }); + if (unknownReexports.length > 0) { + setBailoutReason( + module, + `Reexports in this module do not have a static target (${Array.from( + unknownReexports, + exportInfo => + `${ + exportInfo.name || "other exports" + }: ${exportInfo.getUsedInfo()}` + ).join(", ")})` + ); + continue; + } - // Module must not be an entry point - if (chunkGraph.isEntryModule(module)) { - setInnerBailoutReason(module, "Module is an entry point"); - canBeInner = false; + // Root modules must have a static list of exports + const unknownProvidedExports = relevantExports.filter( + exportInfo => { + return exportInfo.provided !== true; } + ); + if (unknownProvidedExports.length > 0) { + setBailoutReason( + module, + `List of module exports is dynamic (${Array.from( + unknownProvidedExports, + exportInfo => + `${ + exportInfo.name || "other exports" + }: ${exportInfo.getProvidedInfo()} and ${exportInfo.getUsedInfo()}` + ).join(", ")})` + ); + canBeRoot = false; + } - if (canBeRoot) relevantModules.push(module); - if (canBeInner) possibleInners.add(module); + // Module must not be an entry point + if (chunkGraph.isEntryModule(module)) { + setInnerBailoutReason(module, "Module is an entry point"); + canBeInner = false; } - logger.timeEnd("select relevant modules"); - logger.debug( - `${relevantModules.length} potential root modules, ${possibleInners.size} potential inner modules` + + if (canBeRoot) relevantModules.push(module); + if (canBeInner) possibleInners.add(module); + } + logger.timeEnd("select relevant modules"); + logger.debug( + `${relevantModules.length} potential root modules, ${possibleInners.size} potential inner modules` + ); + // sort by depth + // modules with lower depth are more likely suited as roots + // this improves performance, because modules already selected as inner are skipped + logger.time("sort relevant modules"); + relevantModules.sort((a, b) => { + return moduleGraph.getDepth(a) - moduleGraph.getDepth(b); + }); + logger.timeEnd("sort relevant modules"); + + /** @type {Statistics} */ + const stats = { + cached: 0, + alreadyInConfig: 0, + invalidModule: 0, + incorrectChunks: 0, + incorrectDependency: 0, + incorrectModuleDependency: 0, + incorrectChunksOfImporter: 0, + incorrectRuntimeCondition: 0, + importerFailed: 0, + added: 0 + }; + let statsCandidates = 0; + let statsSizeSum = 0; + let statsEmptyConfigurations = 0; + + logger.time("find modules to concatenate"); + const concatConfigurations = []; + const usedAsInner = new Set(); + for (const currentRoot of relevantModules) { + // when used by another configuration as inner: + // the other configuration is better and we can skip this one + // TODO reconsider that when it's only used in a different runtime + if (usedAsInner.has(currentRoot)) continue; + + let chunkRuntime = undefined; + for (const r of chunkGraph.getModuleRuntimes(currentRoot)) { + chunkRuntime = mergeRuntimeOwned(chunkRuntime, r); + } + const exportsInfo = moduleGraph.getExportsInfo(currentRoot); + const filteredRuntime = filterRuntime(chunkRuntime, r => + exportsInfo.isModuleUsed(r) + ); + const activeRuntime = + filteredRuntime === true + ? chunkRuntime + : filteredRuntime === false + ? undefined + : filteredRuntime; + + // create a configuration with the root + const currentConfiguration = new ConcatConfiguration( + currentRoot, + activeRuntime ); - // sort by depth - // modules with lower depth are more likely suited as roots - // this improves performance, because modules already selected as inner are skipped - logger.time("sort relevant modules"); - relevantModules.sort((a, b) => { - return moduleGraph.getDepth(a) - moduleGraph.getDepth(b); - }); - logger.timeEnd("sort relevant modules"); - - /** @type {Statistics} */ - const stats = { - cached: 0, - alreadyInConfig: 0, - invalidModule: 0, - incorrectChunks: 0, - incorrectDependency: 0, - incorrectModuleDependency: 0, - incorrectChunksOfImporter: 0, - incorrectRuntimeCondition: 0, - importerFailed: 0, - added: 0 - }; - let statsCandidates = 0; - let statsSizeSum = 0; - let statsEmptyConfigurations = 0; - - logger.time("find modules to concatenate"); - const concatConfigurations = []; - const usedAsInner = new Set(); - for (const currentRoot of relevantModules) { - // when used by another configuration as inner: - // the other configuration is better and we can skip this one - // TODO reconsider that when it's only used in a different runtime - if (usedAsInner.has(currentRoot)) continue; - - let chunkRuntime = undefined; - for (const r of chunkGraph.getModuleRuntimes(currentRoot)) { - chunkRuntime = mergeRuntimeOwned(chunkRuntime, r); - } - const exportsInfo = moduleGraph.getExportsInfo(currentRoot); - const filteredRuntime = filterRuntime(chunkRuntime, r => - exportsInfo.isModuleUsed(r) - ); - const activeRuntime = - filteredRuntime === true - ? chunkRuntime - : filteredRuntime === false - ? undefined - : filteredRuntime; - - // create a configuration with the root - const currentConfiguration = new ConcatConfiguration( - currentRoot, - activeRuntime - ); - // cache failures to add modules - const failureCache = new Map(); + // cache failures to add modules + const failureCache = new Map(); - // potential optional import candidates - /** @type {Set} */ - const candidates = new Set(); + // potential optional import candidates + /** @type {Set} */ + const candidates = new Set(); - // try to add all imports - for (const imp of this._getImports( - compilation, - currentRoot, - activeRuntime - )) { - candidates.add(imp); - } + // try to add all imports + for (const imp of this._getImports( + compilation, + currentRoot, + activeRuntime + )) { + candidates.add(imp); + } - for (const imp of candidates) { - const impCandidates = new Set(); - const problem = this._tryToAdd( - compilation, - currentConfiguration, - imp, - chunkRuntime, - activeRuntime, - possibleInners, - impCandidates, - failureCache, - chunkGraph, - true, - stats - ); - if (problem) { - failureCache.set(imp, problem); - currentConfiguration.addWarning(imp, problem); - } else { - for (const c of impCandidates) { - candidates.add(c); - } - } - } - statsCandidates += candidates.size; - if (!currentConfiguration.isEmpty()) { - const modules = currentConfiguration.getModules(); - statsSizeSum += modules.size; - concatConfigurations.push(currentConfiguration); - for (const module of modules) { - if (module !== currentConfiguration.rootModule) { - usedAsInner.add(module); - } - } + for (const imp of candidates) { + const impCandidates = new Set(); + const problem = this._tryToAdd( + compilation, + currentConfiguration, + imp, + chunkRuntime, + activeRuntime, + possibleInners, + impCandidates, + failureCache, + chunkGraph, + true, + stats + ); + if (problem) { + failureCache.set(imp, problem); + currentConfiguration.addWarning(imp, problem); } else { - statsEmptyConfigurations++; - const optimizationBailouts = moduleGraph.getOptimizationBailout( - currentRoot - ); - for (const warning of currentConfiguration.getWarningsSorted()) { - optimizationBailouts.push( - formatBailoutWarning(warning[0], warning[1]) - ); + for (const c of impCandidates) { + candidates.add(c); } } } - logger.timeEnd("find modules to concatenate"); - logger.debug( - `${ - concatConfigurations.length - } successful concat configurations (avg size: ${ - statsSizeSum / concatConfigurations.length - }), ${statsEmptyConfigurations} bailed out completely` - ); - logger.debug( - `${statsCandidates} candidates were considered for adding (${stats.cached} cached failure, ${stats.alreadyInConfig} already in config, ${stats.invalidModule} invalid module, ${stats.incorrectChunks} incorrect chunks, ${stats.incorrectDependency} incorrect dependency, ${stats.incorrectChunksOfImporter} incorrect chunks of importer, ${stats.incorrectModuleDependency} incorrect module dependency, ${stats.incorrectRuntimeCondition} incorrect runtime condition, ${stats.importerFailed} importer failed, ${stats.added} added)` - ); - // HACK: Sort configurations by length and start with the longest one - // to get the biggest groups possible. Used modules are marked with usedModules - // TODO: Allow to reuse existing configuration while trying to add dependencies. - // This would improve performance. O(n^2) -> O(n) - logger.time(`sort concat configurations`); - concatConfigurations.sort((a, b) => { - return b.modules.size - a.modules.size; - }); - logger.timeEnd(`sort concat configurations`); - const usedModules = new Set(); - - logger.time("create concatenated modules"); - asyncLib.each( - concatConfigurations, - (concatConfiguration, callback) => { - const rootModule = concatConfiguration.rootModule; - - // Avoid overlapping configurations - // TODO: remove this when todo above is fixed - if (usedModules.has(rootModule)) return callback(); - const modules = concatConfiguration.getModules(); - for (const m of modules) { - usedModules.add(m); + statsCandidates += candidates.size; + if (!currentConfiguration.isEmpty()) { + const modules = currentConfiguration.getModules(); + statsSizeSum += modules.size; + concatConfigurations.push(currentConfiguration); + for (const module of modules) { + if (module !== currentConfiguration.rootModule) { + usedAsInner.add(module); } - - // Create a new ConcatenatedModule - let newModule = ConcatenatedModule.create( - rootModule, - modules, - concatConfiguration.runtime, - compiler.root + } + } else { + statsEmptyConfigurations++; + const optimizationBailouts = moduleGraph.getOptimizationBailout( + currentRoot + ); + for (const warning of currentConfiguration.getWarningsSorted()) { + optimizationBailouts.push( + formatBailoutWarning(warning[0], warning[1]) ); + } + } + } + logger.timeEnd("find modules to concatenate"); + logger.debug( + `${ + concatConfigurations.length + } successful concat configurations (avg size: ${ + statsSizeSum / concatConfigurations.length + }), ${statsEmptyConfigurations} bailed out completely` + ); + logger.debug( + `${statsCandidates} candidates were considered for adding (${stats.cached} cached failure, ${stats.alreadyInConfig} already in config, ${stats.invalidModule} invalid module, ${stats.incorrectChunks} incorrect chunks, ${stats.incorrectDependency} incorrect dependency, ${stats.incorrectChunksOfImporter} incorrect chunks of importer, ${stats.incorrectModuleDependency} incorrect module dependency, ${stats.incorrectRuntimeCondition} incorrect runtime condition, ${stats.importerFailed} importer failed, ${stats.added} added)` + ); + // HACK: Sort configurations by length and start with the longest one + // to get the biggest groups possible. Used modules are marked with usedModules + // TODO: Allow to reuse existing configuration while trying to add dependencies. + // This would improve performance. O(n^2) -> O(n) + logger.time(`sort concat configurations`); + concatConfigurations.sort((a, b) => { + return b.modules.size - a.modules.size; + }); + logger.timeEnd(`sort concat configurations`); + const usedModules = new Set(); + + logger.time("create concatenated modules"); + asyncLib.each( + concatConfigurations, + (concatConfiguration, callback) => { + const rootModule = concatConfiguration.rootModule; + + // Avoid overlapping configurations + // TODO: remove this when todo above is fixed + if (usedModules.has(rootModule)) return callback(); + const modules = concatConfiguration.getModules(); + for (const m of modules) { + usedModules.add(m); + } - const cacheItem = cache.getItemCache( - newModule.identifier(), - null - ); + // Create a new ConcatenatedModule + let newModule = ConcatenatedModule.create( + rootModule, + modules, + concatConfiguration.runtime, + compiler.root + ); - const restore = () => { - cacheItem.get((err, cacheModule) => { + const build = () => { + newModule.build( + compiler.options, + compilation, + null, + null, + err => { if (err) { - return callback(new ModuleRestoreError(newModule, err)); - } - - if (cacheModule) { - cacheModule.updateCacheModule(newModule); - newModule = cacheModule; - } - - build(); - }); - }; - - const build = () => { - newModule.build( - compiler.options, - compilation, - null, - null, - err => { - if (err) { - if (!err.module) { - err.module = newModule; - } - return callback(err); + if (!err.module) { + err.module = newModule; } - integrateAndStore(); + return callback(err); } - ); - }; + integrate(); + } + ); + }; - const integrateAndStore = () => { - ChunkGraph.setChunkGraphForModule(newModule, chunkGraph); - ModuleGraph.setModuleGraphForModule(newModule, moduleGraph); + const integrate = () => { + ChunkGraph.setChunkGraphForModule(newModule, chunkGraph); + ModuleGraph.setModuleGraphForModule(newModule, moduleGraph); - for (const warning of concatConfiguration.getWarningsSorted()) { - moduleGraph - .getOptimizationBailout(newModule) - .push(formatBailoutWarning(warning[0], warning[1])); + for (const warning of concatConfiguration.getWarningsSorted()) { + moduleGraph + .getOptimizationBailout(newModule) + .push(formatBailoutWarning(warning[0], warning[1])); + } + moduleGraph.cloneModuleAttributes(rootModule, newModule); + for (const m of modules) { + // add to builtModules when one of the included modules was built + if (compilation.builtModules.has(m)) { + compilation.builtModules.add(newModule); } - moduleGraph.cloneModuleAttributes(rootModule, newModule); - for (const m of modules) { - // add to builtModules when one of the included modules was built - if (compilation.builtModules.has(m)) { - compilation.builtModules.add(newModule); - } - if (m !== rootModule) { - // attach external references to the concatenated module too - moduleGraph.copyOutgoingModuleConnections( - m, - newModule, - c => { - return ( - c.originModule === m && - !( - c.dependency instanceof HarmonyImportDependency && - modules.has(c.module) - ) - ); - } - ); - // remove module from chunk - for (const chunk of chunkGraph.getModuleChunksIterable( - rootModule - )) { - chunkGraph.disconnectChunkAndModule(chunk, m); + if (m !== rootModule) { + // attach external references to the concatenated module too + moduleGraph.copyOutgoingModuleConnections( + m, + newModule, + c => { + return ( + c.originModule === m && + !( + c.dependency instanceof HarmonyImportDependency && + modules.has(c.module) + ) + ); } + ); + // remove module from chunk + for (const chunk of chunkGraph.getModuleChunksIterable( + rootModule + )) { + chunkGraph.disconnectChunkAndModule(chunk, m); } } - compilation.modules.delete(rootModule); - // remove module from chunk - chunkGraph.replaceModule(rootModule, newModule); - // replace module references with the concatenated module - moduleGraph.moveModuleConnections( - rootModule, - newModule, - c => { - const otherModule = - c.module === rootModule ? c.originModule : c.module; - const innerConnection = - c.dependency instanceof HarmonyImportDependency && - modules.has(otherModule); - return !innerConnection; - } - ); - // add concatenated module to the compilation - compilation.modules.add(newModule); - - // TODO check if module needs build to avoid caching it without change - cacheItem.store(newModule, err => { - if (err) { - return callback(new ModuleStoreError(newModule, err)); - } - - callback(); - }); - }; - - restore(); - }, - err => { - logger.timeEnd("create concatenated modules"); - process.nextTick(() => callback(err)); - } - ); - } - ); - } - ); + } + compilation.modules.delete(rootModule); + // remove module from chunk + chunkGraph.replaceModule(rootModule, newModule); + // replace module references with the concatenated module + moduleGraph.moveModuleConnections(rootModule, newModule, c => { + const otherModule = + c.module === rootModule ? c.originModule : c.module; + const innerConnection = + c.dependency instanceof HarmonyImportDependency && + modules.has(otherModule); + return !innerConnection; + }); + // add concatenated module to the compilation + compilation.modules.add(newModule); + + callback(); + }; + + build(); + }, + err => { + logger.timeEnd("create concatenated modules"); + process.nextTick(() => callback(err)); + } + ); + } + ); + }); } /** diff --git a/lib/util/semver.js b/lib/util/semver.js index 8ce93fdcf29..4597d2bb523 100644 --- a/lib/util/semver.js +++ b/lib/util/semver.js @@ -215,11 +215,11 @@ exports.parseRange = str => { /* eslint-disable eqeqeq */ const rangeToString = range => { + var fixCount = range[0]; + var str = ""; if (range.length === 1) { return "*"; - } else if (0 in range) { - var str = ""; - var fixCount = range[0]; + } else if (fixCount + 0.5) { str += fixCount == 0 ? ">=" @@ -464,7 +464,7 @@ exports.versionLtRuntimeCode = runtimeTemplate => exports.rangeToStringRuntimeCode = runtimeTemplate => `var rangeToString = ${runtimeTemplate.basicFunction("range", [ "// see webpack/lib/util/semver.js for original code", - 'if(1===range.length)return"*";if(0 in range){var r="",n=range[0];r+=0==n?">=":-1==n?"<":1==n?"^":2==n?"~":n>0?"=":"!=";for(var e=1,a=1;a0?".":"")+(e=2,t)}return r}var g=[];for(a=1;a=":-1==r?"<":1==r?"^":2==r?"~":r>0?"=":"!=";for(var e=1,a=1;a0?".":"")+(e=2,t)}return n}var g=[];for(a=1;a { rimraf.sync(outputDirectory); fs.mkdirSync(outputDirectory, { recursive: true }); const deprecationTracker = deprecationTracking.start(); - webpack(options, err => { + webpack(options, (err, stats) => { deprecationTracker(); if (err) return handleFatalError(err, done); + const { modules, children, errorsCount } = stats.toJson({ + all: false, + modules: true, + errorsCount: true + }); + if (errorsCount === 0) { + const allModules = children + ? children.reduce( + (all, { modules }) => all.concat(modules), + modules || [] + ) + : modules; + if ( + allModules.some( + m => m.type !== "cached modules" && !m.cached + ) + ) { + return done( + new Error( + `Some modules were not cached:\n${stats.toString({ + all: false, + modules: true, + modulesSpace: 100 + })}` + ) + ); + } + } done(); }); }, 20000); diff --git a/test/configCases/rebuild/finishModules/loader.js b/test/configCases/rebuild/finishModules/loader.js index 4d3b0b09e50..80d125d3902 100644 --- a/test/configCases/rebuild/finishModules/loader.js +++ b/test/configCases/rebuild/finishModules/loader.js @@ -1,5 +1,7 @@ module.exports = function (source) { - if (this.shouldReplace) + if (this.shouldReplace) { + this._module.buildInfo._isReplaced = true; return "module.exports = { foo: { foo: 'bar' }, doThings: (v) => v}"; + } return source; }; diff --git a/test/configCases/rebuild/finishModules/webpack.config.js b/test/configCases/rebuild/finishModules/webpack.config.js index 685c6fc8e87..50a95eefe24 100644 --- a/test/configCases/rebuild/finishModules/webpack.config.js +++ b/test/configCases/rebuild/finishModules/webpack.config.js @@ -33,6 +33,10 @@ var testPlugin = compiler => { throw new Error("something went wrong"); } + // Check if already build the updated version + // this will happen when using caching + if (module.buildInfo._isReplaced) return callback(); + shouldReplace = true; compilation.rebuildModule(module, err => { shouldReplace = false; diff --git a/test/configCases/rebuild/rebuildWithNewDependencies/loader.js b/test/configCases/rebuild/rebuildWithNewDependencies/loader.js index e5c7b299f4e..fc39654810e 100644 --- a/test/configCases/rebuild/rebuildWithNewDependencies/loader.js +++ b/test/configCases/rebuild/rebuildWithNewDependencies/loader.js @@ -1,7 +1,9 @@ module.exports = function (source) { - if (this.shouldReplace) + if (this.shouldReplace) { + this._module.buildInfo._isReplaced = true; return `import otherFile from './other-file.js'; export default otherFile; `; + } return source; }; diff --git a/test/configCases/rebuild/rebuildWithNewDependencies/webpack.config.js b/test/configCases/rebuild/rebuildWithNewDependencies/webpack.config.js index 767d0068891..9a14a0baf4f 100644 --- a/test/configCases/rebuild/rebuildWithNewDependencies/webpack.config.js +++ b/test/configCases/rebuild/rebuildWithNewDependencies/webpack.config.js @@ -33,6 +33,10 @@ var testPlugin = compiler => { throw new Error("something went wrong"); } + // Check if already build the updated version + // this will happen when using caching + if (module.buildInfo._isReplaced) return callback(); + shouldReplace = true; compilation.rebuildModule(module, err => { shouldReplace = false;