forked from baron/baron-sso
82 lines
2.3 KiB
JavaScript
82 lines
2.3 KiB
JavaScript
import { brotliCompressSync, constants } from 'node:zlib';
|
|
import { createHash } from 'node:crypto';
|
|
import { existsSync, readFileSync, readdirSync, renameSync, writeFileSync } from 'node:fs';
|
|
import { basename, extname, join } from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
import { dirname } from 'node:path';
|
|
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = dirname(__filename);
|
|
const buildDir = process.argv[2] ?? join(__dirname, '..', 'build', 'web');
|
|
const bootstrapPath = join(buildDir, 'flutter_bootstrap.js');
|
|
const hashableEntrypoints = ['main.dart.js', 'main.dart.mjs', 'main.dart.wasm'];
|
|
const compressibleExtensions = new Set([
|
|
'.css',
|
|
'.html',
|
|
'.js',
|
|
'.json',
|
|
'.mjs',
|
|
'.svg',
|
|
'.toml',
|
|
'.wasm',
|
|
]);
|
|
|
|
if (!existsSync(bootstrapPath)) {
|
|
throw new Error(`Missing Flutter bootstrap file: ${bootstrapPath}`);
|
|
}
|
|
|
|
let bootstrap = readFileSync(bootstrapPath, 'utf8');
|
|
|
|
for (const entrypoint of hashableEntrypoints) {
|
|
const sourcePath = join(buildDir, entrypoint);
|
|
if (!existsSync(sourcePath)) {
|
|
continue;
|
|
}
|
|
|
|
const content = readFileSync(sourcePath);
|
|
const hash = createHash('sha256').update(content).digest('hex').slice(0, 12);
|
|
const extension = extname(entrypoint);
|
|
const stem = entrypoint.slice(0, -extension.length);
|
|
const hashedName = `${stem}.${hash}${extension}`;
|
|
const targetPath = join(buildDir, hashedName);
|
|
|
|
renameSync(sourcePath, targetPath);
|
|
bootstrap = bootstrap.replaceAll(entrypoint, hashedName);
|
|
}
|
|
|
|
writeFileSync(bootstrapPath, bootstrap);
|
|
|
|
for (const filePath of walk(buildDir)) {
|
|
if (filePath.endsWith('.br')) {
|
|
continue;
|
|
}
|
|
if (!compressibleExtensions.has(extname(filePath))) {
|
|
continue;
|
|
}
|
|
|
|
const content = readFileSync(filePath);
|
|
writeFileSync(
|
|
`${filePath}.br`,
|
|
brotliCompressSync(content, {
|
|
params: {
|
|
[constants.BROTLI_PARAM_QUALITY]: 11,
|
|
},
|
|
}),
|
|
);
|
|
}
|
|
|
|
function* walk(directory) {
|
|
for (const entry of readdirSync(directory, { withFileTypes: true })) {
|
|
const entryPath = join(directory, entry.name);
|
|
if (entry.isDirectory()) {
|
|
yield* walk(entryPath);
|
|
continue;
|
|
}
|
|
if (entry.isFile()) {
|
|
yield entryPath;
|
|
}
|
|
}
|
|
}
|
|
|
|
console.log(`[userfront] optimized ${basename(buildDir)} with hashed entrypoints and brotli assets`);
|