87 lines
3.1 KiB
JavaScript
87 lines
3.1 KiB
JavaScript
import { copyFile, mkdir, readdir, readFile, rm, stat, writeFile } from 'node:fs/promises';
|
|
import { dirname, extname, join, resolve } from 'node:path';
|
|
import { transform } from 'esbuild';
|
|
|
|
const sourceRoot = resolve(import.meta.dirname, '..');
|
|
const outputRoot = resolve(sourceRoot, 'dist');
|
|
const sourceDirectories = ['public', 'utils'];
|
|
const sourceFiles = ['config.js', 'tokens.css'];
|
|
const totals = {
|
|
css: { files: 0, sourceBytes: 0, outputBytes: 0 },
|
|
js: { files: 0, sourceBytes: 0, outputBytes: 0 }
|
|
};
|
|
|
|
function shouldMinify(filePath) {
|
|
const extension = extname(filePath).toLowerCase();
|
|
return (extension === '.css' || extension === '.js') && !filePath.toLowerCase().endsWith(`.min${extension}`);
|
|
}
|
|
|
|
async function writeBuildFile(sourcePath, destinationPath) {
|
|
await mkdir(dirname(destinationPath), { recursive: true });
|
|
if (!shouldMinify(sourcePath)) {
|
|
await copyFile(sourcePath, destinationPath);
|
|
return;
|
|
}
|
|
|
|
const extension = extname(sourcePath).slice(1).toLowerCase();
|
|
const source = await readFile(sourcePath, 'utf8');
|
|
const result = await transform(source, {
|
|
loader: extension,
|
|
minify: true,
|
|
target: 'es2018',
|
|
charset: 'utf8',
|
|
legalComments: 'inline'
|
|
});
|
|
const output = result.code.trimEnd() + '\n';
|
|
|
|
await writeFile(destinationPath, output, 'utf8');
|
|
totals[extension].files += 1;
|
|
totals[extension].sourceBytes += Buffer.byteLength(source);
|
|
totals[extension].outputBytes += Buffer.byteLength(output);
|
|
}
|
|
|
|
async function copyDirectory(sourceDirectory, destinationDirectory) {
|
|
await mkdir(destinationDirectory, { recursive: true });
|
|
for (const entry of await readdir(sourceDirectory, { withFileTypes: true })) {
|
|
const sourcePath = join(sourceDirectory, entry.name);
|
|
const destinationPath = join(destinationDirectory, entry.name);
|
|
|
|
if (entry.isDirectory()) await copyDirectory(sourcePath, destinationPath);
|
|
else if (entry.isFile()) await writeBuildFile(sourcePath, destinationPath);
|
|
}
|
|
}
|
|
|
|
function formatSavings(type) {
|
|
const total = totals[type];
|
|
const percent = total.sourceBytes
|
|
? Math.round((1 - total.outputBytes / total.sourceBytes) * 100)
|
|
: 0;
|
|
return `${type.toUpperCase()}: ${total.files} 个文件,${total.sourceBytes} → ${total.outputBytes} 字节,减少 ${percent}%`;
|
|
}
|
|
|
|
if (outputRoot !== resolve(sourceRoot, 'dist')) {
|
|
throw new Error('拒绝清理预期目录之外的构建输出');
|
|
}
|
|
|
|
await rm(outputRoot, { recursive: true, force: true });
|
|
await mkdir(outputRoot, { recursive: true });
|
|
|
|
for (const entry of await readdir(sourceRoot, { withFileTypes: true })) {
|
|
if (entry.isFile() && entry.name.endsWith('.html')) {
|
|
await copyFile(join(sourceRoot, entry.name), join(outputRoot, entry.name));
|
|
}
|
|
}
|
|
for (const file of sourceFiles) {
|
|
await writeBuildFile(join(sourceRoot, file), join(outputRoot, file));
|
|
}
|
|
for (const directory of sourceDirectories) {
|
|
const sourceDirectory = join(sourceRoot, directory);
|
|
if ((await stat(sourceDirectory)).isDirectory()) {
|
|
await copyDirectory(sourceDirectory, join(outputRoot, directory));
|
|
}
|
|
}
|
|
|
|
console.log(`压缩构建已生成:${outputRoot}`);
|
|
console.log(formatSavings('css'));
|
|
console.log(formatSavings('js'));
|