Babel preset-env 未加载节点目标的顶级等待语法
Babel preset-env not loading top-level await syntax for node target
我正在尝试使用新的顶级 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 语法插件?
发生这种情况是因为 preset-env 仅加载 accepted proposals. Currently, top level await has not yet been accepted into the language, it's at stage 3。
一般来说,preset-env 可用的插件列表来自 compat-data
package。 babel 为 运行 时 preset-env 实际使用的插件列表取决于
- 提供的 options. Depending on the selected options, additional plugins not present in
compat-data
may be included. E.g., depending on module syntax options, additional plugins may be added.
- Where
babel
is called from. E.g., Webpack supports top level await,所以尽管没有被接受的提议,preset-env 无论如何都会在从 babel-loader
. 调用 babel
时添加它
OP 错误被抛出,因为 Babel 是来自命令行的 运行。如果它是 babel-loader 运行,"missing" 插件会自动添加,如上面第二点所述。
为之前的答案添加一些细节:
TLDR
为了让顶级 await
在 Node.js
中工作:将您的文件名更改为 *.mjs
或将 "type": "module"
添加到您的 package.json
.
深入讲解
首先,top-level-await
has moved to "Stage 4" (meaning: "Finished, Shipping"), and thus is now part of the ES standard. Node v14.18 开箱即用(但下面解释了注意事项)。
遗憾的是,top-level-await
调用方语义相当复杂。这意味着它不“只是工作”,而是取决于“load/require/import 逻辑”:
- CommonJS 不支持顶级
await
。这有几个含义;最重要的是:顶级 await
仅适用于 ESM
s (ECMAScript Modules);或者换句话说:它仅在您使用 import
时有效,而在您使用 require
时无效。更多的影响将在下面讨论。
- 更麻烦的是,顶级
await
语义完全由模块“调用者”或“加载者”决定。没有办法只获取单个顶级 await
文件的代码并使其“与 es5 兼容”而不更改 load/require/import 此代码的文件。正如你在 its few lines of source code 中看到的:没有 bundler,顶级 await
babel 插件实际上根本没有改变代码,它只设置了一些选项,这些选项用于稍后确定代码的正确性下线。
- 从好的方面来说,如果您将顶级
await
与捆绑器(例如 Webpack、Rollup 等)一起使用,它应该可以正常工作,如果您 babel
您的代码并包含preset-env or the top-level-await plugin. This will be your go-to choice most of the time, especially when you want to run things in the browser. NOTE that many frontend framework "wrappers" (such as create-react-app
, @vue/cli
or storybook
) use webpack
under the hood, and thus should work out of the box. preset-env
automatically adds the plugin 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
),否则它不会。这是因为 ESM
s 是更新的功能,并不隐式兼容旧的 CommonJS
模块,当人们尝试混合使用两者时会导致很多挫折。
The node documentation states:
Node.js will treat the following as ES modules when passed to node as the initial input, or when referenced by import statements within ES module code:
- Files ending in .mjs.
- Files ending in .js when the nearest parent package.json file contains a top-level "type" field with a value of "module".
- Strings passed in as an argument to --eval, or piped to node via STDIN, with the flag --input-type=module.
因此,让您的顶级 await
进入 Node 的方法是使用 .mjs
文件扩展名,或在您的 [=14] 中设置 type
字段=].然后一切都会顺利进行,直到您陷入任何其他涉及 ESM 的陷阱。
我正在尝试使用新的顶级 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 语法插件?
发生这种情况是因为 preset-env 仅加载 accepted proposals. Currently, top level await has not yet been accepted into the language, it's at stage 3。
一般来说,preset-env 可用的插件列表来自 compat-data
package。 babel 为 运行 时 preset-env 实际使用的插件列表取决于
- 提供的 options. Depending on the selected options, additional plugins not present in
compat-data
may be included. E.g., depending on module syntax options, additional plugins may be added. - Where
babel
is called from. E.g., Webpack supports top level await,所以尽管没有被接受的提议,preset-env 无论如何都会在从babel-loader
. 调用
babel
时添加它
OP 错误被抛出,因为 Babel 是来自命令行的 运行。如果它是 babel-loader 运行,"missing" 插件会自动添加,如上面第二点所述。
为之前的答案添加一些细节:
TLDR
为了让顶级 await
在 Node.js
中工作:将您的文件名更改为 *.mjs
或将 "type": "module"
添加到您的 package.json
.
深入讲解
首先,top-level-await
has moved to "Stage 4" (meaning: "Finished, Shipping"), and thus is now part of the ES standard. Node v14.18 开箱即用(但下面解释了注意事项)。
遗憾的是,top-level-await
调用方语义相当复杂。这意味着它不“只是工作”,而是取决于“load/require/import 逻辑”:
- CommonJS 不支持顶级
await
。这有几个含义;最重要的是:顶级await
仅适用于ESM
s (ECMAScript Modules);或者换句话说:它仅在您使用import
时有效,而在您使用require
时无效。更多的影响将在下面讨论。 - 更麻烦的是,顶级
await
语义完全由模块“调用者”或“加载者”决定。没有办法只获取单个顶级await
文件的代码并使其“与 es5 兼容”而不更改 load/require/import 此代码的文件。正如你在 its few lines of source code 中看到的:没有 bundler,顶级await
babel 插件实际上根本没有改变代码,它只设置了一些选项,这些选项用于稍后确定代码的正确性下线。 - 从好的方面来说,如果您将顶级
await
与捆绑器(例如 Webpack、Rollup 等)一起使用,它应该可以正常工作,如果您babel
您的代码并包含preset-env or the top-level-await plugin. This will be your go-to choice most of the time, especially when you want to run things in the browser. NOTE that many frontend framework "wrappers" (such ascreate-react-app
,@vue/cli
orstorybook
) usewebpack
under the hood, and thus should work out of the box.preset-env
automatically adds the plugin if the codecaller
(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
),否则它不会。这是因为 ESM
s 是更新的功能,并不隐式兼容旧的 CommonJS
模块,当人们尝试混合使用两者时会导致很多挫折。
The node documentation states:
Node.js will treat the following as ES modules when passed to node as the initial input, or when referenced by import statements within ES module code:
- Files ending in .mjs.
- Files ending in .js when the nearest parent package.json file contains a top-level "type" field with a value of "module".
- Strings passed in as an argument to --eval, or piped to node via STDIN, with the flag --input-type=module.
因此,让您的顶级 await
进入 Node 的方法是使用 .mjs
文件扩展名,或在您的 [=14] 中设置 type
字段=].然后一切都会顺利进行,直到您陷入任何其他涉及 ESM 的陷阱。