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 +25 -0
- package/LICENSE +1 -3
- package/README.md +9 -8
- package/lib/BundleAnalyzerPlugin.js +2 -6
- package/lib/analyzer.js +14 -20
- package/lib/parseUtils.js +11 -10
- package/lib/tree.js +47 -33
- package/lib/viewer.js +2 -6
- package/package.json +9 -8
- package/public/viewer.js +1 -1
- package/public/viewer.js.map +1 -1
- package/src/BundleAnalyzerPlugin.js +2 -6
- package/src/analyzer.js +13 -19
- package/src/parseUtils.js +11 -10
- package/src/tree.js +45 -30
- package/src/viewer.js +2 -6
|
@@ -62,11 +62,7 @@ class BundleAnalyzerPlugin {
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
generateStatsFile(stats) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
if (!path.isAbsolute(statsFilepath)) {
|
|
68
|
-
statsFilepath = path.resolve(this.compiler.outputPath, statsFilepath);
|
|
69
|
-
}
|
|
65
|
+
const statsFilepath = path.resolve(this.compiler.outputPath, this.opts.statsFilename);
|
|
70
66
|
|
|
71
67
|
mkdir.sync(path.dirname(statsFilepath));
|
|
72
68
|
|
|
@@ -98,7 +94,7 @@ class BundleAnalyzerPlugin {
|
|
|
98
94
|
generateStaticReport(stats) {
|
|
99
95
|
viewer.generateReport(stats, {
|
|
100
96
|
openBrowser: this.opts.openAnalyzer,
|
|
101
|
-
reportFilename: this.opts.reportFilename,
|
|
97
|
+
reportFilename: path.resolve(this.compiler.outputPath, this.opts.reportFilename),
|
|
102
98
|
bundleDir: this.getBundleDirFromCompiler(),
|
|
103
99
|
logger: this.logger,
|
|
104
100
|
defaultSizes: this.opts.defaultSizes
|
package/src/analyzer.js
CHANGED
|
@@ -9,6 +9,7 @@ const { Folder } = require('../lib/tree');
|
|
|
9
9
|
const { parseBundle } = require('../lib/parseUtils');
|
|
10
10
|
|
|
11
11
|
const FILENAME_QUERY_REGEXP = /\?.*$/;
|
|
12
|
+
const MULTI_MODULE_REGEXP = /^multi /;
|
|
12
13
|
|
|
13
14
|
module.exports = {
|
|
14
15
|
getViewerData,
|
|
@@ -35,7 +36,6 @@ function getViewerData(bundleStats, bundleDir, opts) {
|
|
|
35
36
|
});
|
|
36
37
|
|
|
37
38
|
// Trying to parse bundle assets and get real module sizes if `bundleDir` is provided
|
|
38
|
-
let parsedModuleSizes = null;
|
|
39
39
|
let bundlesSources = null;
|
|
40
40
|
let parsedModules = null;
|
|
41
41
|
|
|
@@ -53,10 +53,7 @@ function getViewerData(bundleStats, bundleDir, opts) {
|
|
|
53
53
|
bundleInfo = null;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
if (bundleInfo) {
|
|
57
|
-
bundlesSources[statAsset.name] = bundleInfo.src;
|
|
58
|
-
_.assign(parsedModules, bundleInfo.modules);
|
|
59
|
-
} else {
|
|
56
|
+
if (!bundleInfo) {
|
|
60
57
|
logger.warn(
|
|
61
58
|
`\nCouldn't parse bundle asset "${assetFile}".\n` +
|
|
62
59
|
'Analyzer will use module sizes from stats file.\n'
|
|
@@ -65,15 +62,9 @@ function getViewerData(bundleStats, bundleDir, opts) {
|
|
|
65
62
|
bundlesSources = null;
|
|
66
63
|
break;
|
|
67
64
|
}
|
|
68
|
-
}
|
|
69
65
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
moduleSrc => ({
|
|
73
|
-
raw: moduleSrc.length,
|
|
74
|
-
gzip: gzipSize.sync(moduleSrc)
|
|
75
|
-
})
|
|
76
|
-
);
|
|
66
|
+
bundlesSources[statAsset.name] = bundleInfo.src;
|
|
67
|
+
_.assign(parsedModules, bundleInfo.modules);
|
|
77
68
|
}
|
|
78
69
|
}
|
|
79
70
|
|
|
@@ -89,9 +80,8 @@ function getViewerData(bundleStats, bundleDir, opts) {
|
|
|
89
80
|
asset.modules = _(bundleStats.modules)
|
|
90
81
|
.filter(statModule => assetHasModule(statAsset, statModule))
|
|
91
82
|
.each(statModule => {
|
|
92
|
-
if (
|
|
93
|
-
statModule.
|
|
94
|
-
statModule.gzipSize = parsedModuleSizes[statModule.id].gzip;
|
|
83
|
+
if (parsedModules) {
|
|
84
|
+
statModule.parsedSrc = parsedModules[statModule.id];
|
|
95
85
|
}
|
|
96
86
|
});
|
|
97
87
|
|
|
@@ -128,7 +118,7 @@ function createModulesTree(modules) {
|
|
|
128
118
|
const root = new Folder('.');
|
|
129
119
|
|
|
130
120
|
_.each(modules, module => {
|
|
131
|
-
const path = getModulePath(module
|
|
121
|
+
const path = getModulePath(module);
|
|
132
122
|
|
|
133
123
|
if (path) {
|
|
134
124
|
root.addModuleByPath(path, module);
|
|
@@ -138,10 +128,14 @@ function createModulesTree(modules) {
|
|
|
138
128
|
return root;
|
|
139
129
|
}
|
|
140
130
|
|
|
141
|
-
function getModulePath(
|
|
131
|
+
function getModulePath(module) {
|
|
132
|
+
if (MULTI_MODULE_REGEXP.test(module.identifier)) {
|
|
133
|
+
return [module.identifier];
|
|
134
|
+
}
|
|
135
|
+
|
|
142
136
|
const parsedPath = _
|
|
143
137
|
// Removing loaders from module path: they're joined by `!` and the last part is a raw module path
|
|
144
|
-
.last(
|
|
138
|
+
.last(module.name.split('!'))
|
|
145
139
|
// Splitting module path into parts
|
|
146
140
|
.split('/')
|
|
147
141
|
// Removing first `.`
|
package/src/parseUtils.js
CHANGED
|
@@ -8,9 +8,14 @@ module.exports = {
|
|
|
8
8
|
};
|
|
9
9
|
|
|
10
10
|
function parseBundle(bundlePath) {
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
|
|
11
|
+
const content = fs.readFileSync(bundlePath, 'utf8');
|
|
12
|
+
const ast = acorn.parse(content, {
|
|
13
|
+
sourceType: 'script',
|
|
14
|
+
// I believe in a bright future of ECMAScript!
|
|
15
|
+
// Actually, it's set to `2050` to support the latest ECMAScript version that currently exists.
|
|
16
|
+
// Seems like `acorn` supports such weird option value.
|
|
17
|
+
ecmaVersion: 2050
|
|
18
|
+
});
|
|
14
19
|
|
|
15
20
|
const walkState = {
|
|
16
21
|
locations: null
|
|
@@ -78,9 +83,9 @@ function parseBundle(bundlePath) {
|
|
|
78
83
|
}
|
|
79
84
|
|
|
80
85
|
return {
|
|
81
|
-
src:
|
|
86
|
+
src: content,
|
|
82
87
|
modules: _.mapValues(walkState.locations,
|
|
83
|
-
loc =>
|
|
88
|
+
loc => content.slice(loc.start, loc.end)
|
|
84
89
|
)
|
|
85
90
|
};
|
|
86
91
|
}
|
|
@@ -139,7 +144,7 @@ function isArgumentArrayConcatContainingChunks(arg) {
|
|
|
139
144
|
function isModuleWrapper(node) {
|
|
140
145
|
return (
|
|
141
146
|
// It's an anonymous function expression that wraps module
|
|
142
|
-
(node.type === 'FunctionExpression' && !node.id) ||
|
|
147
|
+
((node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression') && !node.id) ||
|
|
143
148
|
// If `DedupePlugin` is used it can be an ID of duplicated module...
|
|
144
149
|
isModuleId(node) ||
|
|
145
150
|
// or an array of shape [<module_id>, ...args]
|
|
@@ -196,9 +201,5 @@ function getModulesLocationFromArrayConcat(arg) {
|
|
|
196
201
|
}
|
|
197
202
|
|
|
198
203
|
function getModuleLocation(node) {
|
|
199
|
-
if (node.type === 'FunctionExpression') {
|
|
200
|
-
node = node.body;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
204
|
return _.pick(node, 'start', 'end');
|
|
204
205
|
}
|
package/src/tree.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const _ = require('lodash');
|
|
2
2
|
const filesize = require('filesize');
|
|
3
|
+
const gzipSize = require('gzip-size');
|
|
3
4
|
|
|
4
5
|
class Node {
|
|
5
6
|
|
|
@@ -35,24 +36,43 @@ class Module extends Node {
|
|
|
35
36
|
this.data = data;
|
|
36
37
|
}
|
|
37
38
|
|
|
39
|
+
get src() {
|
|
40
|
+
return this.data.parsedSrc;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
set src(value) {
|
|
44
|
+
this.data.parsedSrc = value;
|
|
45
|
+
delete this._gzipSize;
|
|
46
|
+
}
|
|
47
|
+
|
|
38
48
|
get size() {
|
|
39
49
|
return this.data.size;
|
|
40
50
|
}
|
|
41
51
|
|
|
52
|
+
set size(value) {
|
|
53
|
+
this.data.size = value;
|
|
54
|
+
}
|
|
55
|
+
|
|
42
56
|
get parsedSize() {
|
|
43
|
-
return this.
|
|
57
|
+
return this.src ? this.src.length : undefined;
|
|
44
58
|
}
|
|
45
59
|
|
|
46
60
|
get gzipSize() {
|
|
47
|
-
|
|
61
|
+
if (!_.has(this, '_gzipSize')) {
|
|
62
|
+
this._gzipSize = this.src ? gzipSize.sync(this.src) : undefined;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return this._gzipSize;
|
|
48
66
|
}
|
|
49
67
|
|
|
50
68
|
mergeData(data) {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
69
|
+
if (data.size) {
|
|
70
|
+
this.size += data.size;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (data.parsedSrc) {
|
|
74
|
+
this.src = (this.src || '') + data.parsedSrc;
|
|
75
|
+
}
|
|
56
76
|
}
|
|
57
77
|
|
|
58
78
|
toString(indent) {
|
|
@@ -80,37 +100,32 @@ class Folder extends Node {
|
|
|
80
100
|
this.children = Object.create(null);
|
|
81
101
|
}
|
|
82
102
|
|
|
103
|
+
get src() {
|
|
104
|
+
if (!_.has(this, '_src')) {
|
|
105
|
+
this._src = this.walk((node, src, stop) => {
|
|
106
|
+
if (node.src === undefined) return stop(undefined);
|
|
107
|
+
return (src += node.src);
|
|
108
|
+
}, '', false);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return this._src;
|
|
112
|
+
}
|
|
113
|
+
|
|
83
114
|
get size() {
|
|
84
115
|
if (!_.has(this, '_size')) {
|
|
85
|
-
this._size = this.walk((node, size) => (size + node.size), 0);
|
|
116
|
+
this._size = this.walk((node, size) => (size + node.size), 0, false);
|
|
86
117
|
}
|
|
87
118
|
|
|
88
119
|
return this._size;
|
|
89
120
|
}
|
|
90
121
|
|
|
91
122
|
get parsedSize() {
|
|
92
|
-
|
|
93
|
-
this._parsedSize = this.walk((node, size, stop) => {
|
|
94
|
-
if (node.parsedSize === undefined) {
|
|
95
|
-
return stop(undefined);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
return (size + node.parsedSize);
|
|
99
|
-
}, 0);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
return this._parsedSize;
|
|
123
|
+
return this.src ? this.src.length : undefined;
|
|
103
124
|
}
|
|
104
125
|
|
|
105
126
|
get gzipSize() {
|
|
106
127
|
if (!_.has(this, '_gzipSize')) {
|
|
107
|
-
this._gzipSize = this.
|
|
108
|
-
if (node.gzipSize === undefined) {
|
|
109
|
-
return stop(undefined);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
return (size + node.gzipSize);
|
|
113
|
-
}, 0);
|
|
128
|
+
this._gzipSize = this.src ? gzipSize.sync(this.src) : undefined;
|
|
114
129
|
}
|
|
115
130
|
|
|
116
131
|
return this._gzipSize;
|
|
@@ -125,7 +140,7 @@ class Folder extends Node {
|
|
|
125
140
|
|
|
126
141
|
this.children[name] = folder;
|
|
127
142
|
delete this._size;
|
|
128
|
-
delete this.
|
|
143
|
+
delete this._src;
|
|
129
144
|
|
|
130
145
|
return folder;
|
|
131
146
|
}
|
|
@@ -147,7 +162,7 @@ class Folder extends Node {
|
|
|
147
162
|
}
|
|
148
163
|
|
|
149
164
|
delete this._size;
|
|
150
|
-
delete this.
|
|
165
|
+
delete this._src;
|
|
151
166
|
|
|
152
167
|
return true;
|
|
153
168
|
}
|
|
@@ -177,11 +192,11 @@ class Folder extends Node {
|
|
|
177
192
|
currentFolder.addModule(fileName, data);
|
|
178
193
|
}
|
|
179
194
|
|
|
180
|
-
walk(walker, state = {}) {
|
|
195
|
+
walk(walker, state = {}, deep = true) {
|
|
181
196
|
let stopped = false;
|
|
182
197
|
|
|
183
198
|
_.each(this.children, child => {
|
|
184
|
-
if (child.walk) {
|
|
199
|
+
if (deep && child.walk) {
|
|
185
200
|
state = child.walk(walker, state, stop);
|
|
186
201
|
} else {
|
|
187
202
|
state = walker(child, state, stop);
|
package/src/viewer.js
CHANGED
|
@@ -59,7 +59,7 @@ async function startServer(bundleStats, opts) {
|
|
|
59
59
|
server.listen(port, host, () => {
|
|
60
60
|
resolve();
|
|
61
61
|
|
|
62
|
-
const url = `http://${host}:${port}`;
|
|
62
|
+
const url = `http://${host}:${server.address().port}`;
|
|
63
63
|
|
|
64
64
|
logger.info(
|
|
65
65
|
`${bold('Webpack Bundle Analyzer')} is started at ${bold(url)}\n` +
|
|
@@ -122,11 +122,7 @@ function generateReport(bundleStats, opts) {
|
|
|
122
122
|
(err, reportHtml) => {
|
|
123
123
|
if (err) return logger.error(err);
|
|
124
124
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
if (!path.isAbsolute(reportFilepath)) {
|
|
128
|
-
reportFilepath = path.resolve(bundleDir || process.cwd(), reportFilepath);
|
|
129
|
-
}
|
|
125
|
+
const reportFilepath = path.resolve(bundleDir || process.cwd(), reportFilename);
|
|
130
126
|
|
|
131
127
|
mkdir.sync(path.dirname(reportFilepath));
|
|
132
128
|
fs.writeFileSync(reportFilepath, reportHtml);
|