Explicitly, point to your main file (usually index.js
). E. g.
import ... from './models' // ❌
import ... from './models/index.js' // ✅
Alternative way (see the below UPDATE):
Use --experimental-specifier-resolution=node
flag. For example:
node --experimental-specifier-resolution=node main.js
See:
https://nodejs.org/api/esm.html#esm_customizing_esm_specifier_resolution_algorithm
UPDATE (Node.js v19+):
Node.js has removed the
--experimental-specifier-resolution
flag. Its functionality can now be achieved via custom loaders.
https://nodejs.org/en/blog/announcements/v19-release-announce/#custom-esm-resolution-adjustments
The simplest loader (that only appends .js
extension if needed) is something like:
import {isBuiltin} from 'node:module'
// noinspection JSUnusedGlobalSymbols
export const resolve = (specifier, context, nextResolve) => // This function can be `async` too
nextResolve(isBuiltin(specifier) || specifier.endsWith('.js') ? specifier : `${specifier}.js`, context)
Name it loader.js
(or loader.mjs
if you don’t set yet "type": "module"
in your package.json
).
Then if you run your script (e.g. ./some-script.js
) using this loader:
node --loader ./loader.js some-script.js
the import
s within some-script.js
(and the files imported in it) can omit .js
extension.
See more complex examples: https://github.com/nodejs/loaders-test