如何在Node 8的REPL中导入ES模块?

87

我有一个ES6模块right.mjs。将其作为参数传递给node执行效果很好:

$ node --version
v8.10.0

$ node --experimental-modules right.mjs
(node:4492) ExperimentalWarning: The ESM module loader is experimental.
executing right module
`executing right module` is the output of the module.

相比之下,REPL中的以下输入会等待进一步的输入:

$ node --experimental-modules
> (node:4526) ExperimentalWarning: The ESM module loader is experimental.

> import 'right.mjs';
...

我不明白为什么。

同样的问题:

> import './right.mjs';
...

试图require会导致:

> require('./right.mjs');
Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: /home/xxx/right.mjs
    at Object.Module._extensions..mjs (module.js:686:11)
    at Module.load (module.js:565:32)
    at tryModuleLoad (module.js:505:12)
    at Function.Module._load (module.js:497:3)
    at Module.require (module.js:596:17)
    at require (internal/module.js:11:18)

那么,我如何在 Node.js REPL 中导入 ES 模块?


有一些支持模块的REPL,比如Replete - diachedelic
5个回答

70

在Node.js v14中是可能的,但您需要使用import函数,而不是import语句。

$ node

Welcome to Node.js v14.4.0.
Type ".help" for more information.
> let myModule;
undefined
> import("./my-module.js").then(module => { myModule = module });
Promise { <pending> }
> myModule.foo();
"bar"

1
这似乎仍然无法处理包含“export”关键字的模块。 - user4698348
4
@hallo 对我来说,无论是命名导出还是默认导出都可以正常工作,但请注意您必须通过 module.default 访问默认导出。 - diachedelic
不幸的是,如果您的文件包含没有在文件名中带有扩展名的导入(import something from './something';),就像大多数项目一样,它会出现错误。有没有什么办法可以解决这个问题? - Merlin -they-them-
3
现在扩展名是强制性的:https://nodejs.org/api/esm.html#esm_mandatory_file_extensions - 我不知道有什么绕过它的方法。 - diachedelic
1
异步导入的问题可以通过 node --experimental-repl-await 解决。然后就是 const myModule = await import("./my-module.js")。扩展名可以省略,使用 --experimental-specifier-resolution=node 或自定义加载器 (--experimental-loader)。 - Estus Flask

58
目前不支持此功能。ES modules 应该从 ES 模块范围导入,而 REPL 不被视为其中之一。由于 ES 模块的支持是实验性的,因此这可能会随着时间的推移得到改进。在 Node.js 模块实现中,使用 require 和 import 是互斥的,而 REPL 已经使用了 require。

自 Node.js 13 开始,REPL 支持动态 import。使用 node --experimental-repl-await 即可:

await import('./right.mjs');

3
我认为现在这个问题正在 https://github.com/nodejs/node/issues/33369 中被跟踪。 - Dan Fabulich
4
从Node 16.6开始,默认启用了REPL await,并且该功能标志具有不同的语法。这与实验性的REPL await不同。 - Phil
2
这需要更新。在Node 18中,let { date } = await import('quasar')可以正常工作,无需任何标志。 - jcollum
除非它是静态导入,否则它并没有“正常工作”。 - Biller Builder
@BillerBuilder,你能澄清一下问题在哪里吗?顶级等待也是支持的。 - Estus Flask
1
只能在当前模块的范围内动态地(因此是异步的)导入ES模块,这正是当前CJS处理ESM导入的方式。因此,当前的REPL显然在某种异步包装器中以CJS模式运行。考虑到REPL主要用于检查同步代码,这可能会导致不同的意外运行时行为。 - Biller Builder

39

有了支持顶层 await 的 Node.js v16.9.1,这甚至更简单:

let { date } = await import('quasar') // module under node_modules, or your own one, etc.
date.getWeekOfYear(new Date())

这也是 Node 18 的正确答案。现在应该接受这个答案,但发帖人基本上已经离开了。 - jcollum

11

这并不是完全符合问题所要求的(不是真正的REPL),但是(使用Node.js 12.6.0),可以通过--eval在命令行中执行ESM代码:

  1. 首先,如果您的ES模块具有.js扩展名而不是.mjs,请将"type": "module"放入文件package.json 中(参见 Modules: ECMAScript modules, Enabling)以允许Node.js将JavaScript文件视为模块
  2. 运行node --experimental-modules --input-type=module --eval 'code here'

例如,您可以将其别名为esmeval

alias esmeval='node --experimental-modules --input-type=module --eval'

然后,您可以像下面这样使用它:

esmeval 'import { method } from "./path/to/utils.js"; console.log(method("param"))'

如果您还不能使用Node.js 12作为主版本,但可以通过nvm进行安装,则可以使别名指向v12安装:

alias esmeval='/c/software/nvm/v12.6.0/node.exe --experimental-modules --input-type=module --eval'


请注意,随着 Node 13 对 ES 模块的实现方式发生变化,这段代码可能无法在更新版本的 Node 中正常工作。 - jakub.g

0

在 REPL 中使用模块的一些搜索让我来到了这里,以下是对我有效的方法:

npm install -g libphonenumber-js​
node​
> let lib = require('/opt/homebrew/lib/node_modules/libphonenumber-js')
> lib.isValidPhoneNumber('+97412345678')
false
> lib.isValidPhoneNumber('+15625551234')
true

1
据我所知,这与ES模块无关。由于您使用了requirelibphonenumber-js作为一个CJS模块被加载,这可能是正确的(?)。如果我没错的话,这与REPL + ESM的问题无关。 - Tomáš Hübelbauer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接