source-map-explorer 1.3.1 → 1.5.0
Sign up to get free protection for your applications and to get access to all the features.
- package/.eslintrc.js +27 -0
- package/.travis.yml +1 -1
- package/README.md +12 -9
- package/index.js +189 -110
- package/package.json +4 -3
- package/test.js +30 -4
- package/tree-viz.html +13 -1
package/.eslintrc.js
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
module.exports = {
|
2
|
+
"env": {
|
3
|
+
"commonjs": true,
|
4
|
+
"mocha": true,
|
5
|
+
"node": true
|
6
|
+
},
|
7
|
+
"extends": "eslint:recommended",
|
8
|
+
"rules": {
|
9
|
+
"indent": [
|
10
|
+
"error",
|
11
|
+
2
|
12
|
+
],
|
13
|
+
"linebreak-style": [
|
14
|
+
"error",
|
15
|
+
"unix"
|
16
|
+
],
|
17
|
+
"quotes": [
|
18
|
+
"error",
|
19
|
+
"single"
|
20
|
+
],
|
21
|
+
"semi": [
|
22
|
+
"error",
|
23
|
+
"always"
|
24
|
+
],
|
25
|
+
"no-console": 0
|
26
|
+
}
|
27
|
+
};
|
package/.travis.yml
CHANGED
package/README.md
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
[![Build Status](https://travis-ci.org/danvk/source-map-explorer.svg?branch=v1.1.0)](https://travis-ci.org/danvk/source-map-explorer) [![NPM version](http://img.shields.io/npm/v/source-map-explorer.svg)](https://www.npmjs.org/package/source-map-explorer)
|
2
|
-
# source-map-explorer
|
3
|
-
Analyze and debug JavaScript code bloat through source maps.
|
2
|
+
# source-map-explorer
|
3
|
+
Analyze and debug JavaScript (or Sass or LESS) code bloat through source maps.
|
4
4
|
|
5
|
-
The source map explorer determines which file each byte in your minified
|
5
|
+
The source map explorer determines which file each byte in your minified code came from. It shows you a [treemap][] visualization to help you debug where all the code is coming from. Check out this [Chrome Developer video][video] (3:25) for a demo of the tool in action.
|
6
6
|
|
7
7
|
Install:
|
8
8
|
|
9
|
-
npm install source-map-explorer
|
9
|
+
npm install -g source-map-explorer
|
10
10
|
|
11
11
|
Use:
|
12
12
|
|
13
13
|
source-map-explorer bundle.min.js
|
14
|
-
source-map-explorer bundle.min.js bundle.min.js.map
|
14
|
+
source-map-explorer bundle.min.js bundle.min.js.map
|
15
15
|
|
16
16
|
This will open up a visualization of how the space is used in your minified bundle:
|
17
17
|
|
@@ -34,7 +34,7 @@ in the bundle (perhaps because of out-of-date dependencies).
|
|
34
34
|
"foo.js": 137
|
35
35
|
}
|
36
36
|
```
|
37
|
-
|
37
|
+
|
38
38
|
* `--tsv`: output tab-delimited values instead of displaying a visualization:
|
39
39
|
|
40
40
|
```
|
@@ -43,7 +43,7 @@ in the bundle (perhaps because of out-of-date dependencies).
|
|
43
43
|
dist/bar.js 62
|
44
44
|
dist/foo.js 137
|
45
45
|
```
|
46
|
-
|
46
|
+
|
47
47
|
If you just want a list of files, you can do `source-map-explorer --tsv foo.min.js | sed 1d | cut -f1`.
|
48
48
|
|
49
49
|
* `--html`: output HTML to stdout. By default, source-map-explorer writes HTML to a temporary file and opens it in your default browser. If you want to save the output (e.g. to share), pipe it to a file:
|
@@ -51,15 +51,17 @@ in the bundle (perhaps because of out-of-date dependencies).
|
|
51
51
|
```
|
52
52
|
source-map-explorer --html foo.min.js > tree.html
|
53
53
|
```
|
54
|
-
|
54
|
+
|
55
55
|
* `--replace`, `--with`: The paths in source maps sometimes have artifacts that are difficult to get rid of. These flags let you do simple find & replaces on the paths. For example:
|
56
56
|
|
57
57
|
```
|
58
58
|
source-map-explorer foo.min.js --replace 'dist/' --with ''
|
59
59
|
```
|
60
|
-
|
60
|
+
|
61
61
|
You can specify these flags multiple times. Be aware that the find/replace is done _after_ eliminating shared prefixes between paths.
|
62
62
|
|
63
|
+
These are regular expressions.
|
64
|
+
|
63
65
|
* `--noroot`: By default, source-map-explorer finds common prefixes between all source files and eliminates them, since they add complexity to the visualization with no real benefit. But if you want to disable this behavior, set the `--noroot` flag.
|
64
66
|
|
65
67
|
## Generating source maps
|
@@ -130,3 +132,4 @@ source-map-explorer path/to/foo.min.js{,.map}
|
|
130
132
|
[exorcist]: https://github.com/thlorenz/exorcist
|
131
133
|
[inline]: /README.md#types-of-source-maps
|
132
134
|
[treemap]: https://github.com/martine/webtreemap
|
135
|
+
[video]: https://www.youtube.com/watch?v=7aY9BoMEpG8
|
package/index.js
CHANGED
@@ -1,84 +1,121 @@
|
|
1
1
|
#!/usr/bin/env node
|
2
2
|
|
3
3
|
var doc = [
|
4
|
-
'Analyze and debug space usage through source maps.',
|
5
|
-
'',
|
6
|
-
'Usage:',
|
7
|
-
' source-map-explorer <script.js> [<script.js.map>]',
|
8
|
-
' source-map-explorer [--json | --html | --tsv] <script.js> [<script.js.map>] [--replace=BEFORE --with=AFTER]... [--noroot]',
|
9
|
-
' source-map-explorer -h | --help | --version',
|
10
|
-
'',
|
11
|
-
'If the script file has an inline source map, you may omit the map parameter.',
|
12
|
-
'',
|
13
|
-
'Options:',
|
14
|
-
' -h --help Show this screen.',
|
15
|
-
' --version Show version.',
|
16
|
-
'',
|
17
|
-
' --json Output JSON (on stdout) instead of generating HTML',
|
18
|
-
' and opening the browser.',
|
19
|
-
' --tsv Output TSV (on stdout) instead of generating HTML',
|
20
|
-
' and opening the browser.',
|
21
|
-
' --html Output HTML (on stdout) rather than opening a browser.',
|
22
|
-
'',
|
23
|
-
'
|
24
|
-
'
|
25
|
-
'
|
26
|
-
'',
|
27
|
-
'
|
28
|
-
'
|
29
|
-
'
|
30
|
-
'
|
31
|
-
'
|
4
|
+
'Analyze and debug space usage through source maps.',
|
5
|
+
'',
|
6
|
+
'Usage:',
|
7
|
+
' source-map-explorer <script.js> [<script.js.map>]',
|
8
|
+
' source-map-explorer [--json | --html | --tsv] [-m | --only-mapped] <script.js> [<script.js.map>] [--replace=BEFORE --with=AFTER]... [--noroot]',
|
9
|
+
' source-map-explorer -h | --help | --version',
|
10
|
+
'',
|
11
|
+
'If the script file has an inline source map, you may omit the map parameter.',
|
12
|
+
'',
|
13
|
+
'Options:',
|
14
|
+
' -h --help Show this screen.',
|
15
|
+
' --version Show version.',
|
16
|
+
'',
|
17
|
+
' --json Output JSON (on stdout) instead of generating HTML',
|
18
|
+
' and opening the browser.',
|
19
|
+
' --tsv Output TSV (on stdout) instead of generating HTML',
|
20
|
+
' and opening the browser.',
|
21
|
+
' --html Output HTML (on stdout) rather than opening a browser.',
|
22
|
+
'',
|
23
|
+
' -m --onlymapped Exclude "unmapped" bytes from the output.',
|
24
|
+
' This will result in total counts less than the file size',
|
25
|
+
'',
|
26
|
+
'',
|
27
|
+
' --noroot To simplify the visualization, source-map-explorer',
|
28
|
+
' will remove any prefix shared by all sources. If you',
|
29
|
+
' wish to disable this behavior, set --noroot.',
|
30
|
+
'',
|
31
|
+
' --replace=BEFORE Apply a simple find/replace on source file',
|
32
|
+
' names. This can be used to fix some oddities',
|
33
|
+
' with paths which appear in the source map',
|
34
|
+
' generation process. Accepts regular expressions.',
|
35
|
+
' --with=AFTER See --replace.'
|
32
36
|
].join('\n');
|
33
37
|
|
34
38
|
var fs = require('fs'),
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
39
|
+
glob = require('glob'),
|
40
|
+
path = require('path'),
|
41
|
+
sourcemap = require('source-map'),
|
42
|
+
convert = require('convert-source-map'),
|
43
|
+
temp = require('temp'),
|
44
|
+
open = require('open'),
|
45
|
+
_ = require('underscore'),
|
46
|
+
docopt = require('docopt').docopt,
|
47
|
+
btoa = require('btoa');
|
44
48
|
|
45
|
-
function
|
49
|
+
function computeSpans(mapConsumer, generatedJs) {
|
46
50
|
var lines = generatedJs.split('\n');
|
47
|
-
var
|
51
|
+
var spans = [];
|
48
52
|
var numChars = 0;
|
49
|
-
var lastSource = null
|
53
|
+
var lastSource = false; // not a string, not null.
|
50
54
|
for (var line = 1; line <= lines.length; line++) {
|
51
55
|
var lineText = lines[line - 1];
|
52
56
|
var numCols = lineText.length;
|
53
57
|
for (var column = 0; column < numCols; column++, numChars++) {
|
54
58
|
var pos = mapConsumer.originalPositionFor({line:line, column:column});
|
55
59
|
var source = pos.source;
|
56
|
-
if (source == null) {
|
57
|
-
// Often this is from the '// #sourceMap' comment itself.
|
58
|
-
continue;
|
59
|
-
}
|
60
60
|
|
61
|
-
if (source
|
62
|
-
|
63
|
-
|
64
|
-
lastSource = source;
|
65
|
-
} else {
|
66
|
-
// source-map reports odd positions for bits between files.
|
67
|
-
}
|
61
|
+
if (source !== lastSource) {
|
62
|
+
lastSource = source;
|
63
|
+
spans.push({source: source, numChars: 1});
|
68
64
|
} else {
|
69
|
-
|
65
|
+
spans[spans.length - 1].numChars += 1;
|
70
66
|
}
|
71
67
|
}
|
72
68
|
}
|
73
|
-
return
|
74
|
-
|
75
|
-
|
69
|
+
return spans;
|
70
|
+
}
|
71
|
+
|
72
|
+
var UNMAPPED = '<unmapped>';
|
73
|
+
|
74
|
+
/**
|
75
|
+
* Calculate the number of bytes contributed by each source file.
|
76
|
+
* @returns {
|
77
|
+
* counts: {[sourceFile: string]: number},
|
78
|
+
* numUnmapped: number,
|
79
|
+
* totalBytes: number
|
80
|
+
* }
|
81
|
+
*/
|
82
|
+
function computeGeneratedFileSizes(mapConsumer, generatedJs) {
|
83
|
+
var spans = computeSpans(mapConsumer, generatedJs);
|
84
|
+
|
85
|
+
var numUnmapped = 0;
|
86
|
+
var counts = {};
|
87
|
+
var totalBytes = 0;
|
88
|
+
for (var i = 0; i < spans.length; i++) {
|
89
|
+
var span = spans[i];
|
90
|
+
var numChars = span.numChars;
|
91
|
+
totalBytes += numChars;
|
92
|
+
if (span.source === null) {
|
93
|
+
numUnmapped += numChars;
|
94
|
+
} else {
|
95
|
+
counts[span.source] = (counts[span.source] || 0) + span.numChars;
|
96
|
+
}
|
97
|
+
}
|
98
|
+
return {
|
99
|
+
counts: counts,
|
100
|
+
numUnmapped: numUnmapped,
|
101
|
+
totalBytes: totalBytes
|
102
|
+
};
|
76
103
|
}
|
77
104
|
|
78
105
|
var SOURCE_MAP_INFO_URL = 'https://github.com/danvk/source-map-explorer/blob/master/README.md#generating-source-maps';
|
79
106
|
|
80
107
|
function loadSourceMap(jsFile, mapFile) {
|
81
|
-
var jsData
|
108
|
+
var jsData;
|
109
|
+
try {
|
110
|
+
jsData = fs.readFileSync(jsFile).toString();
|
111
|
+
} catch(err) {
|
112
|
+
if (err.code === 'ENOENT' ) {
|
113
|
+
console.error('File not found! -- ', err.message);
|
114
|
+
return null;
|
115
|
+
} else {
|
116
|
+
throw err;
|
117
|
+
}
|
118
|
+
}
|
82
119
|
|
83
120
|
var mapConsumer;
|
84
121
|
if (mapFile) {
|
@@ -114,7 +151,7 @@ function loadSourceMap(jsFile, mapFile) {
|
|
114
151
|
function commonPathPrefix(array){
|
115
152
|
if (array.length == 0) return '';
|
116
153
|
var A= array.concat().sort(),
|
117
|
-
|
154
|
+
a1= A[0].split(/(\/)/), a2= A[A.length-1].split(/(\/)/), L= a1.length, i= 0;
|
118
155
|
while(i<L && a1[i] === a2[i]) i++;
|
119
156
|
return a1.slice(0, i).join('');
|
120
157
|
}
|
@@ -129,13 +166,13 @@ function adjustSourcePaths(sizes, findRoot, finds, replaces) {
|
|
129
166
|
var prefix = commonPathPrefix(_.keys(sizes));
|
130
167
|
var len = prefix.length;
|
131
168
|
if (len) {
|
132
|
-
sizes = mapKeys(sizes, function(source) { return source.slice(len); })
|
169
|
+
sizes = mapKeys(sizes, function(source) { return source.slice(len); });
|
133
170
|
}
|
134
171
|
}
|
135
172
|
|
136
173
|
for (var i = 0; i < finds.length; i++) {
|
137
|
-
var before = finds[i],
|
138
|
-
|
174
|
+
var before = new RegExp(finds[i]),
|
175
|
+
after = replaces[i];
|
139
176
|
sizes = mapKeys(sizes, function(source) {
|
140
177
|
return source.replace(before, after);
|
141
178
|
});
|
@@ -151,75 +188,117 @@ function validateArgs(args) {
|
|
151
188
|
}
|
152
189
|
}
|
153
190
|
|
191
|
+
// On Windows, it's helpful if source-map-explorer can expand globs itself.
|
192
|
+
// See https://github.com/danvk/source-map-explorer/issues/52
|
193
|
+
function expandGlob(args) {
|
194
|
+
var arg1 = args['<script.js>'];
|
195
|
+
var arg2 = args['<script.js.map>'];
|
196
|
+
if (arg1 && !arg2) {
|
197
|
+
var files = glob.sync(arg1);
|
198
|
+
if (files.length > 2) {
|
199
|
+
throw new Error(
|
200
|
+
'Glob should match exactly 2 files but matched ' + files.length + ' ' + arg1);
|
201
|
+
} else if (files.length === 2) {
|
202
|
+
// allow the JS and source map file to match in either order.
|
203
|
+
if (files[0].indexOf('.map') >= 0) {
|
204
|
+
var tmp = files[0];
|
205
|
+
files[0] = files[1];
|
206
|
+
files[1] = tmp;
|
207
|
+
}
|
208
|
+
args['<script.js>'] = files[0];
|
209
|
+
args['<script.js.map>'] = files[1];
|
210
|
+
}
|
211
|
+
}
|
212
|
+
return args;
|
213
|
+
}
|
214
|
+
|
154
215
|
|
155
216
|
if (require.main === module) {
|
156
217
|
|
157
|
-
var args = docopt(doc, {version: '1.
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
218
|
+
var args = docopt(doc, {version: '1.5.0'});
|
219
|
+
expandGlob(args);
|
220
|
+
validateArgs(args);
|
221
|
+
var data = loadSourceMap(args['<script.js>'], args['<script.js.map>']);
|
222
|
+
if (!data) {
|
223
|
+
process.exit(1);
|
224
|
+
}
|
225
|
+
var mapConsumer = data.mapConsumer,
|
164
226
|
jsData = data.jsData;
|
165
227
|
|
166
|
-
var sizes = computeGeneratedFileSizes(mapConsumer, jsData);
|
228
|
+
var sizes = computeGeneratedFileSizes(mapConsumer, jsData);
|
229
|
+
var counts = sizes.counts;
|
167
230
|
|
168
|
-
if (_.size(
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
}
|
231
|
+
if (_.size(counts) == 1) {
|
232
|
+
console.error('Your source map only contains one source (',
|
233
|
+
_.keys(counts)[0], ')');
|
234
|
+
console.error('This typically means that your source map doesn\'t map all the way back to the original sources.');
|
235
|
+
console.error('This can happen if you use browserify+uglifyjs, for example, and don\'t set the --in-source-map flag to uglify.');
|
236
|
+
console.error('See ', SOURCE_MAP_INFO_URL);
|
237
|
+
process.exit(1);
|
238
|
+
}
|
176
239
|
|
177
|
-
|
240
|
+
counts = adjustSourcePaths(counts, !args['--noroot'], args['--replace'], args['--with']);
|
178
241
|
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
242
|
+
var onlyMapped = args['--only-mapped'] || args['-m'];
|
243
|
+
var numUnmapped = sizes.numUnmapped;
|
244
|
+
if (!onlyMapped) {
|
245
|
+
counts[UNMAPPED] = numUnmapped;
|
246
|
+
}
|
247
|
+
if (numUnmapped) {
|
248
|
+
var totalBytes = sizes.totalBytes;
|
249
|
+
var pct = 100 * numUnmapped / totalBytes;
|
250
|
+
console.warn(
|
251
|
+
'Unable to map', numUnmapped, '/', totalBytes,
|
252
|
+
'bytes (' + pct.toFixed(2) + '%)');
|
253
|
+
}
|
183
254
|
|
184
|
-
if (args['--
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
}
|
255
|
+
if (args['--json']) {
|
256
|
+
console.log(JSON.stringify(counts, null, ' '));
|
257
|
+
process.exit(0);
|
258
|
+
}
|
189
259
|
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
}
|
260
|
+
if (args['--tsv']) {
|
261
|
+
console.log('Source\tSize');
|
262
|
+
_.each(counts, function(source, size) { console.log(size + '\t' + source); });
|
263
|
+
process.exit(0);
|
264
|
+
}
|
195
265
|
|
196
|
-
var
|
266
|
+
var assets = {
|
267
|
+
underscoreJs: btoa(fs.readFileSync(require.resolve('underscore'))),
|
268
|
+
webtreemapJs: btoa(fs.readFileSync(require.resolve('./vendor/webtreemap.js'))),
|
269
|
+
webtreemapCss: btoa(fs.readFileSync(require.resolve('./vendor/webtreemap.css')))
|
270
|
+
};
|
197
271
|
|
198
|
-
html =
|
199
|
-
.replace('INSERT TITLE HERE', args['<script.js>'])
|
200
|
-
.replace('INSERT underscore.js HERE', 'data:application/javascript;base64,' + assets.underscoreJs)
|
201
|
-
.replace('INSERT webtreemap.js HERE', 'data:application/javascript;base64,' + assets.webtreemapJs)
|
202
|
-
.replace('INSERT webtreemap.css HERE', 'data:text/css;base64,' + assets.webtreemapCss);
|
272
|
+
var html = fs.readFileSync(path.join(__dirname, 'tree-viz.html')).toString();
|
203
273
|
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
274
|
+
html = html.replace('INSERT TREE HERE', JSON.stringify(counts, null, ' '))
|
275
|
+
.replace('INSERT TITLE HERE', args['<script.js>'])
|
276
|
+
.replace('INSERT underscore.js HERE', 'data:application/javascript;base64,' + assets.underscoreJs)
|
277
|
+
.replace('INSERT webtreemap.js HERE', 'data:application/javascript;base64,' + assets.webtreemapJs)
|
278
|
+
.replace('INSERT webtreemap.css HERE', 'data:text/css;base64,' + assets.webtreemapCss);
|
279
|
+
|
280
|
+
if (args['--html']) {
|
281
|
+
console.log(html);
|
282
|
+
process.exit(0);
|
283
|
+
}
|
208
284
|
|
209
|
-
var tempName = temp.path({suffix: '.html'});
|
210
|
-
fs.writeFileSync(tempName, html);
|
211
|
-
open(tempName, function(error) {
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
});
|
285
|
+
var tempName = temp.path({suffix: '.html'});
|
286
|
+
fs.writeFileSync(tempName, html);
|
287
|
+
open(tempName, function(error) {
|
288
|
+
if (!error) return;
|
289
|
+
console.error('Unable to open web browser.');
|
290
|
+
console.error('Either run with --html, --json or --tsv, or view HTML for the visualization at:');
|
291
|
+
console.error(tempName);
|
292
|
+
});
|
217
293
|
|
218
294
|
}
|
219
295
|
|
220
296
|
// Exports are here mostly for testing.
|
221
297
|
module.exports = {
|
298
|
+
loadSourceMap: loadSourceMap,
|
299
|
+
computeGeneratedFileSizes: computeGeneratedFileSizes,
|
222
300
|
adjustSourcePaths: adjustSourcePaths,
|
223
301
|
mapKeys: mapKeys,
|
224
|
-
commonPathPrefix: commonPathPrefix
|
302
|
+
commonPathPrefix: commonPathPrefix,
|
303
|
+
expandGlob: expandGlob
|
225
304
|
};
|
package/package.json
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
{
|
2
2
|
"name": "source-map-explorer",
|
3
|
-
"version": "1.
|
3
|
+
"version": "1.5.0",
|
4
4
|
"description": "Analyze and debug space usage through source maps",
|
5
5
|
"main": "index.js",
|
6
6
|
"bin": {
|
7
7
|
"source-map-explorer": "index.js"
|
8
8
|
},
|
9
9
|
"scripts": {
|
10
|
-
"test": "mocha"
|
10
|
+
"test": "mocha && eslint *.js"
|
11
11
|
},
|
12
12
|
"repository": {
|
13
13
|
"type": "git",
|
@@ -28,7 +28,7 @@
|
|
28
28
|
"btoa": "^1.1.2",
|
29
29
|
"convert-source-map": "^1.1.1",
|
30
30
|
"docopt": "^0.6.2",
|
31
|
-
"
|
31
|
+
"glob": "^7.1.2",
|
32
32
|
"open": "0.0.5",
|
33
33
|
"source-map": "^0.5.1",
|
34
34
|
"temp": "^0.8.3",
|
@@ -36,6 +36,7 @@
|
|
36
36
|
},
|
37
37
|
"devDependencies": {
|
38
38
|
"chai": "^3.3.0",
|
39
|
+
"eslint": "^4.1.1",
|
39
40
|
"mocha": "^2.3.3"
|
40
41
|
}
|
41
42
|
}
|
package/test.js
CHANGED
@@ -3,7 +3,8 @@ var expect = require('chai').expect;
|
|
3
3
|
var sourceMapExplorer = require('./index'),
|
4
4
|
adjustSourcePaths = sourceMapExplorer.adjustSourcePaths,
|
5
5
|
mapKeys = sourceMapExplorer.mapKeys,
|
6
|
-
commonPathPrefix = sourceMapExplorer.commonPathPrefix
|
6
|
+
commonPathPrefix = sourceMapExplorer.commonPathPrefix,
|
7
|
+
expandGlob = sourceMapExplorer.expandGlob;
|
7
8
|
|
8
9
|
describe('source-map-explorer', function() {
|
9
10
|
describe('commonPathPrefix', function() {
|
@@ -28,14 +29,39 @@ describe('source-map-explorer', function() {
|
|
28
29
|
describe('adjustSourcePaths', function() {
|
29
30
|
it('should factor out a common prefix', function() {
|
30
31
|
expect(adjustSourcePaths({'/src/foo.js': 10, '/src/bar.js': 20}, true, [], []))
|
31
|
-
|
32
|
+
.to.deep.equal({'foo.js': 10, 'bar.js': 20});
|
32
33
|
expect(adjustSourcePaths({'/src/foo.js': 10, '/src/foodle.js': 20}, true, [], []))
|
33
|
-
|
34
|
+
.to.deep.equal({'foo.js': 10, 'foodle.js': 20});
|
34
35
|
});
|
35
36
|
|
36
37
|
it('should find/replace', function() {
|
37
38
|
expect(adjustSourcePaths({'/src/foo.js': 10, '/src/foodle.js': 20}, false, ['src'], ['dist']))
|
38
|
-
|
39
|
+
.to.deep.equal({'/dist/foo.js': 10, '/dist/foodle.js': 20});
|
40
|
+
});
|
41
|
+
|
42
|
+
it('should find/replace with regexp', function() {
|
43
|
+
expect(adjustSourcePaths({'/src/foo.js': 10, '/src/foodle.js': 20}, false, ['foo.'], ['bar.']))
|
44
|
+
.to.deep.equal({'/src/bar.js': 10, '/src/bar.le.js': 20});
|
45
|
+
});
|
46
|
+
|
47
|
+
it('should find/replace with regexp, can be used to add root', function() {
|
48
|
+
expect(adjustSourcePaths({'/foo/foo.js': 10, '/foo/foodle.js': 20}, false, ['^/foo'], ['/bar']))
|
49
|
+
.to.deep.equal({'/bar/foo.js': 10, '/bar/foodle.js': 20});
|
50
|
+
});
|
51
|
+
});
|
52
|
+
|
53
|
+
describe('command line parsing', function() {
|
54
|
+
expect(expandGlob({'<script.js>': 'testdata/foo.min.js*'})).to.deep.equal({
|
55
|
+
'<script.js>': 'testdata/foo.min.js',
|
56
|
+
'<script.js.map>': 'testdata/foo.min.js.map'
|
57
|
+
});
|
58
|
+
|
59
|
+
expect(expandGlob({
|
60
|
+
'<script.js>': 'foo.min.js',
|
61
|
+
'<script.js.map>': 'foo.min.js.map'
|
62
|
+
})).to.deep.equal({
|
63
|
+
'<script.js>': 'foo.min.js',
|
64
|
+
'<script.js.map>': 'foo.min.js.map'
|
39
65
|
});
|
40
66
|
});
|
41
67
|
});
|
package/tree-viz.html
CHANGED
@@ -28,6 +28,18 @@ body {
|
|
28
28
|
<div id='map'></div>
|
29
29
|
</body>
|
30
30
|
|
31
|
+
<script>
|
32
|
+
// https://stackoverflow.com/a/18650828/388951
|
33
|
+
function formatBytes(bytes, decimals) {
|
34
|
+
if (bytes == 0) return '0 B';
|
35
|
+
var k = 1000,
|
36
|
+
dm = decimals || 2,
|
37
|
+
sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
|
38
|
+
i = Math.floor(Math.log(bytes) / Math.log(k));
|
39
|
+
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
|
40
|
+
}
|
41
|
+
</script>
|
42
|
+
|
31
43
|
|
32
44
|
<script>
|
33
45
|
var tree = INSERT TREE HERE;
|
@@ -70,7 +82,7 @@ function addNode(path, size) {
|
|
70
82
|
function addSizeToTitle(node, total) {
|
71
83
|
var size = node.data['$area'],
|
72
84
|
pct = 100.0 * size / total;
|
73
|
-
node.name += ' • ' + size
|
85
|
+
node.name += ' • ' + formatBytes(size) + ' • ' + pct.toFixed(1) + '%';
|
74
86
|
node.children.forEach(function(x) { addSizeToTitle(x, total) });
|
75
87
|
}
|
76
88
|
|