2026-08-25 17:17:36 +08:00
|
|
|
|
const childProcess = require('child_process');
|
|
|
|
|
|
|
|
|
|
|
|
function getExecutable(command) {
|
2026-08-26 09:10:45 +08:00
|
|
|
|
// Windows 全局安装的命令通常是 .cmd 文件,其他系统直接使用命令名。
|
2026-08-25 17:17:36 +08:00
|
|
|
|
return process.platform === 'win32' ? `${command}.cmd` : command;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function commandExists(command) {
|
2026-08-26 09:10:45 +08:00
|
|
|
|
// 使用系统自带的命令查找能力,避免为了检测 Yarn 引入额外依赖。
|
2026-08-25 17:17:36 +08:00
|
|
|
|
const lookupCommand = process.platform === 'win32' ? 'where.exe' : 'which';
|
|
|
|
|
|
const result = childProcess.spawnSync(lookupCommand, [getExecutable(command)], {
|
|
|
|
|
|
stdio: 'ignore',
|
|
|
|
|
|
});
|
|
|
|
|
|
return result.status === 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function ensureYarnAvailable(isCommandAvailable = commandExists) {
|
2026-08-26 09:10:45 +08:00
|
|
|
|
// 参数可注入是为了让测试覆盖 Yarn 存在和不存在两种情况。
|
2026-08-25 17:17:36 +08:00
|
|
|
|
if (!isCommandAvailable('yarn')) {
|
|
|
|
|
|
throw new Error('未检测到 Yarn,请先安装 Yarn');
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function runYarn(args, cwd) {
|
|
|
|
|
|
ensureYarnAvailable();
|
|
|
|
|
|
const executable = getExecutable('yarn');
|
|
|
|
|
|
console.log(`执行: yarn ${args.join(' ')}`);
|
|
|
|
|
|
if (process.platform === 'win32') {
|
2026-08-26 09:10:45 +08:00
|
|
|
|
// Node.js 不能始终直接执行 yarn.cmd,因此交给 cmd.exe 解析,避免 spawnSync yarn ENOENT。
|
2026-08-25 17:17:36 +08:00
|
|
|
|
childProcess.execFileSync(process.env.ComSpec || 'cmd.exe', [
|
|
|
|
|
|
'/d',
|
|
|
|
|
|
'/s',
|
|
|
|
|
|
'/c',
|
|
|
|
|
|
executable,
|
|
|
|
|
|
...args,
|
|
|
|
|
|
], { cwd, stdio: 'inherit' });
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
childProcess.execFileSync(executable, args, { cwd, stdio: 'inherit' });
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
module.exports = { commandExists, ensureYarnAvailable, runYarn };
|