Files
bun.sh/docs/snippets/cli/run.mdx
Jarred Sumner bb4d5b9af5 feat(cli/run): add --parallel and --sequential for running multiple scripts with workspace support (#26551)
## Summary

Adds `bun run --parallel` and `bun run --sequential` — new flags for
running multiple package.json scripts concurrently or sequentially with
Foreman-style prefixed output. Includes full `--filter`/`--workspaces`
integration for running scripts across workspace packages.

### Usage

```bash
# Run "build" and "test" concurrently from the current package.json
bun run --parallel build test

# Run "build" and "test" sequentially with prefixed output
bun run --sequential build test

# Glob-matched script names
bun run --parallel "build:*"

# Run "build" in all workspace packages concurrently
bun run --parallel --filter '*' build

# Run "build" in all workspace packages sequentially
bun run --sequential --workspaces build

# Glob-matched scripts across all packages
bun run --parallel --filter '*' "build:*"

# Multiple scripts across all packages
bun run --parallel --filter '*' build lint test

# Continue running even if one package fails
bun run --parallel --no-exit-on-error --filter '*' test

# Skip packages missing the script
bun run --parallel --workspaces --if-present build
```

## How it works

### Output format

Each script's stdout/stderr is prefixed with a colored, padded label:

```
build | compiling...
test  | running suite...
lint  | checking files...
```

### Label format

- **Without `--filter`/`--workspaces`**: labels are just the script name
→ `build | output`
- **With `--filter`/`--workspaces`**: labels are `package:script` →
`pkg-a:build | output`
- **Fallback**: if a package.json has no `name` field, the relative path
from the workspace root is used (e.g., `packages/my-pkg:build`)

### Execution model

- **`--parallel`**: all scripts start immediately, output is interleaved
with prefixes
- **`--sequential`**: scripts run one at a time in order, each waiting
for the previous to finish
- **Pre/post scripts** (`prebuild`/`postbuild`) are grouped with their
main script and run in dependency order within each group
- By default, a failure kills all remaining scripts.
`--no-exit-on-error` lets all scripts finish.

### Workspace integration

The workspace branch in `multi_run.zig` uses a two-pass approach for
deterministic ordering:

1. **Collect**: iterate workspace packages using
`FilterArg.PackageFilterIterator` (same infrastructure as
`filter_run.zig`), filtering with `FilterArg.FilterSet`, collecting
matched packages with their scripts, PATH, and cwd.
2. **Sort**: sort matched packages by name (tiebreak by directory path)
for deterministic ordering — filesystem iteration order from the glob
walker is nondeterministic.
3. **Build configs**: for each sorted package, expand script names
(including globs like `build:*`) against that package's scripts map,
creating `ScriptConfig` entries with `pkg:script` labels and per-package
cwd/PATH.

### Behavioral consistency with `filter_run.zig`

| Behavior | `filter_run.zig` | `multi_run.zig` (this PR) |
|----------|-------------------|---------------------------|
| `--workspaces` skips root package | Yes | Yes |
| `--workspaces` errors on missing script | Yes | Yes |
| `--if-present` silently skips missing | Yes | Yes |
| `--filter` without `--workspaces` includes root | Yes (if matches) |
Yes (if matches) |
| Pre/post script chains | Per-package | Per-package |
| Per-package cwd | Yes | Yes |
| Per-package PATH (`node_modules/.bin`) | Yes | Yes |

### Key implementation details

- Each workspace package script runs in its own package directory with
its own `node_modules/.bin` PATH
- `dirpath` from the glob walker is duped to avoid use-after-free when
the iterator's arena is freed between patterns
- `addScriptConfigs` takes an optional `label_prefix` parameter — `null`
for single-package mode, package name for workspace mode
- `MultiRunProcessHandle` is registered in the `ProcessExitHandler`
tagged pointer union in `process.zig`

## Files changed

| File | Change |
|------|--------|
| `src/cli/multi_run.zig` | New file: process management, output
routing, workspace integration, dependency ordering |
| `src/cli.zig` | Dispatch to `MultiRun.run()` for
`--parallel`/`--sequential`, new context fields |
| `src/cli/Arguments.zig` | Parse `--parallel`, `--sequential`,
`--no-exit-on-error` flags |
| `src/bun.js/api/bun/process.zig` | Register `MultiRunProcessHandle` in
`ProcessExitHandler` tagged pointer union |
| `test/cli/run/multi-run.test.ts` | 118 tests (102 core + 16 workspace
integration) |
| `docs/pm/filter.mdx` | Document `--parallel`/`--sequential` +
`--filter`/`--workspaces` combination |
| `docs/snippets/cli/run.mdx` | Add `--parallel`, `--sequential`,
`--no-exit-on-error` parameter docs |

## Test plan

All 118 tests pass with debug build (`bun bd test
test/cli/run/multi-run.test.ts`). The 16 new workspace tests all fail
with system bun (`USE_SYSTEM_BUN=1`), confirming they test new
functionality.

### Workspace integration tests (16 tests)

1. `--parallel --filter='*'` runs script in all packages
2. `--parallel --filter='pkg-a'` runs only in matching package
3. `--parallel --workspaces` matches all workspace packages
4. `--parallel --filter='*'` with glob expands per-package scripts
5. `--sequential --filter='*'` runs in sequence (deterministic order)
6. Workspace + failure aborts other scripts
7. Workspace + `--no-exit-on-error` lets all finish
8. `--workspaces` skips root package
9. Each workspace script runs in its own package directory (cwd
verification)
10. Multiple script names across workspaces (`build` + `test`)
11. Pre/post scripts work per workspace package
12. `--filter` skips packages without the script (no error)
13. `--workspaces` errors when a package is missing the script
14. `--workspaces --if-present` skips missing scripts silently
15. Labels are padded correctly across workspace packages
16. Package without `name` field uses relative path as label

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Dylan Conway <dylan.conway567@gmail.com>
2026-01-29 20:20:39 -08:00

306 lines
9.6 KiB
Plaintext

# CLI Usage
```bash
bun run <file or script>
```
### General Execution Options
<ParamField path="--silent" type="boolean">
Don't print the script command
</ParamField>
<ParamField path="--if-present" type="boolean">
Exit without an error if the entrypoint does not exist
</ParamField>
<ParamField path="--eval" type="string">
Evaluate argument as a script. Alias: <code>-e</code>
</ParamField>
<ParamField path="--print" type="string">
Evaluate argument as a script and print the result. Alias: <code>-p</code>
</ParamField>
<ParamField path="--help" type="boolean">
Display this menu and exit. Alias: <code>-h</code>
</ParamField>
### Workspace Management
<ParamField path="--elide-lines" type="number" default="10">
Number of lines of script output shown when using --filter (default: 10). Set to 0 to show all lines
</ParamField>
<ParamField path="--filter" type="string">
Run a script in all workspace packages matching the pattern. Alias: <code>-F</code>
</ParamField>
<ParamField path="--workspaces" type="boolean">
Run a script in all workspace packages (from the <code>workspaces</code> field in <code>package.json</code>)
</ParamField>
<ParamField path="--parallel" type="boolean">
Run multiple scripts or workspace scripts concurrently with prefixed output
</ParamField>
<ParamField path="--sequential" type="boolean">
Run multiple scripts or workspace scripts one after another with prefixed output
</ParamField>
<ParamField path="--no-exit-on-error" type="boolean">
When using <code>--parallel</code> or <code>--sequential</code>, continue running other scripts when one fails
</ParamField>
### Runtime &amp; Process Control
<ParamField path="--bun" type="boolean">
Force a script or package to use Bun's runtime instead of Node.js (via symlinking node). Alias: <code>-b</code>
</ParamField>
<ParamField path="--shell" type="string">
Control the shell used for <code>package.json</code> scripts. Supports either <code>bun</code> or <code>system</code>
</ParamField>
<ParamField path="--smol" type="boolean">
Use less memory, but run garbage collection more often
</ParamField>
<ParamField path="--expose-gc" type="boolean">
Expose <code>gc()</code> on the global object. Has no effect on <code>Bun.gc()</code>
</ParamField>
<ParamField path="--no-deprecation" type="boolean">
Suppress all reporting of the custom deprecation
</ParamField>
<ParamField path="--throw-deprecation" type="boolean">
Determine whether or not deprecation warnings result in errors
</ParamField>
<ParamField path="--title" type="string">
Set the process title
</ParamField>
<ParamField path="--zero-fill-buffers" type="boolean">
Boolean to force <code>Buffer.allocUnsafe(size)</code> to be zero-filled
</ParamField>
<ParamField path="--no-addons" type="boolean">
Throw an error if <code>process.dlopen</code> is called, and disable export condition <code>node-addons</code>
</ParamField>
<ParamField path="--unhandled-rejections" type="string">
One of <code>strict</code>, <code>throw</code>, <code>warn</code>, <code>none</code>, or{" "}
<code>warn-with-error-code</code>
</ParamField>
<ParamField path="--console-depth" type="number" default="2">
Set the default depth for <code>console.log</code> object inspection (default: 2)
</ParamField>
### Development Workflow
<ParamField path="--watch" type="boolean">
Automatically restart the process on file change
</ParamField>
<ParamField path="--hot" type="boolean">
Enable auto reload in the Bun runtime, test runner, or bundler
</ParamField>
<ParamField path="--no-clear-screen" type="boolean">
Disable clearing the terminal screen on reload when --hot or --watch is enabled
</ParamField>
### Debugging
<ParamField path="--inspect" type="string">
Activate Bun's debugger
</ParamField>
<ParamField path="--inspect-wait" type="string">
Activate Bun's debugger, wait for a connection before executing
</ParamField>
<ParamField path="--inspect-brk" type="string">
Activate Bun's debugger, set breakpoint on first line of code and wait
</ParamField>
### Dependency &amp; Module Resolution
<ParamField path="--preload" type="string">
Import a module before other modules are loaded. Alias: <code>-r</code>
</ParamField>
<ParamField path="--require" type="string">
Alias of --preload, for Node.js compatibility
</ParamField>
<ParamField path="--import" type="string">
Alias of --preload, for Node.js compatibility
</ParamField>
<ParamField path="--no-install" type="boolean">
Disable auto install in the Bun runtime
</ParamField>
<ParamField path="--install" type="string" default="auto">
Configure auto-install behavior. One of <code>auto</code> (default, auto-installs when no node_modules),{" "}
<code>fallback</code> (missing packages only), <code>force</code> (always)
</ParamField>
<ParamField path="-i" type="boolean">
Auto-install dependencies during execution. Equivalent to --install=fallback
</ParamField>
<ParamField path="--prefer-offline" type="boolean">
Skip staleness checks for packages in the Bun runtime and resolve from disk
</ParamField>
<ParamField path="--prefer-latest" type="boolean">
Use the latest matching versions of packages in the Bun runtime, always checking npm
</ParamField>
<ParamField path="--conditions" type="string">
Pass custom conditions to resolve
</ParamField>
<ParamField path="--main-fields" type="string">
Main fields to lookup in <code>package.json</code>. Defaults to --target dependent
</ParamField>
<ParamField path="--preserve-symlinks" type="boolean">
Preserve symlinks when resolving files
</ParamField>
<ParamField path="--preserve-symlinks-main" type="boolean">
Preserve symlinks when resolving the main entry point
</ParamField>
<ParamField path="--extension-order" type="string" default=".tsx,.ts,.jsx,.js,.json">
Defaults to: <code>.tsx,.ts,.jsx,.js,.json</code>
</ParamField>
### Transpilation &amp; Language Features
<ParamField path="--tsconfig-override" type="string">
Specify custom <code>tsconfig.json</code>. Default <code>$cwd/tsconfig.json</code>
</ParamField>
<ParamField path="--define" type="string">
Substitute K:V while parsing, e.g. <code>--define process.env.NODE_ENV:"development"</code>. Values are parsed as
JSON. Alias: <code>-d</code>
</ParamField>
<ParamField path="--drop" type="string">
Remove function calls, e.g. <code>--drop=console</code> removes all <code>console.*</code> calls
</ParamField>
<ParamField path="--loader" type="string">
Parse files with <code>.ext:loader</code>, e.g. <code>--loader .js:jsx</code>. Valid loaders: <code>js</code>,{" "}
<code>jsx</code>, <code>ts</code>, <code>tsx</code>, <code>json</code>, <code>toml</code>, <code>text</code>,{" "}
<code>file</code>, <code>wasm</code>, <code>napi</code>. Alias: <code>-l</code>
</ParamField>
<ParamField path="--no-macros" type="boolean">
Disable macros from being executed in the bundler, transpiler and runtime
</ParamField>
<ParamField path="--jsx-factory" type="string">
Changes the function called when compiling JSX elements using the classic JSX runtime
</ParamField>
<ParamField path="--jsx-fragment" type="string">
Changes the function called when compiling JSX fragments
</ParamField>
<ParamField path="--jsx-import-source" type="string" default="react">
Declares the module specifier to be used for importing the jsx and jsxs factory functions. Default: <code>react</code>
</ParamField>
<ParamField path="--jsx-runtime" type="string" default="automatic">
<code>automatic</code> (default) or <code>classic</code>
</ParamField>
<ParamField path="--jsx-side-effects" type="boolean">
Treat JSX elements as having side effects (disable pure annotations)
</ParamField>
<ParamField path="--ignore-dce-annotations" type="boolean">
Ignore tree-shaking annotations such as <code>@__PURE__</code>
</ParamField>
### Networking &amp; Security
<ParamField path="--port" type="number">
Set the default port for <code>Bun.serve</code>
</ParamField>
<ParamField path="--fetch-preconnect" type="string">
Preconnect to a URL while code is loading
</ParamField>
<ParamField path="--max-http-header-size" type="number" default="16384">
Set the maximum size of HTTP headers in bytes. Default is 16KiB
</ParamField>
<ParamField path="--dns-result-order" type="string" default="verbatim">
Set the default order of DNS lookup results. Valid orders: <code>verbatim</code> (default), <code>ipv4first</code>,{" "}
<code>ipv6first</code>
</ParamField>
<ParamField path="--use-system-ca" type="boolean">
Use the system's trusted certificate authorities
</ParamField>
<ParamField path="--use-openssl-ca" type="boolean">
Use OpenSSL's default CA store
</ParamField>
<ParamField path="--use-bundled-ca" type="boolean">
Use bundled CA store
</ParamField>
<ParamField path="--redis-preconnect" type="boolean">
Preconnect to <code>$REDIS_URL</code> at startup
</ParamField>
<ParamField path="--sql-preconnect" type="boolean">
Preconnect to PostgreSQL at startup
</ParamField>
<ParamField path="--user-agent" type="string">
Set the default User-Agent header for HTTP requests
</ParamField>
### Global Configuration &amp; Context
<ParamField path="--env-file" type="string">
Load environment variables from the specified file(s)
</ParamField>
<ParamField path="--cwd" type="string">
Absolute path to resolve files &amp; entry points from. This just changes the process' cwd
</ParamField>
<ParamField path="--config" type="string">
Specify path to Bun config file. Default <code>$cwd/bunfig.toml</code>. Alias: <code>-c</code>
</ParamField>
## Examples
Run a JavaScript or TypeScript file:
```bash
bun run ./index.js
bun run ./index.tsx
```
Run a package.json script:
```bash
bun run dev
bun run lint
```