作为 pnpm runnpm run 的直接替代品,nub run 保持了每个标志相同的拼写和语义,甚至包括真实 monorepo 依赖的递归和工作区过滤标志。现有调用无需修改即可工作:

# 从图中断点恢复递归运行
nub run -r --resume-from @scope/pkg build
# CI 用的机器可读每包结果
nub run -r --reporter=ndjson build
nub run -r --stream --reporter-hide-prefix build
nub run -r --workspace-concurrency 4 build
# 跳过没有该脚本的成员
nub run -r --if-present test

它直接读取你的 package.json——冷路径快 24 倍——包管理保持使用你已有的工具(pnpmnpmyarnbun)。本页是使用手册:每节是一个任务,附带要运行的确切命令。

少数标志行为不同。--report-summary 标志通过 --reporter=ndjson 实时流式传输结果,而非写入静态文件。--if-present 标志是工作区范围的:在递归运行期间跳过缺少该脚本的成员。旧版 npm 的 --scripts-prepend-node-path--unsafe-perm--npm-path 旋钮不需要也不被接受。

nub run <script>

run 之后传递脚本名称。名称必须匹配你 package.json"scripts" 字段的一个键。

nub run build
nub run dev
nub run test

运行脚本始终是 nub run <script>——裸词如 nub dev 不会回退到脚本。(如果你输入 nub buildbuild 是一个真实脚本,Nub 的错误会提示你输入 nub run build。)参见运行文件了解 nub <file> 的工作方式。

调度在 Rust 中完成,包装器中没有 Node 引导,所以运行器在脚本第一个字节前只增加几毫秒——比 Node 自身的 node --run 还快:

脚本调度 · 热启动 · 50 次运行

nub run14.7 ms
node --run32.2 ms · 慢 2.2×
npm run329.9 ms · 慢 22×
pnpm run442.7 ms · 慢 30×

查看基准 →

一次运行多个脚本

/regex/ 选择器代替脚本名称可以运行每个名称匹配的脚本,与 pnpm run 一致。这是 npm-run-allrun-p 的包内等价物——无需额外依赖或每个任务的 PM 重启,因为 Nub 直接生成每个脚本主体。

nub run "/^build:/"     # 运行 build:js, build:css, build:types——并发执行

匹配的脚本默认并发运行,上限为 min(4, CPU 核心数),每行以其脚本名称为前缀。改为逐个运行(run-s 行为),将并发上限设为 1:

# 逐个运行,按 package.json 顺序
nub run --workspace-concurrency 1 "/^build:/"

选择器必须是斜杠分隔的正则表达式字面量(/^build:/);普通的 build:* 被视为字面脚本名称,不是 glob。不支持正则标志。确切的脚本名称(无斜杠)始终运行该单个脚本不变。

npm-run-all / npm-run-all2 迁移:

npm-run-allNub
run-p build:js build:cssnub run "/^build:(js|css)$/"
run-p "build:*"nub run "/^build:/"
run-s "build:*"nub run --workspace-concurrency 1 "/^build:/"

选择一个任意、不相关的脚本集(例如 buildlinttest 一起运行,它们不共享名称模式)尚不支持——追踪 #102

转发参数

脚本名称之后的尾随参数直接传递给脚本。你不需要 npm run 要求的 -- 分隔符。

nub run test --watch          # 转发 --watch 给测试脚本
nub run build --target=esnext # 转发 --target=esnext

这与 bun run 一致。显式分隔符仍然适用于肌肉记忆:

nub run test -- --watch       # 相同结果;-- 是可选的

脚本名称前的标志(nub run --filter core test)是 Nub 的;脚本名称后的标志(nub run test --filter)是脚本的。所以 Nub 侧的标志始终放在 <script> 之前:

nub run --silent build        # --silent 是 Nub 的(抑制前导输出)
nub run build --silent        # --silent 转发给构建脚本

生命周期钩子

Nub 自动运行 pre<script>post<script> 钩子,与 npm run 语义一致。给定这些脚本:

{
  "scripts": {
    "prebuild": "rimraf dist",
    "build": "tsc -p .",
    "postbuild": "cp README.md dist/"
  }
}

单个 nub run build 按顺序运行全部三个:

nub run build                 # 运行 prebuild,然后 build,然后 postbuild

跳过 pre / post 钩子(CI 或安全意识运行),传递 --ignore-scripts

nub run --ignore-scripts build

依赖新鲜度

脚本运行前,Nub 将已安装的 node_modules 与你的 package.json 对照检查。当依赖缺失或其安装版本超出了声明范围——常见的"刚克隆,忘了安装"情况——Nub 警告但仍运行,所以过时的树以清晰消息而非原始的 husky: command not found 暴露:

nub: dependencies may be out of date (`chalk` is not installed). Run `nub install`.

检查快速且无需标记文件,无论用什么安装了树——npm、pnpm、Yarn、Bun 或 Nub 自身——都能工作。它偏向静默:歧义树(Yarn PnP、不可解析版本、或故意省略 devDependencies 的仅生产安装)从不警告。

通过 .npmrc 中的 verify-deps-before-run 键设置行为。值为 warn(默认——警告然后运行)、error(打印消息并拒绝运行)和 off(跳过检查):

# .npmrc
verify-deps-before-run = warn

NUB_VERIFY_DEPS_BEFORE_RUN 变量为单个 shell 覆盖文件设置,--no-check(或 nub run 上的 --no-install)为单次调用跳过检查:

nub run --no-check build
NUB_VERIFY_DEPS_BEFORE_RUN=off nub run build

Nub 不会在此替你安装——install 被接受为值但行为类似 warn,因为 Nub 不会重塑另一个包管理器设置的树。看到警告时运行 nub install。检查也适用于 nub <file>nub execnubx,在 --node / 兼容模式和已运行脚本内部被跳过(所以生成 node 的脚本不会重新检查)。

未声明的依赖

Nub 还会标记幻影依赖——你的源码导入但从未声明的包,今天能解析只是因为某个依赖的依赖将其提升到了 node_modules。它在扁平的 npm 或 Yarn 安装下工作,但在隔离安装树时就会中断,所以 Nub 在此发生前命名文件和修复方法:

nub: src/index.ts imports `ansi-styles`, which isn't in package.json. Run `nub add ansi-styles` (WARN_PHANTOM_DEP).

扫描是故意保守的——它只对无条件导入的包(try/catch 可选加载被忽略)、在每个依赖字段中都不存在、且实际存在于 node_modules 中的包发出警告。内置模块、自导入、@types/* 和工作区包从不警告,且它只读取你的第一方源码——node_modules 和构建输出被跳过。它仅警告且从不改变退出码。

通过 .npmrc 中的 phantom-check 键关闭它,或为单个 shell 使用 NUB_PHANTOM_CHECK 变量:

# .npmrc
phantom-check = off

npm_* 环境

Nub 用完整的 npm 兼容环境填充子进程,所以读取 npm_* 变量的脚本和工具行为与在 npm run 下完全一致。本地安装的 CLI 通过 node_modules/.bin 链在 PATH 上,所以你可以按裸名称调用它们。

{
  "scripts": {
    "build": "tsc && echo \"built $npm_package_name@$npm_package_version\""
  }
}
nub run build   # tsc 从 node_modules/.bin 解析;npm_* 变量已设置

注入的集合包括:

npm_lifecycle_event
npm_lifecycle_script
npm_execpath
npm_node_execpath
npm_command
npm_config_user_agent
npm_package_name
npm_package_version
npm_package_json
npm_package_config_*       # package.json#config 中的每包配置键
npm_config_registry        # .npmrc 链中解析的注册表
npm_config_node_gyp        # 可运行 node-gyp 的路径,用于原生构建
INIT_CWD                   # 你调用 `nub run` 的目录

在脚本内部,生成 node(通过 shebang 或 child_process.spawn("node", …))通过 PATH 垫片重新进入 Nub,所以子进程保持转译和增强——参见下方 --node

--script-shell

如果你的 .npmrc 设置了 script-shell,Nub 使用它来调用脚本主体。.npmrc 的其余部分(注册表、认证、node-linkerhoist-pattern)是包管理器领域,在此不读取。

# .npmrc
script-shell = /bin/bash

你也可以按调用覆盖 shell:

nub run --script-shell /bin/bash build

-r, --recursive

在 monorepo 中,-r(或 --recursive)在每个工作区包中运行脚本。Nub 从 package.json"workspaces" 字段(npm / Yarn / Bun)或 pnpm-workspace.yaml(pnpm)读取工作区拓扑。

nub run -r build              # 在每个工作区包中运行 "build"
nub run --recursive test      # --recursive 是长格式
nub run --workspaces lint     # --workspaces 是 -r 的 npm 风格别名

包默认按拓扑顺序运行:包的依赖先于它构建。工作区发现从当前目录向上遍历以找到工作区根目录,所以这些命令从 monorepo 内任何位置都能工作。

--filter

--filter 标志(或 -F)原样采用 pnpm 的过滤语法。按确切名称、scope glob、名称通配符或路径 glob 选择。

nub run --filter @org/api dev          # 按名称选择一个包
nub run --filter "@org/*" build        # scope 中的每个包
nub run --filter "*-utils" test        # 名称通配符
nub run --filter "./packages/*" lint   # 路径 glob
nub run --filter '!@org/legacy' build  # 排除一个包

多个 --filter 标志组合为匹配集的并集:

nub run --filter @org/api --filter @org/web build

--filter "@org/web..." — 图选择器: 依赖图过滤形式遍历工作区包图。使用 ... 语法拉入相关包。

nub run --filter "@org/web..." build   # @org/web + 依赖(下游)
nub run --filter "...@org/web" build   # @org/web + 依赖者(上游)
nub run --filter "@org/web^..." build  # 仅 @org/web 的依赖
nub run --filter "...^@org/web" build  # 仅 @org/web 的依赖者

这些正是 pnpm --filter 支持的形式,所以现有的 monorepo 肌肉记忆直接适用。

--filter "[ref]" — 自此变更: [ref] 过滤形式选择自某个 git 引用以来有变更的包——CI 中只测试变更内容的常见模式。

nub run --filter "[main]" test         # 自 main 以来变更的包
nub run --filter "[HEAD~1]" build      # 自上次提交以来变更的包
# 变更的包及其依赖者
nub run --filter "...[origin/main]" test

--parallel, --sequential

在工作区中,Nub 在尊重拓扑顺序的同时将包并发运行至上限(默认为 CPU 核心数)。调整或覆盖它。

nub run -r --workspace-concurrency 4 build   # 上限 4 个并发脚本
nub run -r --parallel dev                    # 全部同时,无拓扑顺序或上限
nub run -r --sequential migrate              # 逐个运行,忽略拓扑

对没有需要排序内容的长时间运行开发服务器使用 --parallel,对绝不能重叠的脚本使用 --sequential

--no-bail

默认情况下,工作区运行在第一个失败的包时中止(--bail。使用 --no-bail 运行每个包 regardless 并在最后报告失败。

nub run -r test               # 默认:在第一个失败的包处停止
nub run -r --no-bail test     # 运行每个包,最后报告失败

--resume-from

当长时间拓扑运行在中途失败时,--resume-from 跳过包的拓扑前驱,所以你从中断处重启而非从头开始。

nub run -r --resume-from @org/api build

-w, --workspace-root

-w 标志(pnpm 的 --workspace-root)只针对根项目,无论你从哪里调用。要递归跨成员运行并包含根包,使用 npm 风格的 --include-workspace-root

nub run -w lint                          # 仅在工作区根运行 "lint"
nub run -r --include-workspace-root lint # 所有成员加根包

注意 -w 是 pnpm 的布尔根选择器,不是 npm 的成员选择器。以 npm 方式选择成员,使用长格式 --workspace <name>(可重复)或 --filter <name>

nub run --workspace @org/api --workspace @org/web build

--stream, --aggregate-output

在 TTY 上,Nub 流式传输交错的、按包加前缀的输出(<pkg> | <line>,与 pnpm -r 一致)。在 CI / 非 TTY 上它按包缓冲并在完成时刷新。显式覆盖任一默认值。

nub run -r --stream build            # 强制交错实时输出
nub run -r --aggregate-output build  # 强制按包缓冲
nub run -r --reporter ndjson build   # 每行一个 JSON 事件,用于 CI 解析

--silent(或 -s)抑制 Nub 的 $ <command> 前导输出(它不抑制脚本自身的 stdout):

nub run --silent build

--node

默认情况下,nub 可执行文件在 nub run 调用期间被别名为 node,所以任何生成 node 的子进程(脚本 shebang、child_process.spawn("node", …))也获得 Nub 的增强。传递 --node 为单次调用关闭它——参见运行时概览了解完整约定。

nub run --node test

--node 下仍然发生的事情:脚本查找、工作区向上遍历、--filter 评估、npm_* 环境注入、node_modules/.binPATH 上,以及 pre / post 生命周期钩子。当脚本的 #!/usr/bin/env node shebang 链期望纯 Node、二分调试 Nub bug、或 CI 想要字节精确的 Node 运行时行为配合 Nub 的工作区选择时,使用它。

相关

  • 运行器nubx,跨每个层级解析名称的统一运行器。
  • 本地 bin 运行器nub exec,从 node_modules/.bin 运行本地 CLI。
  • 远程 bin 运行器nub dlx,用于未安装的工具。
  • 运行文件nub <file> 执行 TypeScript 和 JavaScript。
  • 监听模式 — 文件变更时重启。
  • FAQ — monorepo、包管理器和 --node 问题的简要回答。