Skip to content

Commit 8dec5e2

Browse files
committed
Add zopfli
1 parent d13511f commit 8dec5e2

File tree

6 files changed

+117
-31
lines changed

6 files changed

+117
-31
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ compress({
4848
| `--from` (alias `--input`) | `from` (alias `input`) | Input folder | `process.cwd()` |
4949
| `--to` (alias `--output`) | `to` (alias `output`) | Output folder | `from` param value |
5050
| `--formats` | `formats` | Formats of output files | `['gzip', 'brotli']`|
51+
| `--with-zopfli` | `withZopfli` | Enable zopfli algorithm for gzip | `false` |
5152
| `--ext-white-list` | `extWhiteList` | A list of extensions that will be used to filter the necessary files | `['.html', '.css', '.js', '.json', '.svg', '.txt', '.xml']` |
5253
| `--concurrency` | `concurrency` | number of parallel handlers | `os.cpus().length` |
5354
| `--file-size` | `fileSize` | File size treshold in bytes. Files smaller than this size will be ignored | `0` |

cli.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ compress({
6363
formats,
6464
extWhiteList: params['ext-white-list'],
6565
concurrency: params['concurrency'],
66-
fileSize: params['file-size']
66+
fileSize: params['file-size'],
67+
withZopfli: params['with-zopfli']
6768
})
6869
.then(() => {
6970
const diffTime = Math.round(performance.now() - startTime);

lib/constants.js

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,31 @@ const FORMATS = {
88

99
exports.FORMATS = FORMATS;
1010

11-
exports.DEFAULT_COMPRESS_SETTINGS = {
12-
[FORMATS.GZIP]: {
13-
level: zlib.constants.Z_BEST_COMPRESSION
14-
},
15-
[FORMATS.BROTLI]: {
16-
params: {
17-
[zlib.constants.BROTLI_PARAM_MODE]: zlib.constants.BROTLI_MODE_TEXT,
18-
[zlib.constants.BROTLI_PARAM_QUALITY]: zlib.constants.BROTLI_MAX_QUALITY
19-
}
20-
},
21-
[FORMATS.ZSTD]: {
22-
params: {
23-
[zlib.constants.ZSTD_c_compressionLevel]: 22,
24-
[zlib.constants.ZSTD_c_checksumFlag]: 1,
11+
exports.DEFAULT_COMPRESS_SETTINGS = (params) => {
12+
const { withZopfli = false } = params;
13+
return {
14+
[FORMATS.GZIP]: withZopfli
15+
? {
16+
verbose: false,
17+
verbose_more: false,
18+
numiterations: 15,
19+
blocksplitting: true,
20+
blocksplittingmax: 15,
21+
}
22+
: {
23+
level: zlib.constants.Z_BEST_COMPRESSION
24+
},
25+
[FORMATS.BROTLI]: {
26+
params: {
27+
[zlib.constants.BROTLI_PARAM_MODE]: zlib.constants.BROTLI_MODE_TEXT,
28+
[zlib.constants.BROTLI_PARAM_QUALITY]: zlib.constants.BROTLI_MAX_QUALITY
29+
}
30+
},
31+
[FORMATS.ZSTD]: {
32+
params: {
33+
[zlib.constants.ZSTD_c_compressionLevel]: 22,
34+
[zlib.constants.ZSTD_c_checksumFlag]: 1,
35+
}
2536
}
2637
}
2738
}

lib/functions.js

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
const os = require('os');
22
const fs = require('fs');
3-
const fsp = require('fs/promises');
43
const path = require('path');
5-
const { pipeline } = require('stream/promises');
4+
const stream = require('stream');
65
const zlib = require('zlib');
6+
const { Buffer } = require('buffer')
7+
const { gzipAsync } = require('@gfx/zopfli');
78

89
const CONSTANTS = require('./constants');
910
const { walk, makeDir } = require('./fs-utils');
@@ -16,20 +17,37 @@ const {
1617
flatMap
1718
} = require('./generators-utils');
1819

19-
20-
const FORMAT_TO_STREAM = {
21-
[CONSTANTS.FORMATS.GZIP]: zlib.createGzip,
22-
[CONSTANTS.FORMATS.BROTLI]: zlib.createBrotliCompress,
23-
[CONSTANTS.FORMATS.ZSTD]: zlib.createZstdCompress || (() => {
24-
throw new Error(`Your version of Node.js doesn't support zstd. Node.js has zstd support since 23.8.0 and 22.15.0.`)
25-
})
20+
const FORMAT_TO_STREAM = (params) => {
21+
const { withZopfli = false } = params;
22+
return {
23+
[CONSTANTS.FORMATS.GZIP]:
24+
withZopfli
25+
? (
26+
function createZopfli(options) {
27+
return stream.Duplex.from(async function* (source) {
28+
const chunks = [];
29+
for await (const chunk of source) {
30+
chunks.push(chunk);
31+
}
32+
yield await gzipAsync(Buffer.concat(chunks), options);
33+
})
34+
}
35+
)
36+
: zlib.createGzip,
37+
[CONSTANTS.FORMATS.BROTLI]: zlib.createBrotliCompress,
38+
[CONSTANTS.FORMATS.ZSTD]: zlib.createZstdCompress || (() => {
39+
throw new Error(`Your version of Node.js doesn't support zstd. Node.js has zstd support since 23.8.0 and 22.15.0.`)
40+
})
41+
}
2642
}
2743

2844

29-
function createCompressStream({ fromPath, toPath, format, compressOptions }) {
30-
return pipeline(
45+
function createCompressStream({ fromPath, toPath, format, compressOptions, withZopfli }) {
46+
const params = { withZopfli }
47+
48+
return stream.promises.pipeline(
3149
fs.createReadStream(fromPath),
32-
FORMAT_TO_STREAM[format](compressOptions || CONSTANTS.DEFAULT_COMPRESS_SETTINGS[format]),
50+
FORMAT_TO_STREAM(params)[format](compressOptions || CONSTANTS.DEFAULT_COMPRESS_SETTINGS[format]),
3351
fs.createWriteStream(toPath + CONSTANTS.FORMAT_TO_EXT[format])
3452
);
3553
}
@@ -51,14 +69,15 @@ async function compress(params) {
5169
formats = [CONSTANTS.FORMATS.GZIP],
5270
extWhiteList = CONSTANTS.EXT_WHITE_LIST,
5371
concurrency = getConcurrency(),
54-
fileSize
72+
fileSize,
73+
withZopfli = false
5574
} = params;
5675

5776
const handlers = [
5877
filter(item => item.dirent.isFile()),
5978
filter(item => extWhiteList.includes(path.extname(item.direntPath))),
6079
fileSize && map(async item => {
61-
const stat = await fsp.stat(item.direntPath);
80+
const stat = await fs.promises.stat(item.direntPath);
6281
return {
6382
...item,
6483
size: stat.size
@@ -83,7 +102,7 @@ async function compress(params) {
83102

84103
await makeDir(toBasePath);
85104

86-
return createCompressStream({ fromPath, toPath, format: item.format });
105+
return createCompressStream({ fromPath, toPath, format: item.format, withZopfli });
87106
}
88107
}),
89108
].filter(Boolean);

package-lock.json

Lines changed: 51 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,8 @@
3434
"zstandard",
3535
"archive",
3636
"CLI"
37-
]
37+
],
38+
"dependencies": {
39+
"@gfx/zopfli": "^1.0.15"
40+
}
3841
}

0 commit comments

Comments
 (0)