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'));