Nx 中 nx:run-commands 执行器完全指南:命令编排、参数转发与 Affected 智能执行
【免费下载链接】nxThe Monorepo Platform that amplifies both developers and AI agents. Nx optimizes your builds, scales your CI, and fixes failed PRs automatically. Ship in half the time.项目地址: https://gitcode.com/GitHub_Trending/nx/nx
nx:run-commands是 Nx 内置的通用执行器,允许你在不编写自定义执行器代码的情况下,把任意 shell 命令纳入 Nx 的任务图与项目图体系。本文基于 Nx 仓库中的官方示例文档 packages/nx/docs/run-commands-examples.md,结合其源码实现与配置 Schema,系统讲解命令串联/并行、工作目录切换、参数插值与转发、自定义完成条件,以及配合nx affected实现按变更范围智能执行的全部实战用法。读完本文,你将能够把项目中的脚本、文档生成、构建命令等任意流程无缝接入 Nx 的缓存、并行与增量执行体系。
基本用法:用 project.json 定义一条自定义命令
nx:run-commands的核心是把一段 shell 命令包装成一个标准的 Nx target,从而让这条命令享受 Nx 任务执行体系的全部能力(依赖调度、缓存、并行、Affected 等)。
在项目(这里以frontend项目为例)的project.json中配置如下 target:
{ // ... "targets": { //... "ls-project-root": { "executor": "nx:run-commands", "options": { "command": "ls apps/frontend/src" } } } }然后在终端执行:
nx run frontend:ls-project-rootnx run <project>:<target>是 Nx 运行任意 target 的统一入口。这里ls-project-root执行的就是ls apps/frontend/src。
需要指出的是,project.json只是 Nx 项目配置的入口之一,它由 packages/nx/src/plugins/project-json/build-nodes/project-json.ts 等插件读取并归并进最终的项目配置中;而在单仓内没有显式project.json的项目,也可以通过package.json脚本配合 Nx 进行执行。
串联多条命令(Chaining Commands)
options.commands选项接受任意数量的命令。默认情况下,这些命令并行执行;若需要按顺序逐个执行,将parallel设置为false即可:
"create-script": { "executor": "nx:run-commands", "options": { "commands": [ "mkdir -p apps/frontend/scripts", "touch apps/frontend/scripts/my-script.sh", "chmod +x apps/frontend/scripts/my-script.sh" ], "parallel": false } }上面的例子依次创建目录、创建脚本文件并赋予可执行权限——这类有先后依赖的步骤必须串行执行。根据 schema.json 中的定义,parallel的默认值为true,也就是说你不显式设置时命令是同时启动的;只有明确需要顺序执行时才需要"parallel": false。
从源码实现看,并行与串行分别由 running-tasks.ts 中的ParallelRunningTasks与SeriallyRunningTasks两个类承载,runCommands会根据options.parallel选择对应的执行器:
const runningTask = isSingleCommandAndCanUsePseudoTerminal ? await runSingleCommandWithPseudoTerminal(...) : options.parallel ? new ParallelRunningTasks(normalized, context, resolvedTaskId) : new SeriallyRunningTasks(normalized, context, tuiEnabled, resolvedTaskId);(见 packages/nx/src/executors/run-commands/run-commands.impl.ts)
设置工作目录(Setting the cwd)
通过cwd选项,可以让每条命令都在指定目录下运行。例如下面的配置使所有命令都在apps/frontend目录中执行:
"create-script": { "executor": "nx:run-commands", "options": { "cwd": "apps/frontend", "commands": [ "mkdir -p scripts", "touch scripts/my-script.sh", "chmod +x scripts/my-script.sh" ], "parallel": false } }注意这里命令里的路径是相对apps/frontend的:mkdir -p scripts实际创建的是apps/frontend/scripts。
关于cwd的取值规则,schema.json 给出了明确说明:
- 不指定时,命令在工作区根目录运行;
- 指定相对路径时,命令在该相对工作区根目录的路径下运行;
- 指定绝对路径时,命令在绝对路径下运行。
参数插值(Interpolating Args)
在脚本中可以通过{args.[someFlag]}语法使用自定义参数:
"create-script": { "executor": "nx:run-commands", "options": { "cwd": "apps/frontend", "commands": [ "mkdir -p scripts", "touch scripts/{args.name}.sh", "chmod +x scripts/{args.name}.sh" ], "parallel": false } }有两种方式传参运行:
nx run frontend:create-script --args="--name=example"或者更简洁的写法(参数直接跟在 target 后面):
nx run frontend:create-script --name=example参数插值的底层实现位于 run-commands.impl.ts 的interpolateArgsIntoCommand函数中。它用正则/{args\.([^}]+)}/g匹配{args.xxx}占位符,并从parsedArgs中取出对应值进行替换;若某个占位符在参数中不存在,则替换为空字符串。Nx 通过 yargs-parser 对命令行参数进行解析(parseArgs函数),解析时启用了 camel-case 展开,因此--some-flag=1与--someFlag=1这类写法都能被识别。
另外还有两种值得了解的用法:
{args}(不带属性)会被替换为全部参数拼接而成的字符串;- 注意约束:一条命令不能同时混用
{args}和{args.*}两种语法,否则运行时会抛出错误(interpolateArgsIntoCommand中会做显式校验)。
关于参数转发的注意点
需要特别留意的是:命令列表中任何未包含插值语法的命令,默认仍会把传入的全部参数原样追加到命令尾部。例如给定配置:
"create-script": { "executor": "nx:run-commands", "options": { "commands": [ "echo {args.name}", "echo done" ], "parallel": false } }运行nx run frontend:create-script --name=example时,第二条命令echo done也会被追加--name=example,实际执行的是echo done --name=example。
如果某些命令不应该接收这些参数,就在该命令对象上显式设置forwardAllArgs: false:
"create-script": { "executor": "nx:run-commands", "options": { "commands": [ "echo {args.name}", { "command": "echo done", "forwardAllArgs": false } ], "parallel": false } }参数转发(Arguments Forwarding)
当命令中没有插值占位符时,所有参数默认都会被转发给命令本身。这一特性在你需要把原始参数字符串原样传给命令时非常有用。
例如运行:
nx run frontend:webpack --args="--config=example.config.js"配合如下配置:
"webpack": { "executor": "nx:run-commands", "options": { "command": "webpack" } }实际执行的就是:webpack --config=example.config.js。
这个行为由interpolateArgsIntoCommand中的forwardAllArgs分支实现:当命令不含{args...}占位符且forwardAllArgs为真时,会把未知选项(unknownOptions)、args以及未解析参数(__unparsed__)三部分拼接后追加到命令末尾(见 packages/nx/src/executors/run-commands/run-commands.impl.ts)。其中forwardAllArgs的默认值为true(见 schema.json)。
如需禁用转发,则必须把单条命令展开为对象形式,并设置forwardAllArgs: false:
"webpack": { "executor": "nx:run-commands", "options": { "commands": [ { "command": "webpack", "forwardAllArgs": false } ] } }forwardAllArgs既可以配置在单条命令对象上(RunCommandsCommandOptions.forwardAllArgs),也可以配置在options顶层作为默认值;单条命令的取值优先级更高:源码中取的是c.forwardAllArgs ?? options.forwardAllArgs ?? true。
简写形式(Shorthand)
当只需要运行一条命令时,可以直接在 target 中写command字段,而省略executor与options两层包装——Nx 会识别这种简写形式,自动将其视为nx:run-commands:
"webpack": { "command": "webpack" }这与完整的executor: "nx:run-commands"+options.command写法完全等价,是官方推荐的单命令最简洁写法。需要注意的是,简写形式只支持单条命令场景;需要多条命令或更复杂的选项(如cwd、readyWhen)时仍应使用完整写法。
自定义完成条件(Custom done conditions)
正常情况下,run-commands会等待所有命令都运行结束才判定任务完成。如果你不需要等待全部命令结束,可以设置一个特殊字符串:一旦该字符串出现在子进程的stdout或stderr中,就立即判定任务完成。
"finish-when-ready": { "executor": "nx:run-commands", "options": { "commands": [ "sleep 5 && echo 'FINISHED'", "echo 'READY'" ], "readyWhen": "READY", "parallel": true } }nx run frontend:finish-when-ready上面的配置会立即完成,而不会等待 5 秒——因为echo 'READY'的输出已经满足完成条件。
当多条命令并行运行时,你可能需要等待多个字符串同时出现在输出中。例如同时启动多个开发服务器,希望所有服务都就绪后再继续:
"finish-when-multiple-ready": { "executor": "nx:run-commands", "options": { "commands": [ "sleep $[ ( $RANDOM % 10 ) + 1 ] && echo 'READY1' && sleep 3600", "sleep $[ ( $RANDOM % 10 ) + 1 ] && echo 'READY2' && sleep 3600", ], "readyWhen": ["READY1", "READY2"], "parallel": true } }nx run frontend:finish-when-multiple-ready上述两条命令都会在随机 1~10 秒内输出各自的 READY 标记(随后各自sleep 3600挂起);只要READY1和READY2都出现过,任务就立即完成,无需等待那额外的一个小时。
关于readyWhen,schema.json 与源码共同给出了以下关键约束与行为:
readyWhen接受字符串或字符串数组;- 运行多条命令时,
readyWhen只能在parallel: true下使用。源码runCommands中对此有显式校验:若设置了readyWhen但parallel为假,会抛出"readyWhen" can only be used when "parallel=true"错误(见 run-commands.impl.ts); - 未设置
readyWhen时,任务在全部子进程结束后才完成; - 在并行 +
readyWhen模式下,任一子进程以非零码退出时会输出Warning: command "..." exited with non-zero status code警告,任务随之结束(见 running-tasks.ts 中ParallelRunningTasks.run的实现)。
从实现细节看,readyWhen的匹配是逐个子进程监听输出:normalizeOptions会把readyWhen(字符串或数组)规整为{ stringToMatch, found }[]状态数组,交给每个RunningNodeProcess实例去扫描输出流。
与 Nx Affected 结合:按变更范围执行自定义命令
run-commands的真正威力在于它经由nx运行,而 Nx 掌握着完整的项目图(project graph)。因此,你可以让自定义命令只针对"受变更影响(affected)"的项目执行。
例如为frontend与api两个项目分别配置文档生成命令:
//... "frontend": { "targets": { //... "generate-docs": { "executor": "nx:run-commands", "options": { "command": "npx compodoc -p apps/frontend/tsconfig.app.json" } } } }, "api": { "targets": { //... "generate-docs": { "executor": "nx:run-commands", "options": { "command": "npx compodoc -p apps/api/tsconfig.app.json" } } } }然后使用nx affected运行:
nx affected --target=generate-docs这样,只有发生变更的项目才会触发文档生成:如果本次提交只改了frontend的代码,就只运行npx compodoc -p apps/frontend/tsconfig.app.json,api的文档生成任务会被跳过。这正是 Nx 在 CI 中大幅节省时间的核心机制之一——把自定义脚本也纳入项目图驱动的增量执行体系。
更多实用选项
除文档示例覆盖的内容外,schema.json 还定义了以下常用选项,可用于更精细的控制:
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
command | string | string[] | — | 要执行的命令;数组形式表示按部分拆分的同一命令 |
commands | array | — | 在子进程中运行的命令列表;每项可为字符串或对象(对象支持command、forwardAllArgs、prefix、prefixColor、color、bgColor、description字段) |
parallel | boolean | true | 是否并行运行各命令 |
readyWhen | string | string[] | — | 出现于 stdout/stderr 即视为任务完成的字符串;多命令时仅限parallel: true |
cwd | string | 工作区根目录 | 命令的工作目录;相对路径基于工作区根,绝对路径按原值使用 |
args | string | string[] | — | 额外参数,配合{args.xxx}插值使用 |
forwardAllArgs | boolean | true | 无插值时是否把所有参数转发给命令 |
envFile | string | — | 自定义.env文件路径 |
env | object | — | 注入命令的环境变量;优先级高于.env文件 |
color | boolean | false | 是否在命令输出中使用颜色 |
几点值得注意的行为与约束:
env与envFile:env中的变量会优先于.env文件中的同名变量(schema 明确说明 "This property has priority over the.envfiles");- 输出着色选项:
prefix、prefixColor、color、bgColor等输出样式选项只能在parallel: true时使用,源码中对此同样有显式校验,违规配置会直接报错(见 run-commands.impl.ts)。可用颜色包括black、red、green、yellow、blue、magenta、cyan、white(bgColor对应bgBlack~bgWhite); description:仅用于在配置中内联记录命令用途,不参与命令的实际执行(源码注释明确说明这一点);- 空命令列表:
commands为空数组时,任务会立即以成功状态返回,不会启动任何子进程; - 至少提供一个命令:Schema 要求
commands与command二者至少声明其一(oneOf约束)。
小结
nx:run-commands是 Nx 中"零代码接入"的万能执行器:从单条命令的简写,到多条命令的串并行编排、cwd工作目录控制、{args.xxx}参数插值与forwardAllArgs转发策略、readyWhen自定义完成条件,再到与nx affected结合的按变更范围执行,它把任意 shell 流程无缝纳入了 Nx 的项目图、任务依赖、并行调度与增量执行体系。本文所有配置示例均来自官方示例文档 packages/nx/docs/run-commands-examples.md,选项细节与行为约束可进一步参考 schema.json 与实现源码 run-commands.impl.ts、running-tasks.ts。
【免费下载链接】nxThe Monorepo Platform that amplifies both developers and AI agents. Nx optimizes your builds, scales your CI, and fixes failed PRs automatically. Ship in half the time.项目地址: https://gitcode.com/GitHub_Trending/nx/nx
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考