Skip to content

Commit e5200f1

Browse files
Add support for also displaying Brotli compressed size (#47)
Co-authored-by: Sindre Sorhus <[email protected]>
1 parent 84c7724 commit e5200f1

File tree

4 files changed

+152
-37
lines changed

4 files changed

+152
-37
lines changed

index.js

+69-35
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,45 @@ const chalk = require('chalk');
66
const prettyBytes = require('pretty-bytes');
77
const StreamCounter = require('stream-counter');
88
const gzipSize = require('gzip-size');
9+
const brotliSize = require('brotli-size');
910

10-
module.exports = options => {
11+
function hasSize(sizes) {
12+
return [...sizes.values()].some(size => size > 0);
13+
}
14+
15+
function promisify(stream, property, event = 'end') {
16+
return new Promise((resolve, reject) => {
17+
stream.on(event, () => resolve(stream[property]))
18+
.on('error', error => reject(error));
19+
});
20+
}
21+
22+
module.exports = (options = {}) => {
1123
options = {
1224
pretty: true,
1325
showTotal: true,
26+
uncompressed: options.uncompressed || !(options.gzip || options.brotli),
1427
...options
1528
};
1629

17-
let totalSize = 0;
1830
let fileCount = 0;
31+
const totalSize = new Map();
1932

20-
function log(what, size) {
33+
const description = new Map([
34+
['uncompressed', ''],
35+
['gzip', ' (gzipped)'],
36+
['brotli', ' (brotli)']
37+
]);
38+
39+
function log(what, sizes) {
2140
let {title} = options;
2241
title = title ? chalk.cyan(title) + ' ' : '';
23-
size = options.pretty ? prettyBytes(size) : (size + ' B');
24-
fancyLog(title + what + ' ' + chalk.magenta(size) + (options.gzip ? chalk.gray(' (gzipped)') : ''));
42+
const sizeStrings = [...sizes].map(([key, size]) => {
43+
size = options.pretty ? prettyBytes(size) : (size + ' B');
44+
return chalk.magenta(size) + chalk.gray(description.get(key));
45+
});
46+
47+
fancyLog(title + what + ' ' + sizeStrings.join(chalk.magenta(', ')));
2548
}
2649

2750
return through.obj((file, encoding, callback) => {
@@ -30,56 +53,67 @@ module.exports = options => {
3053
return;
3154
}
3255

33-
const finish = (error, size) => {
56+
const finish = (error, sizes) => {
3457
if (error) {
3558
callback(new PluginError('gulp-size', error));
3659
return;
3760
}
3861

39-
totalSize += size;
62+
for (const [key, size] of sizes) {
63+
totalSize.set(key, size + (totalSize.get(key) || 0));
64+
}
4065

41-
if (options.showFiles === true && size > 0) {
42-
log(chalk.blue(file.relative), size);
66+
if (options.showFiles === true && hasSize(sizes)) {
67+
log(chalk.blue(file.relative), sizes);
4368
}
4469

4570
fileCount++;
4671
callback(null, file);
4772
};
4873

74+
const selectedSizes = new Map();
4975
if (file.isStream()) {
76+
if (options.uncompressed) {
77+
selectedSizes.set('uncompressed', promisify(file.contents.pipe(new StreamCounter()), 'bytes', 'finish'));
78+
}
79+
5080
if (options.gzip) {
51-
file.contents.pipe(gzipSize.stream())
52-
.on('error', finish)
53-
.on('end', function () {
54-
finish(null, this.gzipSize);
55-
});
56-
} else {
57-
file.contents.pipe(new StreamCounter())
58-
.on('error', finish)
59-
.on('finish', function () {
60-
finish(null, this.bytes);
61-
});
81+
selectedSizes.set('gzip', promisify(file.contents.pipe(gzipSize.stream()), 'gzipSize'));
6282
}
6383

64-
return;
84+
if (options.brotli) {
85+
selectedSizes.set('brotli', promisify(file.contents.pipe(brotliSize.stream()), 'brotliSize'));
86+
}
6587
}
6688

67-
if (options.gzip) {
68-
(async () => {
69-
try {
70-
finish(null, await gzipSize(file.contents));
71-
} catch (error) {
72-
finish(error);
73-
}
74-
})();
75-
} else {
76-
finish(null, file.contents.length);
89+
if (file.isBuffer()) {
90+
if (options.uncompressed) {
91+
selectedSizes.set('uncompressed', file.contents.length);
92+
}
93+
94+
if (options.gzip) {
95+
selectedSizes.set('gzip', gzipSize(file.contents));
96+
}
97+
98+
if (options.brotli) {
99+
selectedSizes.set('brotli', brotliSize.default(file.contents));
100+
}
77101
}
78-
}, function (callback) {
79-
this.size = totalSize;
80-
this.prettySize = prettyBytes(totalSize);
81102

82-
if (!(fileCount === 1 && options.showFiles) && totalSize > 0 && fileCount > 0 && options.showTotal) {
103+
(async () => {
104+
try {
105+
// We want to keep the names
106+
const sizes = await Promise.all([...selectedSizes].map(async ([key, size]) => [key, await size]));
107+
108+
finish(null, new Map(sizes));
109+
} catch (error) {
110+
finish(error);
111+
}
112+
})();
113+
}, function (callback) {
114+
this.size = totalSize.values().next().value;
115+
this.prettySize = prettyBytes(this.size);
116+
if (!(fileCount === 1 && options.showFiles) && hasSize(totalSize) && fileCount > 0 && options.showTotal) {
83117
log(chalk.green('all files'), totalSize);
84118
}
85119

package.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,11 @@
2727
"measure",
2828
"inspect",
2929
"debug",
30-
"gzip"
30+
"gzip",
31+
"brotli"
3132
],
3233
"dependencies": {
34+
"brotli-size": "^4.0.0",
3335
"chalk": "^2.3.0",
3436
"fancy-log": "^1.3.2",
3537
"gzip-size": "^5.1.1",

readme.md

+15-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,21 @@ Give it a title so it's possible to distinguish the output of multiple instances
4848
Type: `boolean`<br>
4949
Default: `false`
5050

51-
Displays the gzipped size instead.
51+
Displays the gzipped size.
52+
53+
##### brotli
54+
55+
Type: `boolean`<br>
56+
Default: `false`
57+
58+
Displays the brotli compressed size.
59+
60+
##### uncompressed
61+
62+
Type: `boolean`<br>
63+
Default: `false` if either of gzip or brotli is `true`, otherwise `true`
64+
65+
Displays the uncompressed size.
5266

5367
##### pretty
5468

test.js

+65
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,51 @@ it('should have `gzip` option', callback => {
9292
stream.end();
9393
});
9494

95+
it('should have `brotli` option', callback => {
96+
const out = process.stdout.write.bind(process.stdout);
97+
const stream = size({brotli: true});
98+
99+
process.stdout.write = string => {
100+
out(string);
101+
102+
if (/brotli/.test(string)) {
103+
assert(true);
104+
process.stdout.write = out;
105+
callback();
106+
}
107+
};
108+
109+
stream.write(new Vinyl({
110+
path: path.join(__dirname, 'fixture.js'),
111+
contents: Buffer.from('unicorn world')
112+
}));
113+
114+
stream.end();
115+
});
116+
117+
it('should show `uncompressed`, `gzip` and `brotli` size', callback => {
118+
const out = process.stdout.write.bind(process.stdout);
119+
const stream = size({uncompressed: true, gzip: true, brotli: true});
120+
121+
process.stdout.write = string => {
122+
out(string);
123+
124+
// Name of compressions protocols and three numbers
125+
if (/gzipped.*brotli/.test(string) && /(?:.*\b\d+){3}/.test(string)) {
126+
assert(true);
127+
process.stdout.write = out;
128+
callback();
129+
}
130+
};
131+
132+
stream.write(new Vinyl({
133+
path: path.join(__dirname, 'fixture.js'),
134+
contents: Buffer.from('unicorn world')
135+
}));
136+
137+
stream.end();
138+
});
139+
95140
it('should not show prettified size when `pretty` option is false', callback => {
96141
const out = process.stdout.write.bind(process.stdout);
97142
const stream = size({pretty: false});
@@ -170,3 +215,23 @@ it('should handle stream contents with `gzip` option', callback => {
170215

171216
stream.end();
172217
});
218+
219+
it('should handle stream contents with `brotli` option', callback => {
220+
const contents = through();
221+
const stream = size({brotli: true});
222+
223+
stream.on('finish', () => {
224+
assert.strictEqual(stream.size, 17);
225+
assert.strictEqual(stream.prettySize, '17 B');
226+
callback();
227+
});
228+
229+
stream.write(new Vinyl({
230+
path: path.join(__dirname, 'fixture.js'),
231+
contents
232+
}));
233+
234+
contents.end(Buffer.from('unicorn world'));
235+
236+
stream.end();
237+
});

0 commit comments

Comments
 (0)