执行脚本时yarn命令报错,直接运行node脚本却正常求助
问题:yarn install触发husky初始化脚本时出现文件找不到错误
我写了一段自动初始化husky的Node脚本,在Windows环境直接运行node install.js完全正常,但把它加到yarn install的postinstall脚本里执行时,却报错找不到.husky/commit-msg文件。直接执行脚本没问题,只有通过yarn触发才会出错。
初始化脚本代码
const { spawn } = require('child_process'); const fs = require('fs'); const path = require('path'); const root = __dirname; function createSpawn(command, next) { const child = spawn(command, { stdio: 'inherit', shell: true, cwd: root, }); child.on('exit', function () { if (typeof next === 'function') { next(); } }); } function install() { fs.rmSync(path.resolve(root, '.husky'), { recursive: true, force: true, }); createSpawn('npx husky install', () => { createSpawn('npx husky add .husky/pre-commit "npm run pre-commit"', () => { createSpawn('npx husky add .husky/commit-msg "npx --no -- commitlint --edit #EE#"', () => { const file = path.resolve(root, '.husky/commit-msg'); let content = fs.readFileSync(file).toString(); content = content.replace('#EE#', '"$1"'); fs.writeFileSync(file, content); }); }); }); } install();
报错信息
yarn install v1.22.19 [1/4] 🔍 Resolving packages... success Already up-to-date. $ node install.js node:internal/fs/utils:344 throw err; ^ Error: ENOENT: no such file or directory, open '/Users/ao/Desktop/Yeastar/PPV3/ppfe/.husky/commit-msg' at Object.openSync (node:fs:585:3) at Object.readFileSync (node:fs:453:35) at /Users/ao/Desktop/Yeastar/PPV3/ppfe/install.js:32:34 at ChildProcess.<anonymous> (/Users/ao/Desktop/Yeastar/PPV3/ppfe/install.js:17:13) at ChildProcess.emit (node:events:526:28) at Process.ChildProcess._handle.onexit (node:internal/child_process:291:12) { errno: -2, syscall: 'open', code: 'ENOENT', path: '/Users/ao/Desktop/Yeastar/PPV3/ppfe/.husky/commit-msg' } error Command failed with exit code 1. info Visit https://yarnpkg.com/en/docs/cli/install for documentation about this command.
问题分析
- 子进程事件监听逻辑缺陷:
createSpawn只监听exit事件,但该事件仅表示子进程退出,不代表命令执行成功。如果npx husky add在yarn环境下执行失败,子进程依然会触发exit,导致后续回调盲目执行,尝试读取不存在的文件。 - 环境上下文差异:直接执行脚本和通过yarn执行时,环境变量、shell解析逻辑不同,可能导致
npx命令行为异常,比如yarn接管包管理逻辑后,husky add未正确生成文件。 - 引号解析问题:
husky add命令中的嵌套引号在不同shell下可能解析错误,导致命令实际执行结果不符合预期。
解决方案
方案1:改进子进程执行校验逻辑
修改createSpawn函数,监听close事件并检查退出码,确保前一步命令执行成功后再执行后续回调:
function createSpawn(command, next) { const child = spawn(command, { stdio: 'inherit', shell: true, cwd: root, }); child.on('close', (code) => { if (code === 0 && typeof next === 'function') { next(); } else { console.error(`命令执行失败: ${command},退出码: ${code}`); process.exit(1); } }); child.on('error', (err) => { console.error(`命令无法启动: ${command},错误: ${err.message}`); process.exit(1); }); }
方案2:替换npx为yarn dlx
在yarn环境下使用yarn dlx替代npx,避免包管理工具的兼容问题:
function install() { fs.rmSync(path.resolve(root, '.husky'), { recursive: true, force: true, }); createSpawn('yarn dlx husky install', () => { createSpawn('yarn dlx husky add .husky/pre-commit "npm run pre-commit"', () => { createSpawn('yarn dlx husky add .husky/commit-msg "npx --no -- commitlint --edit #EE#"', () => { const file = path.resolve(root, '.husky/commit-msg'); let content = fs.readFileSync(file).toString(); content = content.replace('#EE#', '"$1"'); fs.writeFileSync(file, content); }); }); }); }
方案3:手动生成commit-msg文件
跳过husky add创建commit-msg,直接手动生成文件,避免引号解析问题:
function install() { fs.rmSync(path.resolve(root, '.husky'), { recursive: true, force: true, }); createSpawn('npx husky install', () => { createSpawn('npx husky add .husky/pre-commit "npm run pre-commit"', () => { const commitMsgPath = path.resolve(root, '.husky/commit-msg'); fs.mkdirSync(path.dirname(commitMsgPath), { recursive: true }); // 直接写入husky脚本内容 fs.writeFileSync(commitMsgPath, '#!/usr/bin/env sh\n. "$(dirname -- "$0")/_/husky.sh"\n\nnpx --no -- commitlint --edit "$1"\n'); // 非Windows环境添加执行权限 if (process.platform !== 'win32') { fs.chmodSync(commitMsgPath, 0o755); } }); }); }
方案4:修复目录权限(仅macOS/Linux)
如果是类Unix系统,确保项目目录有足够读写权限:
chmod -R 755 /Users/ao/Desktop/Yeastar/PPV3/ppfe
内容的提问来源于stack exchange,提问作者doa
相关产品推荐
相关产品推荐

