執行環境套件管理器打包器測試執行器程式碼檢查格式化指南參考部落格意見回饋

目錄

在 monorepo 中跨多個包共享通用相依版本

目錄可在 monorepo 中的各個包之間共享相依版本。無需在每個工作區包中重複指定相同的版本,只需在根目錄的 package.json 中定義一次,然後在整個專案中引用即可。

概述

無需讓每個工作區軟體包單獨指定自己的版本,你可以:

  1. 在根目錄的 package.json 中定義版本目錄
  2. 使用 catalog: 協議引用這些版本
  3. 只需在一個地方更改版本,即可一次性更新所有軟體包

目錄在大型 monorepo 中最為重要,因為其中數十個包都相依相同版本的關鍵相依項。

如何使用目錄

目錄結構範例

假設有如下結構的 monorepo:

Code
my-monorepo/
├── package.json
├── bun.lock
└── packages/
    ├── app/
    │   └── package.json
    ├── ui/
    │   └── package.json
    └── utils/
        └── package.json

1. 在根目錄 package.json 中定義目錄

在根目錄的 package.json 中,在 workspaces 物件內新增 catalog 或 catalogs 欄位:

package.json
{
  "name": "my-monorepo",
  "workspaces": {
    "packages": ["packages/*"],
    "catalog": {
      "react": "^19.0.0",
      "react-dom": "^19.0.0"
    },
    "catalogs": {
      "testing": {
        "jest": "30.0.0",
        "testing-library": "14.0.0"
      }
    }
  }
}

catalog 和 catalogs 也可以在 package.json 的頂層使用。

2. 在工作區包中引用目錄版本

在工作區包中,使用 catalog: 協議引用版本:

packages/app/package.json
{
  "name": "app",
  "dependencies": {
    "react": "catalog:",
    "react-dom": "catalog:",
    "jest": "catalog:testing"
  }
}
packages/ui/package.json
{
  "name": "ui",
  "dependencies": {
    "react": "catalog:",
    "react-dom": "catalog:"
  },
  "devDependencies": {
    "jest": "catalog:testing",
    "testing-library": "catalog:testing"
  },
  "peerDependencies": {
    "react": "catalog:"
  }
}

catalog: 引用可在 dependencies、devDependencies、optionalDependencies、peerDependencies 中使用,也可作為根目錄 overrides 規則的值使用。目錄引用的行為與直接內聯目錄範圍完全相同。

3. 執行 Bun Install

執行 bun install,即可根據目錄中定義的版本安裝所有相依。

Catalog 與 Catalogs 的區別

Bun 支援兩種定義目錄的方式:

  1. catalog(單數):用於常用相依的單一預設目錄

    package.json
    "catalog": {
      "react": "^19.0.0",
      "react-dom": "^19.0.0"
    }
    

    使用 catalog: 引用:

    packages/app/package.json
    "dependencies": {
      "react": "catalog:"
    }
    
  2. catalogs(複數):用於分組相依的多個命名目錄

    package.json
    "catalogs": {
      "testing": {
        "jest": "30.0.0"
      },
      "ui": {
        "tailwind": "4.0.0"
      }
    }
    

    使用時需要指定目錄名,格式為 catalog:<name>:

    packages/app/package.json
    "dependencies": {
      "jest": "catalog:testing",
      "tailwind": "catalog:ui"
    }
    

使用目錄的好處

  • 一致性:所有軟體包使用關鍵相依項的相同版本
  • 維護性:只需在一個地方更新相依項版本,而不必修改多個 package.json 檔案
  • 清晰性:清楚表明 monorepo 中哪些相依項已實現標準化
  • 簡潔性:無需額外的版本解析策略或外部工具

真實範例

一個更大的範例,適用於 React 應用:

根目錄 package.json

package.json
{
  "name": "react-monorepo",
  "workspaces": {
    "packages": ["packages/*"],
    "catalog": {
      "react": "^19.0.0",
      "react-dom": "^19.0.0",
      "react-router-dom": "^6.15.0"
    },
    "catalogs": {
      "build": {
        "webpack": "5.88.2",
        "babel": "7.22.10"
      },
      "testing": {
        "jest": "29.6.2",
        "react-testing-library": "14.0.0"
      }
    }
  },
  "devDependencies": {
    "typescript": "5.1.6"
  }
}
packages/app/package.json
{
  "name": "app",
  "dependencies": {
    "react": "catalog:",
    "react-dom": "catalog:",
    "react-router-dom": "catalog:",
    "@monorepo/ui": "workspace:*",
    "@monorepo/utils": "workspace:*"
  },
  "devDependencies": {
    "webpack": "catalog:build",
    "babel": "catalog:build",
    "jest": "catalog:testing",
    "react-testing-library": "catalog:testing"
  }
}
packages/ui/package.json
{
  "name": "@monorepo/ui",
  "dependencies": {
    "react": "catalog:",
    "react-dom": "catalog:"
  },
  "devDependencies": {
    "jest": "catalog:testing",
    "react-testing-library": "catalog:testing"
  }
}
packages/utils/package.json
{
  "name": "@monorepo/utils",
  "dependencies": {
    "react": "catalog:"
  },
  "devDependencies": {
    "jest": "catalog:testing"
  }
}

更新版本

要在所有包中更新版本,只需在根目錄 package.json 中更改版本:

package.json
"catalog": {
  "react": "^19.1.0",  // 從 ^19.0.0 更新
  "react-dom": "^19.1.0"  // 從 ^19.0.0 更新
}

然後執行 bun install 即可更新所有包。

使用 bun add 新增到目錄

bun add --catalog 會將條目新增到根目錄目錄中,並在當前包中寫入 "catalog:";--catalog=<name> 會將條目新增到 catalogs.<name> 中,並寫入 "catalog:<name>"。除非傳入顯式版本,否則 Bun 會重複使用現有目錄條目。參見 bun add --catalog。

即使不使用該標誌,bun add react(不帶版本)也會在預設目錄已列出 react 時寫入 "catalog:"。傳入版本則會改為寫入具體的範圍。

鎖檔案整合

Bun 的鎖檔案會跟蹤目錄版本,因此可以確保不同環境中的安裝結果一致。鎖檔案包含:

  • package.json 中的目錄定義
  • 每個目錄相依的解析結果
bun.lock(excerpt)
{
  "lockfileVersion": 2,
  "workspaces": {
    "": {
      "name": "react-monorepo",
    },
    "packages/app": {
      "name": "app",
      "dependencies": {
        "react": "catalog:",
        "react-dom": "catalog:",
        ...
      },
    },
    ...
  },
  "catalog": {
    "react": "^19.0.0",
    "react-dom": "^19.0.0",
    ...
  },
  "catalogs": {
    "build": {
      "webpack": "5.88.2",
      ...
    },
    ...
  },
  "packages": {
    ...
  }
}

限制與注意事項

  • 目錄引用必須對應 catalog 或某個命名 catalogs 中定義的相依項
  • Bun 會忽略目錄名稱中的空字串和空白字元,並將其視為預設目錄
  • catalog:default 與 catalog: 相同。你可以通過 catalog 或 catalogs.default 定義預設目錄,但同一個包同時列在兩者中會報錯
  • 目錄中的無效相依版本會導致 bun install 解析失敗
  • catalog: 僅適用於根目錄和工作區的 package.json 檔案。在已釋出的包中使用時會導致解析失敗。使用 bun publish 或 bun pm pack 釋出,這些命令會將其替換為實際範圍(參見釋出)

釋出

當你執行 bun publish 或 bun pm pack 時,Bun 會將 package.json 中的 catalog: 引用替換為解析後的版本號。釋出的包包含常規的 semver 字串,不再相依你的 catalog 定義。