Skip to content

Commit e7bc27d

Browse files
committed
Parallelize and batch process files
1 parent 8c56e05 commit e7bc27d

File tree

2 files changed

+56
-20
lines changed

2 files changed

+56
-20
lines changed

src/compress.ts

+51-18
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@ import { createBrotliCompress, createGzip } from "node:zlib";
77

88
import * as logger from "./logger.js";
99

10+
interface CompressionOptions {
11+
dir: string;
12+
extensions: Array<string>;
13+
enabled?: boolean;
14+
batchSize?: number;
15+
}
16+
1017
async function* walkDir(dir: string, extensions: Array<string>): AsyncGenerator<string> {
1118
const entries = await readdir(dir, { withFileTypes: true });
1219
for (const entry of entries) {
@@ -23,44 +30,70 @@ const filterFile = (file: string, extensions: Array<string>): boolean => {
2330
return extensions.some((ext) => extname(file) === ext);
2431
};
2532

26-
export const gzip = async (dir: string, extensions: Array<string>, enabled?: boolean): Promise<void> => {
33+
// const compress = async <T>(name: string, compressor: () => T, opts: CompressionOptions): Promise<void> => {};
34+
35+
export const gzip = async (
36+
dir: string,
37+
extensions: Array<string>,
38+
enabled?: boolean,
39+
batchSize = 10,
40+
): Promise<void> => {
2741
if (!enabled) {
2842
logger.warn("gzip compression disabled, skipping...");
2943
return;
3044
}
3145

3246
const start = hrtime.bigint();
33-
34-
let counter = 0;
47+
const files = [];
3548
for await (const file of walkDir(dir, extensions)) {
36-
counter += 1;
37-
const source = createReadStream(file);
38-
const destination = createWriteStream(`${file}.gz`);
39-
const gzip = createGzip({ level: 9 });
40-
await stream.pipeline(source, gzip, destination);
49+
files.push(file);
50+
}
51+
52+
for (let i = 0; i < files.length; i += batchSize) {
53+
const batch = files.slice(i, i + batchSize);
54+
await Promise.all(
55+
batch.map(async (path) => {
56+
const source = createReadStream(path);
57+
const destination = createWriteStream(`${path}.br`);
58+
const brotli = createGzip({ level: 9 });
59+
await stream.pipeline(source, brotli, destination);
60+
}),
61+
);
4162
}
4263

4364
const end = hrtime.bigint();
44-
logger.success(`finished gzip of ${counter} files in ${(end - start) / BigInt(1000000)}ms`);
65+
logger.success(`finished gzip of ${files.length} files in ${(end - start) / BigInt(1000000)}ms`);
4566
};
4667

47-
export const brotli = async (dir: string, extensions: Array<string>, enabled?: boolean): Promise<void> => {
68+
export const brotli = async (
69+
dir: string,
70+
extensions: Array<string>,
71+
enabled?: boolean,
72+
batchSize = 10,
73+
): Promise<void> => {
4874
if (!enabled) {
4975
logger.warn("brotli compression disabled, skipping...");
5076
return;
5177
}
5278

5379
const start = hrtime.bigint();
54-
55-
let counter = 0;
80+
const files = [];
5681
for await (const file of walkDir(dir, extensions)) {
57-
counter += 1;
58-
const source = createReadStream(file);
59-
const destination = createWriteStream(`${file}.br`);
60-
const brotli = createBrotliCompress();
61-
await stream.pipeline(source, brotli, destination);
82+
files.push(file);
83+
}
84+
85+
for (let i = 0; i < files.length; i += batchSize) {
86+
const batch = files.slice(i, i + batchSize);
87+
await Promise.all(
88+
batch.map(async (path) => {
89+
const source = createReadStream(path);
90+
const destination = createWriteStream(`${path}.br`);
91+
const brotli = createBrotliCompress();
92+
await stream.pipeline(source, brotli, destination);
93+
}),
94+
);
6295
}
6396

6497
const end = hrtime.bigint();
65-
logger.success(`finished brotli of ${counter} files in ${(end - start) / BigInt(1000000)}ms`);
98+
logger.success(`finished brotli of ${files.length} files in ${(end - start) / BigInt(1000000)}ms`);
6699
};

src/index.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,15 @@ interface Options {
1414
brotli?: boolean;
1515
/** Extensions to compress, must be in the format `.html`, `.css` etc */
1616
fileExtensions?: Array<string>;
17+
/** Number of files to batch process */
18+
batchSize?: number;
1719
}
1820

1921
const defaultOptions: Required<Options> = {
2022
gzip: true,
2123
brotli: true,
2224
fileExtensions: defaultFileExtensions,
25+
batchSize: 10,
2326
};
2427

2528
export default function (opts: Options = defaultOptions): AstroIntegration {
@@ -31,8 +34,8 @@ export default function (opts: Options = defaultOptions): AstroIntegration {
3134
"astro:build:done": async ({ dir }) => {
3235
const path = fileURLToPath(dir);
3336
await Promise.allSettled([
34-
gzip(path, options.fileExtensions, options.gzip),
35-
brotli(path, options.fileExtensions, options.brotli),
37+
gzip(path, options.fileExtensions, options.gzip, options.batchSize),
38+
brotli(path, options.fileExtensions, options.brotli, options.batchSize),
3639
]);
3740
logger.success("Compression finished\n");
3841
},

0 commit comments

Comments
 (0)