运行时包管理器打包器测试运行器代码检查格式化指南参考博客反馈

目录

在 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 定义。