将 Puppeteer 迁移到 TypeScript

杰克·富兰克林
Jack Franklin

我们是开发者工具团队的 TypeScript 的忠实粉丝,以至于在开发者工具中编写新代码,我们正在将整个代码库转变为由 TypeScript 进行类型检查。如需详细了解此次迁移,请参阅 2020 年 Chrome 开发者峰会上的演讲。因此,您也可以考虑将 Puppeteer 的代码库迁移到 TypeScript。

计划迁移

在规划如何迁移时,我们希望能够轻而易举地取得进展。这样可以降低迁移开销(您随时可以只处理一小部分代码),同时降低风险。如果其中某个步骤出现了问题,您可以轻松还原。Puppeteer 拥有大量用户,发布错误会导致很多用户遇到问题,因此将破坏更改的风险降至最低至关重要。

我们还幸运的是,Puppeteer 具有一套可靠的单元测试,涵盖其所有功能。这意味着我们可以确信,我们在迁移时不会破坏代码,而且我们并未对 API 引入任何变更。迁移的目的是在没有任何 Puppeteer 用户意识到我们已经迁移的情况下完成迁移,而测试是该策略的重要组成部分。如果我们没有获得良好的测试覆盖率,则会在继续迁移之前添加覆盖率。

在不进行测试的情况下执行任何代码更改存在风险,但涉及整个文件或整个代码库的更改尤其存在风险。在进行机械变更时,很容易漏掉某个步骤,而且在多种情况下,测试发现了某个问题,而这些问题的实现者和审核人员都被忽略了。

我们在前期投入的时间是持续集成 (CI) 设置。我们注意到,针对拉取请求的 CI 运行不稳定,经常会失败。这种情况经常发生,以至于我们养成了忽略 CI 并仍然合并拉取请求的习惯,假设失败是 CI 上的一次性问题,而不是 Puppeteer 中的问题。

经过一些常规维护并投入专门的时间来修复一些常规测试问题后,我们使它进入了更加一致的通过状态,这使得我们能够监听 CI,并知道失败表明出现了实际问题。这项工作并不美观,看到无尽的 CI 运行令人沮丧,但考虑到迁移过程中产生的拉取请求数量,让测试套件可靠运行至关重要。

选择并放置 1 个文件

至此,我们已经做好迁移准备,并且构建了包含大量测试的强大 CI 服务器。我们特意挑选一个小文件进行迁移,而不是深入研究任意文件。这是一项很有用的练习,因为它可以让您验证计划中即将执行的流程。如果此方法适用于此文件,则说明您的方法有效;如果无效,您可以返回到绘图板中。

此外,依次按文件(以及常规 Puppeteer 版本,因此所有更改都未包含在同一 npm 版本中)可以降低风险。我们选择了 DeviceDescriptors.js 作为第一个文件,因为它是代码库中最直接的文件之一。完成所有这些准备工作并完成如此小的更改可能会让您感到有点不知所措,但目标不是立即进行重大更改,而是要谨慎行事、有条不紊地逐个归档。验证方法所花费的时间无疑可以为您节省迁移过程中后续遇到复杂文件时花费的时间。

证明模式并重复

幸运的是,对 DeviceDescriptors.js 的更改已成功纳入到代码库中,并且该计划按预期运行!至此,您已经可以胸有成竹地投入精力,而我们也已做得很好。使用 GitHub 标签可以有效地将所有拉取请求分组在一起,我们发现这对于跟踪进度很有用。

完成迁移并在以后进行改进

对于任何单个 JavaScript 文件,我们的处理流程如下:

  1. 将该文件从 .js 重命名.ts
  2. 运行 TypeScript 编译器。
  3. 解决所有问题。
  4. 创建拉取请求

这些初始拉取请求中的大部分工作都是为现有数据结构提取 TypeScript 接口。对于我们之前讨论的迁移 DeviceDescriptors.js第一个拉取请求,代码为:

module.exports = [
  { 
    name: 'Pixel 4',
    … // Other fields omitted to save space
  }, 
  …
]

变成了:

interface Device {
  name: string,
  …
}

const devices: Device[] = [{name: 'Pixel 4', …}, …]

module.exports = devices;

在此过程中,我们会检查代码库的每一行,以检查是否存在问题。与任何代码库一样,代码库已推出几年并且随着时间推移而不断发展壮大,您也可以在多个领域有机会重构代码和改善这种情况。特别是在迁移到 TypeScript 之后,我们发现对代码进行细微重构就能让我们更加依赖编译器,并获得更好的类型安全性。

与直觉恰恰相反,务必不要立即做出这些更改。迁移的目标是将代码库迁移到 TypeScript 中,在大迁移过程中,无论何时,您都应考虑给软件和用户造成破坏的风险。通过尽量减少初始更改,我们将这种风险降低了。文件合并并迁移到 TypeScript 后,我们可以进行后续更改,以改进代码,从而充分利用字体系统。请务必为迁移设置严格的边界,并尽量不超出这些边界。

迁移测试以测试类型定义

将整个源代码迁移到 TypeScript 后,我们可以将重点转移到测试上。我们的测试覆盖率很高,但均使用 JavaScript 编写。这意味着,他们未测试的一点是我们的类型定义。该项目的长期目标(我们仍在努力)之一是使用 Puppeteer 开箱即提供高质量的类型定义,但我们在代码库中对类型定义没有任何检查。

通过将测试迁移到 TypeScript(遵循相同的流程,逐个文件地进行迁移),我们发现 TypeScript 存在一些问题,这些问题本应留给用户给我们找到。现在,我们的测试不仅涵盖所有功能,还可用于对 TypeScript 进行质量检查

作为处理 Puppeteer 代码库的工程师,TypeScript 给我们带来了很大的帮助。有了大幅度改进的 CI 环境,我们得以提高处理 Puppeteer 时的工作效率,还能让 TypeScript 捕获 bug,而这些 bug 原本会导致 npm 版本成为 npm 版本。我们很高兴能够发布高质量的 TypeScript 定义,让所有使用 Puppeteer 的开发者也能从这项工作中受益。

下载预览渠道

不妨考虑将 Chrome Canary 版开发者版Beta 版用作默认开发浏览器。通过这些预览渠道,您可以使用最新的开发者工具功能,测试先进的网络平台 API,并先于用户发现您网站上的问题!

与 Chrome 开发者工具团队联系

使用以下选项讨论博文中的新功能和变化,或讨论与开发者工具有关的任何其他内容。

  • 请通过 crbug.com 向我们提交建议或反馈。
  • 使用开发者工具中的更多选项   了解详情   > Help > Report a DevTools issues,报告开发者工具问题。
  • 您可以前往 @ChromeDevTools 发 Twitter 微博。
  • 请在 YouTube 视频或“开发者工具提示”YouTube 视频中留言说明“开发者工具的新变化”。