Files
eSIM-Tools/scripts/compress.js
copilot-swe-agent[bot] bd522e7d7e Refactor: Improve code quality and performance across scripts and modules
- Add parallel processing and caching to image optimization
- Enhance compression with Brotli and intelligent filtering
- Improve Performance module with memory leak prevention
- Optimize webpack config with better chunking and caching
- Add utility library with debounce/throttle/retry functions
- Create validation middleware for Express server
- Add comprehensive architecture documentation

Co-authored-by: Silentely <[email protected]>
2025-10-04 03:07:23 +00:00

203 lines
5.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const fs = require('fs');
const path = require('path');
const zlib = require('zlib');
const { promisify } = require('util');
const gzip = promisify(zlib.gzip);
const brotliCompress = promisify(zlib.brotliCompress);
// 压缩配置
const compressionOptions = {
gzip: {
level: 9,
memLevel: 9
},
brotli: {
params: {
[zlib.constants.BROTLI_PARAM_QUALITY]: 11,
[zlib.constants.BROTLI_PARAM_MODE]: zlib.constants.BROTLI_MODE_GENERIC,
[zlib.constants.BROTLI_PARAM_SIZE_HINT]: 0
}
}
};
// 需要压缩的文件类型
const compressibleExtensions = ['.js', '.css', '.html', '.json', '.xml', '.svg'];
// 最小压缩文件大小 (1KB)
const MIN_COMPRESS_SIZE = 1024;
// 缓存已压缩文件的元数据
const compressionCache = new Map();
// 压缩单个文件
async function compressFile(filePath) {
try {
const content = fs.readFileSync(filePath);
const ext = path.extname(filePath);
// 只压缩特定类型的文件
if (!compressibleExtensions.includes(ext)) {
return null;
}
// Skip small files (compression not worth it)
if (content.length < MIN_COMPRESS_SIZE) {
return null;
}
const fileName = path.basename(filePath);
const dir = path.dirname(filePath);
const fileStat = fs.statSync(filePath);
// Check cache - skip if already compressed and source hasn't changed
const cacheKey = filePath;
if (compressionCache.has(cacheKey)) {
const cached = compressionCache.get(cacheKey);
if (cached.mtime >= fileStat.mtimeMs) {
return null; // Already compressed
}
}
// Gzip压缩
const gzipPath = path.join(dir, `${fileName}.gz`);
const gzipped = await gzip(content, compressionOptions.gzip);
// Only write if compression achieved meaningful reduction
const compressionRatio = (content.length - gzipped.length) / content.length;
if (compressionRatio > 0.1) { // At least 10% reduction
fs.writeFileSync(gzipPath, gzipped);
} else {
return null; // Skip if compression is not effective
}
// Brotli压缩如果支持
let brotliSize = 0;
try {
const brotlied = await brotliCompress(content, compressionOptions.brotli);
const brotliPath = path.join(dir, `${fileName}.br`);
// Only write if brotli is smaller than gzip
if (brotlied.length < gzipped.length) {
fs.writeFileSync(brotliPath, brotlied);
brotliSize = brotlied.length;
}
} catch (error) {
console.log(`Brotli压缩失败 ${fileName}:`, error.message);
}
const originalSize = content.length;
const gzipSize = gzipped.length;
const compressionRatioPercent = (compressionRatio * 100).toFixed(1);
// Update cache
compressionCache.set(cacheKey, { mtime: fileStat.mtimeMs });
return {
file: fileName,
original: originalSize,
gzip: gzipSize,
brotli: brotliSize,
ratio: compressionRatioPercent
};
} catch (error) {
console.error(`压缩文件失败 ${filePath}:`, error.message);
return null;
}
}
// 递归压缩目录
async function compressDirectory(dirPath) {
const results = [];
async function scanDirectory(dir) {
const files = fs.readdirSync(dir);
for (const file of files) {
const fullPath = path.join(dir, file);
const stat = fs.statSync(fullPath);
if (stat.isDirectory()) {
await scanDirectory(fullPath);
} else if (stat.isFile()) {
const result = await compressFile(fullPath);
if (result) {
results.push(result);
}
}
}
}
await scanDirectory(dirPath);
return results;
}
// 主压缩函数
async function compressBuild() {
const distDir = path.join(__dirname, '../dist');
if (!fs.existsSync(distDir)) {
console.error('dist目录不存在请先运行构建命令');
return;
}
console.log('开始压缩构建文件...');
try {
const results = await compressDirectory(distDir);
if (results.length === 0) {
console.log('没有找到需要压缩的文件');
return;
}
// 显示压缩结果
console.log('\n压缩结果:');
console.log('文件名'.padEnd(30) + '原始大小'.padEnd(12) + 'Gzip大小'.padEnd(12) + 'Brotli大小'.padEnd(12) + '压缩率');
console.log('-'.repeat(80));
let totalOriginal = 0;
let totalGzip = 0;
let totalBrotli = 0;
results.forEach(result => {
totalOriginal += result.original;
totalGzip += result.gzip;
if (result.brotli) totalBrotli += result.brotli;
const originalKB = (result.original / 1024).toFixed(1);
const gzipKB = (result.gzip / 1024).toFixed(1);
const brotliKB = result.brotli ? (result.brotli / 1024).toFixed(1) : '-';
console.log(
result.file.padEnd(30) +
`${originalKB}KB`.padEnd(12) +
`${gzipKB}KB`.padEnd(12) +
`${brotliKB}KB`.padEnd(12) +
`${result.ratio}%`
);
});
const totalRatio = ((totalOriginal - totalGzip) / totalOriginal * 100).toFixed(1);
console.log('-'.repeat(80));
console.log(
'总计'.padEnd(30) +
`${(totalOriginal / 1024).toFixed(1)}KB`.padEnd(12) +
`${(totalGzip / 1024).toFixed(1)}KB`.padEnd(12) +
`${totalBrotli > 0 ? (totalBrotli / 1024).toFixed(1) + 'KB' : '-'}`.padEnd(12) +
`${totalRatio}%`
);
console.log(`\n压缩完成!共处理 ${results.length} 个文件`);
console.log(`节省空间: ${((totalOriginal - totalGzip) / 1024).toFixed(1)}KB`);
} catch (error) {
console.error('压缩失败:', error);
}
}
// 如果直接运行此脚本
if (require.main === module) {
compressBuild();
}
module.exports = { compressBuild, compressFile };