插件可以捆绑一个 @wordpress/* 包,同时从 WordPress 加载该包的依赖项。这样,被捆绑的包与其 WordPress 依赖项就可能来自不同的版本。

例如,插件可能捆绑了较新的 @wordpress/dataviews 包,却运行在提供较旧 @wordpress/components 包的 WordPress 版本上。反过来也可能发生:较旧的插件捆绑包可能运行在较新的 WordPress 版本上。

当更改影响到可以单独更新的部分时,请使用本指南,例如:

  • 公共或私有 API。
  • 包依赖项或入口点。
  • 包注册或某种检查(例如允许列表或选择性加入机制),用于决定入口点能否加载。
  • 兼容性回退。

如果包及其所有依赖项总是一同发布,则无需使用四版本测试矩阵。但公共 API 更改仍需要进行下文的现有消费者检查。

梳理生产环境中运行的内容

首先记录以下信息:

  • 插件或应用程序的捆绑包中包含哪些包。
  • 哪些依赖项是从 WordPress 加载的。
  • 插件或应用程序支持的最旧 WordPress 版本。
  • 仍在维护的插件或应用程序仍在使用的较旧捆绑包版本。

检查每个受影响的入口点。入口点是一条拥有独立构建产物的导入路径,例如主包导入或 /wp 导入。

检查包元数据、构建设置、生成的资源文件以及依赖项提取。依赖项提取决定了捆绑包会将哪些包留给 WordPress 加载。

不要仅依赖包名称或源码导入。它们无法显示生产构建中实际包含的内容。发布新包也不会更新已经部署的捆绑包。

选择要测试的版本

对于旧包,请从更改之前的最后一个发布版本开始。当仍在维护的消费者仍在使用更早的版本时,请将这些版本也纳入测试。

对于 WordPress,请从最旧的受支持版本以及包含所提议更改的版本开始。当中间版本暴露不同的 API 或加载不同的入口点时,请将这些中间版本也加入测试。

如果某个组合超出了支持政策的范围,请明确说明。并记录消费者应改用的包版本、入口点或 WordPress 版本。

测试现有消费者

对于公共 API 更改,请结合候选包使用具有代表性的消费者代码:

  1. 依据该包已发布的 TypeScript 声明对其进行编译。
  2. 使用其常规构建设置进行构建。
  3. 执行已更改的行为。

更新 Gutenberg 仓库内的调用点可以证明新 API 可用,但并不能证明现有消费者仍然可用或拥有迁移路径。

测试四种版本组合

当 WordPress 提供依赖项时,请测试两种混合版本组合:

捆绑包WordPress 依赖项要确认的内容
旧旧现有行为仍然有效。
旧新新依赖项仍支持旧捆绑包。
新旧新捆绑包在每个受支持的 WordPress 版本上都能正常工作。
新新新代码按预期工作。

使用已构建的包进行这些检查。未构建的源码并非消费者所安装的代码。

检查构建

对于每个必需的组合和入口点:

  1. 在隔离的消费者项目中安装确切的包版本。
  2. 依据其已发布的 TypeScript 声明进行编译。
  3. 使用消费者的真实依赖项提取设置进行构建。
  4. 检查生成的依赖项和导出的 API。
  5. 在相应的 WordPress 版本中,确认包注册和其他加载检查会选择预期的入口点。

检查行为

在相应的 WordPress 版本上运行受影响的行为。如果更改添加了回退路径,请将其包含在内。

还要检查必须来自同一个包副本的状态。React 上下文、私有 API 锁、注册表和符号都是常见的例子。相同的导出并不会让各个独立的包副本共享相同的状态。

将每个必需的组合记录为 pass、fail 或 unverified。源码测试或 mock 可以显示运行的是哪个分支,但无法重现依赖项提取、独立的包副本或较旧的 WordPress 运行时。

按安全顺序发布更改

两种混合版本组合需要不同的保护措施:

  • 新捆绑包搭配旧 WordPress:在使用 API 之前先检查它是否存在,提供回退方案,或者明确提高受支持的最低 WordPress 版本。
  • 旧捆绑包搭配新 WordPress:保留旧 API 或兼容路径,直到仍在维护的消费者不再需要它为止。

当新旧 API 可以共存时,请按以下顺序发布:

  1. 将新 API 添加到由 WordPress 提供的包中。在受支持的捆绑包仍需要旧 API 期间保留它。
  2. 更新捆绑的包,使其优先使用新 API,并在较旧的 WordPress 版本未提供该 API 时进行回退。
  3. 防止新代码在回退路径之外使用旧 API。
  4. 在移除任一兼容路径之前,请先发布所需的 npm 版本和 WordPress 版本。

通过检查 API 是否存在来进行判断,而不是比较版本字符串。将回退逻辑集中保留在捆绑包中的一个位置。

将私有兼容 API 和捆绑包的回退作为相互独立的发布更改分别移除。在每次移除之前,重复进行消费者检查和版本检查。计划移除的版本只是一个审查节点,并不能证明已部署的捆绑包已经完成更新。

区分公共 API 与私有 API

生产环境的公共 API 遵循向后兼容政策。

新捆绑的包不得使用私有 API。仓库的 lint 检查和捆绑包检查会标记新增的直接和间接使用,但它们无法更改已经部署的旧捆绑包。

私有 API 可以被移除。临时的兼容路径不会将私有 API 变为公共 API。仅在受支持的旧捆绑包需要它时才保留它。在替代方案发布且必需的组合通过测试之后再将其移除,或者明确说明支持方面的变更。

识别包身份问题

导出回退无法修复包身份问题。由一个包副本创建的 React 上下文无法为使用另一个副本的消费者提供值。由一个副本创建的私有 API 锁也具有同样的限制。

对于这些情况,请使用不共享包局部状态的 API。否则,请更改入口点或受支持的 WordPress 版本,使双方使用同一个包副本。

Gutenberg 中的示例

ThemeProvider

#78958 中的 ThemeProvider 更改在同一版本中将该 API 公开并移除了其私有路径。已发布的捆绑包随即损坏。

#79594 恢复了私有路径。随后 #79620 在消费者迁移期间同时保留了两个路径。

DataViews

#81230 中的 DataViews 清理讨论发现了相反的问题。较新的 WordPress 运行时移除私有 @wordpress/components API 之后,较旧的捆绑 @wordpress/dataviews 包仍可能请求该 API。

#82221 为这些较旧的捆绑包恢复了选择性加入机制。但捆绑的 @wordpress/private-apis 副本仍然无法解锁由不同运行时副本创建的对象。