webpack-bundle-analyzer 4.4.2 → 4.5.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 +10 -0
- package/README.md +2 -2
- package/lib/bin/analyzer.js +4 -4
- package/package.json +30 -33
- package/public/viewer.js +4 -3
- package/public/viewer.js.LICENSE.txt +6 -1
- package/public/viewer.js.map +1 -1
- package/src/BundleAnalyzerPlugin.js +0 -154
- package/src/Logger.js +0 -51
- package/src/analyzer.js +0 -213
- package/src/bin/analyzer.js +0 -168
- package/src/index.js +0 -6
- package/src/parseUtils.js +0 -361
- package/src/statsUtils.js +0 -82
- package/src/template.js +0 -65
- package/src/tree/BaseFolder.js +0 -118
- package/src/tree/ConcatenatedModule.js +0 -72
- package/src/tree/ContentFolder.js +0 -35
- package/src/tree/ContentModule.js +0 -33
- package/src/tree/Folder.js +0 -64
- package/src/tree/Module.js +0 -63
- package/src/tree/Node.js +0 -24
- package/src/tree/utils.js +0 -21
- package/src/utils.js +0 -65
- package/src/viewer.js +0 -189
package/src/parseUtils.js
DELETED
|
@@ -1,361 +0,0 @@
|
|
|
1
|
-
const fs = require('fs');
|
|
2
|
-
const _ = require('lodash');
|
|
3
|
-
const acorn = require('acorn');
|
|
4
|
-
const walk = require('acorn-walk');
|
|
5
|
-
|
|
6
|
-
module.exports = {
|
|
7
|
-
parseBundle
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
function parseBundle(bundlePath) {
|
|
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
|
-
});
|
|
19
|
-
|
|
20
|
-
const walkState = {
|
|
21
|
-
locations: null,
|
|
22
|
-
expressionStatementDepth: 0
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
walk.recursive(
|
|
26
|
-
ast,
|
|
27
|
-
walkState,
|
|
28
|
-
{
|
|
29
|
-
ExpressionStatement(node, state, c) {
|
|
30
|
-
if (state.locations) return;
|
|
31
|
-
|
|
32
|
-
state.expressionStatementDepth++;
|
|
33
|
-
|
|
34
|
-
if (
|
|
35
|
-
// Webpack 5 stores modules in the the top-level IIFE
|
|
36
|
-
state.expressionStatementDepth === 1 &&
|
|
37
|
-
ast.body.includes(node) &&
|
|
38
|
-
isIIFE(node)
|
|
39
|
-
) {
|
|
40
|
-
const fn = getIIFECallExpression(node);
|
|
41
|
-
|
|
42
|
-
if (
|
|
43
|
-
// It should not contain neither arguments
|
|
44
|
-
fn.arguments.length === 0 &&
|
|
45
|
-
// ...nor parameters
|
|
46
|
-
fn.callee.params.length === 0
|
|
47
|
-
) {
|
|
48
|
-
// Modules are stored in the very first variable declaration as hash
|
|
49
|
-
const firstVariableDeclaration = fn.callee.body.body.find(node => node.type === 'VariableDeclaration');
|
|
50
|
-
|
|
51
|
-
if (firstVariableDeclaration) {
|
|
52
|
-
for (const declaration of firstVariableDeclaration.declarations) {
|
|
53
|
-
if (declaration.init) {
|
|
54
|
-
state.locations = getModulesLocations(declaration.init);
|
|
55
|
-
|
|
56
|
-
if (state.locations) {
|
|
57
|
-
break;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
if (!state.locations) {
|
|
66
|
-
c(node.expression, state);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
state.expressionStatementDepth--;
|
|
70
|
-
},
|
|
71
|
-
|
|
72
|
-
AssignmentExpression(node, state) {
|
|
73
|
-
if (state.locations) return;
|
|
74
|
-
|
|
75
|
-
// Modules are stored in exports.modules:
|
|
76
|
-
// exports.modules = {};
|
|
77
|
-
const {left, right} = node;
|
|
78
|
-
|
|
79
|
-
if (
|
|
80
|
-
left &&
|
|
81
|
-
left.object && left.object.name === 'exports' &&
|
|
82
|
-
left.property && left.property.name === 'modules' &&
|
|
83
|
-
isModulesHash(right)
|
|
84
|
-
) {
|
|
85
|
-
state.locations = getModulesLocations(right);
|
|
86
|
-
}
|
|
87
|
-
},
|
|
88
|
-
|
|
89
|
-
CallExpression(node, state, c) {
|
|
90
|
-
if (state.locations) return;
|
|
91
|
-
|
|
92
|
-
const args = node.arguments;
|
|
93
|
-
|
|
94
|
-
// Main chunk with webpack loader.
|
|
95
|
-
// Modules are stored in first argument:
|
|
96
|
-
// (function (...) {...})(<modules>)
|
|
97
|
-
if (
|
|
98
|
-
node.callee.type === 'FunctionExpression' &&
|
|
99
|
-
!node.callee.id &&
|
|
100
|
-
args.length === 1 &&
|
|
101
|
-
isSimpleModulesList(args[0])
|
|
102
|
-
) {
|
|
103
|
-
state.locations = getModulesLocations(args[0]);
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// Async Webpack < v4 chunk without webpack loader.
|
|
108
|
-
// webpackJsonp([<chunks>], <modules>, ...)
|
|
109
|
-
// As function name may be changed with `output.jsonpFunction` option we can't rely on it's default name.
|
|
110
|
-
if (
|
|
111
|
-
node.callee.type === 'Identifier' &&
|
|
112
|
-
mayBeAsyncChunkArguments(args) &&
|
|
113
|
-
isModulesList(args[1])
|
|
114
|
-
) {
|
|
115
|
-
state.locations = getModulesLocations(args[1]);
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// Async Webpack v4 chunk without webpack loader.
|
|
120
|
-
// (window.webpackJsonp=window.webpackJsonp||[]).push([[<chunks>], <modules>, ...]);
|
|
121
|
-
// As function name may be changed with `output.jsonpFunction` option we can't rely on it's default name.
|
|
122
|
-
if (isAsyncChunkPushExpression(node)) {
|
|
123
|
-
state.locations = getModulesLocations(args[0].elements[1]);
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// Webpack v4 WebWorkerChunkTemplatePlugin
|
|
128
|
-
// globalObject.chunkCallbackName([<chunks>],<modules>, ...);
|
|
129
|
-
// Both globalObject and chunkCallbackName can be changed through the config, so we can't check them.
|
|
130
|
-
if (isAsyncWebWorkerChunkExpression(node)) {
|
|
131
|
-
state.locations = getModulesLocations(args[1]);
|
|
132
|
-
return;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
// Walking into arguments because some of plugins (e.g. `DedupePlugin`) or some Webpack
|
|
137
|
-
// features (e.g. `umd` library output) can wrap modules list into additional IIFE.
|
|
138
|
-
args.forEach(arg => c(arg, state));
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
);
|
|
142
|
-
|
|
143
|
-
let modules;
|
|
144
|
-
|
|
145
|
-
if (walkState.locations) {
|
|
146
|
-
modules = _.mapValues(walkState.locations,
|
|
147
|
-
loc => content.slice(loc.start, loc.end)
|
|
148
|
-
);
|
|
149
|
-
} else {
|
|
150
|
-
modules = {};
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
return {
|
|
154
|
-
modules,
|
|
155
|
-
src: content,
|
|
156
|
-
runtimeSrc: getBundleRuntime(content, walkState.locations)
|
|
157
|
-
};
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Returns bundle source except modules
|
|
162
|
-
*/
|
|
163
|
-
function getBundleRuntime(content, modulesLocations) {
|
|
164
|
-
const sortedLocations = Object.values(modulesLocations || {})
|
|
165
|
-
.sort((a, b) => a.start - b.start);
|
|
166
|
-
|
|
167
|
-
let result = '';
|
|
168
|
-
let lastIndex = 0;
|
|
169
|
-
|
|
170
|
-
for (const {start, end} of sortedLocations) {
|
|
171
|
-
result += content.slice(lastIndex, start);
|
|
172
|
-
lastIndex = end;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
return result + content.slice(lastIndex, content.length);
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
function isIIFE(node) {
|
|
179
|
-
return (
|
|
180
|
-
node.type === 'ExpressionStatement' &&
|
|
181
|
-
(
|
|
182
|
-
node.expression.type === 'CallExpression' ||
|
|
183
|
-
(node.expression.type === 'UnaryExpression' && node.expression.argument.type === 'CallExpression')
|
|
184
|
-
)
|
|
185
|
-
);
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
function getIIFECallExpression(node) {
|
|
189
|
-
if (node.expression.type === 'UnaryExpression') {
|
|
190
|
-
return node.expression.argument;
|
|
191
|
-
} else {
|
|
192
|
-
return node.expression;
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
function isModulesList(node) {
|
|
197
|
-
return (
|
|
198
|
-
isSimpleModulesList(node) ||
|
|
199
|
-
// Modules are contained in expression `Array([minimum ID]).concat([<module>, <module>, ...])`
|
|
200
|
-
isOptimizedModulesArray(node)
|
|
201
|
-
);
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
function isSimpleModulesList(node) {
|
|
205
|
-
return (
|
|
206
|
-
// Modules are contained in hash. Keys are module ids.
|
|
207
|
-
isModulesHash(node) ||
|
|
208
|
-
// Modules are contained in array. Indexes are module ids.
|
|
209
|
-
isModulesArray(node)
|
|
210
|
-
);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
function isModulesHash(node) {
|
|
214
|
-
return (
|
|
215
|
-
node.type === 'ObjectExpression' &&
|
|
216
|
-
node.properties
|
|
217
|
-
.map(node => node.value)
|
|
218
|
-
.every(isModuleWrapper)
|
|
219
|
-
);
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
function isModulesArray(node) {
|
|
223
|
-
return (
|
|
224
|
-
node.type === 'ArrayExpression' &&
|
|
225
|
-
node.elements.every(elem =>
|
|
226
|
-
// Some of array items may be skipped because there is no module with such id
|
|
227
|
-
!elem ||
|
|
228
|
-
isModuleWrapper(elem)
|
|
229
|
-
)
|
|
230
|
-
);
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
function isOptimizedModulesArray(node) {
|
|
234
|
-
// Checking whether modules are contained in `Array(<minimum ID>).concat(...modules)` array:
|
|
235
|
-
// https://github.com/webpack/webpack/blob/v1.14.0/lib/Template.js#L91
|
|
236
|
-
// The `<minimum ID>` + array indexes are module ids
|
|
237
|
-
return (
|
|
238
|
-
node.type === 'CallExpression' &&
|
|
239
|
-
node.callee.type === 'MemberExpression' &&
|
|
240
|
-
// Make sure the object called is `Array(<some number>)`
|
|
241
|
-
node.callee.object.type === 'CallExpression' &&
|
|
242
|
-
node.callee.object.callee.type === 'Identifier' &&
|
|
243
|
-
node.callee.object.callee.name === 'Array' &&
|
|
244
|
-
node.callee.object.arguments.length === 1 &&
|
|
245
|
-
isNumericId(node.callee.object.arguments[0]) &&
|
|
246
|
-
// Make sure the property X called for `Array(<some number>).X` is `concat`
|
|
247
|
-
node.callee.property.type === 'Identifier' &&
|
|
248
|
-
node.callee.property.name === 'concat' &&
|
|
249
|
-
// Make sure exactly one array is passed in to `concat`
|
|
250
|
-
node.arguments.length === 1 &&
|
|
251
|
-
isModulesArray(node.arguments[0])
|
|
252
|
-
);
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
function isModuleWrapper(node) {
|
|
256
|
-
return (
|
|
257
|
-
// It's an anonymous function expression that wraps module
|
|
258
|
-
((node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression') && !node.id) ||
|
|
259
|
-
// If `DedupePlugin` is used it can be an ID of duplicated module...
|
|
260
|
-
isModuleId(node) ||
|
|
261
|
-
// or an array of shape [<module_id>, ...args]
|
|
262
|
-
(node.type === 'ArrayExpression' && node.elements.length > 1 && isModuleId(node.elements[0]))
|
|
263
|
-
);
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
function isModuleId(node) {
|
|
267
|
-
return (node.type === 'Literal' && (isNumericId(node) || typeof node.value === 'string'));
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
function isNumericId(node) {
|
|
271
|
-
return (node.type === 'Literal' && Number.isInteger(node.value) && node.value >= 0);
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
function isChunkIds(node) {
|
|
275
|
-
// Array of numeric or string ids. Chunk IDs are strings when NamedChunksPlugin is used
|
|
276
|
-
return (
|
|
277
|
-
node.type === 'ArrayExpression' &&
|
|
278
|
-
node.elements.every(isModuleId)
|
|
279
|
-
);
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
function isAsyncChunkPushExpression(node) {
|
|
283
|
-
const {
|
|
284
|
-
callee,
|
|
285
|
-
arguments: args
|
|
286
|
-
} = node;
|
|
287
|
-
|
|
288
|
-
return (
|
|
289
|
-
callee.type === 'MemberExpression' &&
|
|
290
|
-
callee.property.name === 'push' &&
|
|
291
|
-
callee.object.type === 'AssignmentExpression' &&
|
|
292
|
-
args.length === 1 &&
|
|
293
|
-
args[0].type === 'ArrayExpression' &&
|
|
294
|
-
mayBeAsyncChunkArguments(args[0].elements) &&
|
|
295
|
-
isModulesList(args[0].elements[1])
|
|
296
|
-
);
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
function mayBeAsyncChunkArguments(args) {
|
|
300
|
-
return (
|
|
301
|
-
args.length >= 2 &&
|
|
302
|
-
isChunkIds(args[0])
|
|
303
|
-
);
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
function isAsyncWebWorkerChunkExpression(node) {
|
|
307
|
-
const {callee, type, arguments: args} = node;
|
|
308
|
-
|
|
309
|
-
return (
|
|
310
|
-
type === 'CallExpression' &&
|
|
311
|
-
callee.type === 'MemberExpression' &&
|
|
312
|
-
args.length === 2 &&
|
|
313
|
-
isChunkIds(args[0]) &&
|
|
314
|
-
isModulesList(args[1])
|
|
315
|
-
);
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
function getModulesLocations(node) {
|
|
319
|
-
if (node.type === 'ObjectExpression') {
|
|
320
|
-
// Modules hash
|
|
321
|
-
const modulesNodes = node.properties;
|
|
322
|
-
|
|
323
|
-
return modulesNodes.reduce((result, moduleNode) => {
|
|
324
|
-
const moduleId = moduleNode.key.name || moduleNode.key.value;
|
|
325
|
-
|
|
326
|
-
result[moduleId] = getModuleLocation(moduleNode.value);
|
|
327
|
-
return result;
|
|
328
|
-
}, {});
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
const isOptimizedArray = (node.type === 'CallExpression');
|
|
332
|
-
|
|
333
|
-
if (node.type === 'ArrayExpression' || isOptimizedArray) {
|
|
334
|
-
// Modules array or optimized array
|
|
335
|
-
const minId = isOptimizedArray ?
|
|
336
|
-
// Get the [minId] value from the Array() call first argument literal value
|
|
337
|
-
node.callee.object.arguments[0].value :
|
|
338
|
-
// `0` for simple array
|
|
339
|
-
0;
|
|
340
|
-
const modulesNodes = isOptimizedArray ?
|
|
341
|
-
// The modules reside in the `concat()` function call arguments
|
|
342
|
-
node.arguments[0].elements :
|
|
343
|
-
node.elements;
|
|
344
|
-
|
|
345
|
-
return modulesNodes.reduce((result, moduleNode, i) => {
|
|
346
|
-
if (moduleNode) {
|
|
347
|
-
result[i + minId] = getModuleLocation(moduleNode);
|
|
348
|
-
}
|
|
349
|
-
return result;
|
|
350
|
-
}, {});
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
return {};
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
function getModuleLocation(node) {
|
|
357
|
-
return {
|
|
358
|
-
start: node.start,
|
|
359
|
-
end: node.end
|
|
360
|
-
};
|
|
361
|
-
}
|
package/src/statsUtils.js
DELETED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
const {createWriteStream} = require('fs');
|
|
2
|
-
const {Readable} = require('stream');
|
|
3
|
-
|
|
4
|
-
class StatsSerializeStream extends Readable {
|
|
5
|
-
constructor(stats) {
|
|
6
|
-
super();
|
|
7
|
-
this._indentLevel = 0;
|
|
8
|
-
this._stringifier = this._stringify(stats);
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
get _indent() {
|
|
12
|
-
return ' '.repeat(this._indentLevel);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
_read() {
|
|
16
|
-
let readMore = true;
|
|
17
|
-
|
|
18
|
-
while (readMore) {
|
|
19
|
-
const {value, done} = this._stringifier.next();
|
|
20
|
-
|
|
21
|
-
if (done) {
|
|
22
|
-
this.push(null);
|
|
23
|
-
readMore = false;
|
|
24
|
-
} else {
|
|
25
|
-
readMore = this.push(value);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
* _stringify(obj) {
|
|
31
|
-
if (typeof obj === 'string' || typeof obj === 'number' || typeof obj === 'boolean' || obj === null) {
|
|
32
|
-
yield JSON.stringify(obj);
|
|
33
|
-
} else if (Array.isArray(obj)) {
|
|
34
|
-
yield '[';
|
|
35
|
-
this._indentLevel++;
|
|
36
|
-
|
|
37
|
-
let isFirst = true;
|
|
38
|
-
for (let item of obj) {
|
|
39
|
-
if (item === undefined) {
|
|
40
|
-
item = null;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
yield `${isFirst ? '' : ','}\n${this._indent}`;
|
|
44
|
-
yield* this._stringify(item);
|
|
45
|
-
isFirst = false;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
this._indentLevel--;
|
|
49
|
-
yield obj.length ? `\n${this._indent}]` : ']';
|
|
50
|
-
} else {
|
|
51
|
-
yield '{';
|
|
52
|
-
this._indentLevel++;
|
|
53
|
-
|
|
54
|
-
let isFirst = true;
|
|
55
|
-
const entries = Object.entries(obj);
|
|
56
|
-
for (const [itemKey, itemValue] of entries) {
|
|
57
|
-
if (itemValue === undefined) {
|
|
58
|
-
continue;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
yield `${isFirst ? '' : ','}\n${this._indent}${JSON.stringify(itemKey)}: `;
|
|
62
|
-
yield* this._stringify(itemValue);
|
|
63
|
-
isFirst = false;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
this._indentLevel--;
|
|
67
|
-
yield entries.length ? `\n${this._indent}}` : '}';
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
exports.StatsSerializeStream = StatsSerializeStream;
|
|
73
|
-
exports.writeStats = writeStats;
|
|
74
|
-
|
|
75
|
-
async function writeStats(stats, filepath) {
|
|
76
|
-
return new Promise((resolve, reject) => {
|
|
77
|
-
new StatsSerializeStream(stats)
|
|
78
|
-
.on('end', resolve)
|
|
79
|
-
.on('error', reject)
|
|
80
|
-
.pipe(createWriteStream(filepath));
|
|
81
|
-
});
|
|
82
|
-
}
|
package/src/template.js
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
/* eslint-disable max-len */
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const fs = require('fs');
|
|
4
|
-
|
|
5
|
-
const _ = require('lodash');
|
|
6
|
-
|
|
7
|
-
const projectRoot = path.resolve(__dirname, '..');
|
|
8
|
-
const assetsRoot = path.join(projectRoot, 'public');
|
|
9
|
-
|
|
10
|
-
exports.renderViewer = renderViewer;
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Escapes `<` characters in JSON to safely use it in `<script>` tag.
|
|
14
|
-
*/
|
|
15
|
-
function escapeJson(json) {
|
|
16
|
-
return JSON.stringify(json).replace(/</gu, '\\u003c');
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function getAssetContent(filename) {
|
|
20
|
-
const assetPath = path.join(assetsRoot, filename);
|
|
21
|
-
|
|
22
|
-
if (!assetPath.startsWith(assetsRoot)) {
|
|
23
|
-
throw new Error(`"${filename}" is outside of the assets root`);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
return fs.readFileSync(assetPath, 'utf8');
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function html(strings, ...values) {
|
|
30
|
-
return strings.map((string, index) => `${string}${values[index] || ''}`).join('');
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function getScript(filename, mode) {
|
|
34
|
-
if (mode === 'static') {
|
|
35
|
-
return `<!-- ${_.escape(filename)} -->
|
|
36
|
-
<script>${getAssetContent(filename)}</script>`;
|
|
37
|
-
} else {
|
|
38
|
-
return `<script src="${_.escape(filename)}"></script>`;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function renderViewer({title, enableWebSocket, chartData, defaultSizes, mode} = {}) {
|
|
43
|
-
return html`<!DOCTYPE html>
|
|
44
|
-
<html>
|
|
45
|
-
<head>
|
|
46
|
-
<meta charset="UTF-8"/>
|
|
47
|
-
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
|
48
|
-
<title>${_.escape(title)}</title>
|
|
49
|
-
<link rel="shortcut icon" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAABrVBMVEUAAAD///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////+O1foceMD///+J0/qK1Pr7/v8Xdr/9///W8P4UdL7L7P0Scr2r4Pyj3vwad8D5/f/2/f+55f3E6f34+/2H0/ojfMKpzOd0rNgQcb3F3O/j9f7c8v6g3Pz0/P/w+v/q+P7n9v6T1/uQ1vuE0vqLut/y+v+Z2fvt+f+15Pzv9fuc2/vR7v2V2Pvd6/bg9P7I6/285/2y4/yp3/zp8vk8i8kqgMT7/P31+fyv4vxGkcz6/P6/6P3j7vfS5PNnpNUxhcbO7f7F6v3O4vHK3/DA2u631Ouy0eqXweKJud5wqthfoNMMbLvY8f73+v2dxeR8sNtTmdDx9/zX6PSjyeaCtd1YnNGX2PuQveCGt95Nls42h8dLlM3F4vBtAAAAM3RSTlMAAyOx0/sKBvik8opWGBMOAe3l1snDm2E9LSb06eHcu5JpHbarfHZCN9CBb08zzkdNS0kYaptYAAAFV0lEQVRYw92X51/aYBDHHS2O2qqttVbrqNq9m+TJIAYIShBkWwqIiCgoWvfeq7Z2/s29hyQNyUcR7LveGwVyXy6XH8/9rqxglLfUPLxVduUor3h0rfp2TYvpivk37929TkG037hffoX0+peVtZQc1589rigVUdXS/ABSAyEmGIO/1XfvldSK8vs3OqB6u3m0nxmIrvgB0dj7rr7Y9IbuF68hnfFaiHA/sxqm0wciIG43P60qKv9WXWc1RXGh/mFESFABTSBi0sNAKzqet17eCtOb3kZIDwxEEU0oAIJGYxNBDhBND29e0rtXXbcpuPmED9IhEAAQ/AXEaF8EPmnrrKsv0LvWR3fg5sWDNAFZOgAgaKvZDogHNU9MFwnnYROkc56RD5CjAbQX9Ow4g7upCsvYu55aSI/Nj0H1akgKQEUM94dwK65hYRmFU9MIcH/fqJYOZYcnuJSU/waKDgTOEVaVKhwrTRP5XzgSpAITYzom7UvkhFX5VutmxeNnWDjjswTKTyfgluNDGbUpWissXhF3s7mlSml+czWkg3D0l1nNjGNjz3myOQOa1KM/jOS6ebdbAVTCi4gljHSFrviza7tOgRWcS0MOUX9zdNgag5w7rRqA44Lzw0hr1WqES36dFliSJFlh2rXIae3FFcDDgKdxrUIDePr8jGcSClV1u7A9xeN0ModY/pHMxmR1EzRh8TJiwqsHmKW0l4FCEZI+jHio+JdPPE9qwQtTRxku2D8sIeRL2LnxWSllANCQGOIiqVHAz2ye2JR0DcH+HoxDkaADLjgxjKQ+AwCX/g0+DNgdG0ukYCONAe+dbc2IAc6fwt1ARoDSezNHxV2Cmzwv3O6lDMV55edBGwGK9n1+x2F8EDfAGCxug8MhpsMEcTEAWf3rx2vZhe/LAmtIn/6apE6PN0ULKgywD9mmdxbmFl3OvD5AS5fW5zLbv/YHmcsBTjf/afDz3MaZTVCfAP9z6/Bw6ycv8EUBWJIn9zYcoAWWlW9+OzO3vkTy8H+RANLmdrpOuYWdZYEXpo+TlCJrW5EARb7fF+bWdqf3hhyZI1nWJQHgznErZhbjoEsWqi8dQNoE294aldzFurwSABL2XXMf9+H1VQGke9exw5P/AnA5Pv5ngMul7LOvO922iwACu8WkCwLCafvM4CeWPxfA8lNHcWZSoi8EwMAIciKX2Z4SWCMAa3snCZ/G4EA8D6CMLNFsGQhkkz/gQNEBbPCbWsxGUpYVu3z8IyNAknwJkfPMEhLyrdi5RTyUVACkw4GSFRNWJNEW+fgPGwHD8/JxnRuLabN4CGNRkAE23na2+VmEAUmrYymSGjMAYqH84YUIyzgzs3XC7gNgH36Vcc4zKY9o9fgPBXUAiHHwVboBHGLiX6Zcjp1f2wu4tvzZKo0ecPnDtQYDQvJXaBeNzce45Fp28ZQLrEZVuFqgBwOalArKXnW1UzlnSusQKJqKYNuz4tOnI6sZG4zanpemv+7ySU2jbA9h6uhcgpfy6G2PahirDZ6zvq6zDduMVFTKvzw8wgyEdelwY9in3XkEPs3osJuwRQ4qTkfzifndg9Gfc4pdsu82+tTnHZTBa2EAMrqr2t43pguc8tNm7JQVQ2S0ukj2d22dhXYP0/veWtwKrCkNoNimAN5+Xr/oLrxswKbVJjteWrX7eR63o4j9q0GxnaBdWgGA5VStpanIjQmEhV0/nVt5VOFUvix6awJhPcAaTEShgrG+iGyvb5a0Ndb1YGHFPEwoqAinoaykaID1o1pdPNu7XsnCKQ3R+hwWIIhGvORcJUBYXe3Xa3vq/mF/N9V13ugufMkfXn+KHsRD0B8AAAAASUVORK5CYII=" type="image/x-icon" />
|
|
50
|
-
|
|
51
|
-
<script>
|
|
52
|
-
window.enableWebSocket = ${escapeJson(enableWebSocket)};
|
|
53
|
-
</script>
|
|
54
|
-
${getScript('viewer.js', mode)}
|
|
55
|
-
</head>
|
|
56
|
-
|
|
57
|
-
<body>
|
|
58
|
-
<div id="app"></div>
|
|
59
|
-
<script>
|
|
60
|
-
window.chartData = ${escapeJson(chartData)};
|
|
61
|
-
window.defaultSizes = ${escapeJson(defaultSizes)};
|
|
62
|
-
</script>
|
|
63
|
-
</body>
|
|
64
|
-
</html>`;
|
|
65
|
-
}
|
package/src/tree/BaseFolder.js
DELETED
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
import _ from 'lodash';
|
|
2
|
-
|
|
3
|
-
import Node from './Node';
|
|
4
|
-
|
|
5
|
-
export default class BaseFolder extends Node {
|
|
6
|
-
|
|
7
|
-
constructor(name, parent) {
|
|
8
|
-
super(name, parent);
|
|
9
|
-
this.children = Object.create(null);
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
get src() {
|
|
13
|
-
if (!_.has(this, '_src')) {
|
|
14
|
-
this._src = this.walk((node, src) => (src += node.src || ''), '', false);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
return this._src;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
get size() {
|
|
21
|
-
if (!_.has(this, '_size')) {
|
|
22
|
-
this._size = this.walk((node, size) => (size + node.size), 0, false);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
return this._size;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
getChild(name) {
|
|
29
|
-
return this.children[name];
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
addChildModule(module) {
|
|
33
|
-
const {name} = module;
|
|
34
|
-
const currentChild = this.children[name];
|
|
35
|
-
|
|
36
|
-
// For some reason we already have this node in children and it's a folder.
|
|
37
|
-
if (currentChild && currentChild instanceof BaseFolder) return;
|
|
38
|
-
|
|
39
|
-
if (currentChild) {
|
|
40
|
-
// We already have this node in children and it's a module.
|
|
41
|
-
// Merging it's data.
|
|
42
|
-
currentChild.mergeData(module.data);
|
|
43
|
-
} else {
|
|
44
|
-
// Pushing new module
|
|
45
|
-
module.parent = this;
|
|
46
|
-
this.children[name] = module;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
delete this._size;
|
|
50
|
-
delete this._src;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
addChildFolder(folder) {
|
|
54
|
-
folder.parent = this;
|
|
55
|
-
this.children[folder.name] = folder;
|
|
56
|
-
delete this._size;
|
|
57
|
-
delete this._src;
|
|
58
|
-
|
|
59
|
-
return folder;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
walk(walker, state = {}, deep = true) {
|
|
63
|
-
let stopped = false;
|
|
64
|
-
|
|
65
|
-
Object.values(this.children).forEach(child => {
|
|
66
|
-
if (deep && child.walk) {
|
|
67
|
-
state = child.walk(walker, state, stop);
|
|
68
|
-
} else {
|
|
69
|
-
state = walker(child, state, stop);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
if (stopped) return false;
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
return state;
|
|
76
|
-
|
|
77
|
-
function stop(finalState) {
|
|
78
|
-
stopped = true;
|
|
79
|
-
return finalState;
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
mergeNestedFolders() {
|
|
84
|
-
if (!this.isRoot) {
|
|
85
|
-
let childNames;
|
|
86
|
-
|
|
87
|
-
while ((childNames = Object.keys(this.children)).length === 1) {
|
|
88
|
-
const childName = childNames[0];
|
|
89
|
-
const onlyChild = this.children[childName];
|
|
90
|
-
|
|
91
|
-
if (onlyChild instanceof this.constructor) {
|
|
92
|
-
this.name += `/${onlyChild.name}`;
|
|
93
|
-
this.children = onlyChild.children;
|
|
94
|
-
} else {
|
|
95
|
-
break;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
this.walk(child => {
|
|
101
|
-
child.parent = this;
|
|
102
|
-
|
|
103
|
-
if (child.mergeNestedFolders) {
|
|
104
|
-
child.mergeNestedFolders();
|
|
105
|
-
}
|
|
106
|
-
}, null, false);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
toChartData() {
|
|
110
|
-
return {
|
|
111
|
-
label: this.name,
|
|
112
|
-
path: this.path,
|
|
113
|
-
statSize: this.size,
|
|
114
|
-
groups: _.invokeMap(this.children, 'toChartData')
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
};
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import _ from 'lodash';
|
|
2
|
-
|
|
3
|
-
import Module from './Module';
|
|
4
|
-
import ContentModule from './ContentModule';
|
|
5
|
-
import ContentFolder from './ContentFolder';
|
|
6
|
-
import {getModulePathParts} from './utils';
|
|
7
|
-
|
|
8
|
-
export default class ConcatenatedModule extends Module {
|
|
9
|
-
|
|
10
|
-
constructor(name, data, parent) {
|
|
11
|
-
super(name, data, parent);
|
|
12
|
-
this.name += ' (concatenated)';
|
|
13
|
-
this.children = Object.create(null);
|
|
14
|
-
this.fillContentModules();
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
fillContentModules() {
|
|
18
|
-
this.data.modules.forEach(moduleData => this.addContentModule(moduleData));
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
addContentModule(moduleData) {
|
|
22
|
-
const pathParts = getModulePathParts(moduleData);
|
|
23
|
-
|
|
24
|
-
if (!pathParts) {
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const [folders, fileName] = [pathParts.slice(0, -1), _.last(pathParts)];
|
|
29
|
-
let currentFolder = this;
|
|
30
|
-
|
|
31
|
-
folders.forEach(folderName => {
|
|
32
|
-
let childFolder = currentFolder.getChild(folderName);
|
|
33
|
-
|
|
34
|
-
if (!childFolder) {
|
|
35
|
-
childFolder = currentFolder.addChildFolder(new ContentFolder(folderName, this));
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
currentFolder = childFolder;
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
const module = new ContentModule(fileName, moduleData, this);
|
|
42
|
-
currentFolder.addChildModule(module);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
getChild(name) {
|
|
46
|
-
return this.children[name];
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
addChildModule(module) {
|
|
50
|
-
module.parent = this;
|
|
51
|
-
this.children[module.name] = module;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
addChildFolder(folder) {
|
|
55
|
-
folder.parent = this;
|
|
56
|
-
this.children[folder.name] = folder;
|
|
57
|
-
return folder;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
mergeNestedFolders() {
|
|
61
|
-
_.invokeMap(this.children, 'mergeNestedFolders');
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
toChartData() {
|
|
65
|
-
return {
|
|
66
|
-
...super.toChartData(),
|
|
67
|
-
concatenated: true,
|
|
68
|
-
groups: _.invokeMap(this.children, 'toChartData')
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
};
|