UNPKG

webpack-stats-plugin

Version:
196 lines (179 loc) 7.23 kB
"use strict"; const INDENT = 2; const DEFAULT_TRANSFORM = (data) => JSON.stringify(data, null, INDENT); /** * Stats writer module. * * Stats can be a string or array (we'll have array due to source maps): * * ```js * "assetsByChunkName": { * "main": [ * "cd6371d4131fbfbefaa7.bundle.js", * "../js-map/cd6371d4131fbfbefaa7.bundle.js.map" * ] * }, * ``` * * **Note**: The stats object is **big**. It includes the entire source included * in a bundle. Thus, we default `opts.fields` to `["assetsByChunkName"]` to * only include those. However, if you want the _whole thing_ (maybe doing an * `opts.transform` function), then you can set `fields: null` in options to * get **all** of the stats object. * * You may also pass a custom stats config object (or string preset) via `opts.stats` * in order to select exactly what you want added to the data passed to the transform. * When `opts.stats` is passed, `opts.fields` will default to `null`. * * See: * - https://webpack.js.org/configuration/stats/#stats * - https://webpack.js.org/api/node/#stats-object * * **`filename`**: The `opts.filename` option can be a file name or path relative to * `output.path` in webpack configuration. It should not be absolute. It may also * be a function, in which case it will be passed the current compiler instance * and expected to return a filename to use. * * **`transform`**: By default, the retrieved stats object is `JSON.stringify`'ed * but by supplying an alternate transform you can target _any_ output format. * See [`test/scenarios/webpack5/webpack.config.js`](test/scenarios/webpack5/webpack.config.js) for * various examples including Markdown output. * * - **Warning**: The output of `transform` should be a `String`, not an object. * On Node `v4.x` if you return a real object in `transform`, then webpack * will break with a `TypeError` (See #8). Just adding a simple * `JSON.stringify()` around your object is usually what you need to solve * any problems. * * @param {Object} opts options * @param {String|Function} opts.filename output file name (Default: `"stats.json`") * @param {Array} opts.fields stats obj fields (Default: `["assetsByChunkName"]`) * @param {Object|String} opts.stats stats config object or string preset (Default: `{}`) * @param {Function|Promise} opts.transform transform stats obj (Default: `JSON.stringify()`) * @param {Boolean} opts.emit add stats file to webpack output? (Default: `true`) * * @api public */ class StatsWriterPlugin { constructor(opts) { opts = opts || {}; this.opts = {}; this.opts.filename = opts.filename || "stats.json"; this.opts.fields = typeof opts.fields !== "undefined" ? opts.fields : ["assetsByChunkName"]; this.opts.stats = opts.stats || {}; this.opts.transform = opts.transform || DEFAULT_TRANSFORM; this.opts.emit = opts.emit !== false; if (typeof opts.stats !== "undefined" && typeof opts.fields === "undefined") { // if custom stats config provided, do not filter fields unless explicitly configured this.opts.fields = null; } } apply(compiler) { if (compiler.hooks) { let emitHookSet = false; // Capture the compilation and then set up further hooks. compiler.hooks.thisCompilation.tap("stats-writer-plugin", (compilation) => { if (compilation.hooks.processAssets) { // Modern: `processAssets` is one of the last hooks before frozen assets. // We choose `PROCESS_ASSETS_STAGE_REPORT` which is the last possible // stage after which to emit. // // See: // - https://webpack.js.org/api/compilation-hooks/#processassets // - https://github.com/FormidableLabs/webpack-stats-plugin/issues/56 compilation.hooks.processAssets.tapPromise( { name: "stats-writer-plugin", stage: compilation.constructor.PROCESS_ASSETS_STAGE_REPORT }, () => this.emitStats(compilation) ); } else if (!emitHookSet) { // Legacy. // // Set up the `compiler` level hook only once to avoid multiple // calls during `webpack --watch`. (We have to do this here because // we can't otherwise detect if `compilation.hooks.processAssets` is // available for modern mode.) emitHookSet = true; compiler.hooks.emit.tapPromise("stats-writer-plugin", this.emitStats.bind(this)); } }); } else { // Super-legacy. compiler.plugin("emit", this.emitStats.bind(this)); } } emitStats(curCompiler, callback) { // Get stats. // The second argument automatically skips heavy options (reasons, source, etc) // if they are otherwise unspecified. let stats = curCompiler.getStats().toJson(this.opts.stats); // Filter to fields. if (this.opts.fields) { stats = this.opts.fields.reduce((memo, key) => { memo[key] = stats[key]; return memo; }, {}); } // Transform to string. let err; return Promise.resolve() // Transform. .then(() => this.opts.transform(stats, { compiler: curCompiler })) .catch((e) => { err = e; }) // Finish up. // eslint-disable-next-line max-statements,complexity .then((statsStr) => { // Handle errors. if (err) { curCompiler.errors.push(err); // eslint-disable-next-line promise/no-callback-in-promise if (callback) { return void callback(err); } throw err; } // Short-circuit if not emitting. if (!this.opts.emit) { // eslint-disable-next-line promise/no-callback-in-promise,promise/always-return if (callback) { return void callback(); } return; } // Create simple equivalent of RawSource from webpack-sources. const statsBuf = Buffer.from(statsStr || "", "utf-8"); const webpack = curCompiler.webpack || (curCompiler.compiler || {}).webpack; const source = webpack // webpack5+ abstraction ? new webpack.sources.RawSource(statsBuf) // webpack4- manual class : { source() { return statsBuf; }, size() { return statsBuf.length; } }; const filename = typeof this.opts.filename === "function" ? this.opts.filename(curCompiler) : this.opts.filename; // Add to assets. if (curCompiler.emitAsset) { // Modern method. const asset = curCompiler.getAsset(filename); if (asset) { curCompiler.updateAsset(filename, source); } else { curCompiler.emitAsset(filename, source); } } else { // Fallback to deprecated method. curCompiler.assets[filename] = source; } // eslint-disable-next-line promise/no-callback-in-promise,promise/always-return if (callback) { return void callback(); } }); } } module.exports = StatsWriterPlugin;