mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-11-13 15:51:00 +00:00
04b1fb72bb
Every file under assets/ was compressed again on each download, at the low level a request path can afford. They carry their content hash in the name and never change, so the client build now writes a Brotli copy beside each compressible one (postbuild, scripts/precompress.mjs) and production points a browser that accepts Brotli at that copy before express.static serves it. Measured on a production build of 5.1.2, fetching the 652 precached files the way a service worker install does: 16.3 MB -> 13.9 MB on the wire and about 1.0 s -> 0.2 s of server CPU per install. A client without Brotli, a range request and a build without copies are served exactly as before.
61 lines
2.2 KiB
JavaScript
61 lines
2.2 KiB
JavaScript
/*
|
|
* The work behind scripts/precompress.mjs: a Brotli copy (<file>.br) beside every
|
|
* compressible build file under dist/assets.
|
|
*
|
|
* The server compresses a build file on every download otherwise, at the low level
|
|
* a request path can afford. A file under assets/ carries its content hash in its
|
|
* name and never changes, so it is compressed once here, at the level a build can
|
|
* afford, and the server hands that copy out as it is
|
|
* (server/src/nest/platform/precompressed-static.ts).
|
|
*/
|
|
import { readdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
import { join } from 'node:path';
|
|
import { brotliCompressSync, constants } from 'node:zlib';
|
|
|
|
/** What the server's own compression would take; fonts, images and video are compressed formats already. */
|
|
const COMPRESSIBLE = /\.(?:m?js|css|svg|json|wasm|html|webmanifest)$/i;
|
|
/** The server leaves anything below 1 kB uncompressed too: the headers cost more than the saving. */
|
|
export const MIN_BYTES = 1024;
|
|
/** Quality 11 takes about ten times as long per byte as 9, so the largest chunks get 9. */
|
|
const BIG_BYTES = 1024 * 1024;
|
|
|
|
function* walk(dir) {
|
|
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
const full = join(dir, entry.name);
|
|
if (entry.isDirectory()) yield* walk(full);
|
|
else yield full;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Writes the .br copies under `dir` and returns what it did. A copy that would not be
|
|
* smaller than its file is not written, and a directory that does not exist is no work.
|
|
*/
|
|
export function precompress(dir) {
|
|
const result = { files: 0, before: 0, after: 0 };
|
|
let files;
|
|
try {
|
|
files = [...walk(dir)];
|
|
} catch (err) {
|
|
if (err.code === 'ENOENT') return result;
|
|
throw err;
|
|
}
|
|
for (const file of files) {
|
|
if (!COMPRESSIBLE.test(file)) continue;
|
|
const input = readFileSync(file);
|
|
if (input.length < MIN_BYTES) continue;
|
|
const output = brotliCompressSync(input, {
|
|
params: {
|
|
[constants.BROTLI_PARAM_QUALITY]: input.length > BIG_BYTES ? 9 : 11,
|
|
[constants.BROTLI_PARAM_SIZE_HINT]: input.length,
|
|
},
|
|
});
|
|
if (output.length >= input.length) continue;
|
|
writeFileSync(`${file}.br`, output);
|
|
result.files += 1;
|
|
result.before += input.length;
|
|
result.after += output.length;
|
|
}
|
|
return result;
|
|
}
|