---
Manages Bun monorepos with workspaces, catalogs, and dependency-aware scripts. Handles lockfile migration, TypeScript paths, and optimized CI/CD workflows for 28x faster installs.
/plugin marketplace add shepherdjerred/monorepo/plugin install jerred@shepherdjerredbun.lock replaces binary bun.lockb (v1.2+)Bun workspaces are significantly faster than alternatives:
my-monorepo/
├── package.json # Root config with workspaces field
├── bun.lock # Single lockfile for all packages
├── bunfig.toml # Optional Bun configuration
├── tsconfig.json # Shared TypeScript config
└── packages/
├── shared/
│ ├── package.json
│ ├── index.ts
│ └── tsconfig.json
├── backend/
│ ├── package.json
│ ├── src/
│ └── tsconfig.json
└── frontend/
├── package.json
├── src/
└── tsconfig.json
{
"name": "my-monorepo",
"private": true,
"workspaces": ["packages/*"],
"scripts": {
"dev": "bun --filter '*' dev",
"build": "bun --filter '*' build",
"test": "bun --filter '*' test",
"typecheck": "bun --filter '*' typecheck"
},
"devDependencies": {
"typescript": "^5.0.0"
}
}
{
"workspaces": [
"packages/*",
"apps/*",
"packages/**",
"!packages/**/test/**",
"!packages/deprecated"
]
}
Reference workspace packages in dependencies:
{
"name": "@myorg/backend",
"dependencies": {
"@myorg/shared": "workspace:*"
}
}
| Syntax | Meaning | Published As |
|---|---|---|
workspace:* | Any version | "1.0.0" (actual version) |
workspace:^ | Caret range | "^1.0.0" |
workspace:~ | Tilde range | "~1.0.0" |
workspace:1.0.2 | Exact version | "1.0.2" |
When publishing, workspace: references are replaced with actual versions.
Define shared dependency versions once:
{
"name": "my-monorepo",
"private": true,
"workspaces": ["packages/*"],
"catalog": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"typescript": "^5.0.0",
"zod": "^3.22.0"
}
}
{
"name": "@myorg/frontend",
"dependencies": {
"react": "catalog:",
"react-dom": "catalog:",
"zod": "catalog:"
}
}
{
"catalogs": {
"default": {
"typescript": "^5.0.0"
},
"testing": {
"vitest": "^1.0.0",
"playwright": "^1.40.0"
}
}
}
{
"devDependencies": {
"typescript": "catalog:",
"vitest": "catalog:testing"
}
}
# Install all workspaces
bun install
# Install with frozen lockfile (CI)
bun install --frozen-lockfile
# Generate lockfile only
bun install --lockfile-only
# Filter to specific packages
bun install --filter "pkg-*"
bun install --filter "!pkg-c"
# Add to current workspace
cd packages/backend
bun add express
# Add as dev dependency
bun add -d typescript
# Add with exact version
bun add -E zod@3.22.0
# Add to specific workspace from root
bun add lodash --filter "@myorg/shared"
# Run in all workspaces
bun --filter '*' dev
# Run in specific package
bun --filter '@myorg/backend' dev
# Run with glob pattern
bun --filter 'pkg-*' build
# Run excluding packages
bun --filter '*' --filter '!@myorg/frontend' test
# Run by path
bun --filter './packages/backend' dev
When packages depend on each other, scripts run in dependency order:
# If @myorg/backend depends on @myorg/shared:
bun --filter '*' build
# Runs: shared build → backend build
# Check outdated dependencies
bun outdated
bun outdated --filter 'pkg-*'
# Update dependencies
bun update
# Remove dependency
bun remove lodash
# List installed packages
bun pm ls
bun pm ls --all
Root tsconfig.json:
{
"compilerOptions": {
"strict": true,
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "bundler",
"esModuleInterop": true,
"skipLibCheck": true,
"declaration": true,
"composite": true,
"paths": {
"@myorg/*": ["./packages/*/src"]
}
}
}
Workspace tsconfig.json:
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src"],
"references": [
{ "path": "../shared" }
]
}
// Root eslint.config.js
import baseConfig from "@myorg/eslint-config";
export default [
...baseConfig,
{
ignores: ["**/dist/**", "**/node_modules/**"],
},
];
[install]
# Auto-install missing dependencies
auto = true
# Lifecycle scripts control
lifecycle = ["postinstall"]
[install.lockfile]
# Save text-based lockfile
save = true
# Also generate yarn.lock
print = "yarn"
[install.scopes]
# Registry for scoped packages
"@myorg" = { token = "$NPM_TOKEN", url = "https://npm.pkg.github.com" }
{
"name": "@myorg/shared",
"version": "1.0.0",
"type": "module",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"exports": {
".": {
"import": "./dist/index.js",
"types": "./dist/index.d.ts"
},
"./utils": {
"import": "./dist/utils.js",
"types": "./dist/utils.d.ts"
}
},
"scripts": {
"build": "bun build ./src/index.ts --outdir ./dist --target bun",
"typecheck": "tsc --noEmit"
},
"peerDependencies": {
"typescript": "^5.0.0"
}
}
// packages/backend/src/index.ts
import { validateUser, UserSchema } from "@myorg/shared";
import type { User } from "@myorg/shared/types";
const user = validateUser(data);
{
"scripts": {
"typecheck": "tsc --noEmit -p tsconfig.json",
"typecheck:all": "bun --filter '*' typecheck"
}
}
# Migrate from binary lockfile
bun install --save-text-lockfile --frozen-lockfile --lockfile-only
rm bun.lockb
Bun automatically migrates from:
package-lock.json (npm)yarn.lock (Yarn v1)pnpm-lock.yaml (pnpm)# Install and migrate lockfile
bun install
Force specific versions for transitive dependencies:
{
"overrides": {
"lodash": "4.17.21",
"axios": "^1.6.0"
}
}
Or Yarn-style:
{
"resolutions": {
"lodash": "4.17.21"
}
}
- uses: oven-sh/setup-bun@v1
with:
bun-version: latest
- run: bun install --frozen-lockfile
- run: bun --filter '*' typecheck
- run: bun --filter '*' build
- run: bun --filter '*' test
FROM oven/bun:1
WORKDIR /app
# Copy lockfile first for caching
COPY bun.lock package.json ./
COPY packages/*/package.json ./packages/
RUN bun install --frozen-lockfile
COPY . .
RUN bun --filter '*' build
private: true on root package.jsonworkspace:*) for internal deps--frozen-lockfile in CIbun.lock to version controlbun --filter '*'You are an elite AI agent architect specializing in crafting high-performance agent configurations. Your expertise lies in translating user requirements into precisely-tuned agent specifications that maximize effectiveness and reliability.