Skip to content
📚 4 min read

Monorepo Management ​

Monorepos allow you to manage multiple packages in a single repository. This guide covers best practices and tools for managing JavaScript monorepos effectively.

Workspace Setup ​

npm Workspaces ​

json
// package.json
{
  "name": "my-monorepo",
  "private": true,
  "workspaces": ["packages/*", "apps/*"]
}

Yarn Workspaces ​

yaml
# .yarnrc.yml
nodeLinker: node-modules
plugins:
  - path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs
    spec: '@yarnpkg/plugin-workspace-tools'

pnpm Workspaces ​

yaml
# pnpm-workspace.yaml
packages:
  - 'packages/*'
  - 'apps/*'
  - '!**/test/**'

Project Structure ​

bash
monorepo/
├── package.json
├── packages/
│   ├── shared/
│   │   ├── package.json
│   │   └── src/
│   └── utils/
│       ├── package.json
│       └── src/
├── apps/
│   ├── web/
│   │   ├── package.json
│   │   └─ src/
│   └── api/
│       ├── package.json
│       └── src/
└── tools/
    └── scripts/

Package Management ​

Local Dependencies ​

json
// packages/web/package.json
{
  "name": "@myorg/web",
  "dependencies": {
    "@myorg/shared": "workspace:*",
    "@myorg/utils": "workspace:^1.0.0"
  }
}

Installation Commands ​

bash
# npm
npm install
npm install --workspace=web
npm install lodash --workspace=web

# yarn
yarn install
yarn workspace web add lodash
yarn workspaces foreach install

# pnpm
pnpm install
pnpm --filter web install
pnpm --filter web add lodash

Script Execution ​

Running Scripts ​

bash
# npm
npm run test --workspaces
npm run build --workspace=web

# yarn
yarn workspaces foreach run test
yarn workspace web run build

# pnpm
pnpm -r run test
pnpm --filter web run build

Parallel Execution ​

bash
# npm
npm run test --workspaces --parallel

# yarn
yarn workspaces foreach -p run test

# pnpm
pnpm -r --parallel run test

Version Management ​

Independent Versions ​

json
// lerna.json
{
  "version": "independent",
  "packages": ["packages/*"],
  "command": {
    "version": {
      "allowBranch": "main"
    }
  }
}

Fixed Versions ​

json
// lerna.json
{
  "version": "1.0.0",
  "packages": ["packages/*"],
  "command": {
    "version": {
      "conventionalCommits": true
    }
  }
}

Build Systems ​

Turborepo ​

json
// turbo.json
{
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": ["dist/**"]
    },
    "test": {
      "dependsOn": ["build"],
      "outputs": []
    }
  }
}

Nx ​

json
// nx.json
{
  "tasksRunnerOptions": {
    "default": {
      "runner": "nx/tasks-runners/default",
      "options": {
        "cacheableOperations": ["build", "test"]
      }
    }
  }
}

Dependencies Management ​

Shared Dependencies ​

json
// package.json (root)
{
  "dependencies": {
    "react": "^18.0.0",
    "react-dom": "^18.0.0"
  },
  "devDependencies": {
    "typescript": "^5.0.0",
    "jest": "^29.0.0"
  }
}

Dependency Hoisting ​

json
// .npmrc
hoist=true
shamefully-hoist=true

// .yarnrc.yml
nodeLinker: node-modules

Development Workflow ​

Git Hooks ​

json
// package.json
{
  "scripts": {
    "prepare": "husky install",
    "pre-commit": "lint-staged"
  },
  "lint-staged": {
    "**/*.{js,ts,tsx}": ["eslint --fix", "prettier --write"]
  }
}

Task Running ​

json
// package.json
{
  "scripts": {
    "dev": "turbo run dev",
    "build": "turbo run build",
    "test": "turbo run test",
    "lint": "turbo run lint"
  }
}

CI/CD Integration ​

GitHub Actions ​

yaml
name: CI
on: [push]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
      - uses: actions/cache@v3
        with:
          path: |
            node_modules
            */*/node_modules
          key: ${{ runner.os }}-modules-${{ hashFiles('**/package-lock.json') }}
      - run: npm ci
      - run: npm test --workspaces

CircleCI ​

yaml
version: 2.1
jobs:
  build:
    docker:
      - image: cimg/node:18.0
    steps:
      - checkout
      - restore_cache:
          keys:
            - deps-{{ checksum "package-lock.json" }}
      - run: npm ci
      - save_cache:
          key: deps-{{ checksum "package-lock.json" }}
          paths:
            - node_modules
      - run: npm test --workspaces

Best Practices ​

1. Project Organization ​

  • Use consistent package structure
  • Share configuration files
  • Implement clear dependency boundaries
  • Maintain documentation

2. Dependency Management ​

  • Use workspace protocols
  • Implement hoisting strategy
  • Share common dependencies
  • Regular dependency updates

3. Build Process ​

  • Implement incremental builds
  • Use build caching
  • Optimize CI/CD pipeline
  • Maintain clean artifacts

4. Development Experience ​

  • Consistent tooling
  • Shared configurations
  • Clear documentation
  • Efficient local development

Common Patterns ​

Shared Configurations ​

json
// packages/tsconfig/base.json
{
  "compilerOptions": {
    "target": "es2019",
    "module": "esnext",
    "moduleResolution": "node",
    "strict": true
  }
}

// packages/web/tsconfig.json
{
  "extends": "@myorg/tsconfig/base.json",
  "compilerOptions": {
    "outDir": "./dist"
  }
}

Package Aliases ​

json
// tsconfig.json
{
  "compilerOptions": {
    "paths": {
      "@myorg/*": ["packages/*/src"]
    }
  }
}

Shared ESLint Config ​

javascript
// packages/eslint-config/index.js
module.exports = {
  extends: ['airbnb', 'prettier'],
  rules: {
    // shared rules
  },
};