Skip to content

Commit 63eb646

Browse files
committed
Add a zlib-based compress script
Based on `compress.py` from WebKit#170, with some modifications: - Can be run as `npm run compress` or simply `node compress.mjs` - Uses best zlib compression setting. - Takes arbitrary glob patterns for input files, defaulting to all .z files for decompression. - Copies the file mode over to avoid spurious git diffs.
1 parent b131126 commit 63eb646

File tree

3 files changed

+595
-13
lines changed

3 files changed

+595
-13
lines changed

compress.mjs

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import { globSync } from 'glob';
2+
import zlib from 'zlib';
3+
import fs from 'fs';
4+
5+
const isNPM = process.env.npm_config_user_agent !== undefined;
6+
const command = isNPM ? 'npm run compress --' : 'node compress.mjs';
7+
8+
const usage = `Usage: ${command} [options] <glob>...
9+
10+
Options:
11+
-d, --decompress Decompress files (default: compress).
12+
-k, --keep Keep input files after processing (default: delete).`;
13+
14+
const args = process.argv.slice(2);
15+
if (args.length === 0) {
16+
console.log(usage);
17+
process.exit(1);
18+
}
19+
20+
const keepInputFiles = args.some(arg => arg === '-k' || arg === '--keep');
21+
const decompressMode = args.some(arg => arg === '-d' || arg === '--decompress');
22+
23+
let globs = args.filter(arg => !arg.startsWith('-'));
24+
if (globs.length === 0) {
25+
if (decompressMode) {
26+
const defaultGlob = '**/*.z';
27+
console.log(`No input glob pattern given, using default: ${defaultGlob}`);
28+
globs = [defaultGlob];
29+
} else {
30+
// To prevent accidental compression, require explicit input file patterns.
31+
console.log(usage);
32+
process.exit(1);
33+
}
34+
}
35+
36+
let files = new Set();
37+
if (globs.length > 0) {
38+
for (const glob of globs) {
39+
try {
40+
const matches = globSync(glob, { nodir: true });
41+
for (const match of matches) {
42+
files.add(match);
43+
}
44+
} catch (err) {
45+
console.error(`Error processing glob: ${glob}`, err);
46+
}
47+
}
48+
}
49+
files = Array.from(files).sort();
50+
if (files.length === 0) {
51+
console.log(`No files found to process with the given globs: ${globs.join(', ')}`);
52+
process.exit(0);
53+
}
54+
55+
function compress(inputData) {
56+
const compressedData = zlib.deflateSync(inputData, { level: zlib.constants.Z_BEST_COMPRESSION });
57+
58+
const originalSize = inputData.length;
59+
const compressedSize = compressedData.length;
60+
const compressionRatio = (1 - compressedSize / originalSize) * 100;
61+
console.log(` Original size: ${String(originalSize).padStart(8)} bytes`);
62+
console.log(` Compressed size: ${String(compressedSize).padStart(8)} bytes`);
63+
console.log(` Compression ratio: ${compressionRatio.toFixed(2).padStart(8)}%`);
64+
65+
return compressedData;
66+
}
67+
68+
function decompress(inputData) {
69+
const decompressedData = zlib.inflateSync(inputData);
70+
71+
const compressedSize = inputData.length;
72+
const decompressedSize = decompressedData.length;
73+
const expansionRatio = (decompressedSize / compressedSize - 1) * 100;
74+
console.log(` Compressed size: ${String(compressedSize).padStart(8)} bytes`);
75+
console.log(` Decompressed size: ${String(decompressedSize).padStart(8)} bytes`);
76+
console.log(` Expansion ratio: ${expansionRatio.toFixed(2).padStart(8)}%`);
77+
78+
return decompressedData;
79+
}
80+
81+
const verb = decompressMode ? 'decompress' : 'compress';
82+
console.log(`Found ${files.length} files to ${verb}...`);
83+
84+
for (const inputFile of files) {
85+
try {
86+
console.log(inputFile);
87+
88+
// Copy the mode over to avoid git status entries after a roundtrip.
89+
const { mode } = fs.statSync(inputFile);
90+
const inputData = fs.readFileSync(inputFile);
91+
const outputData = decompressMode ? decompress(inputData) : compress(inputData);
92+
let outputFile;
93+
if (decompressMode) {
94+
outputFile = inputFile.endsWith('.z') ? inputFile.slice(0, -2) : `${inputFile}.decompressed`;
95+
} else {
96+
outputFile = `${inputFile}.z`;
97+
}
98+
fs.writeFileSync(outputFile, outputData, { mode });
99+
100+
if (!keepInputFiles) {
101+
fs.unlinkSync(inputFile);
102+
console.log(` Deleted input file.`);
103+
}
104+
} catch (err) {
105+
console.error(`Error ${verb}ing ${inputFile}:`, err);
106+
}
107+
}

0 commit comments

Comments
 (0)