Yarn 2 隆重推出!🧶🌟
大家好!经过整整365天的高强度开发,我非常高兴地宣布 Yarn 2 的首个稳定版本正式发布。在这篇文章中,我将阐述这个版本对我们社区的意义。系好安全带!
如果您想进一步了解 Yarn 1 的未来发展,请继续阅读,我们将在本文后面详细介绍我们的计划:未来计划。如果您现在就想开始使用 Yarn 2,请查看入门指南或迁移指南。
发布概述
描述此版本特别困难 - 它包含核心、根本的变化,以及我们自己使用时产生的新功能。
亮点
- 重新设计了输出以提高可读性
- 旨在提高可读性
- 我们的 CLI 命令(
yarn add
,...)现在可以识别工作区 yarn install
可以根据每个 repo 的需要选择运行- 更安全的
npx
对手要求yarn dlx
运行一次性工具 - 在所有工作区上运行命令
yarn workspaces foreach
patch:
可以通过协议就地修改包- 可以通过新
portal:
协议引用本地包 - 设计了新的工作流程,以高效地释放工作空间
- 现在可以声明式地对工作区进行 lint 和自动修复
但是也...
- 现在仅在绝对需要时才触发软件包构建
- 现在可以基于每个包启用或禁用包构建
- 脚本现在在规范化的 shell 中执行
- 现在,即使通过
yarn link
- 锁文件现在是正确的 YAML
- 代码库现在完全是 TypeScript
- Yarn 现在可以通过插件进行扩展
重大变更...
这些亮点只是所有变化和改进的一部分;可以在此处找到更详细的变更日志,并在此处找到升级说明。
常见问题
我们应该感谢谁发布了这个版本?
SysGears的larixer做了大量工作,他深入引擎内部,致力于让 Yarn 2 的过渡尽可能轻松。他编写了整个兼容层,这可不是件容易的事!node_modules
我还要感谢所有在开发期间自发加入我们一周或一个月的朋友们。尤其要感谢embraser01提供的初始 Windows 支持、bgotink为我们编写的文件系统 API、deini对 CLI 的贡献,以及Daniel在基础设施迁移方面提供的帮助。
如果没有开源社区许多人的支持,这项工作是不可能完成的 - 我认为特别是来自 Babel 的Nicolò和来自 Browserify 的Jordan,但他们远非唯一的人:Gatsby、Next、Vue、Webpack、Parcel、Husky 等团队......你们的支持确实让世界变得不同。
最后,Yarn 2 的项目负责人兼设计架构师是我,Maël Nison。我的时间很大程度上是由Datadog赞助的,这是一个非常棒的 JS 开发平台(正在招聘😜),还有我的未婚夫和我们的猫。永远不要忘记,所有开源项目的背后都有维护者和他们的家人。
迁移到 Yarn 2 有多容易?
感谢我们的 Beta 测试人员以及整个生态系统的大力支持,我们得以大幅减轻如此重大升级带来的困扰。我们提供了一份迁移指南,其中详细介绍了迁移过程,但一般来说,只要您使用最新版本的工具(ESLint、Babel、TypeScript、Gatsby 等),一切应该都顺利。
但需要特别注意的是:Flow 和 React-Native 目前无法在即插即用 (PnP) 环境下使用。我们期待与各自的团队合作,共同探索如何使我们的技术兼容。在此期间,您可以选择根据需要继续使用 Yarn 1,或者使用node_modules
插件,该插件旨在提供优雅的降级路径,以实现更平滑的升级(请注意,该插件仍在开发中 - 敬请期待)。更多详情,请点击此处。
如果您不想升级所有项目,只需在
yarn policies set-version ^1
需要保留在 Yarn 1 上的仓库中运行,然后提交结果即可。Yarn 始终优先使用已签入的二进制文件,而不是全局二进制文件,这是确保团队中每个人都共享完全相同版本的最佳方法!
遗留代码库会发生什么?
Yarn 1.22 将于下周发布。发布后,1.x 分支将正式进入维护模式——这意味着除非绝对需要修补漏洞,否则我不会再发布任何新版本。新功能将专门针对 Yarn 2 进行开发。具体来说:
-
经典代码库 (
yarnpkg/yarn
) 将迁移至 以yarnpkg/classic
反映其维护状态。该代码库将暂时保持开放,但我们可能会在一两年后将其归档。 -
现代存储库不会被重命名为
yarnpkg/yarn
,因为这会破坏大量反向链接历史记录。yarnpkg/berry
在可预见的未来,它将保留原有的版本。 -
旧网站将迁移至 classic.yarnpkg.com,新网站(目前为next.yarnpkg.com)将迁移至主域名。
-
npm 上的包
yarn
不会改变;我们将使用新yarn set version
命令分发进一步的版本。
我们预计大部分变更将于 2020 年 2 月 1 日完成。
深入了解
CLI 输出
Yarn 发布时,它的 CLI 输出相比其他解决方案有了很大的进步(而且它还支持表情符号!🧶),但仍然存在一些问题。尤其是很多消息相当隐晦,而且颜色与内容格格不入,难以协调。吸取了这次教训后,我们决定在 Yarn 2 中尝试一些不同的方法:
现在几乎所有消息都有各自的错误代码,您可以在我们的文档中搜索。在这里,您可以找到每条消息的详细解释,包括修复建议。现在,颜色用于区分每条消息的重要部分,通常是软件包名称和版本,而不是逐行显示。
我们预计在接下来的几个月里会做出一些调整(特别是在色盲可访问性方面),但随着时间的推移,我想您会喜欢这个新的显示器!
工作区感知 CLI
使用工作区有时会让人不知所措。在向某个工作区添加新的依赖项时,你需要牢记整个项目的状态。“我应该使用哪个版本?我的其他工作区已经使用了哪些依赖项?”等等。
Yarn 现在通过各种方式方便维护此类设置:
yarn up <name>
将同时升级所有工作区中的包yarn add -i <name>
将提供重复使用与其他工作区使用的相同版本(以及一些其他选择)- 版本插件将为您提供一种方法,以便在其中一个工作区再次发布时检查所有相关工作区是否都受到影响。
这些变化凸显了我们希望为 Yarn 带来的新体验:工具成为盟友而不是负担。
零安装
虽然“零安装”本身并不是一项功能,但它涵盖了许多围绕一个特定目标定制的 Yarn 功能 - 通过从等式中消除主要的熵源(Yarn 本身)使您的项目尽可能稳定和快速。
简而言之,由于 Yarn 现在直接从缓存中读取 vendor 文件,因此如果缓存成为仓库的一部分,则无需再次运行 yarn install。当然,这会对仓库大小造成影响,但与Yarn 1 中的离线镜像功能相当——非常合理。
有关更多详细信息(例如“为什么它与在node_modules
目录中检查不同”),请参阅此文档页面。
新命令:yarn dlx
yarn dlx
Yarn 2 引入了一个名为dlx的新命令,它的作用基本相同,但危险性略低。由于dlx 既可用于本地脚本,也可用于远程脚本,因此如果输入错误,攻击者就有可能有机可乘:npx
npx
$ npx serv # Oops, should have been "serve"
这对于 dlx 来说不是问题,它只下载并执行远程脚本,从不下载和执行本地脚本。本地脚本始终可以通过 yarn run 或直接通过其名称来运行:
$ yarn dlx terser my-file.js
$ yarn run serve
$ yarn serve
新命令:yarn workspaces foreach
在多个仓库上运行命令是一个相对常见的用例,之前你需要一个外部工具才能完成。现在,有了扩展 Yarn 的Workspace-tools 插件,你就可以做到这一点:
$ yarn workspaces foreach run build
该命令还支持控制执行的选项,允许您指示 Yarn 遵循依赖关系、并行执行命令、跳过工作区等等。点击此处查看完整的选项列表。
新协议:patch:
Yarn 2 引入了一个名为 的新协议patch:
。每当您需要将更改应用于依赖关系树中的特定包时,都可以使用此协议。其格式类似于以下内容:
{
"dependencies": {
"left-pad": "patch:left-pad@1.3.0#./my-patch.patch"
}
}
结合该resolutions
字段,您甚至可以修补位于依赖关系树深处的包。由于该patch:
协议只是另一个数据源,因此它受益于与所有其他协议相同的机制 - 包括缓存和校验和!
新协议:portal:
Yarn 2 引入了一个名为 的新协议portal:
。您可以将其视为portal:
现有协议的包对应部分link:
。 该link:
协议用于指示 Yarn 创建指向本地磁盘上任何文件夹的符号链接,而portal:
协议则用于创建指向任何包文件夹的符号链接。
{
"dependencies": {
"@my/app": "link:./src",
"eslint-plugin-foo": "portal:./pkgs/eslint-plugin-foo"
}
}
那么你说的区别是什么?很简单:门户遵循传递依赖关系,而链接则不然。更妙的是,无论符号链接的软件包位于何处,门户都能正确地遵循对等依赖关系。
工作区版本
使用工作区本身就存在一系列问题,而可扩展版本可能是其中最大的问题之一。大多数大型开源项目都使用Lerna或类似的工具来自动跟踪应用于工作区的变更。
当我们开始发布 Yarn 2 的 Beta 版本时,我们很快发现我们遇到了同样的难题。我们四处寻找,但现有的解决方案似乎都有一些严格的要求——例如,使用 Lerna,你要么每次都得发布所有包,要么得自己追踪哪些包需要发布。其中一些工作可以自动化,但当你考虑到正在发布的工作区可能还需要再次发布不相关的包(例如,因为他们在预打包步骤中会用到它)时,情况就变得更加复杂了!
为了解决这个问题,我们设计了一个全新的工作流程,可通过名为 的插件使用version
。此工作流程(详情请见此处)允许您将部分发布责任委托给贡献者。更棒的是,它还附带一个可视化界面,让发布管理变得轻而易举!
这个工作流程仍处于实验阶段,但对我们来说效果已经足够好了,我们认为在使用工作区构建大型项目时,它很快就会成为工具包中不可或缺的一部分。
工作区约束
工作区很快就证明了它是我们最有价值的功能之一。多年来,无数的项目和应用程序都迁移到了它。然而,它并非完美无缺。尤其是,保持工作区依赖项的同步需要格外谨慎。
Yarn 2 引入了一个名为“约束”的新概念。约束提供了一种指定通用规则的方法(使用Prolog,一种声明式编程语言),所有工作区都必须满足这些规则才能通过验证。例如,以下代码将阻止您的工作区依赖下划线 - 并且可以自动修复!
gen_enforced_dependency(WorkspaceCwd, 'underscore', null, DependencyType) :-
workspace_has_dependency(WorkspaceCwd, 'underscore', _, DependencyType).
另一个约束将要求所有工作区在其清单中正确描述存储库字段:
gen_enforced_field(WorkspaceCwd, 'repository.type', 'git') :-
workspace(WorkspacedCwd).
gen_enforced_field(WorkspaceCwd, 'repository.url', 'ssh://git@github.com/yarnpkg/berry.git') :-
workspace(WorkspacedCwd).
约束无疑是我们最先进、最强大的功能之一,所以如果您需要时间来理解它,也不用担心。我们会后续发布博客文章,深入探讨它的细节——敬请期待!
构建依赖关系跟踪
Yarn 1 中反复出现的问题之一是,原生包的重建次数远远超出了应有的程度。例如,运行 Yarn 1 时,yarn remove
依赖关系树中的所有包都会被彻底重建。
从 Yarn 2 开始,我们现在会跟踪列出安装后脚本的每个包的单独依赖树,并且仅当这些依赖树以某种方式发生变化时才运行它们:
➤ YN0000: ┌ Link step
➤ YN0007: │ sharp@npm:0.23.0 must be rebuilt because its dependency tree changed
➤ YN0000: └ Completed in 16.92s
➤ YN0000: Done with warnings in 21.07s
每个包的构建配置
Yarn 2 现在允许您指定每个包是否运行构建脚本。目前默认是运行所有包,因此默认情况下您可以选择禁用特定包的构建:
{
"dependenciesMeta": {
"core-js": {
"built": false
}
}
}
如果您更喜欢默认禁用所有内容,只需enableScripts
在设置中将其关闭,然后built
在中明确启用该标志dependenciesMeta
。
规格化壳
当 Yarn 2 还处于早期阶段时,我们收到的第一个外部 PR 是关于 Windows 支持的。事实证明,Windows 用户数量相当庞大,兼容性对他们来说非常重要。尤其是他们经常遇到脚本字段的问题,而该字段通常只在 Bash 上进行测试。
Yarn 2 附带一个基本的 Shell 解释器,它能够提供脚本领域中常用的 90% 的语言结构。借助这个解释器,无论在 OSX 还是 Windows 上执行,你的脚本都能以相同的方式运行:
{
"scripts": {
"redirect": "node ./something.js > hello.md",
"no-cross-env": "NODE_ENV=prod webpack"
}
}
更好的是,这个 shell 允许我们构建更紧密的集成,例如将命令行参数公开给用户脚本:
{
"scripts": {
"lint-and-build": "yarn lint \"$@\" && yarn build \"$@\""
}
}
改进的对等依赖链接
因为 Node 在所有需要的路径上调用 realpath(除非启用了 --preserve-symlinks,但这种情况很少发生),所以对等依赖项无法通过 yarn link 工作,因为它们是从链接包在磁盘上的真实位置的角度加载的,而不是从其依赖项的角度加载的。
由于 Plug'n'Play 可以强制 Node 根据需要多次实例化包以满足其所有依赖集,因此 Yarn 现在可以正确支持这种情况。
新的 Lockfile 格式
Yarn 创建之初,我们决定将 lockfile 格式设计得与 YAML 非常相似,但有一些关键区别(例如,键和值之间没有冒号)。这对于第三方工具开发者来说相当烦人,因为解析器是定制的,语法也完全不符合标准。
从 Yarn 2 开始,锁文件和配置文件的格式都更改为纯 YAML:
"@yarnpkg/parsers@workspace:^2.0.0-rc.6, @yarnpkg/parsers@workspace:packages/yarnpkg-parsers":
version: 0.0.0-use.local
resolution: "@yarnpkg/parsers@workspace:packages/yarnpkg-parsers"
dependencies:
js-yaml: ^3.10.0
pegjs: ^0.10.0
languageName: unknown
linkType: soft
TypeScript 代码库
虽然这可能不会直接影响到您作为用户,但我们已经完全从 Flow 迁移到 TypeScript。一个巨大的优势是,我们的工具和贡献工作流程现在比以往任何时候都更加简单。而且,由于我们现在允许构建 Yarn 插件,您将能够直接使用我们的类型,以确保您的插件在更新期间是安全的。
export interface Package extends Locator {
version: string | null,
languageName: string,
linkType: LinkType,
dependencies: Map<IdentHash, Descriptor>,
peerDependencies: Map<IdentHash, Descriptor>,
dependenciesMeta: Map<string, Map<string | null, DependencyMeta>>,
peerDependenciesMeta: Map<string, PeerDependencyMeta>,
};
模块化架构
我最近就该主题写了一整篇博客文章,所以我不会深入探讨它,但 Yarn 现在遵循非常模块化的架构。
具体来说,这意味着两件有趣的事情:
-
您可以编写 Yarn 将在运行时加载的插件,并且该插件将能够访问 Yarn 所看到的真实依赖树;这使您可以轻松构建工具,例如Lerna、Femto、Patch-Package等。
-
您可以依赖 Yarn 核心本身并自行实例化类(请注意,这部分仍然有点实验性,因为我们找出在这种模式下操作时包含内置插件的最佳方法)。
为了方便您参考,我们构建了一个TypeScript 插件@types/
,它会在您每次运行时自动添加相关的包yarn add
。插件很容易编写 - 我们甚至提供了教程- 所以有空试试吧!
规范化配置
关于 Yarn 1,我们收到的一个非常常见的反馈是关于配置管道的。Yarn 发布时,我们试图尽可能地与 npm 兼容,这促使我们尝试读取 npm 配置文件等。这导致用户很难理解应该在哪里配置设置。
initScope: yarnpkg
npmPublishAccess: public
yarnPath: scripts/run-yarn.js
在 Yarn 2 中,整个配置都进行了改进,现在所有内容都保存在名为 的单一事实来源中.yarnrc.yml
。设置名称也进行了更改,以保持一致(不再使用experimental-pack-script-packages-in-mirror
vs workspaces-experimental
),因此请务必查看我们全新的文档。
严格的包边界
除非软件包确实在其依赖项中列出其他软件包,否则软件包不允许依赖其他软件包。这与我们一年多前推出即插即用功能时所做的更改一致,我们很高兴地宣布,我们与生态系统顶级维护人员的合作卓有成效。如今,很少有软件包仍然与此规则存在兼容性问题。
// Error: Something that got detected as your top-level application
// (because it doesn't seem to belong to any package) tried to access
// a package that is not declared in your dependencies
//
// Required package: not-a-dependency (via "not-a-dependency")
// Required by: /Users/mael/my-app/
require(`not-a-dependency`);
弃用 Bundle 依赖项
Bundle 依赖项是旧时代的产物,所有对它们的支持都已被废弃。安装将优雅降级,并下载依赖项字段中最初列出的软件包。
{
"bundleDependencies": [
"not-supported-anymore"
]
}
如果您使用捆绑依赖项,请查看迁移指南以获取建议的替代方案。
只读包
软件包现在保存在其缓存存档中。为了安全起见并防止缓存损坏,这些存档将以只读驱动器的形式挂载,正常情况下无法修改:
const {writeFileSync} = require(`fs`);
const lodash = require.resolve(`lodash`);
// Error: EROFS: read-only filesystem, open '/node_modules/lodash/lodash.js'
writeFileSync(lodash, `module.exports = 42;`);
如果某个包需要修改其自身的源代码,则需要将其拔出- 可以在dependenciesMeta
字段中明确地进行,也可以通过列出安装后脚本隐式地进行。
结论
哇!内容好丰富啊,不是吗?希望你喜欢这篇更新,这可是我多年准备和坚持的结晶。
我认为包管理应该具备的一切,你都能在这里找到。现在的结果肯定比以前更加主观,但我相信这是未来的方向——我们精心规划了想要提供的长期用户体验,而不是一个没有方向的工具箱。
对我来说,在 Yarn 上工作是一段不可思议的经历。我同时担任项目经理、高级工程师、首席设计师、开发者关系和用户支持。虽然有起有落,但每次听到有人分享他们在 Yarn 上的成功故事,我的内心都会感到一丝欢欣。所以,请这样做:告诉我你喜欢什么,并帮助我解决你不喜欢的地方。
2020年快乐!🎄
文章来源:https://dev.to/arcanis/introducing-yarn-2-4eh1