84

I have an ES6 module right.mjs. Executing it as a parameter to node works well:

$ 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.

In contrast to that, the following input in the REPL waits for further input:

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

> import 'right.mjs';
...

I don't understand why.

The same with:

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

Trying to require results in:

> 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)

So, how can I import an ES module in the Node.js REPL?

jcollum
  • 43,623
  • 55
  • 191
  • 321
Min-Soo Pipefeet
  • 2,208
  • 4
  • 12
  • 31

5 Answers5

68

It is possible in Node.js v14, but you need to use the import function, rather than the import statement.

$ 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"
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
diachedelic
  • 2,195
  • 1
  • 24
  • 28
  • 1
    :( This looks like it still doesn't handle modules containing the `export` keyword –  Nov 01 '20 at 08:15
  • 4
    @hallo it works for me with both named and default exports, but bear in mind you have to access the default export at `module.default`. – diachedelic Nov 02 '20 at 07:07
  • Unfortunately, if your files contain imports that do not have the extension in the filename (`import something from './something';) as most projects do, it will error on you. Is there any way to fix this? – Merlin -they-them- Dec 09 '20 at 00:54
  • 3
    The extension is now mandatory: https://nodejs.org/api/esm.html#esm_mandatory_file_extensions - I don't know of a way around that. – diachedelic Dec 09 '20 at 15:47
  • 1
    The problem with asynchronous import is solved with `node --experimental-repl-await`. It's `const myModule = await import("./my-module.js")` then. An extension can be omitted with `--experimental-specifier-resolution=node` or custom loader (`--experimental-loader`). – Estus Flask Jun 29 '21 at 05:53
57

This is not currently possible. ES modules are supposed to be imported from the ES module scope, while REPL isn't considered one. This can improve with time because the support of ES modules is experimental. The use of require and import is mutually exclusive in the Node.js module implementation, and REPL already uses require.

Dynamic import is supported in the REPL since Node.js 13. With node --experimental-repl-await, it is:

await import('./right.mjs');
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Estus Flask
  • 206,104
  • 70
  • 425
  • 565
  • 3
    I believe this is now being tracked in https://github.com/nodejs/node/issues/33369 – Dan Fabulich May 19 '20 at 22:34
  • 4
    REPL await is turned on by default as of Node 16.6, and has [a different syntax](https://nodejs.org/api/cli.html#--no-experimental-repl-await) for the feature flag. – Phil Jan 03 '22 at 08:42
  • 2
    This needs to be updated. `let { date } = await import('quasar')` works fine in Node 18 without any flags. – jcollum Jul 19 '22 at 17:48
  • Except it's not a static import, so it's not "working fine". – Biller Builder Nov 14 '22 at 11:53
  • @BillerBuilder Can you clarify what's the problem with that? Top-level await is supported as well. – Estus Flask Nov 14 '22 at 12:22
  • 1
    Being able to import ES modules only dynamically (and therefore asynchronously) within the scope of the current module is exactly how the current CJS handles ESM imports. Ergo the current REPL clearly runs in a CJS mode in some sort of async wrapper. Considering REPL is used mostly for checking synchronous code, this can lead to a different unexpected runtime behaviour. – Biller Builder Nov 14 '22 at 14:55
39

With Node.js v16.9.1, which supports top level await, it's even simpler:

let { date } = await import('quasar') // module under node_modules, or your own one, etc.
date.getWeekOfYear(new Date())
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Marshal
  • 4,452
  • 1
  • 23
  • 15
  • This is the right answer for Node 18 as well. Should be the accepted answer now but the poster is essentially gone. – jcollum Jul 19 '22 at 17:45
11

It is not precisely what was asked about (not really REPL), but (using Node.js 12.6.0), it is possible to execute ESM code from the command line via --eval:

  1. First, if your ES modules have a .js extension instead of .mjs, put "type": "module" in file package.json (see Modules: ECMAScript modules, Enabling) to allow Node.js treating JavaScript files as modules
  2. Run node --experimental-modules --input-type=module --eval 'code here'

You could alias this as esmeval for example:

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

And then you can use it as:

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

If you can't use Node.js 12 yet as a primary version, but can install via nvm, make the alias point to the v12 installation:

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

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
jakub.g
  • 38,512
  • 12
  • 92
  • 130
  • Note that implementation of ES modules is changing with node 13 so this might not work in newer versions of node. – jakub.g Feb 25 '20 at 15:26
0

Some googling on using a module in the REPL brought me here, and this is what worked for me:

npm install -g libphonenumber-js​
node​
> let lib = require('/opt/homebrew/lib/node_modules/libphonenumber-js')
> lib.isValidPhoneNumber('+97412345678')
false
> lib.isValidPhoneNumber('+15625551234')
true
jgreen
  • 1,132
  • 2
  • 14
  • 18
  • 1
    As far as I can tell, this has nothing to do with ES modules. Since you are using a `require`, `libphonenumber-js` is loaded as a CJS module which is probably is (?). If I'm correct, this doesn't apply to this question concerning REPL + ESM. – Tomáš Hübelbauer Jul 02 '23 at 09:31