diff --git a/lib/performance/EmittedAssetSizeLimitPlugin.js b/lib/performance/EmittedAssetSizeLimitPlugin.js index 36e85b4a8..e5fd6288b 100644 --- a/lib/performance/EmittedAssetSizeLimitPlugin.js +++ b/lib/performance/EmittedAssetSizeLimitPlugin.js @@ -12,35 +12,51 @@ function EmittedAssetSizeLimitPlugin(performanceOptions) { module.exports = EmittedAssetSizeLimitPlugin; -function normalizeAndCompare(sizeLimit, assetSize) { - // sizeLimit=maxAssetSize is always expressed in kB - // assetSize is expressed in byte size - sizeLimit *= 1024; +function formatSize(size) { + if(size <= 0) return "0 bytes"; + + var abbreviations = ["bytes", "kB", "MB", "GB"]; + var index = Math.floor(Math.log(size) / Math.log(1000)); + var numberFormat = +(size / Math.pow(1000, index)).toPrecision(3); + + return { + number: numberFormat, + string: numberFormat + " " + abbreviations[index] + }; +} + +function doesExceed(sizeLimit, assetSize) { return sizeLimit < assetSize; } -function doesExceedInitialLimit(initialLimit, actualInitialSize) { - - initialLimit *= 1024; +function doesExceedEntrypointLimit(initialLimit, actualInitialSize) { return initialLimit < actualInitialSize; } -function getJSWarnings(noOfAssets, sizeLimit, assetSize) { - var warnings = []; - - if(normalizeAndCompare(sizeLimit, assetSize)) { - if(noOfAssets === 1) { - warnings.push(new Error("EmmittedAssetSizeWarning: ChunkSizeExceeded" + "This asset exceeds " + sizeLimit + "kB. \nConsider reducing the size for optimal web performance.")); - } else { - warnings.push(new Error("EmmittedAssetSizeWarning: ChunkSizeExceeded" + "Highlighted chunks are large and are likely to impact web performance. \nConsider keeping total chunks of page < " + sizeLimit + "kB")); - } - } - - return warnings; +function isAssetJsFile(assetFilename) { + var jsRegex = /\.js($|\?)/i; + + return jsRegex.test(assetFilename); } -EmittedAssetSizeLimitPlugin.prototype.apply = function(compiler) { +function isOverLimit(assetOrEntrypoint) { +} +// function getJSWarnings(noOfAssets, sizeLimit, assetSize) { +// var warnings = []; + +// if(normalizeAndCompare(sizeLimit, assetSize)) { +// if(noOfAssets === 1) { +// warnings.push(new Error("EmmittedAssetSizeWarning: ChunkSizeExceeded" + "This asset exceeds " + sizeLimit + "kB. \nConsider reducing the size for optimal web performance.")); +// } else { +// warnings.push(new Error("EmmittedAssetSizeWarning: ChunkSizeExceeded" + "Highlighted chunks are large and are likely to impact web performance. \nConsider keeping total chunks of page < " + sizeLimit + "kB")); +// } +// } + +// return warnings; +// } + +EmittedAssetSizeLimitPlugin.prototype.apply = function(compiler) { if(!this.hints) { return; } @@ -48,50 +64,120 @@ EmittedAssetSizeLimitPlugin.prototype.apply = function(compiler) { var totalInitialChunkSize = this.maxInitialSize; var sizeLimit = this.maxAssetSize; var hints = this.hints; - var jsRegex = /\.js($|\?)/i; compiler.plugin("after-emit", function(compilation, callback) { - var assets = Object.keys(compilation.assets); - var noOfAssets = assets.length; var warnings = []; - var actualTotalInitialSize = 0; - var hasAsyncChunks = compilation.chunks.filter(function(chunk) { - return chunk.isAsync(); - }).length > 0; + var assetsByFile = {}; + var assetsByChunkName = {}; + var assets = Object.keys(compilation.assets).map(function(asset) { + var obj = { + name: asset, + size: formatSize(compilation.assets[asset].size()), + chunks: [], + chunkNames: [], + emitted: compilation.assets[asset].emitted + }; + + obj.isOverSizeLimit = obj.size.number > sizeLimit; - assets.forEach(function(file) { - var assetSize = compilation.assets[file].size(); - var assetsByChunks = compilation.getAssetsByChunks().chunks; - - for(var chunkKey in assetsByChunks) { - var chunk = assetsByChunks[chunkKey]; - - actualTotalInitialSize += chunk.size; - } - - warnings = jsRegex.test(file) && getJSWarnings(noOfAssets, sizeLimit, assetSize); + assetsByFile[asset] = obj; + return obj; + }).filter(function(asset) { + return asset.emitted; }); - if(doesExceedInitialLimit(totalInitialChunkSize, actualTotalInitialSize)) { - //TODO# Maybe separate warning name - warnings.push( - new Error( - "EmittedAssetSizeWarning: TotalSizeExceeded " + - "The total initial download cost for these assets are likey to impact web performance. \nConsider keeping the total size of your initial assets < " + - totalInitialChunkSize + - "kB") - ); - } + compilation.chunks.forEach(function(chunk) { + chunk.files.forEach(function(asset) { + if (assetsByFile[asset]) { + chunk.ids.forEach(function(id) { + assetsByFile[asset].chunks.push(id); + }); + if (chunk.name) { + assetsByFile[asset].chunkNames.push(chunk.name); + if (assetsByChunkName[chunk.name]) { + assetsByChunkName[chunk.name] = [].concat(assetsByChunkName[chunk.name]).concat([asset]); + } else { + assetsByChunkName[chunk.name] = asset; + } + } + } + }); + }); - if (!hasAsyncChunks) { - warnings.push(new Error("EmittedAssetSizeWarning: NoAsyncChunks: " + - "You can limit the size of your bundles by using System.import() or require.ensure to lazy load some parts of your application after the page has loaded.") - ); - } + var entrypoints = Object.keys(compilation.entrypoints).map(function(ep) { + var files = []; + var entrypoint = compilation.entrypoints[ep]; + var hasAsyncChunks = compilation.chunks.filter(function(chunk) { + return chunk.isAsync(); + }).length > 0; + + entrypoint.assets = {}; - if(warnings.length > 0) { + // TODO: Need to use keys or even better Set for distinct values + entrypoint.chunks.forEach(function(chunk){ + chunk.files.forEach(function(file){ + files.push(file); + }); + }); + + files.forEach(function(file){ + console.log(file, entrypoint); + entrypoint.assets[file] = assetsByFile[file]; + entrypoint.isOverSizeLimit = false; + if (entrypoint.assets[file].isOverSizeLimit) { + entrypoint.isOverSizeLimit = true; + } + console.log(assetsByFile[file]); + }); + }); + + if(!hasAsyncChunks) { + warnings.push(new Error("EmittedAssetSizeWarning: NoAsyncChunks: " + + "You can limit the size of your bundles by using System.import() or require.ensure to lazy load some parts of your application after the page has loaded.")); + } + + if (!!warnings.length) { Array.prototype.push.apply(compilation.warnings, warnings); } + + // 1. Individual Chunk: Size < 200kb + // 2. Collective Initial Chunks (Each Set?): Size < 200kb + // 3. No Async Chunks + // if !1, then 2, if !2 return + // if 1, then 2, then 3, + + // assets.forEach(function(file) { + // var assetSize = compilation.assets[file].size(); + // var assetsByChunks = compilation.getAssetsByChunks().chunks; + + // for(var chunkKey in assetsByChunks) { + // var chunk = assetsByChunks[chunkKey]; + + // actualTotalInitialSize += chunk.size; + // } + + // warnings = jsRegex.test(file) && getJSWarnings(noOfAssets, sizeLimit, assetSize); + // }); + + // if(doesExceedInitialLimit(totalInitialChunkSize, actualTotalInitialSize)) { + // //TODO# Maybe separate warning name + // warnings.push( + // new Error( + // "EmittedAssetSizeWarning: TotalSizeExceeded " + + // "The total initial download cost for these assets are likey to impact web performance. \nConsider keeping the total size of your initial assets < " + + // totalInitialChunkSize + + // "kB") + // ); + // } + + // if(!hasAsyncChunks) { + // warnings.push(new Error("EmittedAssetSizeWarning: NoAsyncChunks: " + + // "You can limit the size of your bundles by using System.import() or require.ensure to lazy load some parts of your application after the page has loaded.")); + // } + + // if(warnings.length > 0) { + // Array.prototype.push.apply(compilation.warnings, warnings); + // } callback(); });