Reputation: 1130
Given the following quote from the ECMAScript documentation and minimal reproducible code example,
why does using the .js
file extension for an Javascript ES module import cause an ERR_MDOULE_NOT_FOUND
error when package.json
has "type": "module"
?
From Node.js v16.3.0 documentation - Determining module system (emphasis mine)
Determining module system
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".
The documentation says that a .js
file extension is treated as an ES module as long as we declare our package's type as module
.
Now consider the following minimal reproducible example of how a .js
file does not get treated as an ES Module unless renamed to .mjs
.
package.json
{
"type": "module"
}
foo.js
export default 'foo module';
index.js
import foo from './foo';
console.log("Hello", foo);
With the above file names and code, the following error occurs.
$ node index.js
node:internal/process/esm_loader:74
internalBinding('errors').triggerUncaughtException(
^
Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/Users/georgep/nodemodulestest/foo' imported from /Users/georgep/nodemodulestest/index.js
Did you mean to import ../foo.js?
at new NodeError (node:internal/errors:363:5)
at finalizeResolution (node:internal/modules/esm/resolve:307:11)
at moduleResolve (node:internal/modules/esm/resolve:742:10)
at Loader.defaultResolve [as _resolve] (node:internal/modules/esm/resolve:853:11)
at Loader.resolve (node:internal/modules/esm/loader:89:40)
at Loader.getModuleJob (node:internal/modules/esm/loader:242:28)
at ModuleWrap.<anonymous> (node:internal/modules/esm/module_job:73:40)
at link (node:internal/modules/esm/module_job:72:36) {
code: 'ERR_MODULE_NOT_FOUND'
}
But, if I change the following
foo.js
to foo.mjs
, andimport
in index.js
to reflect foo.mjs
=> import foo from './foo.mjs';
Then the program executes without error.
Why is the .mjs
file ending necessary in this situation, when the documentation clearly states that setting "type": "module"
in package.json
should mean that node treats regular .js
files like ES modules?
Environment:
$ node -v
v16.3.0
Upvotes: 13
Views: 13740
Reputation: 1130
As pointed out by @ASDFGerte in their comment, and also fully explained in this answer to "Omit the file extension, ES6 module NodeJS":
In ES Modules, file extension is mandatory, so you cannot omit the .js
file extension like you can in CommonJS.
This was the source of my confusion. Once I include the file extension, ES Modules work. For example, this works.
index.js
import foo from './foo.js';
The solution feels pretty obvious, but without knowing the exact reason, this difference between the CommonJS convention and ES Modules would have felt like a bug anyways, so I'm happy to have learned how each module system treats its file extensions.
Upvotes: 8