移除Babel的舞台预设
随着v7的推进,我们决定最好停止发布Babel中的舞台预设(例如:@babel/preset-stage-0 )。
我们不是轻易做出这个决定的,我们想展示TC39、Babel和社区之间相互作用的背后背景。
一些历史
Babel预置是一个可共享的插件列表。
官方的Babel Stage预设跟踪了TC39对JavaScript中新语法建议的分期过程。
每个预设(例如:stage-3,stage-2, etc.)都包括了该阶段的所有插件和其上面的插件。例如,stage-2 包括stage-3 ,以此类推。
这使得想要使用实验性语法的用户可以简单地添加预设,而不是需要配置/安装每个单独的插件。
实际上,我们在Babel v6发布后不久就加入了Stage预设(之前是v5的一个配置标志)。下面显示的是Babel v6的一个老例子。
在配置中看到这个是很常见的。
{
"presets": ["es2015", "react", "stage-0"]
}
babel-preset-stage-0的原始来源。
module.exports = {
presets: [
require("babel-preset-stage-1")
],
plugins: [
require("babel-plugin-transform-do-expressions"),
require("babel-plugin-transform-function-bind")
]
};
问题
这些预设是使用我们都想要的东西的方便方法:新的、闪亮的、"尚未确定 "的JavaScript的未来。
回过头来看,它的效果真的很好(也许太好?)
太好的工作?
像CoffeeScript这样的语言和Traceur这样的工具帮助建立了编译JavaScript的概念。Babel使得使用新的/未来的语法以及与现有工具的整合变得更加容易。人们的期望从怀疑和担心变成了完全接受实验性。
如果不是Babel等编译器的广泛采用,我们可能不会有现在的成就:它加速了ES2015的使用(和教学),让更多的人知道。React的发展进一步推动了使用,因为它的JJSX语法、类属性和对象休息/传播导致人们对这些语法建议有更多的了解。
Babel成为人们的一次性设置,再也没有人想过。它成了底层的基础设施,隐藏在其他工具后面,直到出现SyntaxError 、依赖性问题或整合问题。只需使用stage-0 。
这在某种程度上是令人敬畏的,因为它意味着这些想法正在野外被测试,甚至在生产环境中。然而,这也意味着,如果一个提议发生了重大变化(甚至被完全放弃),许多公司、工具和人员会遇到一些麻烦。
来回奔波
多年来,我们提出了许多问题,讨论如何处理#4914、#4955、#7770中的舞台预置。我甚至在一篇关于Babel 7.0的旧文章中写道,我们并没有 删除它们😅。
然而,我们发现保留舞台预设甚至会导致Babel本身的问题。
- 这是个常见的问题,比如说"使用异步函数需要哪些预置?"。人们不清楚
stage-0,也很少有人会去看它的package.json或源代码。 - 在第三阶段删除一个提议的插件(一旦它进入第四阶段),实际上是一个破坏性的改变。当你试图使用
@babel/preset-env,不编译一个原生支持的提案时,这个问题就更严重了。
"ES7装饰器"
部分问题正是围绕着命名的问题,正如我们经常听到的,命名是很难的。
对于ES6本身,有很多名字。Harmony、ES Next、ES6、ES2015。当人们听到新的想法时,直接选取最新的数字并附上名字是有意义的。
因此,我们很容易在推特/博客文章/讲座中搜索到 "ES7装饰器",并发现这已经成为它的习惯性名称了。
你的提醒,用:: 绑定只是一个处于0阶段的实验性建议,可能永远不会成为JS的一部分。不要叫它 "ES7"。
- 丹-阿布拉莫夫(@dan_abramov)2016年10月9日
在不知不觉中发生这种情况是完全可以理解的,但继续这样做会给语言的发展带来不同的期望。这没什么好内疚的--我们作为一个社区来学习,并互相提醒JavaScript的工作方式。
Jay Phelps写了一篇关于这个问题的好文章。他解释说,最好用他们目前所处的 "阶段 "来称呼他们。"Stage 2 Decorators",或者干脆叫 "Decorators Proposal"。
其理由是,说 "ES7 Decorators "是假设Decorators被期望在ES7中出现。我在上一篇关于编译node_modules的文章中提到了这一点,但是处于一个特定的阶段并不能保证什么:一个提案可以停滞不前,向后移动,或者被完全删除。
当我们决定将提案插件的名字从@babel/plugin-transform- 改为@babel/plugin-proposal- 时,我们想强调这一事实。
BabelScript
在流程的早期为提案提供预设,可能意味着这些提案被保证向前推进或有一个稳定的实现。
TC39敦促在使用第二阶段或以下的提案时要谨慎,因为这可能会导致社区无意中施加压力,使其保持原样,而不是因为担心破坏现有代码或生态系统的分裂而对其进行改进(例如,使用不同的符号,如# ,而不是@ ,用于装饰器)。
人们开玩笑说,使用Babel的开发者使用的是 "BabelScript",而不是JavaScript,这意味着一旦为某个功能制作了Babel插件,就意味着它已经被 "固定 "或正式成为语言的一部分(这并不正确)。对某些人来说,当他们看到一个新的语法/想法(阶段"-1")时,首先想到的是是否有一个Babel插件来实现它。
设定期望值
在像Babel这样的编译器使人们普遍编写ES2015之后,开发者自然希望尝试更多更新、更具实验性的 "功能"。在Babel中的工作方式是在以前的版本中使用stage 标志或stage-x 预设。
作为选择使用任何新功能的最方便的方法,它很快成为人们配置Babel时的默认方法(尽管在Babel v6中它变成了默认不做任何事情,这引起了很多抱怨)。
在图书馆、模板、讲座、推特和幻灯片中,经常可以看到"stage-0" 。
"在生产中对`babel?stage=0`说不就行了。
- Ryan Florence(@ryanflorence)2015年7月31日
甚至在几年前就有很多很好的讨论,但这并不是最容易驾驭的事情:我们不希望在使用时把console.warns来惩罚那些理解权衡的用户,而完全没有这个选项在当时似乎是不合理的。
盲目地选择进入第0阶段(无论我们是默认的),还是人们选择这样做,似乎都很危险,但也从不使用任何提案,这也是过于谨慎了。理想情况下,每个人都应该能够对那些对他们来说似乎合理的功能种类做出明智的决定,并明智地使用它们,无论他们处于哪个阶段。Mike Pennisi就这些问题写了一篇很好的文章。
我们并不打算威胁、催促或强迫特定的东西进入生态系统或JavaScript,而是要忠实地保持围绕新想法的实施/讨论。
犹豫不决
其他考虑因素
我们还可以尝试一下。
- 重新命名预设,以更好地标明稳定性等级(并不能解决版本问题)
- 更好的版本管理策略:独立地对预设进行版本管理,并在需要时立即更新,也许可以使用
0.x - 对预设的旧的过期版本发出警告/错误。
最后,如果我们保持阶段性,人们仍然需要查找哪些提案处于什么阶段,以知道使用哪些提案。
为什么是现在?
为什么不早点删除?阶段预设已经是Babel的一部分了,人们担心会给Babel的使用增加 "复杂性"。很多工具、文档、文章和知识都是围绕舞台预设的。早些时候,我们认为由官方来维护这些预设是比较好的,因为别人会(而且会)不可避免地创造它们。
我们正在努力确定正确的反馈水平:如果只是由委员会来决定什么东西进入语言,可能会导致不需要的功能被很好地规范,但如果社区期望正在进行的、实验性的建议被认为是稳定的或可以在生产中使用而没有后果,那么我们会有其他问题。我们都希望向前迈进,并有意进行:不急于求成,但也不要过于谨慎。Babel是做实验的正确场所,但知道边界在哪里是必要的。
移除预设会被认为是一个 "功能",因为它意味着有人必须明确决定使用每一个提议,这对任何提议都是合理的,因为它们都有不同程度的不稳定性、有用性和复杂性。
我们完全预计到会有一些最初的反弹,但最终觉得从长远来看,取消舞台预设对我们所有人来说都是一个更好的决定。然而,删除以前的默认值或删除舞台预置并不意味着我们不关心易用性、新用户或文档。我们用我们能做的来保持项目的稳定,提供工具来使事情变得更好,并记录我们所知道的。
迁移
为了实现更自动的迁移,我们已经更新了babel-upgrade来为你做这件事(你可以运行
npx babel-upgrade)。
简而言之,就是我们正在删除舞台预置。在某种程度上,人们将不得不选择加入,并知道正在使用哪种提案,而不是假设人们应该使用什么,特别是考虑到其中一些提案的不稳定性质。如果你使用另一个预设或工具链,(例如come-react-app),这个变化可能不会直接影响你。
从7.0.0-beta.52 ,我们已经废弃了舞台预设。如果你现在不想改变你的配置,我们建议你把版本固定在beta.54 ,直到你可以升级;在beta.54 ,我们会抛出一个错误信息,说明如何迁移。并检查你的所有版本在prerelease时都是一样的。
作为一种选择,你可以自由地制作你自己的预设,其中包含相同的插件,并按你的意愿进行升级。在未来,我们可能要开发一个babel-init ,可以帮助你交互式地设置插件,或者更新babel-upgrade 本身,列出并添加当前阶段的插件。也许Babel应该继续作为一个低级工具,而依靠其他高级/框架工具,如create-react-app ,为人们处理这些选择。
防止提案锁定
James DiGioia最近写了一篇关于使用管道运算符的变化的文章(|>)。
帖子中的主要观点是,提案本身处于变化之中,有几个选项需要探索。因为我们想把目前的三种可能性都作为Babel插件来实现,用于规范反馈和用户反馈,我们认为插件的使用方式也应该改变。这对TC39和Babel来说是一个相对较新的方法!
以前,我们会在配置中加入建议插件,仅此而已。现在,我们删除了默认行为,并要求用户选择一个标志,以显示选择哪个提案,并明确表示目前没有一个固定的(甚至是偏爱的)选项。
{
"plugins": [
- "@babel/plugin-proposal-pipeline-operator"
+ ["@babel/plugin-proposal-pipeline-operator", { "proposal": "minimal" }]
]
}
这是我们希望继续向前推进的事情,作为另一个迹象表明,这些建议对变化和我们所有人的反馈是开放的。舞台预设的删除使得这一点变得更加容易,因为以前我们必须把这些选项传下去,即使你不使用这个语法。
生态系统维护的负担
一门语言的 "语法预算 "不仅适用于语言本身的复杂性,而且可以延伸到工具。每一种新的语法添加都会给其他JavaScript项目的维护者带来新的负担。
一旦提出新的语法,很多东西都需要更新:解析器(babylon )、语法高亮(language-babel )、锁定器(babel-eslint )、测试框架(jest/ava )、格式化器(prettier )、代码覆盖率(istanbul )、分解器(babel-minify ),等等。
在acorn,eslint,jshint,typescript 等项目中,有许多问题被提出来,以支持第0阶段的建议,因为它们是在Babel中。没有多少项目会坚持要求他们支持任何提案的政策,因为这对维护工作要求极高。在许多方面,考虑到不断的更新和变化,我们甚至试图在Babel中处理这个问题,这很令人惊讶。
谁在做这些工作,我们有责任确保一切正常吗?这些项目中的每一个(大部分是志愿者)几乎在每一个方面都缺乏帮助,而我们却不断地收到关于这个问题的投诉。作为一个社区,我们如何承担起处理我们的基础设施的责任(与整个开放源码没有什么不同)?
巴别尔承担了支持这些实验性功能的不寻常的负担;同时,其他项目采取更保守的政策也是合理的。如果你希望看到新的语言功能得到整个生态系统的支持,请为TC39和这个项目做出贡献,将这些建议带到第四阶段。
未来
这个项目的目的是作为TC39进程的一部分:成为实施较新的(0-2阶段)建议和接收用户反馈的资源,同时也支持旧版本的JavaScript。我们希望这篇文章能让我们更清楚地了解我们是如何尽力使这个项目更好地融入到JavaScript生态系统中的。我们将很快发布v7的RC版本。
如果你欣赏这篇文章和我们在Babel上所做的工作,你可以在Patreon上支持我,让你的公司在Open Collective上赞助我们,或者最好让你的公司参与Babel,作为你工作的一部分。我们会感谢这种集体所有制。