68

I am getting an error when trying to import a local file, though no problem when using npm packages.

server.js

import express from 'express'
import next from 'next'

import apis from './src/server/api'

api.js

export default {
  ello: 'bye',
  jamie: 'hey'
}

Starting app

node --experimental-modules --inspect server.js

Error

For help, see: https://nodejs.org/en/docs/inspector
(node:20153) ExperimentalWarning: The ESM module loader is experimental.
internal/modules/esm/default_resolve.js:59
  let url = moduleWrapResolve(specifier, parentURL);
            ^

Error: Cannot find module '/var/www/goldendemon.hutber.com/src/server/api' imported from /var/www/goldendemon.hutber.com/server.js
    at Loader.resolve [as _resolve] (internal/modules/esm/default_resolve.js:59:13)
    at Loader.resolve (internal/modules/esm/loader.js:70:33)
    at Loader.getModuleJob (internal/modules/esm/loader.js:143:40)
    at ModuleWrap.<anonymous> (internal/modules/esm/module_job.js:43:40)
    at link (internal/modules/esm/module_job.js:42:36) {
  code: 'ERR_MODULE_NOT_FOUND'
}

4 Answers 4

113

I'm answering my own question if anybody else has this problem.

It turns out in experimental mode you need to define the full path with extension. So I am trying to import index.js thinking it will know.

To fix it:

import express from 'express'
import next from 'next'
import api from './src/server/api/index.js'
5
  • 9
    Jeez that was challenging...adding on the .js extension was what was holding me up ughh. Your answer was helpful in diagnosing this! Commented May 17, 2020 at 20:40
  • 10
    How do you make this work with typescript ? when I run tsc, my files are compiled with imports without the extension. Commented Jun 10, 2020 at 22:34
  • 9
    For TypeScript (using Node 12), see the answer by Mateus (#answer-57558982). Using this (--es-module-specifier-resolution=node) option when running the script with node will solve the problem, though you may face other minor issues (eg, __dirname not being defined). Commented Jul 21, 2020 at 18:40
  • I figured Node experimental mode is too troublesome to just run a simple test script for playing around with ESM style imports, so I've resign myself to just transpiling, using esbuild. I wrote this short bash script to accomplish this: #!/bin/bash script=$1 out_path==/tmp/$script-out.js npx esbuild --platform=node --bundle --outfile=$out_path $script node $out_path Commented Jul 13, 2021 at 17:55
  • Hey, not sure if it will resolve your issue, but give it a shot. Check this Github comment and this Node Docs. Summing up, you can add "#api": "./src/server/api/index.js" to your package.json["imports"], and the import can be resolved with import api from '#api' Commented Oct 3, 2021 at 21:31
55

on Node v12 you have 2 options:

  1. Use type="module" in package.json, experimental modules and specify extensions with specifier-resolution like this:

    node --experimental-modules --es-module-specifier-resolution=node server.js
    
  2. Don't use specifier-resolution, you'll have to specify the extension of your files every where.

Update (from comment), for Node v18:

node --experimental-specifier-resolution=node server.js
2
  • 3
    Please note that there's no clarity yet if --es-module-specifier-resolution is here to stay. It will also mean that the code isn't compatible with other ES module runtimes (e.g. the browser). Commented Jan 19, 2020 at 16:17
  • 1
    got converted to: --experimental-specifier-resolution=node seen here (careful, theres a typo in that post)
    – Basti
    Commented Dec 23, 2021 at 18:07
4

It should also work if you name your module file with a .mjs extension. Also, other ways to enable ESM are mentioned here.

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, or extensionless files, when the nearest parent package.json file contains a top-level field "type" with a value of "module".

  • Strings passed in as an argument to --eval or --print, or piped to node via STDIN, with the flag --input-type=module.

0

Or do like I did and just use transpilation the minute your source code deals with ES style module imports or some other non-standard JavaScript code, (E.g. TypeScript) on Node. For reference see this quick bash script I wrote, saved as .script/run-it.sh inside of my Node project:

#!/bin/bash

script=$1
out_path==/tmp/$script-out.js
npx esbuild --platform=node --bundle --outfile=$out_path $script
node $out_path 

I added it as a run script in my package.json:

"scripts": {
    "test": "sst test",
    "start": "sst start",
    "build": "sst build",
    "deploy": "sst deploy",
    "remove": "sst remove",
    "run-it": "./.script/run-it.sh"
  },

And my target script (import-test.js), what I want to emit/transpile as JavaScript code:

import { default as myImport } from './lib/index.js'
console.log(myImport)

And now I run it:

$ npm run run-it ./import-test.js 

> [email protected] run-it /Users/jmquij0106/git/a-rebalancing-act
> ./.script/run-it.sh "./import-test.js"

[Function: main]

Bottomline is spare yourself the pain and just emit CommonJS compliant code whenever dealing with ES Modules on Node.js, see this comment/issue.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.