7.7.0版发布——错误恢复和TypeScript 3.7

8,030 阅读8分钟

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 AhmadiSamuel Kwok的首次PR表示感谢!

这个版本的发布也得益于与其他开源项目团队的合作:感谢Devon Govett(Parcel)实现了对babel.config.json 文件的支持,感谢George Zahariev(Flow)将Flowenum 声明加入到@babel/parser!

另一个特别感谢的是彭博社组织的开源黑客大会,以鼓励他们的工程师回馈社区!特别是Robin Ricard和Jaeger。特别是Robin RicardJaideep 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 表达式,也可以是await1 的区别。

如果你直接使用@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-loaderrollup-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的工作流程是这样的:

  1. Prettier无法格式化文件
  2. ESLint报告一个Redefinition of __proto__ property 解析器错误
  3. 你删除第二个__proto__ 属性
  4. Prettier无法格式化该文件
  5. ESLint报告一个Identifier 'a' has already been declared 错误
  6. 你移除第二个let 关键字
  7. Prettier格式化了该文件

如果它更像这样,不是更好吗?

  1. Prettier格式化了文件
  2. ESLint报告了两个错误:Redefinition of __proto__ propertyIdentifier 'a' has already been declared
  3. 你删除了第二个__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节点)。

  1. 我们现在避免初始化一些很少使用的对象属性,直到它们被需要,使我们能够避免为几乎每个AST节点分配Object.create(null)

  2. 我们减少了每一个节点访问的记账工作量,通过用获取器取代一些不常见的属性,这样@babel/traverse ,可以跳过更新它们。

  3. 我们通过将用于表示节点遍历状态(即跳过、停止或移除)的几个布尔属性压缩到一个比特数组中,优化了内存的使用。

所有这些改进加起来,在转换性能和内存使用方面有如下区别。

性能内存使用量

你也可以查看上面的图表的原始数据。如果你想阅读更多关于这个主题的内容,你可以阅读Jùnliàng关于他为获得这些改进所做的修改的详细文章