bun-workspaces
A CLI and API on top of native Bun workspaces

v1.0.0-alpha.21

  • Get metadata about your project ๐Ÿค–
  • Run scripts across your workspaces flexibly ๐Ÿงพ
  • Works right away, with no boilerplate required ๐Ÿ”๐Ÿด

What's New

Notable recent updates ๐Ÿ“ฆ

  • Running scripts: Added a way to set the execution order of scripts per workspace via the workspace configuration.
  • Running scripts: New script runtime metadata can be read from environment vars or used in inline scripts and args
  • Running scripts: Added --inline option to run a one-off commands (See API and CLI docs)
  • API: Added methods for directly running scripts in series or parallel in workspaces via FileSystemProject.
  • An API has been made official! ๐Ÿ› ๏ธ๏ธ Go see the documentation here
  • New workspace configuration is supported in either package.json or a separate config file.
    • The root bw.json file is now deprecated (more info)
  • Added --json-outfile option to run-script to output results (more info)

Installation

You can install the package in your project or simply use bunx bun-workspaces to immediately use the latest version of the CLI. Installation is required to use the API.

# 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

If you use the CLI frequently, you might consider using a shell alias.

# Add this to .bashrc, .zshrc, or similar 
# to make it available by default in your shell
alias bw="bunx bun-workspaces"

Examples in these docs will use the alias bw for bunx bun-workspaces for brevity.

Quick Start

CLI

See the full CLI documentation or continue for some quick examples.

# List all workspaces in your project
bw list-workspaces

# ls is an alias for list-workspaces
bw ls --json --pretty # Output as formatted JSON

# Run the lint script for all workspaces
# that have it in their "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-workspace-*" # Run for matching workspace names
bw run lint --parallel # Run at the same time
bw run lint --args="--my-appended-args" # Add args to each script call
bw run lint --args="--my-arg=<workspaceName>" # Use the workspace name in args

# Show usage (you can pass --help to any command)
bw help
bw --help

# 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=silent run my-script

API

See the full API documentation or continue for some quick examples.

import { createFileSystemProject } from "bun-workspaces";

// A Project contains the core functionality of bun-workspaces.
const project = createFileSystemProject({
  rootDirectory: "path/to/your/project",
});

// 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",
    args: "--my --appended --args", // optional, arguments to add to the command
  });
  
  // Get a stream of the script subprocess's output
  for await (const { text, textNoAnsi, streamName } of output) {
    console.log(text); // The output chunk's content (string)
    console.log(textNoAnsi); // Text with ANSI codes sanitized (string)
    console.log(streamName); // The output stream, "stdout" or "stderr"
  }
  
  // Get data about the script execution after it exits
  const exitResult = await exit;
  
  console.log(exitResult.exitCode); // The exit code (number)
  console.log(exitResult.signal); // The exit signal (string), or null
  console.log(exitResult.success); // true if exit code was 0
  console.log(exitResult.startTimeISO); // Start time (string)
  console.log(exitResult.endTimeISO); // End time (string)
  console.log(exitResult.durationMs); // Duration in milliseconds (number)
  console.log(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({
    workspacePatterns: ["*"], // this will run in all workspaces that have my-script
    script: "my-script", // the package.json "scripts" field name to run
    args: "--my --appended --args", // optional, arguments to add to the command
    parallel: true, // optional, run the scripts in parallel
  });
  
  // Get a stream of script output
  for await (const { outputChunk, scriptMetadata } of output) {
    console.log(outputChunk.text); // the output chunk's content (string)
    console.log(outputChunk.textNoAnsi); // text with ANSI codes sanitized (string)
    console.log(outputChunk.streamName); // "stdout" or "stderr"
  
    // The metadata can distinguish which workspace script 
    // the current output chunk came from
    console.log(scriptMetadata.workspace); // Workspace object
  }
  
  // Get final summary data and script exit details after all scripts have completed
  const summaryResult = await summary;
  
  console.log(summaryResult.totalCount); // Total number of scripts
  console.log(summaryResult.allSuccess); // true if all scripts succeeded
  console.log(summaryResult.successCount); // Number of scripts that succeeded
  console.log(summaryResult.failureCount); // Number of scripts that failed
  console.log(summaryResult.startTimeISO); // Start time (string)
  console.log(summaryResult.endTimeISO); // End time (string)
  console.log(summaryResult.durationMs); // Total duration in milliseconds (number)
  
  // The exit details of each workspace script
  for (const exitResult of summaryResult.scriptResults) {
    console.log(exitResult.exitCode); // The exit code (number)
    console.log(exitResult.signal); // The exit signal (string), or null
    console.log(exitResult.success); // true if exit code was 0
    console.log(exitResult.startTimeISO); // Start time (ISO string)
    console.log(exitResult.endTimeISO); // End time (ISO string)
    console.log(exitResult.durationMs); // Duration in milliseconds (number)
    console.log(exitResult.metadata.workspace); // The target workspace (Workspace)
  }
}

bun-workspaces is independent from the Bun project and is not affiliated with or endorsed by Oven. This project aims to enhance enhance the experience of Bun for its users.

โ—
Uses minimal dependencies
โ—
No setup required
โ—
Run workspace scripts in series or parallel
โ—
Set aliases for workspaces
โ—
Output script results to a JSON file
โ—
We strive for thorough tests and docs
โ—
JS API available
โ—
TypeScript friendly
โ—
Intellisense support
โ—
New features always on the way
โ—
We listen to our users
โ—
Configurable logging
โ—
Validations that help protect project hygiene
โ—
Uses minimal dependencies
โ—
No setup required
โ—
Run workspace scripts in series or parallel
โ—
Set aliases for workspaces
โ—
Output script results to a JSON file
โ—
We strive for thorough tests and docs
โ—
JS API available
โ—
TypeScript friendly
โ—
Intellisense support
โ—
New features always on the way
โ—
We listen to our users
โ—
Configurable logging
โ—
Validations that help protect project hygiene