Rush StackShopBlogEvents
跳至主要内容

Heft 0.51迁移指南

· 预计阅读时间 15 分钟
Daniel Nadeau
Pete Gonzalez

Heft 0.51.0 版本推出了一个带来了一些重大架构变化的"多阶段"特性。如果你一直在使用较旧的版本,那么升级将需要对你的Heft 配置文件以及可能编写的自定义插件进行一些更改。在这篇文章中,我们将总结发生了哪些更改,以及如何迁移你的项目。这可能是在 Heft 的 1.0.0 版本发布之前的最后一次重大破坏性更改。

想要更深入地了解多阶段设计及其背后的动机,请参阅我们的另一篇文章Heft 0.51 中的新内容

版本时间线

虽然大部分的重大更改都在 Heft 0.51.0 中,但在后续的几个版本中也进行了一些重要的更改:

  • Heft 0.51.0: 为支持多阶段功能做出的大规模架构变更,包括配置文件架构和插件 API 的破坏性更改
  • Heft 0.52.0: 恢复对 heft start 别名的支持(该别名在 0.51.0 版本中被移除);增加了定义自定义别名的能力;@rushstack/heft-node-rig 现在使用与 @rushstack/heft-web-rig 相同的 heft start 别名启动其开发服务器
  • Heft 0.53.0: 移除了 taskEvents 配置设置;像 copy-files-pluginnode-service-plugin 这样的内建任务现在使用与第三方插件相同的配置(简单地指定 @rushstack/heft 为他们的插件包名称)
  • Heft 0.54.0: 恢复了对 heft test -u-u 等短参数名称的支持(该支持在 0.51.0 版本中被移除)
  • Heft 0.55.0: 从插件 API 的会话对象中移除了 cacheFolderPath,因为 .cache 文件夹不再被使用

为了简化这些迁移说明,在本文中我们将假设你正在升级到 0.55.0 或更新的版本,且你的当前版本是 0.50.x 或更旧的版本

迁移 heft.json 文件

JSON Schema URL 的更改

为了在编辑配置文件时能得到正确的 VS Code 智能提示,需要更新每个 Heft 配置文件中的 "$schema" 字段。只需将 json-schemas/heft/ 替换为 json-schemas/heft/v0 即可。

例如:

  • 旧的:"$schema": "https://developer.microsoft.com/json-schemas/heft/heft.schema.json"
  • 新的:"$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json"

完整的 JSON schema 名称列表可以在此 GitHub 文件夹中找到。这些名称是上面显示的 URL 的最后一部分。

插件必须显式加载

在旧的设计中,许多插件被内置于 @rushstack/heft 中,不需要使用 heft.json 设置显式加载。如果找不到它们相关的配置文件,那么它们的任务就会被默默地跳过。

旧的: 隐式加载的插件:

  • heft-typescript-plugin
  • copy-static-assets-plugin
  • copy-files-plugin
  • delete-globs-plugin
  • run-script-plugin
  • api-extractor-plugin
  • project-validator-plugin
  • node-service-plugin

新的: 迁移后,每个插件必须通过 heft.json 配置文件显式加载。通常这是从你的 rig 继承过来的。这种新模型消除了神秘和不确定性,因为插件及其依赖的完整集合现在在配置文件中表示。

如果你正在使用我们的 @rushstack/heft-node-rig@rushstack/heft-web-rig,你的项目应该只需要做少量的更改,因为更新后的 rigs 现在显式加载所有这些插件。如果你创建了一个自定义的 rig,迁移工作将会更复杂,但你可以从我们的例子中复制:

迁移 package.json 依赖

许多插件已经被提取到它们自己的 NPM 包中。这减少了对某些插件不使用的项目的启动时间和安装占用。

以下是写作时的当前库存:

迁移独立的 heft.json

旧的 heft.json 区分了"事件动作"(即内置任务)和"heftPlugins"(即来自插件包的任务)。

旧的: heft-node-rig 中的 heft.json 摘录

// ⚠️ 旧格式示例 -- 请勿使用!⚠️
{
"$schema": "https://developer.microsoft.com/json-schemas/heft/heft.schema.json",

// "deleteGlobs" 指定在 "clean" 事件下运行
"eventActions": [
{
// 📌 [1] 旧的清理方式
"actionKind": "deleteGlobs",
"heftEvent": "clean",
"actionId": "defaultClean",
"globsToDelete": ["dist", "lib", "lib-commonjs", "temp"]
}
],

// Jest 插件使用 "heftPlugins" 部分加载
// 并且它的事件序列是使用程序逻辑定义的
"heftPlugins": [
// 📌 [2] 旧的加载插件方式
{ "plugin": "@rushstack/heft-jest-plugin" }
]
}

新的: heft-node-rig 中的 heft.json 摘录

{
"$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json",

"phasesByName": {
// ("build" 是用户定义的名称,不是模式字段)
"build": {
// 📌 [1] 新的清理方式
"cleanFiles": [
{ "sourcePath": "dist" },
{ "sourcePath": "lib" },
{ "sourcePath": "lib-commonjs" }
],
"tasksByName": {
// ("typescript" 是用户定义的名称,不是模式字段)
"typescript": {
"taskPlugin": {
"pluginPackage": "@rushstack/heft-typescript-plugin"
}
},
"lint": {
"taskDependencies": ["typescript"],
"taskPlugin": {
"pluginPackage": "@rushstack/heft-lint-plugin"
}
},
"api-extractor": {
"taskDependencies": ["typescript"],
"taskPlugin": {
"pluginPackage": "@rushstack/heft-api-extractor-plugin"
}
},
"node-service": {
"taskDependencies": ["typescript"],
"taskPlugin": {
// 这个内置插件指定 "@rushstack/heft" 作为它的包名称
"pluginPackage": "@rushstack/heft",
"pluginName": "node-service-plugin"
}
}
}
},

// ("test" 是用户定义的名称,不是模式字段)
"test": {
"phaseDependencies": ["build"],
"tasksByName": {
// ("jest" 是用户定义的任务名称)
"jest": {
// 📌 [2] 新的加载插件方式
"taskPlugin": {
"pluginPackage": "@rushstack/heft-jest-plugin"
}
}
}
}
}
}

观察上面的示例,主要的更改有:

  • 每个任务必须从 pluginPackage 中显式加载,因此 rig 的 heft.json 现在更冗长(但更易理解!)
  • 内置任务(例如 node-service)与外部插件具有相同的规范
  • 旧的 "heftEvent" 生命周期已被 phaseDependenciestaskDependencies 取代,其依赖图确定了任务的顺序

完整的配置文件可以在这里找到:heft-node-rig/profiles/default/config/heft.json

迁移已配置的 heft.json

这是来自TSDoc Playground项目的另一个示例,它的 heft.json 继承自我们的 heft-web-rig

旧的: playground/config/heft.json 中的 heft.json 摘录

// ⚠️ 旧格式示例 -- 请勿使用!⚠️
{
"$schema": "https://developer.microsoft.com/json-schemas/heft/heft.schema.json",

"extends": "@rushstack/heft-web-rig/profiles/library/config/heft.json",

"eventActions": [
{
"actionId": "copyLicenseToDistFolder",
"actionKind": "copyFiles",
// 📌 [3] 旧的编译后操作方式
"heftEvent": "compile",
"copyOperations": [
{
"destinationFolders": ["./dist"],
// 📌 [4] 旧的指定源文件夹方式
"sourceFolder": "..",
"includeGlobs": ["LICENSE"]
}
]
}
]
}

新的: playground/config/heft.json 中的 heft.json 摘录

{
"$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json",

"extends": "@rushstack/heft-web-rig/profiles/library/config/heft.json",

"phasesByName": {
// ("build" 是用户定义的名称,不是模式字段)
"build": {
"tasksByName": {
// ("post-compile-copy" 是用户定义的名称,不是模式字段)
"post-compile-copy": {
// 📌 [3] 新的编译后操作方式,通过依赖于相关任务来实现

// 在 "typescript" 完成后才应运行 "post-compile-copy" 任务
"taskDependencies": ["typescript"],

"taskPlugin": {
"pluginName": "copy-files-plugin",
"pluginPackage": "@rushstack/heft",
"options": {
"copyOperations": [
{
// 📌 [4] 新的指定源文件夹(或文件路径)的方式
"sourcePath": "..",
"destinationFolders": ["./dist"],
"includeGlobs": ["LICENSE"]
}
]
}
}
}
}
}
}
}

观察:

  • 这里的更改很小,因为 rig 提供了大部分的构建定义
  • 最新的 heft-web-rig 使用了 heft-webpack5-plugin,因此我们需要在转换过程中从 Webpack 4 升级到 5
  • "heftEvent": "compile" 事件不再存在;相反,它必须通过等效的 "taskDependencies" 条目来表示,该条目引用了 rig 的 "typescript" 任务定义

迁移"pre-compile"操作

在上面的示例中,我们通过将 "heftEvent": "compile" 替换为 "taskDependencies": ["typescript"] 进行了配置文件的迁移,通过表达在 "typescript" 任务完成之后才能执行该操作。但是 "taskDependencies" 是单向关系。在这种新模型中,我们如何表示诸如 pre-compile 这样的事件呢?

考虑以下示例:

旧版: heft.json 样本

// ⚠️ 旧格式示例 -- 请勿使用!⚠️
{
"$schema": "https://developer.microsoft.com/json-schemas/heft/heft.schema.json",

"extends": "@rushstack/heft-web-rig/profiles/app/config/heft.json",

"eventActions": [
{
"actionKind": "copyFiles",
"actionId": "copyAssets",
// 📌 [5] 旧的执行"pre-compile"操作的方式
"heftEvent": "pre-compile",
"copyOperations": [
{
"sourceFolder": "node_modules/some-library/dist",
"destinationFolders": ["temp/typings"],
"includeGlobs": ["*.d.ts"]
}
]
}
]
}

新版: heft.json 样本

{
"$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json",

"extends": "@rushstack/heft-web-rig/profiles/app/config/heft.json",

"phasesByName": {
// ("build" 是用户定义的名称,不是模式字段)
"build": {
"tasksByName": {
// ("pre-compile-copy" 是用户定义的名称,不是模式字段)
"pre-compile-copy": {
"taskPlugin": {
"pluginPackage": "@rushstack/heft",
"pluginName": "copy-files-plugin",
"options": {
"copyOperations": [
{
"sourcePath": "node_modules/some-library/dist",
"destinationFolders": ["temp/typings"],
"includeGlobs": ["*.d.ts"]
}
]
}
}
},

// ("typescript" 是用户定义的名称,最初在 rig 中定义)
"typescript": {
// 📌 [5] 新的执行"pre-compile"操作的方式
// "typescript" 任务在 "pre-compile-copy" 完成之后才能运行
"taskDependencies": ["pre-compile-copy"]
}
}
}
}
}

供参考,@rushstack/heft-web-rig 如下定义了 "typescript" 任务:

heft-web-rig/profiles/app/config/heft.json 摘录

  . . .
"typescript": {
"taskDependencies": ["sass"],
"taskPlugin": {
"pluginPackage": "@rushstack/heft-typescript-plugin"
}
},
. . .

观察:

  • 回想一下,我们通过为自己的任务指定 taskDependencies"taskDependencies": ["typescript"])来实现了 "post-compile-copy"
  • 相比之下,我们通过修改 rig 的 "typescript" 任务的 taskDependencies"taskDependencies": ["pre-compile-copy"])来实现 "pre-compile-copy"
  • rig 已经有 "taskDependencies": ["sass"]。但我们不需要指定 "taskDependencies": ["typescript", "sass"],因为 Heft 的配置解析器默认会通过追加而不是替换的方式合并数组。
  • 这种合并行为由 @rushstack/heft-config-file 实现,并且可以使用属性继承指令进行自定义。

迁移命令行语法

旧的 --watch 命令行参数已被移除。现在,可以通过在操作名称后附加 -watch 来启用监听模式。

旧的:

heft build --watch --verbose

新的:

heft build-watch --verbose

命令别名

在旧的设计中,heft start 是一个特殊操作,用于启动开发服务器。在新的设计中,它是在 heft.json 中定义的命令别名。新的别名系统允许您定义自己的自定义别名,以缩短常用命令的长度。

heft-web-rig/profiles/app/config/heft.json 摘录

  // 将 "heft start" 定义为 "heft build-watch --serve" 的别名。
"aliasesByName": {
"start": {
"actionName": "build-watch",
"defaultParameters": ["--serve"]
}
},

The --serve CLI 参数是我们启动localhost开发服务器的标准约定。它被heft-webpack5-plugin和内置的node-service-plugin所支持。

迁移自定义插件

在更新到新版本的 Heft 时,插件也需要进行兼容性更新。一些较为显著的 API 更改包括:

  • heft-plugin.json清单文件必须随插件包一起提供。如果找不到heft-plugin.json文件,Heft 将报告错误。
  • 插件类必须具有无参数的构造函数,并且必须是heft-plugin.json中的entryPoint属性指向的文件的默认导出。
  • 现在可以使用heft-plugin.json中的optionsSchema属性指定在heft.json中提供的选项的模式文件,并且 Heft 将对其进行验证。
  • 参数现在在heft-plugin.json中定义,并且通过插件的IHeftTaskSession.parametersIHeftLifecycleSession.parameters属性来使用。 注意:除了默认的 Heft 参数外,只有调用插件定义的参数是可访问的
  • 插件不再能够定义自己的操作。如果插件需要自己的操作,应将专用阶段添加到消费者的heft.json中。
  • runScript Heft 事件已被修改,仅接受runAsync方法,并且属性已更新以反映常规 Heft 任务插件可用的内容。
  • 与路径相关的变量已重命名以明确表示它们是路径(例如,HeftConfiguration.buildFolder现在是HeftConfiguration.buildFolderPath
  • runIncremental钩子现在可以用于确保按正确的依赖顺序进行监视模式的重建
  • clean钩子已被删除,以便使用heft.json中的cleanFiles选项清除文件,以便明确指定正在清除的文件和时间
  • 作为后果,插件不再能够以编程方式计算heft clean命令要清除的文件夹;其行为由静态配置文件预先确定,从而使整个系统更简单和更可预测。

其他迁移说明

  • jest.config.json中,已删除folderNameForTestsextensionForTests属性,应改用testMatch属性处理。
  • 内置的node-service-plugin现在支持--serve参数,以保持与@rushstack/heft-webpack5-plugin开发服务器的一致性。
  • 如果指定了--serve并且省略了config/node-service.json,那么node-service-plugin将以硬错误失败。
  • 尽管@rushstack/heft-lint-plugin@rushstack/heft-typescript-plugin已分别提取到单独的 NPM 包中,但它们必须