news 2026/9/16 15:32:51

Gutenberg 仓库 Workspace 开发指南:基于 npm workspaces 的内部工作区新建、注册与日常依赖管理

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Gutenberg 仓库 Workspace 开发指南:基于 npm workspaces 的内部工作区新建、注册与日常依赖管理

Gutenberg 仓库 Workspace 开发指南:基于 npm workspaces 的内部工作区新建、注册与日常依赖管理

【免费下载链接】gutenbergThe Block Editor project for WordPress and beyond. Plugin is available from the official repository.项目地址: https://gitcode.com/GitHub_Trending/gu/gutenberg

导读

Gutenberg 仓库不仅发布packages/*下的@wordpress/*npm 包,还通过 npm workspaces 为主体,结合根 package.json、tsconfig.json 与各工作区真实的package.json源码,完整讲解:为什么内部依赖要落在工作区而非根依赖、工作区分布在哪里、如何从零新建一个工作区、以及日常如何增删依赖与运行脚本。读完你就能按仓库规范安全地扩展这套 monorepo,并理解 CI 与本地命令为何要保持一致。

为什么依赖应放在工作区,而不是根 package.json

monorepo 根目录的package.json是仓库的"总指挥",但任何添加进根devDependencies的依赖,都会隐式地对每一个工作区可见。这会让"某个工作区到底依赖什么"变得模糊不清。当前仓库刻意保持根依赖精简——根 package.json 只保留仓库级工具链(huskylernalint-stagedsyncpackpatch-packageconcurrentlycross-envwait-on以及@wordpress/scripts@wordpress/env@wordpress/eslint-tools@wordpress/monorepo-tools@wordpress/release-tools@wordpress/stylelint-tools等内部工具),页面级代码与测试所需的依赖全部下沉到各自工作区。

把依赖放进工作区而非根依赖,主要有四个好处:

  • 关注点分离(Separation of concerns):每个工作区在自己的package.json中声明其真正需要的依赖,代码与依赖的对应关系一目了然。
  • 根目录更干净(Cleaner root):根package.json只承载 lint、format、类型检查、git hooks 与 monorepo 编排,依赖变更的 review 变得更轻松。
  • 更少的合并冲突(Fewer merge conflicts):贡献者更新某个工作区的依赖时无需触碰根package.json
  • 防止"幽灵依赖"(Phantom dependency prevention):npm 采用提升(hoisting)安装策略,依赖会被提升到根node_modules,一个工作区可能"碰巧" import 到它从未声明的包。保持根目录精简,能让这种依赖关系保持诚实,也是未来迁移到隔离依赖方案(仓库讨论中的 pnpm 迁移)的前提——届时工作区只能看到自己声明的依赖。

因此,"这个依赖该放哪?"的默认答案永远是工作区,而不是根。如果下意识想往根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/*测试基础设施(unit、integration、e2e、performance、storybook-playwright 等)
storybookGutenberg Storybook 宿主
routes/*编辑器路由入口点
widgets/*组件面板(widget)打包产物

以仓库实际内容核对:tools/下现有agentsdocseslintmonorepopr-metareact-18react-19releasestylelintvalidation等工作区;test/下现有ai-developmente2eintegrationperformancephpstorybook-playwrightunit等。

这些 glob 的注册集合就写在根 package.json 的"workspaces"数组中:

"workspaces": [ "packages/*", "routes/*", "storybook", "test/*", "tools/*", "widgets/*", "!test/ai-development" ]

两点值得注意:

  • 任何被现有 glob 匹配到的目录(例如tools/*),只要其内部出现了package.json,就会被 npm自动识别为工作区,无需额外注册;
  • 数组中的!test/ai-development是一条排除规则,用于把该目录从test/*的匹配中剔除,说明test/ai-development虽然放在test/下,却是独立管理依赖的(根脚本中test:agent-evals使用npm --prefix test/ai-development run eval --单独调用)。

创建一个新工作区的完整步骤

该模式在 #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。仓库中的 tools/monorepo/package.json、tools/validation/package.json、test/unit/package.json 均是此写法,尽管它们同时声明了publishConfig.accessprivate字段仍从根上禁止发布;
  • 只声明该工作区实际 import 或执行的依赖。例如 tools/validation/package.json 只列出校验脚本真正用到的chalkglobjsonc-parsersimple-git等;
  • 要依赖 monorepo 内的另一个工作区,使用file:引用。例如 tools/monorepo/package.json 中"@wordpress/data": "file:../../packages/data",根 package.json 中"@wordpress/scripts": "file:./packages/scripts""@wordpress/monorepo-tools": "file:./tools/monorepo"也是同一机制——file:让 npm 直接链接本地目录而不是去 registry 拉取。

第 2 步:(可选)添加tsconfig.json

如果工作区包含 TypeScript,应继承共享基础配置:

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

这里的@wordpress/monorepo-tools/tsconfig/base.json是真实存在的共享基座,见 tools/monorepo/tsconfig/base.json:它开启strictnoImplicitReturnscompositedeclarationdeclarationMapisolatedModules等严格选项,并约定rootDirdeclarationDir,其可被引用的路径正是通过 tools/monorepo/package.json 的exports字段("./tsconfig/*": "./tsconfig/*")暴露出来的。

随后,需要在根 tsconfig.json 的references数组中为新工作区添加一条项目引用(project reference),例如{ "path": "tools/pr-meta" }。从仓库现状看,根 tsconfig 的 references 覆盖了routes/*storybooktest/e2etest/performancetest/storybook-playwrightwidgets以及tools/pr-meta等目录,可以推断并非每个工作区都要求有 TypeScript 项目引用,只有含 TS 源码且需要被类型检查覆盖的工作区才需要。

第 3 步:注册工作区(如需要)

如果新工作区位于根package.jsonworkspaces已有 glob 覆盖的路径下(例如tools/*),它会被自动注册。否则,需要在根package.jsonworkspaces数组中新增一条记录。当前仓库对tools/*test/*routes/*widgets/*均已配置 glob,因此绝大多数新建工作区无需改数组;只有在路径超出既有模式(如test/ai-development这类需要隔离的情况)时才会动它。

第 4 步:从仓库根暴露脚本

将贡献者应从根目录运行的脚本用npm run --workspace转发:

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

结尾的--用于把额外的 CLI 参数透传给工作区脚本,例如npm run my-task -- --watch。这在根 package.json 中是贯穿全篇的惯例:

  • "test:unit": "npm run --workspace @wordpress/unit-tests test:unit --"
  • "lint:lockfile": "npm run --workspace @wordpress/validation-tools validate-package-lock --"
  • "docs:api-ref": "npm run --workspace @wordpress/docs-tools docs:api-ref --"
  • "agents:setup": "npm run --workspace @wordpress/agent-tools setup --"
  • "storybook:build": "npm run --workspace @wordpress/storybook storybook:build"

第 5 步:添加 README

在工作区目录中放置一个README.md,说明该工作区的职责、暴露的脚本以及任何不显而易见的设置。例如test/unit工作区的 homepage 字段就指向其 README(见 test/unit/package.json)。

第 6 步:更新 CI 工作流

.github/workflows/下的 CI 工作流应通过第 4 步建立的根npm run包装调用工作区,而不是cd进工作区目录再执行。这样贡献者在本地运行的命令与 CI 完全一致:

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

仓库实况可以印证这一点:在 .github/workflows/unit-test.yml 中,Jest 与 Vitest 分区分别通过npm run test:unit -- ...npm run test:unit:vitest:shuffled -- ...调用,最终落到@wordpress/unit-tests工作区的脚本(见 test/unit/package.json 的test:unittest:unit:vitest等定义),而 CI 从不直接进入工作区目录执行。

日常开发中的工作区操作

增删某个工作区的依赖

始终把依赖变更限定在使用它的工作区:

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>

--if-present保证没有定义该脚本的工作区被安静跳过。根 package.json 的prelint:js就是这一模式的范例:

"prelint:js": "npm run --if-present --workspaces prelint:js"

深入验证:一个真实工作区的解剖

tools/monorepo为例,把整套规范串起来看:

  • 它叫@wordpress/monorepo-tools"private": true(见 tools/monorepo/package.json),符合"内部工具不发布"的定位;
  • 它通过file:引用同仓库的@wordpress/data包(tools/monorepo/package.json),是"工作区间依赖"的标准写法;
  • 它通过exports暴露./tsconfig/*子路径(tools/monorepo/package.json),使得其他工作区能用@wordpress/monorepo-tools/tsconfig/base.json继承共享 TS 配置;
  • package.json"@wordpress/monorepo-tools": "file:./tools/monorepo"(根 package.json)把它引入根工具链,同时多个根脚本(如lint:tsconfiglint:lockfile)通过npm run --workspace @wordpress/validation-tools之类的方式调用工作区脚本。

这恰好覆盖了"新建工作区六步"中的每一步:package.jsontsconfig基座、file:依赖、根脚本转发,以及 CI 中经根脚本统一调用的约定。当你在 Gutenberg monorepo 中新增或修改内部工作区时,这套模式就是唯一需要遵循的规范。

结语

Gutenberg 的 workspace 体系本质上是"用 npm workspaces 把一个巨大的 JS/TS monorepo 拆成职责清晰的小单元":根package.json只做编排,所有功能代码与测试基础设施各自声明依赖、各自暴露脚本。掌握 docs/contributors/code/workspace-development.md 中这套"默认放工作区、六步新建、根脚本转发、CI 对齐"的约定后,你既能安全地为仓库添加新工具或测试工作区,也能在提交依赖变更时避免无谓的合并冲突与幽灵依赖问题。

【免费下载链接】gutenbergThe Block Editor project for WordPress and beyond. Plugin is available from the official repository.项目地址: https://gitcode.com/GitHub_Trending/gu/gutenberg

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/16 15:30:50

Humanizer 去 AI 味实测:两轮改写跑完,再用自查清单把关

Humanizer 去 AI 味实测&#xff1a;两轮改写跑完&#xff0c;再用自查清单把关 【免费下载链接】humanizer Agent skill that removes signs of AI-generated writing from text 项目地址: https://gitcode.com/GitHub_Trending/humani/humanizer 改前&#xff1a;AI 编…

作者头像 李华
网站建设 2026/9/16 15:29:40

sccache 缓存机制详解:哈希键的生成原理与预处理器缓存模式

sccache 缓存机制详解&#xff1a;哈希键的生成原理与预处理器缓存模式 【免费下载链接】sccache Sccache is a ccache-like tool. It is used as a compiler wrapper and avoids compilation when possible. Sccache has the capability to utilize caching in remote storage…

作者头像 李华
网站建设 2026/9/16 15:29:07

STM32 USB CDC虚拟串口配置

一、STM32内置USB虚拟串口简述 USB虚拟串口&#xff0c;简称VPC&#xff0c;Virtual Port Com 的简写。但更习惯于把虚拟串口叫作: CDC&#xff0c;因为它是利用 USB 的 CDC类 实现的一种通信接口。 1.1 为什么使用USB虚拟串口 在嵌入式开发中&#xff0c;串口&#xff08;UAR…

作者头像 李华
网站建设 2026/9/16 15:27:50

卡密社区SUP系统总控与主站分销架构设计:签名鉴权与幂等实践

简介&#xff1a;一套完整的卡密社区SUP系统总控与主站分销源码&#xff0c;专为需要搭建卡密自助交易、分站分销业务的开发者或站长准备。系统涵盖总控端、主站后台和分站后台三套管理界面&#xff0c;可实现系统商模式的平台分配、卡密发行、分站开通与API对接&#xff0c;配…

作者头像 李华
网站建设 2026/9/16 15:27:50

网页设计大作业成品源代码:从模板修改到答辩演示的完整指南

简介&#xff1a;面向高校网页设计课程或大作业提交场景&#xff0c;这套多选一成品源码包提供了数套风格与难度各异的网页设计作品&#xff0c;涵盖网页制作基础课程作业、Web大作业、期末6页面个人主页等常见课题&#xff0c;并涉及Dreamweaver工具实践、视频嵌入、脚本交互等…

作者头像 李华