7.7.0版发布——错误恢复和TypeScript 3.7
今天我们发布了Babel 7.7.0!
这个版本包括新的分析器功能,如顶层等待(await x() ,阶段3)和流enum 声明(流建议)。而且现在,@babel/parser ,可以选择从某些语法错误中恢复!
我们还增加了对TypeScript 3.7的支持:Babel可以解析和转换带有类型注释的私有类字段、使用declare 关键字定义的公共类字段注释、类型断言函数签名和enum 声明中的模板字样。
Babel现在可以理解三个新的配置文件。babel.config.json,babel.config.cjs 和.babelrc.cjs ,它们的行为与babel.config.js 和.babelrc.js 文件相同。
最后,Babel 7.7.0比7.6.0少用20%的内存。
你可以在GitHub上阅读整个更新日志。
向Alejandro Sánchez,Chris Garrett,彭驰,Daniel Arthur Gallagher,ExE-Boss,Eugene Myunster,Georgii Dolzhykov,Gerald,Linus Unnebäck,Martin Forsgren,Matthew Whitworth,Micah Zoltu,Mohammad Ahmadi和Samuel Kwok的首次PR表示感谢!
这个版本的发布也得益于与其他开源项目团队的合作:感谢Devon Govett(Parcel)实现了对babel.config.json 文件的支持,感谢George Zahariev(Flow)将Flowenum 声明加入到@babel/parser!
另一个特别感谢的是彭博社组织的开源黑客大会,以鼓励他们的工程师回馈社区!特别是Robin Ricard和Jaeger。特别是Robin Ricard和Jaideep Bhoosreddy,他们正积极致力于对Test262套件进行Babel转换的自动化测试。
如果你或你的公司想支持Babel和JavaScript的发展,但不确定如何支持,你可以在OpenCollective上向我们捐款,更好的是,直接与我们合作实施新的ECMAScript提案作为一个志愿者驱动的项目,我们依靠社区的支持,既为我们支持广大的JavaScript用户的努力提供资金,也为代码的所有权提供资金。如果你想进一步讨论,请联系Henry:henry@babeljs.io!
顶层await 解析
顶层await 建议允许你在模块中await 承诺,就像它们被包裹在一个大的异步函数中一样。这很有用,例如,有条件地加载一个依赖关系或执行应用程序的初始化。
// Dynamic dependency path
const strings = await import(`./i18n/${navigator.language}.mjs`);
// Resource initialization
const connection = await dbConnector();
@babel/parser 从7.0.0版本开始,我们支持在异步函数之外通过 选项使用 。allowAwaitOutsideFunction await
7.7.0版本引入了一个新的topLevelAwait 解析器插件,它有几个关键的区别:
- 它只允许在模块内部的顶层
await,而不是在脚本内部,就像提案中规定的那样。这是有必要的,因为基于脚本的同步模块系统(如CommonJS)不能支持异步依赖性。 - 它允许在使用
sourceType: "unambiguous"时检测正确的sourceType。请注意,由于await在脚本中是一个有效的标识符,许多看起来毫不含糊的模块结构实际上是模棱两可的,Babel会把它们解析为脚本。例如,await -1可以是一个等待-1的 await 表达式,也可以是await和1的区别。
如果你直接使用@babel/parser ,你可以启用topLevelAwait 插件。
parser.parse(inputCode, {
plugins: ["topLevelAwait"]
});
我们还创建了@babel/plugin-syntax-top-level-await 包,你可以把它添加到你的Babel配置中。
// babel.config.js
module.exports = {
plugins: [
"@babel/plugin-syntax-top-level-await"
]
}
请注意,使用顶级的await ,需要在你的模块捆绑器中支持。Babel本身并不进行转换:如果你使用Rollup,你可以启用 experimentalTopLevelAwait选项,而webpack 5支持 experiments.topLevelAwait选项。
从这个版本开始,如果caller 支持,@babel/preset-env 会自动启用@babel/plugin-syntax-top-level-await 。注意:babel-loader 和rollup-plugin-babel 还没有告诉 Babel 它们支持这种语法,但我们正在与各自的维护者一起努力。
解析器错误恢复
像许多其他的JavaScript解析器一样,@babel/parser ,只要遇到一些无效的语法就会抛出一个错误。这种行为对Babel很有效,因为为了将一个JavaScript程序转换为另一个程序,我们必须首先确定输入是有效的。
鉴于Babel的流行,还有许多其他的工具依赖于@babel/parser :首先是babel-eslint 和 Prettier。对于这两个工具来说,一个一出错就放弃的解析器是次优的。
考虑一下这段代码,由于重复的__proto__ 属性,它是无效的。
let a = {
__proto__: x,
__proto__: y
}
let a = 2;
目前ESLint和Prettier的工作流程是这样的:
- Prettier无法格式化文件
- ESLint报告一个
Redefinition of __proto__ property解析器错误 - 你删除第二个
__proto__属性 - Prettier无法格式化该文件
- ESLint报告一个
Identifier 'a' has already been declared错误 - 你移除第二个
let关键字 - Prettier格式化了该文件
如果它更像这样,不是更好吗?
- Prettier格式化了文件
- ESLint报告了两个错误:
Redefinition of __proto__ property和Identifier 'a' has already been declared - 你删除了第二个
__proto__属性和第二个let关键字
在这个版本中,我们为@babel/parser 增加了一个新的选项:errorRecovery 。当它被设置为真时,产生的AST将有一个errors 属性,包含所有@babel/parser 能够恢复的错误。
const input = `
let a = {
__proto__: x,
__proto__: y
}
let a = 2;
`;
parser.parse(input); // Throws "Redefinition of __proto__ property"
const ast = parser.parse(input, { errorRecovery: true });
ast.errors == [
SyntaxError: "Redefinition of __proto__ property",
SyntaxError: "Identifier 'a' has already been declared",
];
@babel/parser 由于目前不是每个错误都可以恢复,所以仍然可以抛出。我们将继续改进这些情况!
新的配置文件扩展
Babel 6只支持单一的配置文件。.babelrc,其内容必须使用JSON来指定。
Babel 7改变了.babelrcs的含义,并引入了两个新的配置文件:babel.config.js 和.babelrc.js (您可以在文档中阅读它们的区别)。我们增加了带有JavaScript的配置文件,允许在启用/禁用插件/选项时定义自己的逻辑。
然而,JSON文件的一个很大的好处是更容易缓存。同样的JavaScript文件在调用两次时可能会产生不同的值,而JSON文件保证总是评估为同一个对象。另外,JSON配置很容易序列化,而像函数或具有隐含数据或关系的JavaScript对象不可能序列化。
请注意,在使用基于JavaScript的配置时,Babel也会缓存转换,但必须评估配置文件(以了解缓存是否仍然有效)并手动配置缓存。
由于这些原因,Babel 7.7.0引入了对一个新的配置文件的支持。babel.config.json,其行为与babel.config.js 相同。
我们还增加了对两个不同的配置文件的支持:babel.config.cjs 和.babelrc.cjs ,在使用 node 的 "type": "module"选项时必须使用package.json (因为Babel不支持配置文件中的ECMAScript模块)。除了这个"type": "module" ,它们的行为与babel.config.js 和.babelrc.js 完全一样。
TypeScript 3.7
TypeScript 3.7 RC包括对可选链、nullish凝聚运算符、断言函数、纯类型字段声明和更多类型相关功能的支持。
自7.0.0以来,Babel已经通过@babel/plugin-proposal-optional-chaining 和@babel/plugin-proposal-nullish-coalescing-operator 支持可选链 (a?.b) 和空值凝聚 (a ?? b) 。
在Babel 7.7.0中,您现在可以在类域中使用断言函数和declare 。
function assertString(x): assert x is string {
if (typeof x !== "string") throw new Error("It must be a string!");
}
class Developer extends Person {
declare usingBabel: boolean;
}
为了避免破坏性的变化,我们在一个标志后面引入了对类字段中declare 的支持。"allowDeclareFields",由@babel/plugin-transform-typescript 和@babel/preset-typescript 支持。这可能会成为默认行为,所以建议你迁移你的配置以使用它。
{
"presets": [
["@babel/preset-typescript", {
"allowDeclareFields": true
}]
]
}
在编译的JJSX中使用对象传播
当在JJSX元素中使用传播属性时,Babel默认会注入一个运行时帮助器。
<a x {...y} />
// 🡇 🡇 🡇
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
React.createElement("a", _extends({
x: true
}, y));
useBuiltIns 2016年,随着对本地ES6支持的提高,我们在@babel/plugin-transform-react-jsx ,允许编译后的输出直接使用Object.assign ,并删除了多余的代码。
<a x {...y} />
// 🡇 🡇 🡇
React.createElement("a", Object.assign({
x: true
}, y));
然而鉴于对对象传播的本地支持,它允许我们产生更多的优化代码。
<a x {...y} />
// 🡇 🡇 🡇
React.createElement("a", { x: true, ...y });
你可以使用useSpread 选项和@babel/preset-react 或@babel/plugin-transform-react-jsx 来启用它。
{
presets: [
["@babel/react", { useSpread: true }]
]
}
内存使用的改进
从一开始,我们就在努力(#433,#3475,#7028等)提高性能。与7.6.0相比,Babel 7.7.0现在使用的内存减少了20%,转换大文件的速度也提高了8%。
为了实现这些结果,我们优化了在生命周期内进行的不同操作NodePath 对象(用于包裹每个AST节点)。
-
我们现在避免初始化一些很少使用的对象属性,直到它们被需要,使我们能够避免为几乎每个AST节点分配
Object.create(null)。 -
我们减少了每一个节点访问的记账工作量,通过用获取器取代一些不常见的属性,这样
@babel/traverse,可以跳过更新它们。 -
我们通过将用于表示节点遍历状态(即跳过、停止或移除)的几个布尔属性压缩到一个比特数组中,优化了内存的使用。
所有这些改进加起来,在转换性能和内存使用方面有如下区别。
| 性能 | 内存使用量 |
|---|---|
![]() | ![]() |
你也可以查看上面的图表的原始数据。如果你想阅读更多关于这个主题的内容,你可以阅读Jùnliàng关于他为获得这些改进所做的修改的详细文章

