我有一个多包组件库,希望在修改某个包后可以将该包的版本+1并执行build命令,接着执行pack命令,将文件复制到特定的文件夹中。
根目录package.json执行的命令
"scripts": {
"tgz": "node ./run_script/index.js"
},
在run_script/index.js
const readDirFn = require("./readDir.js");
// npm run tgz 启动函数
readDirFn();
在readDir.js
const path = require("path");
const fs = require("fs");
const { promisify } = require("util");
// 要读取的目录
const pkgPath = path.join(__dirname, '../packages');
// promise 包装
const readDir = promisify(fs.readdir);
const stat = promisify(fs.stat);
// utils
const checkboxPrompt = require('./prompt.js');
const startPack = require("./startPack.js");
/**
* 读区文件目录
*/
async function readDirFn() {
try {
const dirs = await readDir(pkgPath);
// 去掉系统文件的干扰项
const filterDirs = dirs.filter(d => !d?.startsWith?.('.'));
// 多选交互
const answers = await checkboxPrompt(filterDirs);
if (answers?.length) {
console.log('\x1B[36m%s\x1B[0m', '---> npm pack正在执行🚀... <---');
// 对选择的目录开始打包
for (dir of answers) {
const dirPath = path.join(pkgPath, dir);
const dirInfo = await stat(dirPath);
const isFileDir = dirInfo?.isDirectory();
if (isFileDir) {
await startPack(dirPath);
}
}
console.log('\x1B[32m', '---> npm pack执行完成✅ <---');
} else {
console.log('\x1B[31m%s\x1B[0m', '---> 未选择任何npm pack的包🙅 <---');
}
} catch (error) {
console.log('readDirFn function error', error);
} finally {
process.exit();
}
}
module.exports = readDirFn;
promisify: 取自node util核心库,作用是将异部函数转换成可以使用await的形式,node自带不需要npm安装
stat:读取文件的基本信息,可以判断该文件是否是一个文件夹。
for of: 在for of中支持使用await,其他的for循环,如果包在async函数内也支持使用await
在prompt.js
const inquirer = require("inquirer");
const { CHECKBOX_KEY } = require('./constants.js');
/**
* 多选prompt交互
*/
async function checkboxPrompt(filterDirs = []) {
try {
const answer = await inquirer.prompt([
{
type: 'checkbox',
name: CHECKBOX_KEY,
message: '请选择npm pack构建的包:',
loop: false,
choices: filterDirs,
}
]);
return answer[CHECKBOX_KEY] || [];
} catch (error) {
console.log('checkboxPrompt function error', error);
}
}
module.exports = checkboxPrompt;
inquirer: 处理命令行交互
startPack.js
const path = require("path");
const fs = require("fs");
const { promisify } = require("util");
const { exec } = require("child_process");
// promise 包装
const mkdir = promisify(fs.mkdir);
const execShell = promisify(exec);
const readFile = promisify(fs.readFile);
const writeFile = promisify(fs.writeFile);
// lib
const libPath = path.join(process.cwd(), 'lib');
/**
* cd && 开始打包
* @param {*} dirPath 目标源代码文件目录
*/
async function startPack(dirPath, isUpgrade, isPack) {
try {
// 切换目录
process.chdir(dirPath);
// lib目录是否存在
const isLibExist = fs.existsSync(libPath);
if (!isLibExist) {
await mkdir(libPath);
}
if (isUpgrade) {
const packageJsonPath = path.join(process.cwd(), 'package.json');
// 版本号+1
await modifyPackageJson(packageJsonPath);
}
await buildAndPack(isPack);
} catch (error) {
console.log('startPack function error', error);
}
}
/**
* package.json文件修改,入参文件地址
*/
async function modifyPackageJson(dirPath) {
try {
const data = await readFile(dirPath, 'utf8');
const packageJson = JSON.parse(data);
const versionComponents = packageJson.version.split('.');
if (versionComponents.length === 3) {
versionComponents[2] = parseInt(versionComponents[2], 10) + 1; // 递增修订号
packageJson.version = versionComponents.join('.');
}
// 将修改后的对象转换回 JSON 字符串并写入文件
const updatedPackageJsonContent = JSON.stringify(packageJson, null, 2);
await writeFile(dirPath, updatedPackageJsonContent,'utf8');
} catch (error) {
console.log('modifyPackageJson function error', error);
}
}
async function buildAndPack(isPack) {
try {
// 执行 npm run build 并实时打印输出
const buildProcess = execShell('npm run build');
// 同时打印标准输出和标准错误流到控制台
buildProcess.child.stdout.pipe(process.stdout);
buildProcess.child.stderr.pipe(process.stderr);
// 等待构建完成
await buildProcess;
// 执行 npm pack 并实时打印输出
if (isPack) {
const packProcess = execShell('npm pack');
// 同样打印
packProcess.child.stdout.pipe(process.stdout);
packProcess.child.stderr.pipe(process.stderr);
// 等待打包完成
const { stdout: packResult } = await packProcess;
const packageName = packResult.trim(); // 获取包文件名
// 移动包文件到指定目录
const moveProcess = execShell(`mv ${packageName} ../../lib`);
// 打印移动过程的输出
moveProcess.child.stdout.pipe(process.stdout);
moveProcess.child.stderr.pipe(process.stderr);
// 等待移动完成
await moveProcess;
console.log(`Package moved to ../../lib/${packageName}`);
}
} catch (error) {
// 输出错误信息
console.error('Error occurred:', error);
}
}
module.exports = startPack;
readFile: 读取文件内容
moveProcess.child.stdout.pipe:执行命令时输出日志。
writeFile: 修改文件内容
execShell(mv ${packageName} ../../lib): 移动文件
总结
使用到的node命令有inqurer做命令交互,promisify将函数转换为可以使用await的版本,文件使用相关fs.stat查询文件信息,fs.readFile/fs.writeFile读写文件,fs.mkdir创建目录。process.chdir 切换目录。文件路径相关,path.join取得绝对地址。执行命令相关 execShell,以及如何输出命令日志的 child.stdout.pipe(process.stdout);child.stderr.pipe(process.stderr);