使用方法
适用于一个目录下有多个项目,运行node build_and_compress 或者一键打包.bat。
const fs = require('fs');
const path = require('path');
const archiver = require('archiver');
const readline = require('readline');
const { exec } = require('child_process');
// 自动获取项目目录,根目录为当前脚本运行的目录
function getProjects() {
const rootDir = __dirname;
const projects = [];
const entries = fs.readdirSync(rootDir, { withFileTypes: true });
// 递归查找 vite.config.js 文件,跳过 node_modules 目录
function findViteConfig(dir) {
const files = fs.readdirSync(dir, { withFileTypes: true });
for (const file of files) {
const filePath = path.join(dir, file.name);
if (file.isDirectory()) {
// 跳过 node_modules 目录
if (file.name === 'node_modules') {
continue;
}
const result = findViteConfig(filePath);
if (result) {
return result;
}
} else if (file.name == 'vite.config.js') {
return dir;
}
}
return null;
}
entries.forEach(entry => {
if (entry.isDirectory()) {
const projectPath = path.join(rootDir, entry.name);
const viteConfigDir = findViteConfig(projectPath);
if (viteConfigDir) {
projects.push({
// 使用目录名作为项目名
name: entry.name,
path: viteConfigDir
});
}
}
});
return projects;
}
const projects = getProjects();
// 列出所有项目目录
console.log('可用项目如下:');
if (projects.length === 0) {
console.log('没有找到任何项目目录。');
return;
} else {
projects.forEach((project, index) => {
console.log(`${index}: ${project.name}${' '.repeat(14 - project.name.length)} - ${project.path}`);
});
}
// 获取桌面路径
const desktopPath = path.join(require('os').homedir(), 'Desktop');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
// 读取 vite.config.js 中的 build.outDir 配置
function getBuildOutDir(projectPath) {
const viteConfigPath = path.join(projectPath, 'vite.config.js');
if (fs.existsSync(viteConfigPath)) {
const viteConfigContent = fs.readFileSync(viteConfigPath, 'utf8');
const match = viteConfigContent.match(/outDir:\s*["']([^"']+)["']/);
if (match) {
return match[1];
}
}
// 默认值
return 'dist';
}
// 记录总开始时间
const totalStartTime = Date.now();
// 提示用户输入多个项目索引
rl.question('请输入要打包的项目索引(多个索引以逗号隔开,从 0 开始),输入 all 打包所有项目: ', (input) => {
let validIndexes = [];
if (input.trim().toLowerCase() === 'all') {
validIndexes = Array.from({ length: projects.length }, (_, i) => i);
} else {
const indexes = input.split(',').map(index => parseInt(index.trim()));
validIndexes = indexes.filter(index => {
return !isNaN(index) && index >= 0 && index < projects.length;
});
}
if (validIndexes.length === 0) {
console.log('无效的索引,请输入有效的项目索引或输入 all。');
rl.close();
return;
}
const processProject = (index) => {
if (index >= validIndexes.length) {
// 记录总结束时间并计算总耗时
const totalEndTime = Date.now();
const totalTimeInSeconds = (totalEndTime - totalStartTime) / 1000;
const minutes = Math.floor(totalTimeInSeconds / 60);
const seconds = Math.floor(totalTimeInSeconds % 60);
console.log(`所有项目打包完成,总计耗时: ${minutes} 分钟 ${seconds} 秒`);
rl.close();
return;
}
const projectIndex = validIndexes[index];
const project = projects[projectIndex];
console.log(`正在打包项目: ${project.name} [${index + 1}/${validIndexes.length}]`);
// 记录单个项目开始时间
const projectStartTime = Date.now();
// 进度动画变量
let dots = 0;
const intervalId = setInterval(() => {
dots = (dots + 1) % 4;
process.stdout.write(`\r正在打包中${'.'.repeat(dots)}`);
}, 500);
// 使用 exec 异步执行打包命令
exec('npm run build', { cwd: project.path }, (error) => {
clearInterval(intervalId);
process.stdout.write('\r \r'); // 清除进度动画
if (error) {
// 记录单个项目结束时间并计算耗时(即使出错)
const projectEndTime = Date.now();
const projectTotalTime = (projectEndTime - projectStartTime) / 1000;
console.error(`打包项目 ${project.name} 时出错,耗时: ${projectTotalTime} 秒,错误信息:`, error.message);
processProject(index + 1);
return;
}
// 获取 build.outDir 配置
const outDir = getBuildOutDir(project.path);
const buildOutputPath = path.join(project.path, outDir);
// 检查打包输出目录是否存在
if (!fs.existsSync(buildOutputPath)) {
console.error(`项目 ${project.name} 的打包输出目录 ${buildOutputPath} 不存在。`);
// 记录单个项目结束时间并计算耗时
const projectEndTime = Date.now();
const projectTotalTime = (projectEndTime - projectStartTime) / 1000;
console.log(`项目 ${project.name} 耗时: ${projectTotalTime} 秒`);
processProject(index + 1);
return;
}
// 压缩 build.outDir 对应的目标文件夹
const outputPath = path.join(desktopPath, `${project.name}_build.zip`);
const output = fs.createWriteStream(outputPath);
const archive = archiver('zip', { zlib: { level: 9 } });
output.on('close', () => {
// 记录单个项目结束时间并计算耗时
const projectEndTime = Date.now();
const projectTotalTime = (projectEndTime - projectStartTime) / 1000;
console.log(`项目 ${project.name} 已打包并压缩到 ${outputPath},耗时: ${projectTotalTime} 秒`);
processProject(index + 1);
});
archive.on('error', (err) => {
throw err;
});
archive.pipe(output);
// 直接压缩 build.outDir 文件夹
archive.directory(buildOutputPath, path.basename(buildOutputPath));
archive.finalize();
});
};
processProject(0);
});
@echo off
rem 检查 Node.js 是否安装
node -v >nul 2>&1
if %errorLevel% neq 0 (
echo Node.js 未安装,请先安装 Node.js。
pause
exit /b 1
)
rem 安装依赖(如果未安装)
if not exist "node_modules\archiver" (
echo 正在安装 archiver 依赖...
npm install archiver
)
rem 运行 Node.js 脚本
node build_and_compress.js
rem 暂停窗口,方便查看输出结果
pause