Gutenberg 仓库是一个 npm workspaces monorepo。除了位于 packages/ 目录下已发布的 @wordpress/* 包之外,该仓库还包含用于开发工具的内部工作区(tools/*)、测试基础设施(test/*)以及相关项目(storybook、widgets/*、routes/*)。

本指南将说明如何添加或修改这些内部工作区,以及为什么依赖项应放在工作区中而不是根目录的 package.json 文件中。

为什么要使用工作区,而不是根依赖

添加到根 package.json 中的任何内容都会隐式地对仓库中的每个工作区可用,这使得很难分辨哪个工作区实际上需要某个特定的依赖项。保持根目录精简有以下几个好处:

  • 关注点分离。 每个工作区声明其运行所需的依赖项,使代码与依赖项之间的关系更加明确。
  • 更干净的根目录。 根 package.json 仅包含全仓库范围的工具(lint、format、type-check、git hooks、monorepo 编排)。当根目录较小时,审查依赖项变更会更容易。
  • 更少的合并冲突。 贡献者可以在不触及根目录的情况下更新某个工作区的依赖项。
  • 防止幻影依赖。 该仓库使用 npm 的隔离安装策略(在 .npmrc 中设置 install-strategy = "linked",于 #83847 中采用)。依赖项存储在 node_modules/.store/ 中,并仅符号链接到声明了它们的工作区中,因此工作区无法导入它从未声明过的依赖项。根依赖是例外:Node 仍然可以通过向上查找至根 node_modules 来找到它们,这也是保持根目录精简的另一个原因。

对于“这个依赖项应该放在哪里?”这个问题,默认答案是一个工作区,而不是根目录。如果你发现自己想要修改根 package.json,请考虑该依赖项是否可以存在于:

  • tools/ 或 test/ 下的一个现有工作区,该工作区已经涵盖了用例(例如,@wordpress/eslint-tools、@wordpress/release-tools、@wordpress/validation-tools、@wordpress/unit-tests)。
  • 如果没有现有的合适工作区,则在 tools/ 下创建一个新工作区(如果是测试基础设施,则在 test/ 下创建)。

工作区的位置

位置用途
packages/*已发布的 @wordpress/* npm 包。参见 Managing Packages。
tools/*内部开发工具(ESLint 配置、发布 CLI、API 文档生成器、验证等)。未发布到 npm。
test/*测试基础设施(单元、集成、e2e、性能、原生、storybook-playwright)。
storybookGutenberg Storybook 主机。
routes/*编辑器路由入口点。
widgets/*小部件捆绑包。

注册的工作区通配符集合位于根 package.json 的 "workspaces" 数组中。一旦匹配现有通配符(例如,tools/*)的任何目录中包含 package.json,它就会被自动拾取。

创建一个新的工作区

此模式确立于 #74640(Storybook 转换),并作为后续转换的模型。

1. 在工作区目录中添加 package.json

对于未发布到 npm 的内部工具:

{
    "name": "@wordpress/<workspace-name>",
    "version": "0.0.0",
    "description": "<short description>",
    "private": true,
    "author": "The WordPress Contributors",
    "license": "GPL-2.0-or-later",
    "homepage": "https://github.com/WordPress/gutenberg/tree/HEAD/tools/<workspace-name>",
    "repository": {
        "type": "git",
        "url": "git+https://github.com/WordPress/gutenberg.git",
        "directory": "tools/<workspace-name>"
    },
    "bugs": {
        "url": "https://github.com/WordPress/gutenberg/issues"
    },
    "devDependencies": {},
    "scripts": {}
}

注意事项:

  • 对于永不应发布的工作区,请使用 "private": true。
  • 仅列出该工作区实际导入或执行的依赖项。
  • 要依赖于 monorepo 中的另一个工作区,请使用 file: 引用(例如,"@wordpress/scripts": "file:../../packages/scripts")。

2. (可选)添加 tsconfig.json

如果工作区包含 TypeScript,请扩展共享基础配置:

{
    "extends": "@wordpress/monorepo-tools/tsconfig/base.json",
    "compilerOptions": {
        "rootDir": "./",
        "outDir": "./build"
    },
    "include": [ "./**/*.ts" ]
}

然后在根 tsconfig.json 中为新工作区添加项目引用。

3. 注册工作区(如果需要)

如果新工作区位于根 package.json 中通配符已覆盖的路径下(例如,tools/*),它将自动注册。否则,请在根 package.json 的 workspaces 中添加一个条目。

4. 从仓库根目录暴露脚本

使用 npm run --workspace 转发贡献者应从仓库根目录运行的任何脚本:

"scripts": {
    "my-task": "npm run --workspace @wordpress/<workspace-name> my-task --"
}

尾部的 -- 会将额外的 CLI 参数转发给工作区脚本(例如,npm run my-task -- --watch)。

5. 添加 README

在工作区目录中包含一个 README.md,描述工作区的功能、它暴露的脚本以及任何非显而易见的设置。

6. 更新 CI 工作流程

.github/workflows/ 下的 CI 工作流程应通过步骤 4 中添加的根 npm run 包装器调用工作区,而不是 cd 进入工作区目录。保持 CI 与根脚本一致意味着贡献者在本地运行与 CI 相同的命令。

- run: npm ci
- run: npm run my-task

日常工作流中使用工作区

在工作区中添加或删除依赖项

始终将依赖项更改限定在使用它们的工作区内:

npm install <package> --workspace @wordpress/<workspace-name>
npm uninstall <package> --workspace @wordpress/<workspace-name>

这些命令会更新工作区的 package.json 和根 package-lock.json,而不会触及根 package.json。

运行工作区脚本

从仓库根目录:

npm run <script> --workspace @wordpress/<workspace-name>

或者,如果存在根级转发脚本(见上述步骤 4):

npm run <root-script>

在所有工作区中运行脚本

要在定义它的每个工作区中运行相同的脚本:

npm run --if-present --workspaces <script>

根 prelint:js 脚本就是这种模式的一个示例。