diff --git a/lib/BannerPlugin.js b/lib/BannerPlugin.js index 54b0ede73d7..322d854cb99 100644 --- a/lib/BannerPlugin.js +++ b/lib/BannerPlugin.js @@ -108,10 +108,9 @@ class BannerPlugin { const comment = compilation.getPath(banner(data), data); - compilation.assets[file] = new ConcatSource( - comment, - "\n", - compilation.assets[file] + compilation.updateAsset( + file, + old => new ConcatSource(comment, "\n", old) ); } } diff --git a/lib/Compilation.js b/lib/Compilation.js index a171a0d6101..2047c585d78 100644 --- a/lib/Compilation.js +++ b/lib/Compilation.js @@ -104,6 +104,21 @@ const buildChunkGraph = require("./buildChunkGraph"); * @property {string[]=} trace */ +/** + * @typedef {Object} AssetInfo + * @property {boolean=} immutable true, if the asset can be long term cached forever (contains a hash) + * @property {number=} size size in bytes, only set after asset has been emitted + * @property {boolean=} development true, when asset is only used for development and doesn't count towards user-facing assets + * @property {boolean=} hotModuleReplacement true, when asset ships data for updating an existing application (HMR) + */ + +/** + * @typedef {Object} Asset + * @property {string} name the filename of the asset + * @property {Source} source source of the asset + * @property {AssetInfo} info info about the asset + */ + /** * @param {Chunk} a first chunk to sort by id * @param {Chunk} b second chunk to sort by id @@ -446,6 +461,7 @@ class Compilation extends Tapable { this.entries = []; /** @private @type {{name: string, request: string, module: Module}[]} */ this._preparedEntrypoints = []; + /** @type {Map} */ this.entrypoints = new Map(); /** @type {Chunk[]} */ this.chunks = []; @@ -465,6 +481,8 @@ class Compilation extends Tapable { this.additionalChunkAssets = []; /** @type {CompilationAssets} */ this.assets = {}; + /** @type {Map} */ + this.assetsInfo = new Map(); /** @type {WebpackError[]} */ this.errors = []; /** @type {WebpackError[]} */ @@ -1233,6 +1251,7 @@ class Compilation extends Tapable { this.namedChunkGroups.clear(); this.additionalChunkAssets.length = 0; this.assets = {}; + this.assetsInfo.clear(); for (const module of this.modules) { module.unseal(); } @@ -1963,13 +1982,101 @@ class Compilation extends Tapable { this.hash = this.fullHash.substr(0, hashDigestLength); } + /** + * @param {string} file file name + * @param {Source} source asset source + * @param {AssetInfo} assetInfo extra asset information + * @returns {void} + */ + emitAsset(file, source, assetInfo = {}) { + if (this.assets[file]) { + if (this.assets[file] !== source) { + throw new Error( + `Conflict: Multiple assets emit to the same filename ${file}` + ); + } + const oldInfo = this.assetsInfo.get(file); + this.assetsInfo.set(file, Object.assign({}, oldInfo, assetInfo)); + return; + } + this.assets[file] = source; + this.assetsInfo.set(file, assetInfo); + } + + /** + * @param {string} file file name + * @param {Source | function(Source): Source} newSourceOrFunction new asset source or function converting old to new + * @param {AssetInfo | function(AssetInfo | undefined): AssetInfo} assetInfoUpdateOrFunction new asset info or function converting old to new + */ + updateAsset( + file, + newSourceOrFunction, + assetInfoUpdateOrFunction = undefined + ) { + if (!this.assets[file]) { + throw new Error( + `Called Compilation.updateAsset for not existing filename ${file}` + ); + } + if (typeof newSourceOrFunction === "function") { + this.assets[file] = newSourceOrFunction(this.assets[file]); + } else { + this.assets[file] = newSourceOrFunction; + } + if (assetInfoUpdateOrFunction !== undefined) { + const oldInfo = this.assetsInfo.get(file); + if (typeof assetInfoUpdateOrFunction === "function") { + this.assetsInfo.set(file, assetInfoUpdateOrFunction(oldInfo || {})); + } else { + this.assetsInfo.set( + file, + Object.assign({}, oldInfo, assetInfoUpdateOrFunction) + ); + } + } + } + + getAssets() { + /** @type {Asset[]} */ + const array = []; + for (const assetName of Object.keys(this.assets)) { + if (Object.prototype.hasOwnProperty.call(this.assets, assetName)) { + array.push({ + name: assetName, + source: this.assets[assetName], + info: this.assetsInfo.get(assetName) || {} + }); + } + } + return array; + } + + /** + * @param {string} name the name of the asset + * @returns {Asset | undefined} the asset or undefined when not found + */ + getAsset(name) { + if (!Object.prototype.hasOwnProperty.call(this.assets, name)) + return undefined; + return { + name, + source: this.assets[name], + info: this.assetsInfo.get(name) || {} + }; + } + createModuleAssets() { for (let i = 0; i < this.modules.length; i++) { const module = this.modules[i]; if (module.buildInfo.assets) { + const assetsInfo = module.buildInfo.assetsInfo; for (const assetName of Object.keys(module.buildInfo.assets)) { const fileName = this.getPath(assetName); - this.assets[fileName] = module.buildInfo.assets[assetName]; + this.emitAsset( + fileName, + module.buildInfo.assets[assetName], + assetsInfo ? assetsInfo.get(assetName) : undefined + ); this.hooks.moduleAsset.call(module, fileName); } } @@ -2003,7 +2110,12 @@ class Compilation extends Tapable { const cacheName = fileManifest.identifier; const usedHash = fileManifest.hash; filenameTemplate = fileManifest.filenameTemplate; - file = this.getPath(filenameTemplate, fileManifest.pathOptions); + const pathAndInfo = this.getPathWithInfo( + filenameTemplate, + fileManifest.pathOptions + ); + file = pathAndInfo.path; + const assetInfo = pathAndInfo.info; // check if the same filename was already written by another chunk const alreadyWritten = alreadyWrittenFiles.get(file); @@ -2051,12 +2163,7 @@ class Compilation extends Tapable { }; } } - if (this.assets[file] && this.assets[file] !== source) { - throw new Error( - `Conflict: Multiple assets emit to the same filename ${file}` - ); - } - this.assets[file] = source; + this.emitAsset(file, source, assetInfo); chunk.files.push(file); this.hooks.chunkAsset.call(chunk, file); alreadyWrittenFiles.set(file, { @@ -2084,6 +2191,17 @@ class Compilation extends Tapable { return this.mainTemplate.getAssetPath(filename, data); } + /** + * @param {string} filename used to get asset path with hash + * @param {TODO=} data // TODO: figure out this param type + * @returns {{ path: string, info: AssetInfo }} interpolated path and asset info + */ + getPathWithInfo(filename, data) { + data = data || {}; + data.hash = data.hash || this.hash; + return this.mainTemplate.getAssetPathWithInfo(filename, data); + } + /** * This function allows you to run another instance of webpack inside of webpack however as * a child with different settings and configurations (if desired) applied. It copies all hooks, plugins diff --git a/lib/Compiler.js b/lib/Compiler.js index fb00a577cb2..376aa285499 100644 --- a/lib/Compiler.js +++ b/lib/Compiler.js @@ -329,8 +329,8 @@ class Compiler extends Tapable { if (err) return callback(err); this.parentCompilation.children.push(compilation); - for (const name of Object.keys(compilation.assets)) { - this.parentCompilation.assets[name] = compilation.assets[name]; + for (const { name, source, info } of compilation.getAssets()) { + this.parentCompilation.emitAsset(name, source, info); } const entries = Array.from( @@ -356,9 +356,9 @@ class Compiler extends Tapable { if (err) return callback(err); asyncLib.forEachLimit( - compilation.assets, + compilation.getAssets(), 15, - (source, file, callback) => { + ({ name: file, source }, callback) => { let targetFile = file; const queryStringIdx = targetFile.indexOf("?"); if (queryStringIdx >= 0) { @@ -396,10 +396,18 @@ class Compiler extends Tapable { // if yes, we skip writing the file // as it's already there // (we assume one doesn't remove files while the Compiler is running) + + compilation.updateAsset(file, cacheEntry.sizeOnlySource, { + size: cacheEntry.sizeOnlySource.size() + }); + return callback(); } } + // TODO webpack 5: if info.immutable check if file already exists in output + // skip emitting if it's already there + // get the binary (Buffer) content from the Source /** @type {Buffer} */ let content; @@ -418,7 +426,9 @@ class Compiler extends Tapable { // This allows to GC all memory allocated by the Source // (expect when the Source is stored in any other cache) cacheEntry.sizeOnlySource = new SizeOnlySource(content.length); - compilation.assets[file] = cacheEntry.sizeOnlySource; + compilation.updateAsset(file, cacheEntry.sizeOnlySource, { + size: content.length + }); // Write the file to output file system this.outputFileSystem.writeFile(targetPath, content, err => { diff --git a/lib/HotModuleReplacementPlugin.js b/lib/HotModuleReplacementPlugin.js index 46acc677052..06423d3fe73 100644 --- a/lib/HotModuleReplacementPlugin.js +++ b/lib/HotModuleReplacementPlugin.js @@ -277,12 +277,19 @@ module.exports = class HotModuleReplacementPlugin { compilation.moduleTemplates.javascript, compilation.dependencyTemplates ); - const filename = compilation.getPath(hotUpdateChunkFilename, { + const { + path: filename, + info: assetInfo + } = compilation.getPathWithInfo(hotUpdateChunkFilename, { hash: records.hash, chunk: currentChunk }); compilation.additionalChunkAssets.push(filename); - compilation.assets[filename] = source; + compilation.emitAsset( + filename, + source, + Object.assign({ hotModuleReplacement: true }, assetInfo) + ); hotUpdateMainContent.c[chunkId] = true; currentChunk.files.push(filename); compilation.hooks.chunkAsset.call(currentChunk, filename); @@ -292,10 +299,17 @@ module.exports = class HotModuleReplacementPlugin { } } const source = new RawSource(JSON.stringify(hotUpdateMainContent)); - const filename = compilation.getPath(hotUpdateMainFilename, { + const { + path: filename, + info: assetInfo + } = compilation.getPathWithInfo(hotUpdateMainFilename, { hash: records.hash }); - compilation.assets[filename] = source; + compilation.emitAsset( + filename, + source, + Object.assign({ hotModuleReplacement: true }, assetInfo) + ); } ); diff --git a/lib/MainTemplate.js b/lib/MainTemplate.js index 9028823ed0d..5134106a83a 100644 --- a/lib/MainTemplate.js +++ b/lib/MainTemplate.js @@ -122,7 +122,7 @@ module.exports = class MainTemplate extends Tapable { "moduleExpression" ]), currentHash: new SyncWaterfallHook(["source", "requestedLength"]), - assetPath: new SyncWaterfallHook(["path", "options"]), + assetPath: new SyncWaterfallHook(["path", "options", "assetInfo"]), hash: new SyncHook(["hash"]), hashForChunk: new SyncHook(["hash", "chunk"]), globalHashPaths: new SyncWaterfallHook(["paths"]), @@ -521,6 +521,13 @@ module.exports = class MainTemplate extends Tapable { return this.hooks.assetPath.call(path, options); } + getAssetPathWithInfo(path, options) { + const assetInfo = {}; + // TODO webpack 5: refactor assetPath hook to receive { path, info } object + const newPath = this.hooks.assetPath.call(path, options, assetInfo); + return { path: newPath, info: assetInfo }; + } + /** * Updates hash with information from this template * @param {Hash} hash the hash to update diff --git a/lib/NormalModule.js b/lib/NormalModule.js index a6d9915378d..36a338636a7 100644 --- a/lib/NormalModule.js +++ b/lib/NormalModule.js @@ -210,15 +210,17 @@ class NormalModule extends Module { } }; }, - emitFile: (name, content, sourceMap) => { + emitFile: (name, content, sourceMap, assetInfo) => { if (!this.buildInfo.assets) { this.buildInfo.assets = Object.create(null); + this.buildInfo.assetsInfo = new Map(); } this.buildInfo.assets[name] = this.createSourceForAsset( name, content, sourceMap ); + this.buildInfo.assetsInfo.set(name, assetInfo); }, rootContext: options.context, webpack: true, @@ -432,7 +434,9 @@ class NormalModule extends Module { this.buildInfo = { cacheable: false, fileDependencies: new Set(), - contextDependencies: new Set() + contextDependencies: new Set(), + assets: undefined, + assetsInfo: undefined }; return this.doBuild(options, compilation, resolver, fs, err => { diff --git a/lib/SourceMapDevToolPlugin.js b/lib/SourceMapDevToolPlugin.js index 402c427eb25..8a59bcb1eee 100644 --- a/lib/SourceMapDevToolPlugin.js +++ b/lib/SourceMapDevToolPlugin.js @@ -175,14 +175,20 @@ class SourceMapDevToolPlugin { reportProgress(0.0); const tasks = []; files.forEach(({ file, chunk }, idx) => { - const asset = compilation.assets[file]; + const asset = compilation.getAsset(file).source; const cache = assetsCache.get(asset); /** * If presented in cache, reassigns assets. Cache assets already have source maps. */ if (cache && cache.file === file) { for (const cachedFile in cache.assets) { - compilation.assets[cachedFile] = cache.assets[cachedFile]; + if (cachedFile === file) { + compilation.updateAsset(cachedFile, cache.assets[cachedFile]); + } else { + compilation.emitAsset(cachedFile, cache.assets[cachedFile], { + development: true + }); + } /** * Add file to chunk, if not presented there */ @@ -353,20 +359,24 @@ class SourceMapDevToolPlugin { * Add source map url to compilation asset, if {@link currentSourceMappingURLComment} presented */ if (currentSourceMappingURLComment !== false) { - assets[file] = compilation.assets[file] = new ConcatSource( + const asset = new ConcatSource( new RawSource(source), compilation.getPath( currentSourceMappingURLComment, Object.assign({ url: sourceMapUrl }, pathParams) ) ); + assets[file] = asset; + compilation.updateAsset(file, asset); } /** * Add source map file to compilation assets and chunk files */ - assets[sourceMapFile] = compilation.assets[ - sourceMapFile - ] = new RawSource(sourceMapString); + const asset = new RawSource(sourceMapString); + assets[sourceMapFile] = asset; + compilation.emitAsset(sourceMapFile, asset, { + development: true + }); chunk.files.push(sourceMapFile); } else { if (currentSourceMappingURLComment === false) { @@ -377,7 +387,7 @@ class SourceMapDevToolPlugin { /** * Add source map as data url to asset */ - assets[file] = compilation.assets[file] = new ConcatSource( + const asset = new ConcatSource( new RawSource(source), currentSourceMappingURLComment .replace(/\[map\]/g, () => sourceMapString) @@ -390,6 +400,8 @@ class SourceMapDevToolPlugin { ).toString("base64")}` ) ); + assets[file] = asset; + compilation.updateAsset(file, asset); } }); reportProgress(1.0); diff --git a/lib/Stats.js b/lib/Stats.js index 40bfd9ba783..153e6f0e8af 100644 --- a/lib/Stats.js +++ b/lib/Stats.js @@ -404,26 +404,27 @@ class Stats { } if (showAssets) { const assetsByFile = {}; - const compilationAssets = Object.keys(compilation.assets).sort(); + const compilationAssets = compilation + .getAssets() + .sort((a, b) => (a.name < b.name ? -1 : 1)); obj.assetsByChunkName = {}; obj.assets = compilationAssets - .map(asset => { + .map(({ name, source, info }) => { const obj = { - name: asset, - size: compilation.assets[asset].size(), + name, + size: source.size(), chunks: [], chunkNames: [], + info, // TODO webpack 5: remove .emitted - emitted: - compilation.assets[asset].emitted || - compilation.emittedAssets.has(asset) + emitted: source.emitted || compilation.emittedAssets.has(name) }; if (showPerformance) { - obj.isOverSizeLimit = compilation.assets[asset].isOverSizeLimit; + obj.isOverSizeLimit = source.isOverSizeLimit; } - assetsByFile[asset] = obj; + assetsByFile[name] = obj; return obj; }) .filter(createAssetFilter()); @@ -1040,7 +1041,14 @@ class Stats { color: colors.bold }, { - value: asset.emitted ? "[emitted]" : "", + value: [ + asset.emitted && "[emitted]", + asset.info.immutable && "[immutable]", + asset.info.development && "[dev]", + asset.info.hotModuleReplacement && "[hmr]" + ] + .filter(Boolean) + .join(" "), color: colors.green }, { diff --git a/lib/TemplatedPathPlugin.js b/lib/TemplatedPathPlugin.js index 01ad9ed0d9e..d463f51e4f0 100644 --- a/lib/TemplatedPathPlugin.js +++ b/lib/TemplatedPathPlugin.js @@ -23,8 +23,9 @@ const REGEXP_HASH_FOR_TEST = new RegExp(REGEXP_HASH.source, "i"), REGEXP_CONTENTHASH_FOR_TEST = new RegExp(REGEXP_CONTENTHASH.source, "i"), REGEXP_NAME_FOR_TEST = new RegExp(REGEXP_NAME.source, "i"); -const withHashLength = (replacer, handlerFn) => { +const withHashLength = (replacer, handlerFn, assetInfo) => { const fn = (match, hashLength, ...args) => { + if (assetInfo) assetInfo.immutable = true; const length = hashLength && parseInt(hashLength, 10); if (length && handlerFn) { return handlerFn(length); @@ -59,7 +60,7 @@ const escapePathVariables = value => { : value; }; -const replacePathVariables = (path, data) => { +const replacePathVariables = (path, data, assetInfo) => { const chunk = data.chunk; const chunkId = chunk && chunk.id; const chunkName = chunk && (chunk.name || chunk.id); @@ -97,19 +98,23 @@ const replacePathVariables = (path, data) => { path .replace( REGEXP_HASH, - withHashLength(getReplacer(data.hash), data.hashWithLength) + withHashLength(getReplacer(data.hash), data.hashWithLength, assetInfo) ) .replace( REGEXP_CHUNKHASH, - withHashLength(getReplacer(chunkHash), chunkHashWithLength) + withHashLength(getReplacer(chunkHash), chunkHashWithLength, assetInfo) ) .replace( REGEXP_CONTENTHASH, - withHashLength(getReplacer(contentHash), contentHashWithLength) + withHashLength( + getReplacer(contentHash), + contentHashWithLength, + assetInfo + ) ) .replace( REGEXP_MODULEHASH, - withHashLength(getReplacer(moduleHash), moduleHashWithLength) + withHashLength(getReplacer(moduleHash), moduleHashWithLength, assetInfo) ) .replace(REGEXP_ID, getReplacer(chunkId)) .replace(REGEXP_MODULEID, getReplacer(moduleId)) diff --git a/lib/optimize/ConcatenatedModule.js b/lib/optimize/ConcatenatedModule.js index dd581325477..88933aa672c 100644 --- a/lib/optimize/ConcatenatedModule.js +++ b/lib/optimize/ConcatenatedModule.js @@ -375,6 +375,14 @@ class ConcatenatedModule extends Module { } Object.assign(this.buildInfo.assets, m.buildInfo.assets); } + if (m.buildInfo.assetsInfo) { + if (this.buildInfo.assetsInfo === undefined) { + this.buildInfo.assetsInfo = new Map(); + } + for (const [key, value] of m.buildInfo.assetsInfo) { + this.buildInfo.assetsInfo.set(key, value); + } + } } } this._identifier = this._createIdentifier(); diff --git a/lib/performance/SizeLimitsPlugin.js b/lib/performance/SizeLimitsPlugin.js index 93b43651fa6..5da62ab8a19 100644 --- a/lib/performance/SizeLimitsPlugin.js +++ b/lib/performance/SizeLimitsPlugin.js @@ -7,6 +7,9 @@ const EntrypointsOverSizeLimitWarning = require("./EntrypointsOverSizeLimitWarni const AssetsOverSizeLimitWarning = require("./AssetsOverSizeLimitWarning"); const NoAsyncChunksWarning = require("./NoAsyncChunksWarning"); +/** @typedef {import("../Compiler")} Compiler */ +/** @typedef {import("../Entrypoint")} Entrypoint */ + module.exports = class SizeLimitsPlugin { constructor(options) { this.hints = options.hints; @@ -14,54 +17,70 @@ module.exports = class SizeLimitsPlugin { this.maxEntrypointSize = options.maxEntrypointSize; this.assetFilter = options.assetFilter; } + + /** + * @param {Compiler} compiler webpack compiler + * @returns {void} + */ apply(compiler) { const entrypointSizeLimit = this.maxEntrypointSize; const assetSizeLimit = this.maxAssetSize; const hints = this.hints; - const assetFilter = this.assetFilter || (asset => !asset.endsWith(".map")); + const assetFilter = + this.assetFilter || ((name, source, info) => !info.development); compiler.hooks.afterEmit.tap("SizeLimitsPlugin", compilation => { const warnings = []; + /** + * @param {Entrypoint} entrypoint an entrypoint + * @returns {number} the size of the entrypoint + */ const getEntrypointSize = entrypoint => entrypoint.getFiles().reduce((currentSize, file) => { - if (assetFilter(file) && compilation.assets[file]) { - return currentSize + compilation.assets[file].size(); + const asset = compilation.getAsset(file); + if ( + assetFilter(asset.name, asset.source, asset.info) && + asset.source + ) { + return currentSize + (asset.info.size || asset.source.size()); } return currentSize; }, 0); const assetsOverSizeLimit = []; - for (const assetName of Object.keys(compilation.assets)) { - if (!assetFilter(assetName)) { + for (const { name, source, info } of compilation.getAssets()) { + if (!assetFilter(name, source, info) || !source) { continue; } - const asset = compilation.assets[assetName]; - const size = asset.size(); + const size = info.size || source.size(); if (size > assetSizeLimit) { assetsOverSizeLimit.push({ - name: assetName, - size: size + name, + size }); - asset.isOverSizeLimit = true; + /** @type {any} */ (source).isOverSizeLimit = true; } } + const fileFilter = name => { + const asset = compilation.getAsset(name); + return assetFilter(asset.name, asset.source, asset.info); + }; + const entrypointsOverLimit = []; - for (const pair of compilation.entrypoints) { - const name = pair[0]; - const entry = pair[1]; + for (const [name, entry] of compilation.entrypoints) { const size = getEntrypointSize(entry); if (size > entrypointSizeLimit) { entrypointsOverLimit.push({ name: name, size: size, - files: entry.getFiles().filter(assetFilter) + files: entry.getFiles().filter(fileFilter) }); - entry.isOverSizeLimit = true; + /** @type {any} */ (entry).isOverSizeLimit = true; } } diff --git a/test/Stats.unittest.js b/test/Stats.unittest.js index b8689b74959..4c98fcfb43b 100644 --- a/test/Stats.unittest.js +++ b/test/Stats.unittest.js @@ -115,7 +115,7 @@ describe("Stats", () => { const mockStats = new Stats({ errors: ["firstError"], warnings: [], - assets: [], + getAssets: () => [], entrypoints: new Map(), namedChunkGroups: new Map(), chunks: [], @@ -142,7 +142,7 @@ describe("Stats", () => { const mockStats = new Stats({ errors: [], warnings: [], - assets: [], + getAssets: () => [], entrypoints: new Map(), chunks: [], namedChunkGroups: new Map(), diff --git a/test/__snapshots__/StatsTestCases.test.js.snap b/test/__snapshots__/StatsTestCases.test.js.snap index 600b17f3098..0bb66c75f90 100644 --- a/test/__snapshots__/StatsTestCases.test.js.snap +++ b/test/__snapshots__/StatsTestCases.test.js.snap @@ -6,11 +6,11 @@ Child fitting: Hash: 73cc73495122bdbc0b52 Time: Xms Built at: Thu Jan 01 1970 00:00:00 GMT - Asset Size Chunks Chunk Names - 33966214360bbbb31383.js 1.94 KiB 2 [emitted] - 445d4c6a1d7381d6cb2c.js 1.94 KiB 3 [emitted] - 89433e8d9a08f7d757d9.js 11.2 KiB 1 [emitted] - d4b551c6319035df2898.js 1.05 KiB 0 [emitted] + Asset Size Chunks Chunk Names + 33966214360bbbb31383.js 1.94 KiB 2 [emitted] [immutable] + 445d4c6a1d7381d6cb2c.js 1.94 KiB 3 [emitted] [immutable] + 89433e8d9a08f7d757d9.js 11.2 KiB 1 [emitted] [immutable] + d4b551c6319035df2898.js 1.05 KiB 0 [emitted] [immutable] Entrypoint main = 33966214360bbbb31383.js 445d4c6a1d7381d6cb2c.js 89433e8d9a08f7d757d9.js chunk {0} d4b551c6319035df2898.js 916 bytes <{1}> <{2}> <{3}> > ./g [4] ./index.js 7:0-13 @@ -32,11 +32,11 @@ Child content-change: Hash: 73cc73495122bdbc0b52 Time: Xms Built at: Thu Jan 01 1970 00:00:00 GMT - Asset Size Chunks Chunk Names - 33966214360bbbb31383.js 1.94 KiB 2 [emitted] - 445d4c6a1d7381d6cb2c.js 1.94 KiB 3 [emitted] - 89433e8d9a08f7d757d9.js 11.2 KiB 1 [emitted] - d4b551c6319035df2898.js 1.05 KiB 0 [emitted] + Asset Size Chunks Chunk Names + 33966214360bbbb31383.js 1.94 KiB 2 [emitted] [immutable] + 445d4c6a1d7381d6cb2c.js 1.94 KiB 3 [emitted] [immutable] + 89433e8d9a08f7d757d9.js 11.2 KiB 1 [emitted] [immutable] + d4b551c6319035df2898.js 1.05 KiB 0 [emitted] [immutable] Entrypoint main = 33966214360bbbb31383.js 445d4c6a1d7381d6cb2c.js 89433e8d9a08f7d757d9.js chunk {0} d4b551c6319035df2898.js 916 bytes <{1}> <{2}> <{3}> > ./g [4] ./index.js 7:0-13 @@ -60,19 +60,19 @@ exports[`StatsTestCases should print correct stats for aggressive-splitting-on-d "Hash: 288ee5d5dfd32d7f1180 Time: Xms Built at: Thu Jan 01 1970 00:00:00 GMT - Asset Size Chunks Chunk Names -01a8254701931adbf278.js 1.01 KiB 9 [emitted] -07830cd8072d83cdc6ad.js 1.01 KiB 10 [emitted] -2736cf9d79233cd0a9b6.js 1.93 KiB 0 [emitted] -29de52df747b400f6177.js 1 KiB 1 [emitted] -41be79832883258c21e6.js 1.94 KiB 6 [emitted] -43c1ac24102c075ecb2d.js 1.94 KiB 3, 1 [emitted] -5bc7f208cd99a83b4e33.js 1.94 KiB 8 [emitted] -7f83e5c2f4e52435dd2c.js 1.96 KiB 2 [emitted] -ba9fedb7aa0c69201639.js 1.94 KiB 11 [emitted] -d40ae25f5e7ef09d2e24.js 1.94 KiB 7, 10 [emitted] -e5fb899955fa03a8053b.js 1.94 KiB 5 [emitted] -fee750e8c7671a0612b7.js 9.86 KiB 4 [emitted] main + Asset Size Chunks Chunk Names +01a8254701931adbf278.js 1.01 KiB 9 [emitted] [immutable] +07830cd8072d83cdc6ad.js 1.01 KiB 10 [emitted] [immutable] +2736cf9d79233cd0a9b6.js 1.93 KiB 0 [emitted] [immutable] +29de52df747b400f6177.js 1 KiB 1 [emitted] [immutable] +41be79832883258c21e6.js 1.94 KiB 6 [emitted] [immutable] +43c1ac24102c075ecb2d.js 1.94 KiB 3, 1 [emitted] [immutable] +5bc7f208cd99a83b4e33.js 1.94 KiB 8 [emitted] [immutable] +7f83e5c2f4e52435dd2c.js 1.96 KiB 2 [emitted] [immutable] +ba9fedb7aa0c69201639.js 1.94 KiB 11 [emitted] [immutable] +d40ae25f5e7ef09d2e24.js 1.94 KiB 7, 10 [emitted] [immutable] +e5fb899955fa03a8053b.js 1.94 KiB 5 [emitted] [immutable] +fee750e8c7671a0612b7.js 9.86 KiB 4 [emitted] [immutable] main Entrypoint main = fee750e8c7671a0612b7.js chunk {0} 2736cf9d79233cd0a9b6.js 1.76 KiB <{4}> ={1}= ={2}= ={3}= ={6}= ={10}= [recorded] aggressive splitted > ./b ./d ./e ./f ./g [11] ./index.js 5:0-44 @@ -650,9 +650,9 @@ Child Hash: 4f1817092830e01f8cf9 Time: Xms Built at: Thu Jan 01 1970 00:00:00 GMT - Asset Size Chunks Chunk Names - app.js 6.76 KiB 0 [emitted] app - vendor.aa94f0c872c214f6cb2e.js 619 bytes 1 [emitted] vendor + Asset Size Chunks Chunk Names + app.js 6.76 KiB 0 [emitted] app + vendor.aa94f0c872c214f6cb2e.js 619 bytes 1 [emitted] [immutable] vendor Entrypoint app = vendor.aa94f0c872c214f6cb2e.js app.js [./constants.js] 87 bytes {1} [built] [./entry-1.js] ./entry-1.js + 2 modules 190 bytes {0} [built] @@ -663,9 +663,9 @@ Child Hash: a25845a78602d62320c1 Time: Xms Built at: Thu Jan 01 1970 00:00:00 GMT - Asset Size Chunks Chunk Names - app.js 6.78 KiB 0 [emitted] app - vendor.aa94f0c872c214f6cb2e.js 619 bytes 1 [emitted] vendor + Asset Size Chunks Chunk Names + app.js 6.78 KiB 0 [emitted] app + vendor.aa94f0c872c214f6cb2e.js 619 bytes 1 [emitted] [immutable] vendor Entrypoint app = vendor.aa94f0c872c214f6cb2e.js app.js [./constants.js] 87 bytes {1} [built] [./entry-2.js] ./entry-2.js + 2 modules 197 bytes {0} [built] @@ -1071,6 +1071,12 @@ chunk {5} y.js (y) 0 bytes <{3}> <{4}> [rendered] import() ./module-y [0] ./module-x.js 1:0-47" `; +exports[`StatsTestCases should print correct stats for immutable 1`] = ` +" Asset Size Chunks Chunk Names +0.430ed4d8dc4c398f3e47.js 292 bytes 0 [emitted] [immutable] + 9e9e93814cb2524148a0.js 8.44 KiB main [emitted] [immutable] main" +`; + exports[`StatsTestCases should print correct stats for import-context-filter 1`] = ` "Hash: faf093b90d98cd894c1a Time: Xms @@ -1129,21 +1135,21 @@ Child Hash: c59def46138141fa7a14 Time: Xms Built at: Thu Jan 01 1970 00:00:00 GMT - Asset Size Chunks Chunk Names - a-all~main-0034bb84916bcade4cc7.js 154 bytes all~main [emitted] all~main - a-main-14ee9c594789bd77b887.js 108 bytes main [emitted] main - a-runtime~main-99691078705b39185f99.js 6.12 KiB runtime~main [emitted] runtime~main + Asset Size Chunks Chunk Names + a-all~main-0034bb84916bcade4cc7.js 154 bytes all~main [emitted] [immutable] all~main + a-main-14ee9c594789bd77b887.js 108 bytes main [emitted] [immutable] main + a-runtime~main-99691078705b39185f99.js 6.12 KiB runtime~main [emitted] [immutable] runtime~main Entrypoint main = a-runtime~main-99691078705b39185f99.js a-all~main-0034bb84916bcade4cc7.js a-main-14ee9c594789bd77b887.js [0] ./a.js 18 bytes {all~main} [built] Child Hash: 5d8be42dd7b646f5c59e Time: Xms Built at: Thu Jan 01 1970 00:00:00 GMT - Asset Size Chunks Chunk Names - b-all~main-3f0b62a9e243706ccaf8.js 468 bytes all~main [emitted] all~main - b-main-09f4ddfc4098d7f3f188.js 123 bytes main [emitted] main - b-runtime~main-99691078705b39185f99.js 6.12 KiB runtime~main [emitted] runtime~main - b-vendors~main-f7664221ad5d986cf06a.js 163 bytes vendors~main [emitted] vendors~main + Asset Size Chunks Chunk Names + b-all~main-3f0b62a9e243706ccaf8.js 468 bytes all~main [emitted] [immutable] all~main + b-main-09f4ddfc4098d7f3f188.js 123 bytes main [emitted] [immutable] main + b-runtime~main-99691078705b39185f99.js 6.12 KiB runtime~main [emitted] [immutable] runtime~main + b-vendors~main-f7664221ad5d986cf06a.js 163 bytes vendors~main [emitted] [immutable] vendors~main Entrypoint main = b-runtime~main-99691078705b39185f99.js b-vendors~main-f7664221ad5d986cf06a.js b-all~main-3f0b62a9e243706ccaf8.js b-main-09f4ddfc4098d7f3f188.js [0] ./node_modules/vendor.js 23 bytes {vendors~main} [built] [1] ./b.js 17 bytes {all~main} [built] @@ -1151,12 +1157,12 @@ Child Hash: 0937f17cc4cb6ec81b29 Time: Xms Built at: Thu Jan 01 1970 00:00:00 GMT - Asset Size Chunks Chunk Names - c-0-5b8bdddff2dcbbac44bf.js 450 bytes 0 [emitted] - c-1-5eacbd7fee2224716029.js 153 bytes 1 [emitted] - c-all~main-3de9f206741c28715d19.js 305 bytes all~main [emitted] all~main - c-main-75156155081cda3092db.js 114 bytes main [emitted] main - c-runtime~main-e6fdf542ac2732af2e78.js 9.81 KiB runtime~main [emitted] runtime~main + Asset Size Chunks Chunk Names + c-0-5b8bdddff2dcbbac44bf.js 450 bytes 0 [emitted] [immutable] + c-1-5eacbd7fee2224716029.js 153 bytes 1 [emitted] [immutable] + c-all~main-3de9f206741c28715d19.js 305 bytes all~main [emitted] [immutable] all~main + c-main-75156155081cda3092db.js 114 bytes main [emitted] [immutable] main + c-runtime~main-e6fdf542ac2732af2e78.js 9.81 KiB runtime~main [emitted] [immutable] runtime~main Entrypoint main = c-runtime~main-e6fdf542ac2732af2e78.js c-all~main-3de9f206741c28715d19.js c-main-75156155081cda3092db.js (prefetch: c-1-5eacbd7fee2224716029.js c-0-5b8bdddff2dcbbac44bf.js) [0] ./b.js 17 bytes {0} [built] [1] ./c.js 61 bytes {all~main} [built] @@ -2080,15 +2086,15 @@ Entrypoints: exports[`StatsTestCases should print correct stats for preset-normal-performance-ensure-filter-sourcemaps 1`] = ` "Time: Xms Built at: Thu Jan 01 1970 00:00:00 GMT - Asset Size Chunks Chunk Names - 1.js 262 bytes 1 [emitted] - 1.js.map 216 bytes 1 [emitted] - 2.js 182 bytes 2 [emitted] - 2.js.map 156 bytes 2 [emitted] - 3.js 319 bytes 3 [emitted] - 3.js.map 210 bytes 3 [emitted] - main.js 301 KiB 0 [emitted] [big] main -main.js.map 1.72 MiB 0 [emitted] main + Asset Size Chunks Chunk Names + 1.js 262 bytes 1 [emitted] + 1.js.map 216 bytes 1 [emitted] [dev] + 2.js 182 bytes 2 [emitted] + 2.js.map 156 bytes 2 [emitted] [dev] + 3.js 319 bytes 3 [emitted] + 3.js.map 210 bytes 3 [emitted] [dev] + main.js 301 KiB 0 [emitted] [big] main +main.js.map 1.72 MiB 0 [emitted] [dev] main Entrypoint main [big] = main.js main.js.map [0] ./index.js 52 bytes {0} [built] [1] ./a.js 293 KiB {0} [built] diff --git a/test/statsCases/immutable/chunk.js b/test/statsCases/immutable/chunk.js new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/statsCases/immutable/index.js b/test/statsCases/immutable/index.js new file mode 100644 index 00000000000..d7401296bb3 --- /dev/null +++ b/test/statsCases/immutable/index.js @@ -0,0 +1 @@ +import("./chunk"); diff --git a/test/statsCases/immutable/webpack.config.js b/test/statsCases/immutable/webpack.config.js new file mode 100644 index 00000000000..99361f45e3b --- /dev/null +++ b/test/statsCases/immutable/webpack.config.js @@ -0,0 +1,11 @@ +module.exports = { + mode: "development", + entry: "./index.js", + output: { + filename: "[contenthash].js" + }, + stats: { + all: false, + assets: true + } +};