const childProcess = require('child_process'); function getExecutable(command) { // Windows 全局安装的命令通常是 .cmd 文件,其他系统直接使用命令名。 return process.platform === 'win32' ? `${command}.cmd` : command; } function commandExists(command) { // 使用系统自带的命令查找能力,避免为了检测 Yarn 引入额外依赖。 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) { // 参数可注入是为了让测试覆盖 Yarn 存在和不存在两种情况。 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') { // Node.js 不能始终直接执行 yarn.cmd,因此交给 cmd.exe 解析,避免 spawnSync yarn ENOENT。 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 };