webpack-bundle-analyzer 3.5.2 → 3.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,12 +7,11 @@ const Logger = require('./Logger');
7
7
  const viewer = require('./viewer');
8
8
 
9
9
  class BundleAnalyzerPlugin {
10
-
11
10
  constructor(opts = {}) {
12
11
  this.opts = {
13
12
  analyzerMode: 'server',
14
13
  analyzerHost: '127.0.0.1',
15
- reportFilename: 'report.html',
14
+ reportFilename: null,
16
15
  defaultSizes: 'parsed',
17
16
  openAnalyzer: true,
18
17
  generateStatsFile: false,
@@ -51,6 +50,8 @@ class BundleAnalyzerPlugin {
51
50
  actions.push(() => this.startAnalyzerServer(stats.toJson()));
52
51
  } else if (this.opts.analyzerMode === 'static') {
53
52
  actions.push(() => this.generateStaticReport(stats.toJson()));
53
+ } else if (this.opts.analyzerMode === 'json') {
54
+ actions.push(() => this.generateJSONReport(stats.toJson()));
54
55
  }
55
56
 
56
57
  if (actions.length) {
@@ -115,10 +116,19 @@ class BundleAnalyzerPlugin {
115
116
  }
116
117
  }
117
118
 
119
+ async generateJSONReport(stats) {
120
+ await viewer.generateJSONReport(stats, {
121
+ reportFilename: path.resolve(this.compiler.outputPath, this.opts.reportFilename || 'report.json'),
122
+ bundleDir: this.getBundleDirFromCompiler(),
123
+ logger: this.logger,
124
+ excludeAssets: this.opts.excludeAssets
125
+ });
126
+ }
127
+
118
128
  async generateStaticReport(stats) {
119
129
  await viewer.generateReport(stats, {
120
130
  openBrowser: this.opts.openAnalyzer,
121
- reportFilename: path.resolve(this.compiler.outputPath, this.opts.reportFilename),
131
+ reportFilename: path.resolve(this.compiler.outputPath, this.opts.reportFilename || 'report.html'),
122
132
  bundleDir: this.getBundleDirFromCompiler(),
123
133
  logger: this.logger,
124
134
  defaultSizes: this.opts.defaultSizes,
@@ -26,9 +26,10 @@ const program = commander
26
26
  )
27
27
  .option(
28
28
  '-m, --mode <mode>',
29
- 'Analyzer mode. Should be `server` or `static`.' +
30
- br('In `server` mode analyzer will start HTTP server to show bundle report.') +
31
- br('In `static` mode single HTML file with bundle report will be generated.'),
29
+ 'Analyzer mode. Should be `server`,`static` or `json`.' +
30
+ br('In `server` mode analyzer will start HTTP server to show bundle report.') +
31
+ br('In `static` mode single HTML file with bundle report will be generated.') +
32
+ br('In `json` mode single JSON file with bundle report will be generated.'),
32
33
  'server'
33
34
  )
34
35
  .option(
@@ -45,8 +46,7 @@ const program = commander
45
46
  )
46
47
  .option(
47
48
  '-r, --report <file>',
48
- 'Path to bundle report file that will be generated in `static` mode.',
49
- 'report.html'
49
+ 'Path to bundle report file that will be generated in `static` mode.'
50
50
  )
51
51
  .option(
52
52
  '-s, --default-sizes <type>',
@@ -86,7 +86,9 @@ let {
86
86
  const logger = new Logger(logLevel);
87
87
 
88
88
  if (!bundleStatsFile) showHelp('Provide path to Webpack Stats file as first argument');
89
- if (mode !== 'server' && mode !== 'static') showHelp('Invalid mode. Should be either `server` or `static`.');
89
+ if (mode !== 'server' && mode !== 'static' && mode !== 'json') {
90
+ showHelp('Invalid mode. Should be either `server`, `static` or `json`.');
91
+ }
90
92
  if (mode === 'server') {
91
93
  if (!host) showHelp('Invalid host name');
92
94
 
@@ -118,15 +120,22 @@ if (mode === 'server') {
118
120
  excludeAssets,
119
121
  logger: new Logger(logLevel)
120
122
  });
121
- } else {
123
+ } else if (mode === 'static') {
122
124
  viewer.generateReport(bundleStats, {
123
125
  openBrowser,
124
- reportFilename: resolve(reportFilename),
126
+ reportFilename: resolve(reportFilename || 'report.html'),
125
127
  defaultSizes,
126
128
  bundleDir,
127
129
  excludeAssets,
128
130
  logger: new Logger(logLevel)
129
131
  });
132
+ } else if (mode === 'json') {
133
+ viewer.generateJSONReport(bundleStats, {
134
+ reportFilename: resolve(reportFilename || 'report.json'),
135
+ bundleDir,
136
+ excludeAssets,
137
+ logger: new Logger(logLevel)
138
+ });
130
139
  }
131
140
 
132
141
  function showHelp(error) {
package/src/parseUtils.js CHANGED
@@ -25,6 +25,22 @@ function parseBundle(bundlePath) {
25
25
  ast,
26
26
  walkState,
27
27
  {
28
+ AssignmentExpression(node, state) {
29
+ if (state.locations) return;
30
+
31
+ // Modules are stored in exports.modules:
32
+ // exports.modules = {};
33
+ const {left, right} = node;
34
+
35
+ if (
36
+ left &&
37
+ left.object && left.object.name === 'exports' &&
38
+ left.property && left.property.name === 'modules' &&
39
+ isModulesHash(right)
40
+ ) {
41
+ state.locations = getModulesLocations(right);
42
+ }
43
+ },
28
44
  CallExpression(node, state, c) {
29
45
  if (state.locations) return;
30
46
 
@@ -63,6 +79,15 @@ function parseBundle(bundlePath) {
63
79
  return;
64
80
  }
65
81
 
82
+ // Webpack v4 WebWorkerChunkTemplatePlugin
83
+ // globalObject.chunkCallbackName([<chunks>],<modules>, ...);
84
+ // Both globalObject and chunkCallbackName can be changed through the config, so we can't check them.
85
+ if (isAsyncWebWorkerChunkExpression(node)) {
86
+ state.locations = getModulesLocations(args[1]);
87
+ return;
88
+ }
89
+
90
+
66
91
  // Walking into arguments because some of plugins (e.g. `DedupePlugin`) or some Webpack
67
92
  // features (e.g. `umd` library output) can wrap modules list into additional IIFE.
68
93
  _.each(args, arg => c(arg, state));
@@ -182,12 +207,6 @@ function isAsyncChunkPushExpression(node) {
182
207
  callee.type === 'MemberExpression' &&
183
208
  callee.property.name === 'push' &&
184
209
  callee.object.type === 'AssignmentExpression' &&
185
- callee.object.left.object &&
186
- (
187
- callee.object.left.object.name === 'window' ||
188
- // Webpack 4 uses `this` instead of `window`
189
- callee.object.left.object.type === 'ThisExpression'
190
- ) &&
191
210
  args.length === 1 &&
192
211
  args[0].type === 'ArrayExpression' &&
193
212
  mayBeAsyncChunkArguments(args[0].elements) &&
@@ -202,6 +221,18 @@ function mayBeAsyncChunkArguments(args) {
202
221
  );
203
222
  }
204
223
 
224
+ function isAsyncWebWorkerChunkExpression(node) {
225
+ const {callee, type, arguments: args} = node;
226
+
227
+ return (
228
+ type === 'CallExpression' &&
229
+ callee.type === 'MemberExpression' &&
230
+ args.length === 2 &&
231
+ isChunkIds(args[0]) &&
232
+ isModulesList(args[1])
233
+ );
234
+ }
235
+
205
236
  function getModulesLocations(node) {
206
237
  if (node.type === 'ObjectExpression') {
207
238
  // Modules hash
package/src/utils.js CHANGED
@@ -44,7 +44,7 @@ exports.getCurrentTime = function () {
44
44
  const year = time.getFullYear();
45
45
  const month = MONTHS[time.getMonth()];
46
46
  const day = time.getDate();
47
- const hour = time.getHours();
48
- const minute = time.getMinutes();
47
+ const hour = `0${time.getHours()}`.slice(-2);
48
+ const minute = `0${time.getMinutes()}`.slice(-2);
49
49
  return `${day} ${month} ${year} at ${hour}:${minute}`;
50
50
  };
package/src/viewer.js CHANGED
@@ -20,6 +20,7 @@ const assetsRoot = path.join(projectRoot, 'public');
20
20
  module.exports = {
21
21
  startServer,
22
22
  generateReport,
23
+ generateJSONReport,
23
24
  // deprecated
24
25
  start: startServer
25
26
  };
@@ -121,7 +122,7 @@ async function startServer(bundleStats, opts) {
121
122
  async function generateReport(bundleStats, opts) {
122
123
  const {
123
124
  openBrowser = true,
124
- reportFilename = 'report.html',
125
+ reportFilename,
125
126
  bundleDir = null,
126
127
  logger = new Logger(),
127
128
  defaultSizes = 'parsed',
@@ -158,9 +159,7 @@ async function generateReport(bundleStats, opts) {
158
159
  mkdir.sync(path.dirname(reportFilepath));
159
160
  fs.writeFileSync(reportFilepath, reportHtml);
160
161
 
161
- logger.info(
162
- `${bold('Webpack Bundle Analyzer')} saved report to ${bold(reportFilepath)}`
163
- );
162
+ logger.info(`${bold('Webpack Bundle Analyzer')} saved report to ${bold(reportFilepath)}`);
164
163
 
165
164
  if (openBrowser) {
166
165
  opener(`file://${reportFilepath}`);
@@ -174,6 +173,19 @@ async function generateReport(bundleStats, opts) {
174
173
  });
175
174
  }
176
175
 
176
+ async function generateJSONReport(bundleStats, opts) {
177
+ const {reportFilename, bundleDir = null, logger = new Logger(), excludeAssets = null} = opts || {};
178
+
179
+ const chartData = getChartData({logger, excludeAssets}, bundleStats, bundleDir);
180
+
181
+ if (!chartData) return;
182
+
183
+ mkdir.sync(path.dirname(reportFilename));
184
+ fs.writeFileSync(reportFilename, JSON.stringify(chartData));
185
+
186
+ logger.info(`${bold('Webpack Bundle Analyzer')} saved JSON report to ${bold(reportFilename)}`);
187
+ }
188
+
177
189
  function getAssetContent(filename) {
178
190
  const assetPath = path.join(assetsRoot, filename);
179
191