diff --git a/lib/bundle/bundler.js b/lib/bundle/bundler.js index ff265ab..51a0779 100644 --- a/lib/bundle/bundler.js +++ b/lib/bundle/bundler.js @@ -1,11 +1,25 @@ +let rollupAnalyzer = require("rollup-plugin-analyzer").plugin; let rollup = require("rollup"); module.exports = function generateBundle(entryPoint, target, config, cache) { - let { readConfig, writeConfig } = config; + let { readConfig, writeConfig, reporting } = config; let options = Object.assign({}, readConfig, { input: entryPoint, cache }); + + if(reporting) { + options.plugins.push(rollupAnalyzer({ + root: reporting.referenceDir, + // TODO: support for options: `limit`, `filter`, `showExports`, `hideDeps` + onAnalysis: res => void reporting.report({ + size: res.bundleSize, + originalSize: res.bundleOrigSize, + reduction: res.bundleReduction + }) + })); + } + return rollup.rollup(options). then(bundle => { let modules = bundle.modules.reduce(collectModulePaths, new Set()); diff --git a/lib/bundle/index.js b/lib/bundle/index.js index 989c1f6..24f91b0 100644 --- a/lib/bundle/index.js +++ b/lib/bundle/index.js @@ -4,22 +4,49 @@ let generateBundle = require("./bundler"); let generateConfig = require("./config"); let { generateError } = require("../util"); let SerializedRunner = require("faucet-pipeline/lib/util/runner"); +let { repr } = require("faucet-pipeline/lib/util"); +let path = require("path"); let DEFAULTS = { - format: "iife" + config: { + format: "iife" + }, + reporting: { + threshold: 100000 * 1024 // ~100 kB + } }; module.exports = class Bundle { - constructor(entryPoint, target, config, { browsers, resolvePath }) { + constructor(entryPoint, target, config, { browsers, referenceDir, resolvePath }) { this.entryPoint = entryPoint; this.target = target; - config = Object.assign({}, DEFAULTS, config); + config = Object.assign({}, DEFAULTS.config, config); if(config.fingerprint !== undefined) { this.fingerprint = config.fingerprint; delete config.fingerprint; } this._config = generateConfig(config, { browsers, resolvePath }); + + let { reporting } = config; + if(reporting !== false) { + let { threshold } = Object.assign({}, DEFAULTS.reporting, reporting); + let bundle = repr(path.relative(referenceDir, target), false); + this._config.reporting = { + referenceDir, + report: ({ size, originalSize, reduction }) => { + let b2kb = i => `${Math.round(i / 1024)} kB`; + console.error(`${bundle}: ${b2kb(originalSize)} → ` + + `${b2kb(size)} (Δ ${Math.round(reduction)} %)`); + if(size > threshold) { + console.error("⚠️ this bundle looks to be fairly big - " + + "you might want to double-check whether " + + "that's intended and consider performance " + + "implications for your users: " + bundle); + } + } + }; + } } // recompiles the bundle if its dependency graph includes any of the given files diff --git a/lib/index.js b/lib/index.js index 9419787..1d10973 100644 --- a/lib/index.js +++ b/lib/index.js @@ -2,13 +2,12 @@ let Bundle = require("./bundle"); let { abort, repr } = require("faucet-pipeline/lib/util"); -let path = require("path"); module.exports = (config, assetManager, { watcher, browsers, compact } = {}) => { let bundles = config.map(bundleConfig => { // NB: bundle-specific configuration can override global options bundleConfig = Object.assign({ compact }, bundleConfig); - return makeBundle(bundleConfig, assetManager.resolvePath, { browsers }); + return makeBundle(bundleConfig, assetManager, { browsers }); }); let res = bundles.map(bundle => { @@ -38,13 +37,6 @@ module.exports = (config, assetManager, { watcher, browsers, compact } = {}) => function makeWriter(bundle, assetManager, { fingerprint } = {}) { return ({ code, error }) => { - if(code.length > 100000) { // ~100 kB -- XXX: arbitrary -- TODO: configurable - console.error("⚠️ this bundle looks to be fairly big, you might " + - "want to double-check whether that's intended and " + - "consider performance implications for your users:\n " + - path.relative(assetManager.referenceDir, bundle.target)); - } - let options = { error }; if(fingerprint !== undefined) { options.fingerprint = fingerprint; @@ -53,7 +45,7 @@ function makeWriter(bundle, assetManager, { fingerprint } = {}) { }; } -function makeBundle(config, resolvePath, { browsers }) { +function makeBundle(config, { referenceDir, resolvePath }, { browsers }) { // dissect configuration for constructor config = Object.assign({}, config); let [entryPoint, target] = extract(config, "source", "target"); @@ -64,7 +56,8 @@ function makeBundle(config, resolvePath, { browsers }) { entryPoint = resolvePath(entryPoint); target = resolvePath(target, { enforceRelative: true }); - return new Bundle(entryPoint, target, config, { browsers, resolvePath }); + return new Bundle(entryPoint, target, config, + { browsers, referenceDir, resolvePath }); } // removes properties from object, returning their respective values diff --git a/package.json b/package.json index 8f73620..50eaa44 100644 --- a/package.json +++ b/package.json @@ -28,12 +28,13 @@ "dependencies": { "faucet-pipeline": "~1.0.0-rc.7", "rollup": "~0.62.0", + "rollup-plugin-analyzer": "^2.1.0", "rollup-plugin-cleanup": "~3.0.0", "rollup-plugin-commonjs": "~9.1.3", "rollup-plugin-node-resolve": "~3.3.0" }, "devDependencies": { - "eslint-config-fnd-jsx": "^1.4.0", + "eslint-config-fnd-jsx": "^1.6.0", "faucet-pipeline-esnext": "file:pkg/faucet-pipeline-esnext", "faucet-pipeline-jsx": "file:pkg/faucet-pipeline-jsx", "faucet-pipeline-typescript": "file:pkg/faucet-pipeline-typescript", diff --git a/test/cli/test_custom_config/assets.js b/test/cli/test_custom_config/assets.js index 2c5d05c..846d281 100644 --- a/test/cli/test_custom_config/assets.js +++ b/test/cli/test_custom_config/assets.js @@ -5,7 +5,10 @@ let path = require("path"); module.exports = { js: [{ source: "./index.js", - target: "./dist/bundle.js" + target: "./dist/bundle.js", + reporting: { + threshold: 100 + } }], plugins: { js: path.resolve(__dirname, "../../..") diff --git a/test/cli/test_custom_config/expected.js b/test/cli/test_custom_config/expected.js index 049c7ef..24cb380 100644 --- a/test/cli/test_custom_config/expected.js +++ b/test/cli/test_custom_config/expected.js @@ -5,6 +5,22 @@ if(typeof global === "undefined" && typeof window !== "undefined") { window.global = window; } -// N/A +// lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod +// tempor incididunt ut labore et dolore magna aliqua +// ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut +// aliquip ex ea commodo consequat +// duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore +// eu fugiat nulla pariatur +// excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia +// deserunt mollit anim id est laborum +// +// lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod +// tempor incididunt ut labore et dolore magna aliqua +// ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut +// aliquip ex ea commodo consequat +// duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore +// eu fugiat nulla pariatur +// excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia +// deserunt mollit anim id est laborum }()); diff --git a/test/cli/test_custom_config/index.js b/test/cli/test_custom_config/index.js index 6cb0b2d..90f3c91 100644 --- a/test/cli/test_custom_config/index.js +++ b/test/cli/test_custom_config/index.js @@ -1 +1,17 @@ -// N/A +// lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod +// tempor incididunt ut labore et dolore magna aliqua +// ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut +// aliquip ex ea commodo consequat +// duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore +// eu fugiat nulla pariatur +// excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia +// deserunt mollit anim id est laborum +// +// lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod +// tempor incididunt ut labore et dolore magna aliqua +// ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut +// aliquip ex ea commodo consequat +// duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore +// eu fugiat nulla pariatur +// excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia +// deserunt mollit anim id est laborum