用 node --run 直接执行 package.json 里的脚本

49 次浏览8 条回复

Node 22 以后可以不用先敲 npm run,直接让 Node 执行 package.json 里的脚本:

环境:Node.js 22+。临时试一下:

mkdir node-run-demo && cd node-run-demo
npm init -y
npm pkg set scripts.hello="node -e \"console.log('hello from script')\""
node --run hello

它会读取当前项目的 scripts.hello 并执行,少了一层包管理器命令。只是本地顺手跑脚本时挺轻便;如果 CI 里要兼容旧 Node,还是继续用 npm run hello 更稳。

这个命令不会改 package.json,只是执行已有脚本。

这个在本地跑小脚本确实挺省字。

我觉得团队项目里可以顺手在 package.json 里写一下 engines.node,或者 README 标一下 Node 22+,不然有人习惯性用 20 跑 node --run 会直接懵。CI 里继续保留 npm run 也更兼容。

单包项目里这个挺顺手,尤其是 lint、test 这种短脚本。

不过 monorepo 里我会稍微保守点用:node --run 主要就是执行当前包的 scripts,不会替你处理 pnpm --filter、npm -w 那类 workspace 选择。跨包任务还是交给包管理器命令更清楚。

还有个小差异可以留意:如果脚本里依赖 npm_lifecycle_event、npm_package_* 这类 npm 注入的环境变量,换成 node --run 之后最好先确认一下。

普通的 node_modules/.bin 命令一般没啥问题,但脚本越像发布流程、构建流程,就越不建议直接全量替换。拿来跑本地 lint、test 这种短命令比较舒服。

还有一种情况我会继续敲包管理器命令:脚本里本身会调用 pnpm、yarn 或 workspace 相关能力时,入口统一用 pnpm run xxx 之类更不容易混。

node --run 更像是少打一层命令的快捷方式,不太适合顺手把项目里的所有脚本入口都替掉。

还可以顺手看一下脚本有没有依赖 npm 的生命周期钩子。比如项目里有 pretest、posttest 这类配套脚本时,换成 node --run test 前最好先在临时分支跑一遍确认行为。

所以我感觉它适合当本地快捷入口,用在简单脚本上很舒服;涉及发布、生成版本号、连带钩子的脚本,还是别急着替。

补一个容易忽略的点:它只是在入口处少打一层命令,不会让脚本本身变得跨平台。脚本里如果写了 bash 语法、rm 这类命令,换成 node --run 后 Windows 上照样可能跑不通。简单的 lint、test 没这个负担,涉及 shell 命令的脚本还是得看项目的运行环境。

ZiorLv1#6

补一个容易忽略的点:它只是在入口处少打一层命令,不会让脚本本身变得跨平台。脚本里如果写了 bash 语法、rm 这类命令,换成 node --run 后 Windows 上照样可能跑不通。简单的 lint、test 没这个负担,涉及 shell 命令的脚本还是得看项目的运行环境。

这个补充很关键。node --run 只是换了脚本入口,&&、重定向、$VAR 这类内容还是交给当前 shell 解释,并不会自动变成跨平台脚本。复杂逻辑可以移到 Node 脚本或专门的跨平台工具里,node --run 留给 lint、test 这类简单入口,边界会清楚些。

MukiLv1#7

这个补充很关键。node --run 只是换了脚本入口,&&、重定向、$VAR 这类内容还是交给当前 shell 解释,并不会自动变成跨平台脚本。复杂逻辑可以移到 Node 脚本或专门的跨平台工具里,node --run 留给 lint、test 这类简单入口,边界会清楚些。

再补个 monorepo 里容易踩的点:node --run 会从当前目录向上找 package.json。如果人在仓库根目录执行,拿到的可能是根脚本;想跑某个子包时先确认工作目录,跨包任务还是用 workspace 的 --filter 或 -w 更明确。