【问题标题】:Babel preset-env not loading top-level await syntax for node targetBabel preset-env 未加载节点目标的顶级等待语法
【发布时间】:2020-04-06 08:49:34
【问题描述】:

我正在尝试新的顶级 await 语法来导入模块,但 Babel 无法识别语法,尽管使用了 preset-env,除非我明确设置了插件 @babel/plugin-syntax-top-level-await。为什么我必须手动指定这个插件?我的印象是 preset-env 会自动处理这些事情?

对于上下文,我的设置如下:

    presets: [
      [
        '@babel/preset-env',
        {
          debug: true,
          modules: false,
          useBuiltIns: 'usage',
          corejs: 3,
        },
      ],
      '@babel/preset-typescript',
    ],
    // plugins: ['@babel/plugin-syntax-top-level-await'], // Commented b/c I was experimenting
  };

运行yarn run babel myFile.ts时,输出和抛出的错误是:

@babel/preset-env: `DEBUG` option
Using targets:
{
  "node": "13.12"
}
Using modules transform: false
Using plugins:
  proposal-nullish-coalescing-operator { "node":"13.12" }
  proposal-optional-chaining { "node":"13.12" }
  syntax-json-strings { "node":"13.12" }
  syntax-optional-catch-binding { "node":"13.12" }
  syntax-async-generators { "node":"13.12" }
  syntax-object-rest-spread { "node":"13.12" }
  syntax-dynamic-import { "node":"13.12" }
Using polyfills with `usage` option:
SyntaxError: /path/to/project/src/storage/index.ts: Unexpected token, expected ";" (4:6)
  2 | import { AppError } from '../errors';
  3 | 
> 4 | await import('./db/dbDiskMethods');
    |       ^
  5 | 
  6 | const getDbDiskMethods = async () => {
  7 |   return await import('./db/dbDiskMethods');

作为一个附带问题,为什么 preset-env 会加载调试输出中显示的 5 个语法插件,但会跳过顶级 await 语法插件?

【问题讨论】:

    标签: javascript node.js babeljs top-level-await


    【解决方案1】:

    这是因为preset-env 只加载accepted proposals。目前,顶级 await 尚未被该语言接受,它位于 stage 3

    一般来说,preset-env 可用的插件列表来自compat-datapackage。在运行 Babel 时,preset-env 实际使用的插件列表取决于

    抛出 OP 错误是因为 Babel 是从命令行运行的。如果它是由 babel-loader 运行的,那么“缺失”的插件会自动添加,如上面第二点所述。

    【讨论】:

      【解决方案2】:

      为上一个答案添加一些细节:

      TLDR

      为了让顶级awaitNode.js 中工作:将您的文件名更改为*.mjs 或将"type": "module" 添加到您的package.json

      深入解释

      首先,top-level-awaithas moved to "Stage 4"(意思是:“完成,发货”),因此现在是 ES 标准的一部分。 Node v14.18 开箱即用(但有一些注意事项如下所述)。

      遗憾的是,top-level-await 调用者语义相当复杂。这意味着它不是“正常工作”,而是取决于“加载/需要/导入逻辑”:

      1. CommonJS 不支持顶级await。这有几个含义;最重要的是:顶级await只适用于ESMs (ECMAScript Modules);或者换句话说:它仅在您使用import 时有效,如果您使用require 则无效。更多含义将在下文讨论。
      2. 更麻烦的是,顶级await 语义完全由模块“调用者”或“加载者”决定。没有办法只获取单个顶级 await 文件的代码并使其“与 es5 兼容”而不更改加载/需要/导入此代码的文件。正如您在its few lines of source code 中看到的:没有捆绑器,顶级await babel 插件实际上根本不会更改代码,它只是设置了一些用于确定代码正确性的选项。
      3. 从好的方面来说,如果您将顶级 await 与捆绑器一起使用(例如 Webpack、Rollup 等),如果您 babel 您的代码并包含 preset-env 或 @987654326,它应该可以正常工作@。大多数情况下,这将是您的首选,尤其是当您想在浏览器中运行时。请注意,许多前端框架“包装器”(例如create-react-app@vue/clistorybook)在底层使用webpack,因此应该开箱即用。 preset-env 自动添加插件if the code caller (e.g. babel-loader for webpack) allows it

      Node.js 中的顶级await

      由于上述语法的复杂性,Node.js 默认不支持顶级await。如果您尝试运行node myscript.js,您将收到错误消息:await is only valid in async functions and the top level bodies of modules。这个错误信息其实很清楚,但是对于不熟悉 Node.js 复杂模块系统的人来说是难以理解的。

      问题是,除非您采取非常具体的步骤告诉 Node 它将您的文件视为“模块”(或“ES 模块”或ESM),否则它不会。这是因为ESMs 是一个较新的功能,并且与旧的CommonJS 模块并不完全兼容,当人们尝试将两者混合使用时会导致很多挫败感。

      The node documentation states:

      Node.js 在将以下内容作为 ES 模块传递给 node 作为初始输入时,或在 ES 模块代码中的 import 语句引用时:

      • 以 .mjs 结尾的文件。
      • 当最近的父 package.json 文件包含值为“module”的顶级“type”字段时,文件以 .js 结尾。
      • 作为参数传入 --eval 的字符串,或通过 STDIN 管道传输到节点,带有标志 --input-type=module。

      因此,让您的顶级 await 进入 Node 的方法是使用 .mjs 文件扩展名,或在您的 package.json 中设置 type 字段。然后一切都会顺利进行,直到您陷入涉及 ESM 的任何其他陷阱。

      【讨论】:

        猜你喜欢
        • 2019-03-17
        • 2019-06-06
        • 1970-01-01
        • 2018-04-27
        • 2018-03-18
        • 1970-01-01
        • 2021-03-03
        • 2018-09-06
        • 2021-03-09
        相关资源
        最近更新 更多