webpack-bundle-analyzer 3.6.0 → 3.9.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.
package/CHANGELOG.md CHANGED
@@ -14,6 +14,43 @@ _Note: Gaps between patch versions are faulty, broken or test releases._
14
14
 
15
15
  <!-- Add changelog entries for new changes under this section -->
16
16
 
17
+ ## 3.9.0
18
+
19
+ * **New Feature**
20
+ * Adds option `reportTitle` to set title in HTML reports; default remains date of report generation ([#354](https://github.com/webpack-contrib/webpack-bundle-analyzer/pull/354) by [@eoingroat](https://github.com/eoingroat))
21
+
22
+ * **Improvement**
23
+ * Added capability to parse bundles that have child assets generated ([#376](https://github.com/webpack-contrib/webpack-bundle-analyzer/pull/376) by [@masterkidan](https://github.com/masterkidan) and [#378](https://github.com/webpack-contrib/webpack-bundle-analyzer/pull/378) by [@https://github.com/dabbott](https://github.com/https://github.com/dabbott))
24
+
25
+ ## 3.8.0
26
+
27
+ * **Improvement**
28
+ * Added support for exports.modules when webpack target = node ([#345](https://github.com/webpack-contrib/webpack-bundle-analyzer/pull/345) by [@Spikef](https://github.com/Spikef))
29
+
30
+ * **New Feature**
31
+ * Support [WebWorkerChunkTemplatePlugin](https://github.com/webpack/webpack/blob/c9d4ff7b054fc581c96ce0e53432d44f9dd8ca72/lib/webworker/WebWorkerChunkTemplatePlugin.js) ([#353](https://github.com/webpack-contrib/webpack-bundle-analyzer/pull/353) by [@Gongreg](https://github.com/Gongreg))
32
+
33
+ * **Bug Fix**
34
+ * Support any custom `globalObject` option in Webpack Config. ([#352](https://github.com/webpack-contrib/webpack-bundle-analyzer/pull/352) by [@Gongreg](https://github.com/Gongreg))
35
+
36
+ ## 3.7.0
37
+
38
+ * **New Feature**
39
+ * Added JSON output option (`analyzerMode: "json"` in plugin, `--mode json` in CLI) ([#341](https://github.com/webpack-contrib/webpack-bundle-analyzer/pull/341) by [@Gongreg](https://github.com/Gongreg))
40
+
41
+ * **Improvement**
42
+ * Persist "Show content of concatenated modules" option ([#322](https://github.com/webpack-contrib/webpack-bundle-analyzer/pull/322) by [@lorenzos](https://github.com/lorenzos))
43
+
44
+ ## 3.6.1
45
+
46
+ * **Bug Fix**
47
+ * Add leading zero to hour & minute on `<title />` when needed ([#314](https://github.com/webpack-contrib/webpack-bundle-analyzer/pull/314) by [@mhxbe](https://github.com/mhxbe))
48
+
49
+ * **Internal**
50
+ * Update some dependencies to get rid of vulnerability warnings ([#339](https://github.com/webpack-contrib/webpack-bundle-analyzer/pull/339))
51
+
52
+ ## 3.6.0
53
+
17
54
  * **Improvement**
18
55
  * Support webpack builds where `output.globalObject` is set to `'self'` ([#323](https://github.com/webpack-contrib/webpack-bundle-analyzer/pull/323) by [@lemonmade](https://github.com/lemonmade))
19
56
  * Improve readability of tooltips ([#320](https://github.com/webpack-contrib/webpack-bundle-analyzer/pull/320) by [@lorenzos](https://github.com/lorenzos))
package/README.md CHANGED
@@ -56,10 +56,11 @@ new BundleAnalyzerPlugin(options?: object)
56
56
 
57
57
  |Name|Type|Description|
58
58
  |:--:|:--:|:----------|
59
- |**`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`. |
59
+ |**`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`. |
60
60
  |**`analyzerHost`**|`{String}`|Default: `127.0.0.1`. Host that will be used in `server` mode to start HTTP server.|
61
61
  |**`analyzerPort`**|`{Number}` or `auto`|Default: `8888`. Port that will be used in `server` mode to start HTTP server.|
62
62
  |**`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).|
63
+ |**`reportTitle`**|`{String\|function}`|Default: function that returns pretty printed current date and time. Content of the HTML `title` element; or a function of the form `() => string` that provides the content.|
63
64
  |**`defaultSizes`**|One of: `stat`, `parsed`, `gzip`|Default: `parsed`. Module sizes to show in report by default. [Size definitions](#size-definitions) section describes what these values mean.|
64
65
  |**`openAnalyzer`**|`{Boolean}`|Default: `true`. Automatically open report in default browser.|
65
66
  |**`generateStatsFile`**|`{Boolean}`|Default: `false`. If `true`, webpack stats JSON file will be generated in bundle output directory|
@@ -111,12 +112,14 @@ Directory containing all generated bundles.
111
112
 
112
113
  ```
113
114
  -V, --version output the version number
114
- -m, --mode <mode> Analyzer mode. Should be `server` or `static`.
115
+ -m, --mode <mode> Analyzer mode. Should be `server`, `static` or `json`.
115
116
  In `server` mode analyzer will start HTTP server to show bundle report.
116
- In `static` mode single HTML file with bundle report will be generated. (default: server)
117
+ In `static` mode single HTML file with bundle report will be generated.
118
+ In `json` mode single JSON file with bundle report will be generated. (default: server)
117
119
  -h, --host <host> Host that will be used in `server` mode to start HTTP server. (default: 127.0.0.1)
118
120
  -p, --port <n> Port that will be used in `server` mode to start HTTP server. Should be a number or `auto` (default: 8888)
119
121
  -r, --report <file> Path to bundle report file that will be generated in `static` mode. (default: report.html)
122
+ -t, --title <title> String to use in title element of html report. (default: pretty printed current date)
120
123
  -s, --default-sizes <type> Module sizes to show in treemap by default.
121
124
  Possible values: stat, parsed, gzip (default: parsed)
122
125
  -O, --no-open Don't open report in default browser automatically.
@@ -6,7 +6,7 @@ function _asyncToGenerator(fn) { return function () { var self = this, args = ar
6
6
 
7
7
  function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
8
8
 
9
- function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
9
+ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
10
10
 
11
11
  function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
12
12
 
@@ -24,12 +24,15 @@ const Logger = require('./Logger');
24
24
 
25
25
  const viewer = require('./viewer');
26
26
 
27
+ const utils = require('./utils');
28
+
27
29
  class BundleAnalyzerPlugin {
28
30
  constructor(opts = {}) {
29
31
  this.opts = _objectSpread({
30
32
  analyzerMode: 'server',
31
33
  analyzerHost: '127.0.0.1',
32
- reportFilename: 'report.html',
34
+ reportFilename: null,
35
+ reportTitle: utils.defaultTitle,
33
36
  defaultSizes: 'parsed',
34
37
  openAnalyzer: true,
35
38
  generateStatsFile: false,
@@ -67,13 +70,13 @@ class BundleAnalyzerPlugin {
67
70
  actions.push(() => this.startAnalyzerServer(stats.toJson()));
68
71
  } else if (this.opts.analyzerMode === 'static') {
69
72
  actions.push(() => this.generateStaticReport(stats.toJson()));
73
+ } else if (this.opts.analyzerMode === 'json') {
74
+ actions.push(() => this.generateJSONReport(stats.toJson()));
70
75
  }
71
76
 
72
77
  if (actions.length) {
73
78
  // Making analyzer logs to be after all webpack logs in the console
74
- setImmediate(
75
- /*#__PURE__*/
76
- _asyncToGenerator(function* () {
79
+ setImmediate( /*#__PURE__*/_asyncToGenerator(function* () {
77
80
  try {
78
81
  yield Promise.all(actions.map(action => action()));
79
82
  callback();
@@ -128,6 +131,7 @@ class BundleAnalyzerPlugin {
128
131
  openBrowser: _this2.opts.openAnalyzer,
129
132
  host: _this2.opts.analyzerHost,
130
133
  port: _this2.opts.analyzerPort,
134
+ reportTitle: _this2.opts.reportTitle,
131
135
  bundleDir: _this2.getBundleDirFromCompiler(),
132
136
  logger: _this2.logger,
133
137
  defaultSizes: _this2.opts.defaultSizes,
@@ -137,21 +141,35 @@ class BundleAnalyzerPlugin {
137
141
  })();
138
142
  }
139
143
 
140
- generateStaticReport(stats) {
144
+ generateJSONReport(stats) {
141
145
  var _this3 = this;
142
146
 
143
147
  return _asyncToGenerator(function* () {
144
- yield viewer.generateReport(stats, {
145
- openBrowser: _this3.opts.openAnalyzer,
146
- reportFilename: path.resolve(_this3.compiler.outputPath, _this3.opts.reportFilename),
148
+ yield viewer.generateJSONReport(stats, {
149
+ reportFilename: path.resolve(_this3.compiler.outputPath, _this3.opts.reportFilename || 'report.json'),
147
150
  bundleDir: _this3.getBundleDirFromCompiler(),
148
151
  logger: _this3.logger,
149
- defaultSizes: _this3.opts.defaultSizes,
150
152
  excludeAssets: _this3.opts.excludeAssets
151
153
  });
152
154
  })();
153
155
  }
154
156
 
157
+ generateStaticReport(stats) {
158
+ var _this4 = this;
159
+
160
+ return _asyncToGenerator(function* () {
161
+ yield viewer.generateReport(stats, {
162
+ openBrowser: _this4.opts.openAnalyzer,
163
+ reportFilename: path.resolve(_this4.compiler.outputPath, _this4.opts.reportFilename || 'report.html'),
164
+ reportTitle: _this4.opts.reportTitle,
165
+ bundleDir: _this4.getBundleDirFromCompiler(),
166
+ logger: _this4.logger,
167
+ defaultSizes: _this4.opts.defaultSizes,
168
+ excludeAssets: _this4.opts.excludeAssets
169
+ });
170
+ })();
171
+ }
172
+
155
173
  getBundleDirFromCompiler() {
156
174
  switch (this.compiler.outputFileSystem.constructor.name) {
157
175
  case 'MemoryFileSystem':
package/lib/analyzer.js CHANGED
@@ -20,7 +20,7 @@ const {
20
20
  createAssetsFilter
21
21
  } = require('./utils');
22
22
 
23
- const FILENAME_QUERY_REGEXP = /\?[\0-\t\x0B\f\x0E-\u2027\u202A-\u{10FFFF}]*$/u;
23
+ const FILENAME_QUERY_REGEXP = /\?.*$/u;
24
24
  const FILENAME_EXTENSIONS = /\.(js|mjs)$/iu;
25
25
  module.exports = {
26
26
  getViewerData,
@@ -35,7 +35,26 @@ function getViewerData(bundleStats, bundleDir, opts) {
35
35
  const isAssetIncluded = createAssetsFilter(excludeAssets); // Sometimes all the information is located in `children` array (e.g. problem in #10)
36
36
 
37
37
  if (_.isEmpty(bundleStats.assets) && !_.isEmpty(bundleStats.children)) {
38
- bundleStats = bundleStats.children[0];
38
+ const {
39
+ children
40
+ } = bundleStats;
41
+ bundleStats = bundleStats.children[0]; // Sometimes if there are additional child chunks produced add them as child assets,
42
+ // leave the 1st one as that is considered the 'root' asset.
43
+
44
+ for (let i = 1; i < children.length; i++) {
45
+ bundleStats.children[i].assets.forEach(asset => {
46
+ asset.isChild = true;
47
+ bundleStats.assets.push(asset);
48
+ });
49
+ }
50
+ } else if (!_.isEmpty(bundleStats.children)) {
51
+ // Sometimes if there are additional child chunks produced add them as child assets
52
+ bundleStats.children.forEach(child => {
53
+ child.assets.forEach(asset => {
54
+ asset.isChild = true;
55
+ bundleStats.assets.push(asset);
56
+ });
57
+ });
39
58
  } // Picking only `*.js or *.mjs` assets from bundle that has non-empty `chunks` array
40
59
 
41
60
 
@@ -77,9 +96,11 @@ function getViewerData(bundleStats, bundleDir, opts) {
77
96
  }
78
97
  }
79
98
 
80
- const modules = getBundleModules(bundleStats);
81
-
82
99
  const assets = _.transform(bundleStats.assets, (result, statAsset) => {
100
+ // If asset is a childAsset, then calculate appropriate bundle modules by looking through stats.children
101
+ const assetBundles = statAsset.isChild ? getChildAssetBundles(bundleStats, statAsset.name) : bundleStats;
102
+ const modules = assetBundles ? getBundleModules(assetBundles) : [];
103
+
83
104
  const asset = result[statAsset.name] = _.pick(statAsset, 'size');
84
105
 
85
106
  if (bundlesSources && _.has(bundlesSources, statAsset.name)) {
@@ -116,6 +137,10 @@ function readStatsFromFile(filename) {
116
137
  return JSON.parse(fs.readFileSync(filename, 'utf8'));
117
138
  }
118
139
 
140
+ function getChildAssetBundles(bundleStats, assetName) {
141
+ return _.find(bundleStats.children, c => _(c.assetsByChunkName).values().flatten().includes(assetName));
142
+ }
143
+
119
144
  function getBundleModules(bundleStats) {
120
145
  return _(bundleStats.chunks).map('modules').concat(bundleStats.modules).compact().flatten().uniqBy('id').value();
121
146
  }
@@ -20,6 +20,8 @@ const viewer = require('../viewer');
20
20
 
21
21
  const Logger = require('../Logger');
22
22
 
23
+ const utils = require('../utils');
24
+
23
25
  const SIZES = new Set(['stat', 'parsed', 'gzip']);
24
26
  const program = commander.version(require('../../package.json').version).usage(`<bundleStatsFile> [bundleDir] [options]
25
27
 
@@ -28,14 +30,15 @@ const program = commander.version(require('../../package.json').version).usage(`
28
30
  bundleStatsFile Path to Webpack Stats JSON file.
29
31
  bundleDir Directory containing all generated bundles.
30
32
  You should provided it if you want analyzer to show you the real parsed module sizes.
31
- By default a directory of stats file is used.`).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.'), 'server').option( // Had to make `host` parameter optional in order to let `-h` flag output help message
33
+ By default a directory of stats file is used.`).option('-m, --mode <mode>', '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( // Had to make `host` parameter optional in order to let `-h` flag output help message
32
34
  // Fixes https://github.com/webpack-contrib/webpack-bundle-analyzer/issues/239
33
- '-h, --host [host]', 'Host that will be used in `server` mode to start HTTP server.', '127.0.0.1').option('-p, --port <n>', 'Port that will be used in `server` mode to start HTTP server.', 8888).option('-r, --report <file>', 'Path to bundle report file that will be generated in `static` mode.', 'report.html').option('-s, --default-sizes <type>', 'Module sizes to show in treemap by default.' + br(`Possible values: ${[...SIZES].join(', ')}`), 'parsed').option('-O, --no-open', "Don't open report in default browser automatically.").option('-e, --exclude <regexp>', 'Assets that should be excluded from the report.' + br('Can be specified multiple times.'), array()).option('-l, --log-level <level>', 'Log level.' + br(`Possible values: ${[...Logger.levels].join(', ')}`), Logger.defaultLevel).parse(process.argv);
35
+ '-h, --host [host]', 'Host that will be used in `server` mode to start HTTP server.', '127.0.0.1').option('-p, --port <n>', 'Port that will be used in `server` mode to start HTTP server.', 8888).option('-r, --report <file>', 'Path to bundle report file that will be generated in `static` mode.').option('-t, --title <title>', 'String to use in title element of html report.').option('-s, --default-sizes <type>', 'Module sizes to show in treemap by default.' + br(`Possible values: ${[...SIZES].join(', ')}`), 'parsed').option('-O, --no-open', "Don't open report in default browser automatically.").option('-e, --exclude <regexp>', 'Assets that should be excluded from the report.' + br('Can be specified multiple times.'), array()).option('-l, --log-level <level>', 'Log level.' + br(`Possible values: ${[...Logger.levels].join(', ')}`), Logger.defaultLevel).parse(process.argv);
34
36
  let {
35
37
  mode,
36
38
  host,
37
39
  port,
38
40
  report: reportFilename,
41
+ title: reportTitle,
39
42
  defaultSizes,
40
43
  logLevel,
41
44
  open: openBrowser,
@@ -43,8 +46,16 @@ let {
43
46
  args: [bundleStatsFile, bundleDir]
44
47
  } = program;
45
48
  const logger = new Logger(logLevel);
49
+
50
+ if (typeof reportTitle === 'undefined') {
51
+ reportTitle = utils.defaultTitle;
52
+ }
53
+
46
54
  if (!bundleStatsFile) showHelp('Provide path to Webpack Stats file as first argument');
47
- if (mode !== 'server' && mode !== 'static') showHelp('Invalid mode. Should be either `server` or `static`.');
55
+
56
+ if (mode !== 'server' && mode !== 'static' && mode !== 'json') {
57
+ showHelp('Invalid mode. Should be either `server`, `static` or `json`.');
58
+ }
48
59
 
49
60
  if (mode === 'server') {
50
61
  if (!host) showHelp('Invalid host name');
@@ -71,19 +82,28 @@ if (mode === 'server') {
71
82
  port,
72
83
  host,
73
84
  defaultSizes,
85
+ reportTitle,
74
86
  bundleDir,
75
87
  excludeAssets,
76
88
  logger: new Logger(logLevel)
77
89
  });
78
- } else {
90
+ } else if (mode === 'static') {
79
91
  viewer.generateReport(bundleStats, {
80
92
  openBrowser,
81
- reportFilename: resolve(reportFilename),
93
+ reportFilename: resolve(reportFilename || 'report.html'),
94
+ reportTitle,
82
95
  defaultSizes,
83
96
  bundleDir,
84
97
  excludeAssets,
85
98
  logger: new Logger(logLevel)
86
99
  });
100
+ } else if (mode === 'json') {
101
+ viewer.generateJSONReport(bundleStats, {
102
+ reportFilename: resolve(reportFilename || 'report.json'),
103
+ bundleDir,
104
+ excludeAssets,
105
+ logger: new Logger(logLevel)
106
+ });
87
107
  }
88
108
 
89
109
  function showHelp(error) {
package/lib/parseUtils.js CHANGED
@@ -25,6 +25,20 @@ function parseBundle(bundlePath) {
25
25
  locations: null
26
26
  };
27
27
  walk.recursive(ast, walkState, {
28
+ AssignmentExpression(node, state) {
29
+ if (state.locations) return; // Modules are stored in exports.modules:
30
+ // exports.modules = {};
31
+
32
+ const {
33
+ left,
34
+ right
35
+ } = node;
36
+
37
+ if (left && left.object && left.object.name === 'exports' && left.property && left.property.name === 'modules' && isModulesHash(right)) {
38
+ state.locations = getModulesLocations(right);
39
+ }
40
+ },
41
+
28
42
  CallExpression(node, state, c) {
29
43
  if (state.locations) return;
30
44
  const args = node.arguments; // Main chunk with webpack loader.
@@ -50,6 +64,14 @@ function parseBundle(bundlePath) {
50
64
  if (isAsyncChunkPushExpression(node)) {
51
65
  state.locations = getModulesLocations(args[0].elements[1]);
52
66
  return;
67
+ } // Webpack v4 WebWorkerChunkTemplatePlugin
68
+ // globalObject.chunkCallbackName([<chunks>],<modules>, ...);
69
+ // Both globalObject and chunkCallbackName can be changed through the config, so we can't check them.
70
+
71
+
72
+ if (isAsyncWebWorkerChunkExpression(node)) {
73
+ state.locations = getModulesLocations(args[1]);
74
+ return;
53
75
  } // Walking into arguments because some of plugins (e.g. `DedupePlugin`) or some Webpack
54
76
  // features (e.g. `umd` library output) can wrap modules list into additional IIFE.
55
77
 
@@ -129,15 +151,22 @@ function isAsyncChunkPushExpression(node) {
129
151
  callee,
130
152
  arguments: args
131
153
  } = node;
132
- return callee.type === 'MemberExpression' && callee.property.name === 'push' && callee.object.type === 'AssignmentExpression' && callee.object.left.object && (callee.object.left.object.name === 'window' || // `self` is a common output.globalObject value used to support both workers and browsers
133
- callee.object.left.object.name === 'self' || // Webpack 4 uses `this` instead of `window`
134
- callee.object.left.object.type === 'ThisExpression') && args.length === 1 && args[0].type === 'ArrayExpression' && mayBeAsyncChunkArguments(args[0].elements) && isModulesList(args[0].elements[1]);
154
+ return callee.type === 'MemberExpression' && callee.property.name === 'push' && callee.object.type === 'AssignmentExpression' && args.length === 1 && args[0].type === 'ArrayExpression' && mayBeAsyncChunkArguments(args[0].elements) && isModulesList(args[0].elements[1]);
135
155
  }
136
156
 
137
157
  function mayBeAsyncChunkArguments(args) {
138
158
  return args.length >= 2 && isChunkIds(args[0]);
139
159
  }
140
160
 
161
+ function isAsyncWebWorkerChunkExpression(node) {
162
+ const {
163
+ callee,
164
+ type,
165
+ arguments: args
166
+ } = node;
167
+ return type === 'CallExpression' && callee.type === 'MemberExpression' && args.length === 2 && isChunkIds(args[0]) && isModulesList(args[1]);
168
+ }
169
+
141
170
  function getModulesLocations(node) {
142
171
  if (node.type === 'ObjectExpression') {
143
172
  // Modules hash
@@ -19,7 +19,7 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
19
19
 
20
20
  function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
21
21
 
22
- function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
22
+ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
23
23
 
24
24
  function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
25
25
 
@@ -11,7 +11,7 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
11
11
 
12
12
  function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
13
13
 
14
- function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
14
+ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
15
15
 
16
16
  function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
17
17
 
@@ -11,7 +11,7 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
11
11
 
12
12
  function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
13
13
 
14
- function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
14
+ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
15
15
 
16
16
  function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
17
17
 
@@ -21,7 +21,7 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
21
21
 
22
22
  function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
23
23
 
24
- function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
24
+ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
25
25
 
26
26
  function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
27
27
 
package/lib/utils.js CHANGED
@@ -40,12 +40,13 @@ function createAssetsFilter(excludePatterns) {
40
40
  * */
41
41
 
42
42
 
43
- exports.getCurrentTime = function () {
43
+ exports.defaultTitle = function () {
44
44
  const time = new Date();
45
45
  const year = time.getFullYear();
46
46
  const month = MONTHS[time.getMonth()];
47
47
  const day = time.getDate();
48
- const hour = time.getHours();
49
- const minute = time.getMinutes();
50
- return `${day} ${month} ${year} at ${hour}:${minute}`;
48
+ const hour = `0${time.getHours()}`.slice(-2);
49
+ const minute = `0${time.getMinutes()}`.slice(-2);
50
+ const currentTime = `${day} ${month} ${year} at ${hour}:${minute}`;
51
+ return `${process.env.npm_package_name || 'Webpack Bundle Analyzer'} [${currentTime}]`;
51
52
  };
package/lib/viewer.js CHANGED
@@ -26,21 +26,28 @@ const {
26
26
  bold
27
27
  } = require('chalk');
28
28
 
29
- const utils = require('./utils');
30
-
31
29
  const Logger = require('./Logger');
32
30
 
33
31
  const analyzer = require('./analyzer');
34
32
 
35
33
  const projectRoot = path.resolve(__dirname, '..');
36
34
  const assetsRoot = path.join(projectRoot, 'public');
35
+
36
+ function resolveTitle(reportTitle) {
37
+ if (typeof reportTitle === 'function') {
38
+ return reportTitle();
39
+ } else {
40
+ return reportTitle;
41
+ }
42
+ }
43
+
37
44
  module.exports = {
38
45
  startServer,
39
46
  generateReport,
47
+ generateJSONReport,
40
48
  // deprecated
41
49
  start: startServer
42
50
  };
43
- const title = `${process.env.npm_package_name || 'Webpack Bundle Analyzer'} [${utils.getCurrentTime()}]`;
44
51
 
45
52
  function startServer(_x, _x2) {
46
53
  return _startServer.apply(this, arguments);
@@ -55,7 +62,8 @@ function _startServer() {
55
62
  bundleDir = null,
56
63
  logger = new Logger(),
57
64
  defaultSizes = 'parsed',
58
- excludeAssets = null
65
+ excludeAssets = null,
66
+ reportTitle
59
67
  } = opts || {};
60
68
  const analyzerOpts = {
61
69
  logger,
@@ -73,7 +81,7 @@ function _startServer() {
73
81
  app.use('/', (req, res) => {
74
82
  res.render('viewer', {
75
83
  mode: 'server',
76
- title,
84
+ title: resolveTitle(reportTitle),
77
85
 
78
86
  get chartData() {
79
87
  return chartData;
@@ -138,7 +146,8 @@ function _generateReport() {
138
146
  _generateReport = _asyncToGenerator(function* (bundleStats, opts) {
139
147
  const {
140
148
  openBrowser = true,
141
- reportFilename = 'report.html',
149
+ reportFilename,
150
+ reportTitle,
142
151
  bundleDir = null,
143
152
  logger = new Logger(),
144
153
  defaultSizes = 'parsed',
@@ -152,7 +161,7 @@ function _generateReport() {
152
161
  yield new Promise((resolve, reject) => {
153
162
  ejs.renderFile(`${projectRoot}/views/viewer.ejs`, {
154
163
  mode: 'static',
155
- title,
164
+ title: resolveTitle(reportTitle),
156
165
  chartData,
157
166
  defaultSizes,
158
167
  enableWebSocket: false,
@@ -186,6 +195,30 @@ function _generateReport() {
186
195
  return _generateReport.apply(this, arguments);
187
196
  }
188
197
 
198
+ function generateJSONReport(_x5, _x6) {
199
+ return _generateJSONReport.apply(this, arguments);
200
+ }
201
+
202
+ function _generateJSONReport() {
203
+ _generateJSONReport = _asyncToGenerator(function* (bundleStats, opts) {
204
+ const {
205
+ reportFilename,
206
+ bundleDir = null,
207
+ logger = new Logger(),
208
+ excludeAssets = null
209
+ } = opts || {};
210
+ const chartData = getChartData({
211
+ logger,
212
+ excludeAssets
213
+ }, bundleStats, bundleDir);
214
+ if (!chartData) return;
215
+ mkdir.sync(path.dirname(reportFilename));
216
+ fs.writeFileSync(reportFilename, JSON.stringify(chartData));
217
+ logger.info(`${bold('Webpack Bundle Analyzer')} saved JSON report to ${bold(reportFilename)}`);
218
+ });
219
+ return _generateJSONReport.apply(this, arguments);
220
+ }
221
+
189
222
  function getAssetContent(filename) {
190
223
  const assetPath = path.join(assetsRoot, filename);
191
224
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "webpack-bundle-analyzer",
3
- "version": "3.6.0",
3
+ "version": "3.9.0",
4
4
  "description": "Webpack plugin and CLI utility that represents bundle content as convenient interactive zoomable treemap",
5
5
  "author": "Yury Grunin <grunin.ya@ya.ru>",
6
6
  "license": "MIT",
@@ -33,8 +33,8 @@
33
33
  "views"
34
34
  ],
35
35
  "dependencies": {
36
- "acorn": "^6.0.7",
37
- "acorn-walk": "^6.1.1",
36
+ "acorn": "^7.1.1",
37
+ "acorn-walk": "^7.1.1",
38
38
  "bfj": "^6.1.1",
39
39
  "chalk": "^2.4.1",
40
40
  "commander": "^2.18.0",
@@ -42,7 +42,7 @@
42
42
  "express": "^4.16.3",
43
43
  "filesize": "^3.6.1",
44
44
  "gzip-size": "^5.0.0",
45
- "lodash": "^4.17.15",
45
+ "lodash": "^4.17.19",
46
46
  "mkdirp": "^0.5.1",
47
47
  "opener": "^1.5.1",
48
48
  "ws": "^6.0.0"
@@ -73,22 +73,22 @@
73
73
  "eslint-config-th0r-react": "2.0.0",
74
74
  "eslint-plugin-react": "7.12.4",
75
75
  "exports-loader": "0.7.0",
76
- "gulp": "4.0.0",
76
+ "gulp": "4.0.2",
77
77
  "gulp-babel": "8.0.0",
78
78
  "mobx": "5.9.4",
79
79
  "mobx-preact": "3.0.0",
80
- "mocha": "6.1.2",
80
+ "mocha": "6.2.2",
81
81
  "nightmare": "3.0.1",
82
82
  "postcss-icss-values": "2.0.2",
83
83
  "postcss-loader": "3.0.0",
84
84
  "preact": "8.4.2",
85
85
  "stream-combiner2": "1.1.1",
86
86
  "style-loader": "0.23.1",
87
- "terser-webpack-plugin": "1.2.3",
87
+ "terser-webpack-plugin": "1.4.3",
88
88
  "url-loader": "1.1.2",
89
- "webpack": "4.29.6",
90
- "webpack-cli": "3.3.0",
91
- "webpack-dev-server": "3.3.1"
89
+ "webpack": "4.42.0",
90
+ "webpack-cli": "3.3.11",
91
+ "webpack-dev-server": "3.10.3"
92
92
  },
93
93
  "keywords": [
94
94
  "webpack",