webpack-bundle-analyzer 2.8.1 → 2.9.1

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
@@ -10,6 +10,31 @@
10
10
 
11
11
  _Note: Gaps between patch versions are faulty, broken or test releases._
12
12
 
13
+ ## 2.9.1
14
+
15
+ * **Bug Fix**
16
+ * Bump `ws` dependency to fix DoS vulnerability (closes [#130](https://github.com/webpack-contrib/webpack-bundle-analyzer/issues/130))
17
+
18
+ ## 2.9.0
19
+ * **New Feature**
20
+ * Show chunk sizes in sidebar (closes #91)
21
+
22
+ * **Bug Fix**
23
+ * Properly parse webpack bundles that use arrow functions as module wrappers (#108, @regiontog)
24
+
25
+ ## 2.8.3
26
+ * **Bug Fix**
27
+ * Correctly advertise port when using a random one (#89, @yannickcr)
28
+ * Add proper support for `multi` entries (fixes #92, #87)
29
+ * Support parsing of ESNext features (fixes #94)
30
+
31
+ ## 2.8.2
32
+ * **Improvement**
33
+ * Greatly improved accuracy of gzip sizes
34
+
35
+ * **Bug Fix**
36
+ * Generate report file in the bundle output directory when used with Webpack Dev Server (fixes #75)
37
+
13
38
  ## 2.8.1
14
39
  * **Improvement**
15
40
  * Improve warning message when analyzer client couldn't connect to WebSocket server
package/LICENSE CHANGED
@@ -1,6 +1,4 @@
1
- (The MIT License)
2
-
3
- Copyright (c) 2016 Yuriy Grunin <grunin.ya@ya.ru>
1
+ Copyright JS Foundation and other contributors
4
2
 
5
3
  Permission is hereby granted, free of charge, to any person obtaining
6
4
  a copy of this software and associated documentation files (the
package/README.md CHANGED
@@ -81,7 +81,7 @@ command:
81
81
  webpack --profile --json > stats.json
82
82
  ```
83
83
 
84
- If you're on Windows and using PowerShell, you can generate the stats file with this command to [avoid BOM issues](https://github.com/th0r/webpack-bundle-analyzer/issues/47):
84
+ If you're on Windows and using PowerShell, you can generate the stats file with this command to [avoid BOM issues](https://github.com/webpack-contrib/webpack-bundle-analyzer/issues/47):
85
85
 
86
86
  ```
87
87
  webpack --profile --json | Out-file 'stats.json' -Encoding OEM
@@ -108,14 +108,15 @@ as Uglify, then this value will reflect the minified size of your code.
108
108
 
109
109
  ### Gzip size
110
110
 
111
- This is the size of running the file(s) through gzip compression. Note that the
112
- value listed for the entire bundle is correct, but it will be slightly off if
113
- you look at the gzip value for individual files.
111
+ This is the size of running the file(s) through gzip compression.
114
112
 
115
- This is because of the way gzip works: the more files that are gzipped together,
116
- the more opportunities there are for things to be compressed. Accordingly, the
117
- gzip value reported for each individual file will be slightly _larger_ than the
118
- actual contribution that the file makes to the overall gzipped bundle.
113
+ ## Troubleshooting
114
+
115
+ ### I can't see all the dependencies in a chunk
116
+
117
+ This is a known caveat when `webpack.optimize.ModuleConcatenationPlugin` is used. The way `ModuleConcatenationPlugin` works is that it merges multiple modules into a single one, and so that resulting module doesn't have edges anymore.
118
+
119
+ If you are interested to drill down to exact dependencies, try analyzing your bundle without `ModuleConcatenationPlugin`. See [issue #115](https://github.com/webpack-contrib/webpack-bundle-analyzer/issues/115) for more discussion.
119
120
 
120
121
  ## Contributing
121
122
 
@@ -85,11 +85,7 @@ var BundleAnalyzerPlugin = function () {
85
85
  }, {
86
86
  key: 'generateStatsFile',
87
87
  value: function generateStatsFile(stats) {
88
- var statsFilepath = this.opts.statsFilename;
89
-
90
- if (!path.isAbsolute(statsFilepath)) {
91
- statsFilepath = path.resolve(this.compiler.outputPath, statsFilepath);
92
- }
88
+ var statsFilepath = path.resolve(this.compiler.outputPath, this.opts.statsFilename);
93
89
 
94
90
  mkdir.sync(path.dirname(statsFilepath));
95
91
 
@@ -126,7 +122,7 @@ var BundleAnalyzerPlugin = function () {
126
122
  value: function generateStaticReport(stats) {
127
123
  viewer.generateReport(stats, {
128
124
  openBrowser: this.opts.openAnalyzer,
129
- reportFilename: this.opts.reportFilename,
125
+ reportFilename: path.resolve(this.compiler.outputPath, this.opts.reportFilename),
130
126
  bundleDir: this.getBundleDirFromCompiler(),
131
127
  logger: this.logger,
132
128
  defaultSizes: this.opts.defaultSizes
package/lib/analyzer.js CHANGED
@@ -15,6 +15,7 @@ var _require2 = require('../lib/parseUtils'),
15
15
  parseBundle = _require2.parseBundle;
16
16
 
17
17
  var FILENAME_QUERY_REGEXP = /\?.*$/;
18
+ var MULTI_MODULE_REGEXP = /^multi /;
18
19
 
19
20
  module.exports = {
20
21
  getViewerData,
@@ -43,7 +44,6 @@ function getViewerData(bundleStats, bundleDir, opts) {
43
44
  });
44
45
 
45
46
  // Trying to parse bundle assets and get real module sizes if `bundleDir` is provided
46
- var parsedModuleSizes = null;
47
47
  var bundlesSources = null;
48
48
  var parsedModules = null;
49
49
 
@@ -68,15 +68,15 @@ function getViewerData(bundleStats, bundleDir, opts) {
68
68
  bundleInfo = null;
69
69
  }
70
70
 
71
- if (bundleInfo) {
72
- bundlesSources[statAsset.name] = bundleInfo.src;
73
- _.assign(parsedModules, bundleInfo.modules);
74
- } else {
71
+ if (!bundleInfo) {
75
72
  logger.warn(`\nCouldn't parse bundle asset "${assetFile}".\n` + 'Analyzer will use module sizes from stats file.\n');
76
73
  parsedModules = null;
77
74
  bundlesSources = null;
78
75
  break;
79
76
  }
77
+
78
+ bundlesSources[statAsset.name] = bundleInfo.src;
79
+ _.assign(parsedModules, bundleInfo.modules);
80
80
  }
81
81
  } catch (err) {
82
82
  _didIteratorError = true;
@@ -92,15 +92,6 @@ function getViewerData(bundleStats, bundleDir, opts) {
92
92
  }
93
93
  }
94
94
  }
95
-
96
- if (parsedModules) {
97
- parsedModuleSizes = _.mapValues(parsedModules, function (moduleSrc) {
98
- return {
99
- raw: moduleSrc.length,
100
- gzip: gzipSize.sync(moduleSrc)
101
- };
102
- });
103
- }
104
95
  }
105
96
 
106
97
  var assets = _.transform(bundleStats.assets, function (result, statAsset) {
@@ -115,9 +106,8 @@ function getViewerData(bundleStats, bundleDir, opts) {
115
106
  asset.modules = _(bundleStats.modules).filter(function (statModule) {
116
107
  return assetHasModule(statAsset, statModule);
117
108
  }).each(function (statModule) {
118
- if (parsedModuleSizes) {
119
- statModule.parsedSize = parsedModuleSizes[statModule.id].raw;
120
- statModule.gzipSize = parsedModuleSizes[statModule.id].gzip;
109
+ if (parsedModules) {
110
+ statModule.parsedSrc = parsedModules[statModule.id];
121
111
  }
122
112
  });
123
113
 
@@ -152,7 +142,7 @@ function createModulesTree(modules) {
152
142
  var root = new Folder('.');
153
143
 
154
144
  _.each(modules, function (module) {
155
- var path = getModulePath(module.name);
145
+ var path = getModulePath(module);
156
146
 
157
147
  if (path) {
158
148
  root.addModuleByPath(path, module);
@@ -162,10 +152,14 @@ function createModulesTree(modules) {
162
152
  return root;
163
153
  }
164
154
 
165
- function getModulePath(path) {
155
+ function getModulePath(module) {
156
+ if (MULTI_MODULE_REGEXP.test(module.identifier)) {
157
+ return [module.identifier];
158
+ }
159
+
166
160
  var parsedPath = _
167
161
  // Removing loaders from module path: they're joined by `!` and the last part is a raw module path
168
- .last(path.split('!'))
162
+ .last(module.name.split('!'))
169
163
  // Splitting module path into parts
170
164
  .split('/')
171
165
  // Removing first `.`
package/lib/parseUtils.js CHANGED
@@ -10,9 +10,14 @@ module.exports = {
10
10
  };
11
11
 
12
12
  function parseBundle(bundlePath) {
13
- var contentBuffer = fs.readFileSync(bundlePath);
14
- var contentStr = contentBuffer.toString('utf8');
15
- var ast = acorn.parse(contentStr, { sourceType: 'script' });
13
+ var content = fs.readFileSync(bundlePath, 'utf8');
14
+ var ast = acorn.parse(content, {
15
+ sourceType: 'script',
16
+ // I believe in a bright future of ECMAScript!
17
+ // Actually, it's set to `2050` to support the latest ECMAScript version that currently exists.
18
+ // Seems like `acorn` supports such weird option value.
19
+ ecmaVersion: 2050
20
+ });
16
21
 
17
22
  var walkState = {
18
23
  locations: null
@@ -63,9 +68,9 @@ function parseBundle(bundlePath) {
63
68
  }
64
69
 
65
70
  return {
66
- src: contentStr,
71
+ src: content,
67
72
  modules: _.mapValues(walkState.locations, function (loc) {
68
- return contentBuffer.toString('utf8', loc.start, loc.end);
73
+ return content.slice(loc.start, loc.end);
69
74
  })
70
75
  };
71
76
  }
@@ -114,7 +119,7 @@ function isArgumentArrayConcatContainingChunks(arg) {
114
119
  function isModuleWrapper(node) {
115
120
  return (
116
121
  // It's an anonymous function expression that wraps module
117
- node.type === 'FunctionExpression' && !node.id ||
122
+ (node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression') && !node.id ||
118
123
  // If `DedupePlugin` is used it can be an ID of duplicated module...
119
124
  isModuleId(node) ||
120
125
  // or an array of shape [<module_id>, ...args]
@@ -171,9 +176,5 @@ function getModulesLocationFromArrayConcat(arg) {
171
176
  }
172
177
 
173
178
  function getModuleLocation(node) {
174
- if (node.type === 'FunctionExpression') {
175
- node = node.body;
176
- }
177
-
178
179
  return _.pick(node, 'start', 'end');
179
180
  }
package/lib/tree.js CHANGED
@@ -12,6 +12,7 @@ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Cons
12
12
 
13
13
  var _ = require('lodash');
14
14
  var filesize = require('filesize');
15
+ var gzipSize = require('gzip-size');
15
16
 
16
17
  var Node = function () {
17
18
  function Node(name, parent) {
@@ -61,13 +62,13 @@ var Module = function (_Node) {
61
62
  _createClass(Module, [{
62
63
  key: 'mergeData',
63
64
  value: function mergeData(data) {
64
- var _this2 = this;
65
+ if (data.size) {
66
+ this.size += data.size;
67
+ }
65
68
 
66
- _.each(['size', 'parsedSize', 'gzipSize'], function (prop) {
67
- if (data[prop]) {
68
- _this2.data[prop] = (_this2.data[prop] || 0) + data[prop];
69
- }
70
- });
69
+ if (data.parsedSrc) {
70
+ this.src = (this.src || '') + data.parsedSrc;
71
+ }
71
72
  }
72
73
  }, {
73
74
  key: 'toString',
@@ -86,20 +87,36 @@ var Module = function (_Node) {
86
87
  gzipSize: this.gzipSize
87
88
  };
88
89
  }
90
+ }, {
91
+ key: 'src',
92
+ get: function get() {
93
+ return this.data.parsedSrc;
94
+ },
95
+ set: function set(value) {
96
+ this.data.parsedSrc = value;
97
+ delete this._gzipSize;
98
+ }
89
99
  }, {
90
100
  key: 'size',
91
101
  get: function get() {
92
102
  return this.data.size;
103
+ },
104
+ set: function set(value) {
105
+ this.data.size = value;
93
106
  }
94
107
  }, {
95
108
  key: 'parsedSize',
96
109
  get: function get() {
97
- return this.data.parsedSize;
110
+ return this.src ? this.src.length : undefined;
98
111
  }
99
112
  }, {
100
113
  key: 'gzipSize',
101
114
  get: function get() {
102
- return this.data.gzipSize;
115
+ if (!_.has(this, '_gzipSize')) {
116
+ this._gzipSize = this.src ? gzipSize.sync(this.src) : undefined;
117
+ }
118
+
119
+ return this._gzipSize;
103
120
  }
104
121
  }]);
105
122
 
@@ -112,10 +129,10 @@ var Folder = function (_Node2) {
112
129
  function Folder(name, parent) {
113
130
  _classCallCheck(this, Folder);
114
131
 
115
- var _this3 = _possibleConstructorReturn(this, (Folder.__proto__ || Object.getPrototypeOf(Folder)).call(this, name, parent));
132
+ var _this2 = _possibleConstructorReturn(this, (Folder.__proto__ || Object.getPrototypeOf(Folder)).call(this, name, parent));
116
133
 
117
- _this3.children = Object.create(null);
118
- return _this3;
134
+ _this2.children = Object.create(null);
135
+ return _this2;
119
136
  }
120
137
 
121
138
  _createClass(Folder, [{
@@ -130,7 +147,7 @@ var Folder = function (_Node2) {
130
147
 
131
148
  this.children[name] = folder;
132
149
  delete this._size;
133
- delete this._parsedSize;
150
+ delete this._src;
134
151
 
135
152
  return folder;
136
153
  }
@@ -153,7 +170,7 @@ var Folder = function (_Node2) {
153
170
  }
154
171
 
155
172
  delete this._size;
156
- delete this._parsedSize;
173
+ delete this._src;
157
174
 
158
175
  return true;
159
176
  }
@@ -189,11 +206,12 @@ var Folder = function (_Node2) {
189
206
  key: 'walk',
190
207
  value: function walk(walker) {
191
208
  var state = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
209
+ var deep = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
192
210
 
193
211
  var stopped = false;
194
212
 
195
213
  _.each(this.children, function (child) {
196
- if (child.walk) {
214
+ if (deep && child.walk) {
197
215
  state = child.walk(walker, state, stop);
198
216
  } else {
199
217
  state = walker(child, state, stop);
@@ -237,13 +255,25 @@ var Folder = function (_Node2) {
237
255
  groups: _.invokeMap(this.children, 'toChartData')
238
256
  };
239
257
  }
258
+ }, {
259
+ key: 'src',
260
+ get: function get() {
261
+ if (!_.has(this, '_src')) {
262
+ this._src = this.walk(function (node, src, stop) {
263
+ if (node.src === undefined) return stop(undefined);
264
+ return src += node.src;
265
+ }, '', false);
266
+ }
267
+
268
+ return this._src;
269
+ }
240
270
  }, {
241
271
  key: 'size',
242
272
  get: function get() {
243
273
  if (!_.has(this, '_size')) {
244
274
  this._size = this.walk(function (node, size) {
245
275
  return size + node.size;
246
- }, 0);
276
+ }, 0, false);
247
277
  }
248
278
 
249
279
  return this._size;
@@ -251,29 +281,13 @@ var Folder = function (_Node2) {
251
281
  }, {
252
282
  key: 'parsedSize',
253
283
  get: function get() {
254
- if (!_.has(this, '_parsedSize')) {
255
- this._parsedSize = this.walk(function (node, size, stop) {
256
- if (node.parsedSize === undefined) {
257
- return stop(undefined);
258
- }
259
-
260
- return size + node.parsedSize;
261
- }, 0);
262
- }
263
-
264
- return this._parsedSize;
284
+ return this.src ? this.src.length : undefined;
265
285
  }
266
286
  }, {
267
287
  key: 'gzipSize',
268
288
  get: function get() {
269
289
  if (!_.has(this, '_gzipSize')) {
270
- this._gzipSize = this.walk(function (node, size, stop) {
271
- if (node.gzipSize === undefined) {
272
- return stop(undefined);
273
- }
274
-
275
- return size + node.gzipSize;
276
- }, 0);
290
+ this._gzipSize = this.src ? gzipSize.sync(this.src) : undefined;
277
291
  }
278
292
 
279
293
  return this._gzipSize;
package/lib/viewer.js CHANGED
@@ -45,7 +45,7 @@ var startServer = function () {
45
45
  server.listen(port, host, function () {
46
46
  resolve();
47
47
 
48
- var url = `http://${host}:${port}`;
48
+ var url = `http://${host}:${server.address().port}`;
49
49
 
50
50
  logger.info(`${bold('Webpack Bundle Analyzer')} is started at ${bold(url)}\n` + `Use ${bold('Ctrl+C')} to close it`);
51
51
 
@@ -139,11 +139,7 @@ function generateReport(bundleStats, opts) {
139
139
  }, function (err, reportHtml) {
140
140
  if (err) return logger.error(err);
141
141
 
142
- var reportFilepath = reportFilename;
143
-
144
- if (!path.isAbsolute(reportFilepath)) {
145
- reportFilepath = path.resolve(bundleDir || process.cwd(), reportFilepath);
146
- }
142
+ var reportFilepath = path.resolve(bundleDir || process.cwd(), reportFilename);
147
143
 
148
144
  mkdir.sync(path.dirname(reportFilepath));
149
145
  fs.writeFileSync(reportFilepath, reportHtml);
package/package.json CHANGED
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "webpack-bundle-analyzer",
3
- "version": "2.8.1",
3
+ "version": "2.9.1",
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",
7
- "homepage": "https://github.com/th0r/webpack-bundle-analyzer",
8
- "changelog": "https://github.com/th0r/webpack-bundle-analyzer/blob/master/CHANGELOG.md",
7
+ "homepage": "https://github.com/webpack-contrib/webpack-bundle-analyzer",
8
+ "changelog": "https://github.com/webpack-contrib/webpack-bundle-analyzer/blob/master/CHANGELOG.md",
9
9
  "bugs": {
10
- "url": "https://github.com/th0r/webpack-bundle-analyzer/issues"
10
+ "url": "https://github.com/webpack-contrib/webpack-bundle-analyzer/issues"
11
11
  },
12
12
  "repository": {
13
13
  "type": "git",
14
- "url": "git+https://github.com/th0r/webpack-bundle-analyzer.git"
14
+ "url": "git+https://github.com/webpack-contrib/webpack-bundle-analyzer.git"
15
15
  },
16
16
  "main": "lib/index.js",
17
17
  "bin": "lib/bin/analyzer.js",
@@ -33,7 +33,7 @@
33
33
  "views"
34
34
  ],
35
35
  "dependencies": {
36
- "acorn": "^5.0.3",
36
+ "acorn": "^5.1.1",
37
37
  "chalk": "^1.1.3",
38
38
  "commander": "^2.9.0",
39
39
  "ejs": "^2.5.6",
@@ -43,7 +43,7 @@
43
43
  "lodash": "^4.17.4",
44
44
  "mkdirp": "^0.5.1",
45
45
  "opener": "^1.4.3",
46
- "ws": "^2.3.1"
46
+ "ws": "^3.3.1"
47
47
  },
48
48
  "devDependencies": {
49
49
  "babel-core": "6.24.1",
@@ -72,7 +72,8 @@
72
72
  "sinon": "2.2.0",
73
73
  "stream-combiner2": "1.1.1",
74
74
  "style-loader": "0.17.0",
75
- "webpack": "2.5.1"
75
+ "webpack": "2.5.1",
76
+ "webpack-dev-server": "2.4.5"
76
77
  },
77
78
  "keywords": [
78
79
  "webpack",