diff --git a/packages/webpack-cli/README.md b/packages/webpack-cli/README.md index 53fa3fab654..b61c56274f4 100644 --- a/packages/webpack-cli/README.md +++ b/packages/webpack-cli/README.md @@ -53,7 +53,7 @@ yarn add webpack-cli --dev --no-hot Disables Hot Module Replacement -d, --devtool string Controls if and how source maps are generated. --prefetch string Prefetch this request - -j, --json Prints result as JSON + -j, --json string Prints result as JSON or store it in a file --mode string Defines the mode to pass to webpack -v, --version Get current version --stats string It instructs webpack on how to treat the stats diff --git a/packages/webpack-cli/lib/groups/resolveStats.js b/packages/webpack-cli/lib/groups/resolveStats.js index acc49dfdd76..be538f0352b 100644 --- a/packages/webpack-cli/lib/groups/resolveStats.js +++ b/packages/webpack-cli/lib/groups/resolveStats.js @@ -14,7 +14,7 @@ const resolveStats = (args) => { finalOptions.options.stats = stats; } if (json) { - finalOptions.outputOptions.json = true; + finalOptions.outputOptions.json = json; } return finalOptions; }; diff --git a/packages/webpack-cli/lib/utils/Compiler.js b/packages/webpack-cli/lib/utils/Compiler.js index 7c18dd25e5a..0479354a624 100644 --- a/packages/webpack-cli/lib/utils/Compiler.js +++ b/packages/webpack-cli/lib/utils/Compiler.js @@ -1,6 +1,7 @@ const { packageExists } = require('@webpack-cli/package-utils'); const webpack = packageExists('webpack') ? require('webpack') : undefined; const logger = require('./logger'); +const { writeFileSync } = require('fs'); const bailAndWatchWarning = require('./warnings/bailAndWatchWarning'); const { CompilerOutput } = require('./CompilerOutput'); @@ -58,7 +59,7 @@ class Compiler { if (!outputOptions.watch && (stats.hasErrors() || stats.hasWarnings())) { process.exitCode = 1; } - if (outputOptions.json) { + if (outputOptions.json === true) { process.stdout.write(JSON.stringify(stats.toJson(outputOptions), null, 2) + '\n'); } else if (stats.hash !== lastHash) { lastHash = stats.hash; @@ -69,6 +70,15 @@ class Compiler { statsErrors.push({ name: statErr.message, loc: errLoc }); }); } + const JSONStats = JSON.stringify(stats.toJson(outputOptions), null, 2); + if (typeof outputOptions.json === 'string') { + try { + writeFileSync(outputOptions.json, JSONStats); + logger.info(`stats are successfully stored as json to ${outputOptions.json}`); + } catch (err) { + logger.error(err); + } + } return this.generateOutput(outputOptions, stats, statsErrors); } } diff --git a/packages/webpack-cli/lib/utils/cli-flags.js b/packages/webpack-cli/lib/utils/cli-flags.js index 85f30cab7d8..09b6872a52e 100644 --- a/packages/webpack-cli/lib/utils/cli-flags.js +++ b/packages/webpack-cli/lib/utils/cli-flags.js @@ -178,9 +178,9 @@ const core = [ { name: 'json', usage: '--json', - type: Boolean, + type: [String, Boolean], alias: 'j', - description: 'Prints result as JSON', + description: 'Prints result as JSON or store it in a file', }, { name: 'mode', diff --git a/scripts/cleanupTest.js b/scripts/cleanupTest.js index 1d86780adbc..43ab80577e0 100644 --- a/scripts/cleanupTest.js +++ b/scripts/cleanupTest.js @@ -3,7 +3,7 @@ const rimraf = require('rimraf'); const { join } = require('path'); const collectTestFolders = require('./utils'); -const outputDirectories = ['bin', 'binary', 'dist', 'test', 'test-assets', 'test-plugin', 'test-loader']; +const outputDirectories = ['bin', 'binary', 'dist', 'test', 'test-assets', 'test-plugin', 'test-loader', 'stats.json']; function folderStrategy(stats, file) { return stats.isDirectory() && outputDirectories.includes(file); diff --git a/test/json/json.test.js b/test/json/json.test.js index 909cc1dc4a1..5200f2ee06e 100644 --- a/test/json/json.test.js +++ b/test/json/json.test.js @@ -1,5 +1,7 @@ 'use strict'; const { run } = require('../utils/test-utils'); +const { stat, readFile } = require('fs'); +const { resolve } = require('path'); describe('json flag', () => { it('should return valid json', () => { @@ -13,6 +15,26 @@ describe('json flag', () => { expect(parseJson).not.toThrow(); }); + it('should store json to a file', (done) => { + const { stdout } = run(__dirname, ['--json', 'stats.json']); + + expect(stdout).toContain('stats are successfully stored as json to stats.json'); + stat(resolve(__dirname, './stats.json'), (err, stats) => { + expect(err).toBe(null); + expect(stats.isFile()).toBe(true); + done(); + }); + readFile(resolve(__dirname, 'stats.json'), 'utf-8', (err, data) => { + expect(err).toBe(null); + expect(data).toContain('hash'); + expect(data).toContain('time'); + expect(data).toContain('errors'); + expect(data).toContain('version'); + expect(() => JSON.parse(data)).not.toThrow(); + done(); + }); + }); + it('should return valid json with -j alias', () => { const { stdout } = run(__dirname, ['-j']);