https://github.com/bun-workspaces/bun-workspaces
A tool for managing monorepos using Bun's native workspaces feature. Upgrade how you develop JavaScript and TypeScript projects with the bun-workspaces CLI and API.
https://github.com/bun-workspaces/bun-workspaces
api bun cli javascript monorepo npm npm-package typescript workspace workspaces
Last synced: 15 days ago
JSON representation
A tool for managing monorepos using Bun's native workspaces feature. Upgrade how you develop JavaScript and TypeScript projects with the bun-workspaces CLI and API.
- Host: GitHub
- URL: https://github.com/bun-workspaces/bun-workspaces
- Owner: bun-workspaces
- License: mit
- Created: 2024-12-08T19:36:42.000Z (over 1 year ago)
- Default Branch: main
- Last Pushed: 2026-03-27T16:21:16.000Z (about 2 months ago)
- Last Synced: 2026-03-27T17:45:40.078Z (about 2 months ago)
- Topics: api, bun, cli, javascript, monorepo, npm, npm-package, typescript, workspace, workspaces
- Language: TypeScript
- Homepage: https://bunworkspaces.com
- Size: 2.52 MB
- Stars: 44
- Watchers: 3
- Forks: 4
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- Funding: .github/FUNDING.yml
- License: LICENSE.md
Awesome Lists containing this project
README
Full Documentation: [https://bunworkspaces.com](https://bunworkspaces.com)
Changelog: [GitHub Releases](https://github.com/bun-workspaces/bun-workspaces/releases)
# bun-workspaces
A [monorepo](http://sonarsource.com/resources/library/monorepo/) tool that enhances native [Bun workspaces](https://bun.sh/docs/install/workspaces).
- Works right away, with **no boilerplate required** π½οΈ
- Get **rich metadata** about your monorepo π€
- **Orchestrate** your workspaces' package.json scripts π»
- Run one-off [**Bun Shell**](https://bun.com/docs/runtime/shell) commands in your workspaces π
- Use with Bun as your package manager for **Node** projects π
- Determine **affected workspaces** based on changed files πΈοΈ
- Use the [MCP server](https://bunworkspaces.com/ai/mcp) to make your AI tooling aware of `bun-workspaces` and its documentation resources! π οΈ
To get started, all you need is a repo using Bun's workspaces feature for nested JavaScript/TypeScript packages. This adds enhanced features on top of plain workspaces.
Start running some [CLI commands](https://bunworkspaces.com/cli) right away in your repo, or take full advantage of the [TypeScript API](https://bunworkspaces.com/api) and its features.
This package is unopinionated and works with any project structure you want. Think of this as a power suit you can snap onto native workspaces, rather than whole new monorepo framework.
## Quick Start
Installation:
```bash
$ # Install to use the API and/or lock your CLI version for your project
$ bun add --dev bun-workspaces
$ # Start using the CLI with or without the installation step
$ bunx bun-workspaces --help
```
Note that you need to run `bun install` in your project for `bun-workspaces` to find your project's workspaces. This is because it reads `bun.lock`. This also means that if you update your workspaces, such as changing their name, you must run `bun install` for the change to reflect.
### CLI
[Full CLI documentation here](https://bunworkspaces.com/cli)
```bash
# You can add this to .bashrc, .zshrc, or similar.
# You can also invoke "bw" in your root package.json scripts.
alias bw="bunx bun-workspaces"
# List all workspaces in your project
bw list-workspaces
# ls is an alias for list-workspaces
bw ls --json --pretty # Output as formatted JSON
# Get info about a workspace
bw workspace-info my-workspace
bw info my-workspace --json --pretty # info is alias for workspace-info
# Get info about a script, such as the workspaces that have it
bw script-info my-script
# Run the lint script for all workspaces
# that have it in their package.json "scripts" field
bw run-script lint
# run is an alias for run-script
bw run lint my-workspace # Run for a single workspace
bw run lint my-workspace-a my-workspace-b # Run for multiple workspaces
bw run lint my-alias-a my-alias-b # Run by alias (set by optional config)
# A workspace's script will wait until any workspaces it depends on have completed
# Similar to Bun's --filter behavior
bw run lint --dep-order
# Continue running scripts even if a dependency fails
bw run lint --dep-order --ignore-dep-failure
bw run lint "my-workspace-*" # Run for matching workspace names
bw run lint "alias:my-alias-*" "path:my-glob/**/*" "tag:my-tag" # Use matching specifiers
bw run lint "*" "not:path:my-path/*" # Run for all workspaces not in my-path/
bw run lint --args="--my-appended-args" # Add args to each script call
bw run lint --args="--my-arg=" # Use the workspace name in args
bw run "bun build" --inline # Run an inline command via the Bun shell
# Scripts run in parallel by default
bw run lint --parallel=false # Run in series
bw run lint --parallel=2 # Run in parallel with a max of 2 concurrent scripts
bw run lint --parallel=auto # Default, based on number of available logical CPUs
bw run lint --parallel=50% # Run in parallel with a max of 50% of the "auto" limit
# Use the grouped output style (default when on a TTY)
bw run my-script --output-style=grouped
# Set the max preview lines for script output in grouped output style
bw run my-script --output-style=grouped --grouped-lines=auto
bw run my-script --output-style=grouped --grouped-lines=10
# Use simple script output with workspace prefixes (default when not on a TTY)
bw run my-script --output-style=prefixed
# Use the plain output style (no workspace prefixes)
bw run my-script --output-style=plain
# List affected workspaces based on git diff (main vs. HEAD when not configured)
bw list-affected
# Set the git base and head for comparison
bw list-affected --base=my-branch-a --head=my-branch-b
# See detailed reasons for affected workspaces
bw list-affected --explain --detailed
# Run a script across the workspaces affected by a change
bw run-affected my-script
# Silence all output of the run command
bw --log-level=silent run my-script --output-style=none
# Show usage (you can pass --help to any command)
bw help
bw --help
# Show version
bw --version
# Pass --cwd to any command
bw --cwd=/path/to/your/project ls
bw --cwd=/path/to/your/project run my-script
# Pass --log-level to any command (debug, info, warn, error, or silent)
bw --log-level=debug ls
```
### API
[Full API documentation here](https://bunworkspaces.com/api)
```typescript
import { createFileSystemProject } from "bun-workspaces";
// A Project contains the core functionality of bun-workspaces.
// Below defaults to process.cwd() for the project root directory
// Pass { rootDirectory: "path/to/your/project" } to use a different root directory
const project = createFileSystemProject();
// A Workspace that matches the name or alias "my-workspace"
const myWorkspace = project.findWorkspaceByNameOrAlias("my-workspace");
// Array of workspaces whose names match the wildcard pattern
const wildcardWorkspaces = project.findWorkspacesByPattern("my-workspace-*");
// Array of workspaces that have "my-script" in their package.json "scripts"
const workspacesWithScript = project.listWorkspacesWithScript("my-script");
// Run a script in a workspace
const runSingleScript = async () => {
const { output, exit } = project.runWorkspaceScript({
workspaceNameOrAlias: "my-workspace",
script: "my-script",
// Optional. Arguments to add to the command
// Can be a string or an array of strings
// If string, the argv will be parsed POSIX-style
args: ["--my", "--appended", "--args"],
// Optional. Whether to ignore all output from the script.
// This saves memory when you don't need script output.
ignoreOutput: false,
});
// Get a stream of the script subprocess's output
for await (const { chunk, metadata } of output.text()) {
// console.log(chunk); // The output chunk's content (string)
// console.log(metadata.streamName); // The output stream, "stdout" or "stderr"
// console.log(metadata.workspace); // The target Workspace
}
// Get data about the script execution after it exits
const exitResult = await exit;
// exitResult.exitCode // The exit code (number)
// exitResult.signal // The exit signal (string), or null
// exitResult.success // true if exit code was 0
// exitResult.startTimeISO // Start time (string)
// exitResult.endTimeISO // End time (string)
// exitResult.durationMs // Duration in milliseconds (number)
// exitResult.metadata.workspace // The target workspace (Workspace)
};
// Run a script in all workspaces that have it in their package.json "scripts" field
const runManyScripts = async () => {
const { output, summary } = project.runScriptAcrossWorkspaces({
// Optional. This will run in all matching workspaces that have my-script
// Accepts same values as the CLI run-script command's workspace patterns
// When not provided, all workspaces that have the script will be used.
workspacePatterns: ["my-workspace", "my-name-pattern-*"],
// Required. The package.json "scripts" field name to run
script: "my-script",
// Optional. Arguments to add to the command (same as for runWorkspaceScript)
args: ["--my", "--appended", "--args"],
// Optional. Whether to run the scripts in parallel (default: true)
parallel: true,
// Optional. When true, a workspace's script will wait
// until any workspaces it depends on have completed
dependencyOrder: false,
// Optional. When true and dependencyOrder is true,
// continue running scripts even if a dependency fails
ignoreDependencyFailure: false,
// Optional. Whether to ignore all output from the scripts.
// This saves memory when you don't need script output.
ignoreOutput: false,
// Optional, callback when script starts, skips, or exits
onScriptEvent: (event, { workspace, exitResult }) => {
// event: "start", "skip", "exit"
},
});
// Get a stream of script output
for await (const { chunk, metadata } of output.text()) {
// console.log(chunk); // the output chunk's content (string)
// console.log(metadata.streamName); // "stdout" or "stderr"
// console.log(metadata.workspace); // the Workspace that the output came from
}
// Get final summary data and script exit details after all scripts have completed
const summaryResult = await summary;
// summaryResult.totalCount // Total number of scripts
// summaryResult.allSuccess // true if all scripts succeeded
// summaryResult.successCount // Number of scripts that succeeded
// summaryResult.failureCount // Number of scripts that failed
// summaryResult.startTimeISO // Start time (string)
// summaryResult.endTimeISO // End time (string)
// summaryResult.durationMs // Total duration in milliseconds (number)
// The exit details of each workspace script
for (const exitResult of summaryResult.scriptResults) {
// exitResult.exitCode // The exit code (number)
// exitResult.signal // The exit signal (string), or null
// exitResult.success // true if exit code was 0
// exitResult.startTimeISO // Start time (ISO string)
// exitResult.endTimeISO // End time (ISO string)
// exitResult.durationMs // Duration in milliseconds (number)
// exitResult.metadata.workspace // The target workspace (Workspace)
}
};
```
### Configuration
`bun-workspaces` has no required configuration, but there are optional config files.
#### Workspace Config
Workspace configs can be placed in a workspace's directory at `bw.workspace.ts`.
[Workspace configuration documentation here](https://bunworkspaces.com/config/workspace)
```typescript
// bw.workspace.ts β place in a workspace directory
// Also supported: bw.workspace.js, bw.workspace.json, bw.workspace.jsonc,
// or a "bw" key in package.json
import { defineWorkspaceConfig } from "bun-workspaces/config";
export default defineWorkspaceConfig({
alias: "my-web-app", // shorthand name; use array for multiple
tags: ["app", "frontend"],
// Optional, for configuring affected workspace resolution inputs
// Applies to all scripts that don't configure their own inputs
defaultInputs: {
// File paths, directory paths, or globs relative to the workspace's path.
// Default is all git-trackable files in the workspace directory.
files: ["src/**/*.ts", "!src/**/*.test.ts"],
// Workspaces to treat like dependencies that aren't package.json dependencies
workspacePatterns: ["tag:lib"],
// Dependency names (e.g. "react") to treat as dependencies (default: all)
externalDependencies: ["react"],
},
scripts: {
// lower order runs first in sequenced script execution
build: {
// Optional, for setting the default script execution order
order: 1,
// Optional, for configuring affected workspace resolution inputs
// Applies to the build script only
inputs: { files: ["src/**/*.ts"] },
},
test: { order: 2 },
},
rules: {
workspaceDependencies: {
// Only "my-workspace" or workspaces tagged "lib" are allowed as dependencies
allowPatterns: ["tag:lib", "my-workspace"],
// Workspaces tagged "backend" are forbidden as dependencies
denyPatterns: ["tag:backend"],
},
},
});
```
#### Root Config
A root config can be placed in the project root directory at `bw.root.ts`,
which can also apply workspace configs in bulk by using workspace patterns.
[Root configuration documentation here](https://bunworkspaces.com/config/root)
[More on workspace pattern configs here](https://bunworkspaces.com/config/workspace-pattern-configs)
```typescript
// bw.root.ts β place in your project root directory
// Also supported: bw.root.js, bw.root.json, bw.root.jsonc,
// or a "bw-root" key in package.json
import { defineRootConfig } from "bun-workspaces/config";
export default defineRootConfig({
defaults: {
// default value for --parallel option
parallelMax: 4,
// default value for --shell option
shell: "system",
// default value for global --include-root-workspace option
includeRootWorkspace: false,
},
// Apply workspace configs in bulk by workspace pattern, in order.
// Each entry merges into matching workspaces' accumulated config.
// Pattern matching reflects aliases and tags added by earlier entries.
workspacePatternConfigs: [
{
patterns: ["path:packages/apps/**/*"],
config: { tags: ["app"] },
},
{
patterns: ["path:packages/libs/**/*"],
config: { tags: ["lib"] },
},
{
// "tag:app" matches because the first entry added it
patterns: ["tag:app"],
config: {
// Inputs always override previous entries instead of deep merging
defaultInputs: { files: ["src/**/*.ts"] },
scripts: {
build: { order: 1, inputs: { files: ["src/**/*.ts"] } },
},
rules: {
workspaceDependencies: {
allowPatterns: ["tag:lib"], // apps may only depend on libs
},
},
},
},
{
patterns: ["tag:app"],
// Factory form: receives static workspace data and accumulated config
config: (workspace, prevConfig) => ({
alias: workspace.name.replace(/^@my-scope\//, ""),
}),
},
],
});
```
_`bun-workspaces` is independent from the [Bun](https://bun.sh) project and is not affiliated with or endorsed by Anthropic. This project aims to enhance the experience of Bun for its users._
Developed By: