乐趣区

译babel版本770功能更新

[译]babel 版本 7.7.0 功能更新

7.7.0 发布:错误恢复和 TypeScript 3.7

2019 年 11 月 5 日

今天我们将发布 Babel 7.7.0!

此版本包括新的分析器功能,如顶级的 await(await x(),第 3 阶段)和 Flow enum 声明(Flow 的建议)。现在,@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 上阅读整个变更日志。


顶级 await 解析(#10449)

顶级 await proposal 允许您在模块中 await promise,就像它们被包装在一个大型异步函数中一样。例如,这对于有条件地加载依赖项或执行应用程序初始化很有用:

// Dynamic dependency path
const strings = await import(`./i18n/${navigator.language}.mjs`);

// Resource initialization
const connection = await dbConnector();

@babel/parser 自版本 7.0.0 起,已支持 await 通过该 allowAwaitOutsideFunction 选项使用异步功能之外的功能。

版本 7.7.0 引入了一个新的 topLevelAwait 解析器插件,该插件有一些主要区别:

  • 根据 await 提案的要求,它仅允许顶层内部模块,而不允许内部脚本。这是必需的,因为基于同步脚本的模块系统(例如 CommonJS)无法支持异步依赖关系。
  • 使用它可以检测正确的 sourceType 时间 sourceType: "unambiguous"。请注意,由于await 在脚本中是有效的标识符,因此许多看起来似乎毫不含糊的构造实际上是模棱两可的,Babel 会将它们解析为脚本。例如,await -1可以是一个 waiting 表达式的 waiting -1,或者是 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 本身并没有进行转换:如果您使用汇总,则可以启用该 experimentalTopLevelAwait 选项,而 webpack 5 支持该 experiments.topLevelAwait 选项。

从此版本开始,如果支持,它将自动启用 @babel/preset-env。

解析器错误恢复(#10363)

像许多其他 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。设置为 true 时,生成的 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 仍然可以抛出,因为并非每个错误当前都可以恢复。我们将继续改善这些情况!

新的配置文件扩展名(#10501,#10599)

Babel 6 仅支持一个配置文件:.babelrc,其内容必须使用 JSON 指定。

Babel 7 更改了.babelrcs 的含义,并引入了两个新的配置文件:babel.config.js 和.babelrc.js(您可以在 docs 中了解它们之间的区别)。我们使用 JavaScript 添加了配置文件,以允许在启用 / 禁用插件 / 选项时定义自己的逻辑。

但是,JSON 文件的一大好处是更易于缓存。两次调用时,同一个 JavaScript 文件可以产生不同的值,而 JSON 文件可以保证始终对同一对象求值。此外,JSON 配置易于序列化,而无法使用隐式数据或关系序列化 JavaScript 值(如函数或 JavaScript 对象)。

请注意,Babel 在使用基于 JavaScript 的配置时也会缓存转换,但是必须评估 config 文件(以便知道缓存是否仍然有效),并手动配置缓存。

由于这些原因,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(#10543,#10545)

TypeScript 3.7 RC 支持可选的链接,无效的合并运算符,断言函数,类型字段声明以及许多其他与类型相关的功能。

自 7.0.0 起,通过和都支持 Babel 中的可选链(a?.b)和无效合并(a ?? b)。@babel/plugin-proposal-optional-chaining 和 @babel/plugin-proposal-nullish-coalescing-operator

在 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}]
  ]
}

使用对象在已编译的 JSX 中传播(#10572)

在 JSX 元素中使用传播属性时,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));

2016 年,随着对本地 ES6 的支持得到改善,我们添加了一个 useBuiltIns 选项,@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}]
  ]
}

内存使用率改进(#10480)

从一开始,我们就一直在努力(#433,#3475,#7028 等)来提高性能。Babel 7.7.0 现在使用的内存减少了 20%,与 7.6.0 相比,转换大型文件的速度提高了 8%。

为了获得这些结果,我们优化了在生存期 NodePath 对象(用于包装每个 AST 节点)中完成的不同操作:

  1. 现在我们避免初始化一些很少使用的对象属性,直到需要它们为止,这使我们避免 Object.create(null)为几乎每个 AST 节点分配资源。
  2. 通过用 bookkeeping 替换一些不常见的属性,从而 @babel/traverse 可以跳过更新它们,我们减少了每次访问单个节点的工作量。
  3. 我们通过将表示节点遍历(即,跳过,停止或删除)的状态的几个布尔属性压缩到位数组中来优化内存使用。

所有这些改进加起来在转换性能和内存使用方面存在以下差异:

您也可以检出上面图表的原始数据。如果您想了解更多有关此主题的信息,则可以阅读 Jùnliàng 关于他为获得这些改进所做的更改的详细文章!

退出移动版