Skip to content

Commit

Permalink
Added JSON output to the cli and webpack plugin (#341)
Browse files Browse the repository at this point in the history
* Added JSON output to the cli and webpack plugin

* Fixed format

* Added test

* Fixed typo

* Fixed missing variable

* Lint fix

* Update analyzer.js

* Update README.md

* Update src/bin/analyzer.js

Co-Authored-By: Vesa Laakso <482561+valscion@users.noreply.github.com>

* Split out json test

* Reducing complexity by splitting html and json generation early

* removed redundant path resolvement

* Update BundleAnalyzerPlugin.js

* Added plugin test

* Reverted changes

* Update src/viewer.js

Co-Authored-By: Vesa Laakso <482561+valscion@users.noreply.github.com>

* Restart build

Co-authored-by: Vesa Laakso <482561+valscion@users.noreply.github.com>
  • Loading branch information
Gongreg and valscion committed Apr 10, 2020
1 parent 660dba6 commit acf2d1c
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 30 deletions.
7 changes: 4 additions & 3 deletions README.md
Expand Up @@ -56,7 +56,7 @@ new BundleAnalyzerPlugin(options?: object)

|Name|Type|Description|
|:--:|:--:|:----------|
|**`analyzerMode`**|One of: `server`, `static`, `disabled`|Default: `server`. In `server` mode analyzer will start HTTP server to show bundle report. In `static` mode single HTML file with bundle report will be generated. In `disabled` mode you can use this plugin to just generate Webpack Stats JSON file by setting `generateStatsFile` to `true`. |
|**`analyzerMode`**|One of: `server`, `static`, `json`, `disabled`|Default: `server`. In `server` mode analyzer will start HTTP server to show bundle report. In `static` mode single HTML file with bundle report will be generated. In `json` mode single JSON file with bundle report will be generated. In `disabled` mode you can use this plugin to just generate Webpack Stats JSON file by setting `generateStatsFile` to `true`. |
|**`analyzerHost`**|`{String}`|Default: `127.0.0.1`. Host that will be used in `server` mode to start HTTP server.|
|**`analyzerPort`**|`{Number}` or `auto`|Default: `8888`. Port that will be used in `server` mode to start HTTP server.|
|**`reportFilename`**|`{String}`|Default: `report.html`. Path to bundle report file that will be generated in `static` mode. It can be either an absolute path or a path relative to a bundle output directory (which is output.path in webpack config).|
Expand Down Expand Up @@ -111,9 +111,10 @@ Directory containing all generated bundles.

```
-V, --version output the version number
-m, --mode <mode> Analyzer mode. Should be `server` or `static`.
-m, --mode <mode> Analyzer mode. Should be `server`, `static` or `json`.
In `server` mode analyzer will start HTTP server to show bundle report.
In `static` mode single HTML file with bundle report will be generated. (default: server)
In `static` mode single HTML file with bundle report will be generated.
In `json` mode single JSON file with bundle report will be generated. (default: server)
-h, --host <host> Host that will be used in `server` mode to start HTTP server. (default: 127.0.0.1)
-p, --port <n> Port that will be used in `server` mode to start HTTP server. Should be a number or `auto` (default: 8888)
-r, --report <file> Path to bundle report file that will be generated in `static` mode. (default: report.html)
Expand Down
16 changes: 13 additions & 3 deletions src/BundleAnalyzerPlugin.js
Expand Up @@ -7,12 +7,11 @@ const Logger = require('./Logger');
const viewer = require('./viewer');

class BundleAnalyzerPlugin {

constructor(opts = {}) {
this.opts = {
analyzerMode: 'server',
analyzerHost: '127.0.0.1',
reportFilename: 'report.html',
reportFilename: null,
defaultSizes: 'parsed',
openAnalyzer: true,
generateStatsFile: false,
Expand Down Expand Up @@ -51,6 +50,8 @@ class BundleAnalyzerPlugin {
actions.push(() => this.startAnalyzerServer(stats.toJson()));
} else if (this.opts.analyzerMode === 'static') {
actions.push(() => this.generateStaticReport(stats.toJson()));
} else if (this.opts.analyzerMode === 'json') {
actions.push(() => this.generateJSONReport(stats.toJson()));
}

if (actions.length) {
Expand Down Expand Up @@ -115,10 +116,19 @@ class BundleAnalyzerPlugin {
}
}

async generateJSONReport(stats) {
await viewer.generateJSONReport(stats, {
reportFilename: path.resolve(this.compiler.outputPath, this.opts.reportFilename || 'report.json'),
bundleDir: this.getBundleDirFromCompiler(),
logger: this.logger,
excludeAssets: this.opts.excludeAssets
});
}

async generateStaticReport(stats) {
await viewer.generateReport(stats, {
openBrowser: this.opts.openAnalyzer,
reportFilename: path.resolve(this.compiler.outputPath, this.opts.reportFilename),
reportFilename: path.resolve(this.compiler.outputPath, this.opts.reportFilename || 'report.html'),
bundleDir: this.getBundleDirFromCompiler(),
logger: this.logger,
defaultSizes: this.opts.defaultSizes,
Expand Down
25 changes: 17 additions & 8 deletions src/bin/analyzer.js
Expand Up @@ -26,9 +26,10 @@ const program = commander
)
.option(
'-m, --mode <mode>',
'Analyzer mode. Should be `server` or `static`.' +
br('In `server` mode analyzer will start HTTP server to show bundle report.') +
br('In `static` mode single HTML file with bundle report will be generated.'),
'Analyzer mode. Should be `server`,`static` or `json`.' +
br('In `server` mode analyzer will start HTTP server to show bundle report.') +
br('In `static` mode single HTML file with bundle report will be generated.') +
br('In `json` mode single JSON file with bundle report will be generated.'),
'server'
)
.option(
Expand All @@ -45,8 +46,7 @@ const program = commander
)
.option(
'-r, --report <file>',
'Path to bundle report file that will be generated in `static` mode.',
'report.html'
'Path to bundle report file that will be generated in `static` mode.'
)
.option(
'-s, --default-sizes <type>',
Expand Down Expand Up @@ -86,7 +86,9 @@ let {
const logger = new Logger(logLevel);

if (!bundleStatsFile) showHelp('Provide path to Webpack Stats file as first argument');
if (mode !== 'server' && mode !== 'static') showHelp('Invalid mode. Should be either `server` or `static`.');
if (mode !== 'server' && mode !== 'static' && mode !== 'json') {
showHelp('Invalid mode. Should be either `server`, `static` or `json`.');
}
if (mode === 'server') {
if (!host) showHelp('Invalid host name');

Expand Down Expand Up @@ -118,15 +120,22 @@ if (mode === 'server') {
excludeAssets,
logger: new Logger(logLevel)
});
} else {
} else if (mode === 'static') {
viewer.generateReport(bundleStats, {
openBrowser,
reportFilename: resolve(reportFilename),
reportFilename: resolve(reportFilename || 'report.html'),
defaultSizes,
bundleDir,
excludeAssets,
logger: new Logger(logLevel)
});
} else if (mode === 'json') {
viewer.generateJSONReport(bundleStats, {
reportFilename: resolve(reportFilename || 'report.json'),
bundleDir,
excludeAssets,
logger: new Logger(logLevel)
});
}

function showHelp(error) {
Expand Down
20 changes: 16 additions & 4 deletions src/viewer.js
Expand Up @@ -20,6 +20,7 @@ const assetsRoot = path.join(projectRoot, 'public');
module.exports = {
startServer,
generateReport,
generateJSONReport,
// deprecated
start: startServer
};
Expand Down Expand Up @@ -121,7 +122,7 @@ async function startServer(bundleStats, opts) {
async function generateReport(bundleStats, opts) {
const {
openBrowser = true,
reportFilename = 'report.html',
reportFilename,
bundleDir = null,
logger = new Logger(),
defaultSizes = 'parsed',
Expand Down Expand Up @@ -158,9 +159,7 @@ async function generateReport(bundleStats, opts) {
mkdir.sync(path.dirname(reportFilepath));
fs.writeFileSync(reportFilepath, reportHtml);

logger.info(
`${bold('Webpack Bundle Analyzer')} saved report to ${bold(reportFilepath)}`
);
logger.info(`${bold('Webpack Bundle Analyzer')} saved report to ${bold(reportFilepath)}`);

if (openBrowser) {
opener(`file://${reportFilepath}`);
Expand All @@ -174,6 +173,19 @@ async function generateReport(bundleStats, opts) {
});
}

async function generateJSONReport(bundleStats, opts) {
const {reportFilename, bundleDir = null, logger = new Logger(), excludeAssets = null} = opts || {};

const chartData = getChartData({logger, excludeAssets}, bundleStats, bundleDir);

if (!chartData) return;

mkdir.sync(path.dirname(reportFilename));
fs.writeFileSync(reportFilename, JSON.stringify(chartData));

logger.info(`${bold('Webpack Bundle Analyzer')} saved JSON report to ${bold(reportFilename)}`);
}

function getAssetContent(filename) {
const assetPath = path.join(assetsRoot, filename);

Expand Down
18 changes: 15 additions & 3 deletions test/analyzer.js
@@ -1,5 +1,6 @@
const _ = require('lodash');
const fs = require('fs');
const path = require('path');
const del = require('del');
const childProcess = require('child_process');

Expand Down Expand Up @@ -106,18 +107,29 @@ describe('Analyzer', function () {
generateReportFrom('with-modules-chunk.json');
await expectValidReport({bundleLabel: 'bundle.mjs'});
});

it('should support generating JSON output for the report', async function () {
generateJSONReportFrom('with-modules-in-chunks/stats.json');

const chartData = require(path.resolve(__dirname, 'output/report.json'));
expect(chartData).to.containSubset(require('./stats/with-modules-in-chunks/expected-chart-data'));
});
});

function generateJSONReportFrom(statsFilename) {
childProcess.execSync(`../lib/bin/analyzer.js -m json -r output/report.json stats/${statsFilename}`, {
cwd: __dirname
});
}

function generateReportFrom(statsFilename) {
childProcess.execSync(`../lib/bin/analyzer.js -m static -r output/report.html -O stats/${statsFilename}`, {
cwd: __dirname
});
}

async function getChartData() {
return await nightmare
.goto(`file://${__dirname}/output/report.html`)
.evaluate(() => window.chartData);
return await nightmare.goto(`file://${__dirname}/output/report.html`).evaluate(() => window.chartData);
}

function forEachChartItem(chartData, cb) {
Expand Down
35 changes: 26 additions & 9 deletions test/plugin.js
@@ -1,7 +1,7 @@
const fs = require('fs');
const del = require('del');
const _ = require('lodash');

const path = require('path');
const BundleAnalyzerPlugin = require('../lib/BundleAnalyzerPlugin');

describe('Plugin', function () {
Expand Down Expand Up @@ -47,6 +47,19 @@ describe('Plugin', function () {
});
});

it('should allow to generate json report', async function () {
const config = makeWebpackConfig({
analyzerOpts: {
analyzerMode: 'json'
}
});

await webpackCompile(config);

const chartData = await getChartDataFromJSONReport();
expect(chartData).to.exist;
});

it('should support webpack config with `multi` module', async function () {
const config = makeWebpackConfig();

Expand All @@ -58,11 +71,13 @@ describe('Plugin', function () {
await webpackCompile(config);

const chartData = await getChartDataFromReport();
expect(chartData[0].groups).to.containSubset([{
label: 'multi ./src/a.js ./src/b.js',
path: './multi ./src/a.js ./src/b.js',
groups: undefined
}]);
expect(chartData[0].groups).to.containSubset([
{
label: 'multi ./src/a.js ./src/b.js',
path: './multi ./src/a.js ./src/b.js',
groups: undefined
}
]);
});

describe('options', function () {
Expand Down Expand Up @@ -104,9 +119,11 @@ describe('Plugin', function () {
});
}

function getChartDataFromJSONReport(reportFilename = 'report.json') {
return require(path.resolve(__dirname, `output/${reportFilename}`));
}

async function getChartDataFromReport(reportFilename = 'report.html') {
return await nightmare
.goto(`file://${__dirname}/output/${reportFilename}`)
.evaluate(() => window.chartData);
return await nightmare.goto(`file://${__dirname}/output/${reportFilename}`).evaluate(() => window.chartData);
}
});

0 comments on commit acf2d1c

Please sign in to comment.