diff --git a/lib/SizeFormatHelpers.js b/lib/SizeFormatHelpers.js index ca0971552..fceb3ae79 100644 --- a/lib/SizeFormatHelpers.js +++ b/lib/SizeFormatHelpers.js @@ -16,17 +16,3 @@ SizeFormatHelpers.formatSize = function(size) { .toPrecision(3) + " " + abbreviations[index]; } -SizeFormatHelpers.getEntrypointSize = function(entrypoint, compilation) { - var files = entrypoint.getFiles(); - - return files - .filter(function(asset) { - return !(/\.map$/.test(asset)) - }) - .map(function(file) { - return compilation.assets[file].size() - }) - .reduce(function(currentSize, nextSize) { - return currentSize + nextSize - }, 0); -} diff --git a/lib/Stats.js b/lib/Stats.js index 6b6142a29..af4656980 100644 --- a/lib/Stats.js +++ b/lib/Stats.js @@ -3,7 +3,7 @@ Author Tobias Koppers @sokra */ var RequestShortener = require("./RequestShortener"); -var formatSize = require("./SizeFormatHelpers").formatSize; +var SizeFormatHelpers = require("./SizeFormatHelpers"); function Stats(compilation) { this.compilation = compilation; @@ -501,7 +501,7 @@ Stats.jsonToString = function jsonToString(obj, useColors) { value: asset.name, color: getAssetColor(asset, colors.green) }, { - value: formatSize(asset.size), + value: SizeFormatHelpers.formatSize(asset.size), color: getAssetColor(asset, colors.normal) }, { value: asset.chunks.join(", "), @@ -548,7 +548,7 @@ Stats.jsonToString = function jsonToString(obj, useColors) { function processModuleAttributes(module) { colors.normal(" "); - colors.normal(formatSize(module.size)); + colors.normal(SizeFormatHelpers.formatSize(module.size)); if(module.chunks) { module.chunks.forEach(function(chunk) { colors.normal(" {"); @@ -658,7 +658,7 @@ Stats.jsonToString = function jsonToString(obj, useColors) { colors.normal(")"); } colors.normal(" "); - colors.normal(formatSize(chunk.size)); + colors.normal(SizeFormatHelpers.formatSize(chunk.size)); chunk.parents.forEach(function(id) { colors.normal(" {"); colors.yellow(id); diff --git a/lib/WebpackOptionsApply.js b/lib/WebpackOptionsApply.js index de31d11c9..816e0422d 100644 --- a/lib/WebpackOptionsApply.js +++ b/lib/WebpackOptionsApply.js @@ -42,7 +42,7 @@ var FlagIncludedChunksPlugin = require("./optimize/FlagIncludedChunksPlugin"); var OccurrenceOrderPlugin = require("./optimize/OccurrenceOrderPlugin"); var FlagDependencyUsagePlugin = require("./FlagDependencyUsagePlugin"); var FlagDependencyExportsPlugin = require("./FlagDependencyExportsPlugin"); -var EmittedAssetSizeLimitPlugin = require("./performance/EmittedAssetSizeLimitPlugin"); +var SizeLimitsPlugin = require("./performance/SizeLimitsPlugin"); var ResolverFactory = require("enhanced-resolve").ResolverFactory; @@ -267,7 +267,9 @@ WebpackOptionsApply.prototype.process = function(options, compiler) { new FlagDependencyUsagePlugin() ); - compiler.apply(new EmittedAssetSizeLimitPlugin(options.performance)); + if(options.performance) { + compiler.apply(new SizeLimitsPlugin(options.performance)); + } compiler.apply(new TemplatedPathPlugin()); diff --git a/lib/WebpackOptionsDefaulter.js b/lib/WebpackOptionsDefaulter.js index bb3299987..47793e992 100644 --- a/lib/WebpackOptionsDefaulter.js +++ b/lib/WebpackOptionsDefaulter.js @@ -70,11 +70,11 @@ function WebpackOptionsDefaulter() { this.set("node.__dirname", "mock"); this.set("performance.maxAssetSize", 250000); - this.set("performance.maxInitialChunkSize", 250000); + this.set("performance.maxEntrypointSize", 250000); this.set("performance.errorOnHint", false); this.set("performance.hints", "make", function(options) { - if(options.target === "web") - return true; + if(options.target === "web" || options.target === "webworker") + return "warning"; else return false; }); diff --git a/lib/performance/AssetsOverSizeLimitWarning.js b/lib/performance/AssetsOverSizeLimitWarning.js index 05d0de044..896575f76 100644 --- a/lib/performance/AssetsOverSizeLimitWarning.js +++ b/lib/performance/AssetsOverSizeLimitWarning.js @@ -2,7 +2,7 @@ MIT License http://www.opensource.org/licenses/mit-license.php Author Sean Larkin @thelarkinn */ -var SizeFormatHelpers = require('../SizeFormatHelpers'); +var SizeFormatHelpers = require("../SizeFormatHelpers"); function AssetsOverSizeLimitWarning(assetsOverSizeLimit, assetLimit) { Error.call(this); diff --git a/lib/performance/EmittedAssetSizeLimitPlugin.js b/lib/performance/EmittedAssetSizeLimitPlugin.js deleted file mode 100644 index c5d577b76..000000000 --- a/lib/performance/EmittedAssetSizeLimitPlugin.js +++ /dev/null @@ -1,106 +0,0 @@ -/* - MIT License http://www.opensource.org/licenses/mit-license.php - Author Sean Larkin @thelarkinn -*/ -var path = require('path'); - -var EntrypointsOverSizeLimitWarning = require('./EntrypointsOverSizeLimitWarning'); -var AssetsOverSizeLimitWarning = require('./AssetsOverSizeLimitWarning'); -var NoAsyncChunksWarning = require('./NoAsyncChunksWarning'); -var SizeFormatHelpers = require('../SizeFormatHelpers'); - -function EmittedAssetSizeLimitPlugin(performanceOptions) { - this.maxAssetSize = performanceOptions.maxAssetSize; - this.maxInitialSize = performanceOptions.maxInitialChunkSize; - this.hints = performanceOptions.hints; - this.errorOnHint = performanceOptions.errorOnHint; -} - -module.exports = EmittedAssetSizeLimitPlugin; - -// When using this we should always -// compare byte size and then format later -function doesExceedLimit(limit, actualSize) { - return limit < actualSize; -} - -EmittedAssetSizeLimitPlugin.prototype.apply = function(compiler) { - if(!this.hints) { - return; - } - var entrypointSizeLimit = this.maxInitialSize; - var sizeLimit = this.maxAssetSize; - var hints = this.hints; - var shouldErrorOnHint = this.errorOnHint; - - compiler.plugin("after-emit", function(compilation, callback) { - var warnings = []; - var assetsOverSizeLimit = []; - - Object.keys(compilation.assets) - .filter(function(asset) { - return !(/\.map$/.test(asset)) - }) - .forEach(function(asset) { - var obj = { - name: asset, - size: compilation.assets[asset].size() - }; - - if(doesExceedLimit(sizeLimit, obj.size)) { - obj.isOverSizeLimit = true; - assetsOverSizeLimit.push(obj); - compilation.assets[asset].isOverSizeLimit = true; - } - }); - - var hasAsyncChunks = compilation.chunks.filter(function(chunk) { - return !chunk.isInitial(); - }).length > 0; - - var entrypointsOverLimit = Object.keys(compilation.entrypoints) - .map(function(key) { - return compilation.entrypoints[key] - }) - .filter(function(entry) { - return doesExceedLimit(entrypointSizeLimit, SizeFormatHelpers.getEntrypointSize(entry, compilation)) - }); - - // 1. Individual Chunk: Size < 250kb - // 2. Collective Initial Chunks [entrypoint] (Each Set?): Size < 250kb - // 3. No Async Chunks - // if !1, then 2, if !2 return - if(assetsOverSizeLimit.length > 0) { - warnings.push( - new AssetsOverSizeLimitWarning( - assetsOverSizeLimit, - sizeLimit - ) - ); - } - if(entrypointsOverLimit.length > 0) { - warnings.push( - new EntrypointsOverSizeLimitWarning( - entrypointsOverLimit, - compilation, - entrypointSizeLimit - ) - ); - } - - if(warnings.length > 0) { - if(!hasAsyncChunks) { - warnings.push(new NoAsyncChunksWarning()); - } - - if(shouldErrorOnHint) { - Array.prototype.push.apply(compilation.errors, warnings); - } else { - Array.prototype.push.apply(compilation.warnings, warnings); - } - } - - callback(); - }); - -}; diff --git a/lib/performance/EntrypointsOverSizeLimitWarning.js b/lib/performance/EntrypointsOverSizeLimitWarning.js index d1b34a4c7..45ff0d8f7 100644 --- a/lib/performance/EntrypointsOverSizeLimitWarning.js +++ b/lib/performance/EntrypointsOverSizeLimitWarning.js @@ -2,23 +2,19 @@ MIT License http://www.opensource.org/licenses/mit-license.php Author Sean Larkin @thelarkinn */ -var SizeFormatHelpers = require('../SizeFormatHelpers'); +var SizeFormatHelpers = require("../SizeFormatHelpers"); -function EntrypointsOverSizeLimitWarning(entrypoints, compilation, entrypointLimit) { +function EntrypointsOverSizeLimitWarning(entrypoints, entrypointLimit) { Error.call(this); Error.captureStackTrace(this, EntrypointsOverSizeLimitWarning); this.name = "EntrypointsOverSizeLimitWarning"; this.entrypoints = entrypoints; - var entrypointCompilation = compilation; var entrypointList = this.entrypoints.map(function(entrypoint) { - return "\n " + entrypoint.name + " (" + SizeFormatHelpers.formatSize(SizeFormatHelpers.getEntrypointSize(entrypoint, entrypointCompilation)) + ")\n" + - entrypoint.getFiles() - .filter(function(asset) { - return !(/\.map$/.test(asset)) - }) - .map(function(filename, index) { - return " " + entrypoint.getFiles()[index] + "\n"; + return "\n " + entrypoint.name + " (" + SizeFormatHelpers.formatSize(entrypoint.size) + ")\n" + + entrypoint.files + .map(function(asset) { + return " " + asset + "\n"; }).join(""); }).join(""); diff --git a/lib/performance/SizeLimitsPlugin.js b/lib/performance/SizeLimitsPlugin.js new file mode 100644 index 000000000..9bd824c53 --- /dev/null +++ b/lib/performance/SizeLimitsPlugin.js @@ -0,0 +1,118 @@ +/* + MIT License http://www.opensource.org/licenses/mit-license.php + Author Sean Larkin @thelarkinn +*/ +var path = require("path"); + +var EntrypointsOverSizeLimitWarning = require("./EntrypointsOverSizeLimitWarning"); +var AssetsOverSizeLimitWarning = require("./AssetsOverSizeLimitWarning"); +var NoAsyncChunksWarning = require("./NoAsyncChunksWarning"); + +function SizeLimitsPlugin(options) { + this.hints = options.hints; + this.maxAssetSize = options.maxAssetSize; + this.maxEntrypointSize = options.maxEntrypointSize; + this.assetFilter = options.assetFilter; +} + +module.exports = SizeLimitsPlugin; + +SizeLimitsPlugin.prototype.apply = function(compiler) { + var entrypointSizeLimit = this.maxEntrypointSize; + var assetSizeLimit = this.maxAssetSize; + var hints = this.hints; + var assetFilter = this.assetFilter || function(asset) { + return !(/\.map$/.test(asset)) + }; + + compiler.plugin("after-emit", function(compilation, callback) { + var warnings = []; + + var getEntrypointSize = function(entrypoint) { + var files = entrypoint.getFiles(); + + return files + .filter(assetFilter) + .map(function(file) { + return compilation.assets[file].size() + }) + .reduce(function(currentSize, nextSize) { + return currentSize + nextSize + }, 0); + }; + + var assetsOverSizeLimit = []; + Object.keys(compilation.assets) + .filter(assetFilter) + .forEach(function(assetName) { + var asset = compilation.assets[assetName]; + var size = asset.size(); + + if(size > assetSizeLimit) { + assetsOverSizeLimit.push({ + name: assetName, + size: size + }); + asset.isOverSizeLimit = true; + } + }); + + var entrypointsOverLimit = []; + Object.keys(compilation.entrypoints) + .forEach(function(key) { + var entry = compilation.entrypoints[key]; + var size = getEntrypointSize(entry, compilation); + + if(size > entrypointSizeLimit) { + entrypointsOverLimit.push({ + name: key, + size: size, + files: entry.getFiles().filter(assetFilter) + }); + entry.isOverSizeLimit = true; + } + }); + + if(hints) { + // 1. Individual Chunk: Size < 250kb + // 2. Collective Initial Chunks [entrypoint] (Each Set?): Size < 250kb + // 3. No Async Chunks + // if !1, then 2, if !2 return + if(assetsOverSizeLimit.length > 0) { + warnings.push( + new AssetsOverSizeLimitWarning( + assetsOverSizeLimit, + assetSizeLimit + ) + ); + } + if(entrypointsOverLimit.length > 0) { + warnings.push( + new EntrypointsOverSizeLimitWarning( + entrypointsOverLimit, + entrypointSizeLimit + ) + ); + } + + if(warnings.length > 0) { + var hasAsyncChunks = compilation.chunks.filter(function(chunk) { + return !chunk.isInitial(); + }).length > 0; + + if(!hasAsyncChunks) { + warnings.push(new NoAsyncChunksWarning()); + } + + if(hints === "error") { + Array.prototype.push.apply(compilation.errors, warnings); + } else { + Array.prototype.push.apply(compilation.warnings, warnings); + } + } + } + + callback(); + }); + +}; diff --git a/schemas/webpackOptionsSchema.json b/schemas/webpackOptionsSchema.json index 44afc5e61..f1a7374e6 100644 --- a/schemas/webpackOptionsSchema.json +++ b/schemas/webpackOptionsSchema.json @@ -749,26 +749,39 @@ }, "performance": { "description": "Configuration for web performance recommendations.", - "additionalProperties": false, - "properties": { - "hints": { - "description": "Turn hints on or off", - "type": "boolean" + "anyOf": [ + { + "enum": [ + false + ] }, - "maxInitialChunkSize": { - "description": "Total size of all initial chunks (in bytes)", - "type": "number" - }, - "maxAssetSize": { - "description": "Filesize limit (in bytes) when exceeded, that webpack will provide performance hints", - "type": "number" - }, - "errorOnHint": { - "description": "Throw errors instead of warnings when performance hints are found", - "type": "boolean" + { + "additionalProperties": false, + "properties": { + "assetFilter": { + "description": "Filter function to select assets that are checked", + "instanceof": "Function" + }, + "hints": { + "description": "Sets the format of the hints: warnings, errors or nothing at all", + "enum": [ + false, + "warning", + "error" + ] + }, + "maxEntrypointSize": { + "description": "Total size of an entry point (in bytes)", + "type": "number" + }, + "maxAssetSize": { + "description": "Filesize limit (in bytes) when exceeded, that webpack will provide performance hints", + "type": "number" + } + }, + "type": "object" } - }, - "type": "object" + ] }, "plugins": { "description": "Add additional plugins to the compiler.", diff --git a/test/statsCases/performance-error/webpack.config.js b/test/statsCases/performance-error/webpack.config.js index 5ad2e8ec9..d4d3106be 100644 --- a/test/statsCases/performance-error/webpack.config.js +++ b/test/statsCases/performance-error/webpack.config.js @@ -5,6 +5,6 @@ module.exports = { hash: false }, performance: { - errorOnHint: true + hints: "error" } };