Continuing the Node.js ESM content, I'd like to talk about the comparitively straightforward alternative to using .mjs to get your Node.js applications to run as ECMAScript Modules (ESM) rather than CommonJS: including "type": "module"
in your package.json
.
"type": "module"
#Let's assume we've started with the following package.json
for a zero (production) dependency application:
{
"name": "apollo-lunar-module",
"version": "0.0.1",
"description": "A simple, fast, nice lunar lander module",
"main": "index.js",
"scripts": {
"lint": "standard"
},
"author": "Tierney Cyren <hello@bnb.im> (https://bnb.im/)",
"license": "MIT",
"devDependencies": {
"standard": "^16.0.3"
}
}
To have implicit ESM - that is, have our .js
files parsed as ESM - we' need to make the following change:
{
"name": "apollo-lunar-module",
"version": "0.0.1",
"description": "A simple, fast, nice lunar lander module",
"main": "index.js",
+ "type": "module",
"scripts": {
"lint": "standard"
},
"author": "Tierney Cyren <hello@bnb.im> (https://bnb.im/)",
"license": "MIT",
"devDependencies": {
"standard": "^16.0.3"
}
}
This specifically tells Node.js to parse your .js
files under this package.json
as ESM. Otherwise, by default (or when you use "type": "commonjs"
), Node.js will parse your .js
files as CommonJS. There's a few things to note:
Node.js specifically looks for the closest package.json
to determine whether or not to parse .js
as ESM or CommonJS.
"Closest" is important here. If there's a package.json
that's closer to .js
files than your project's package.json
, and it does not have "type": "module"
(or a dual export, which is out of the scope of this post), CommonJS will be used for those .js
files. The most common/obvious example of this is the code within your /node_modules/
that may not be ESM, and shouldn't be parsed as such.
Further, it's worth noting that explicitly using .cjs
overrides "type": "module"
. This is extremely useful if you're converting a codebase from CommonJS to ESM.
"type": "module"
? #For you, the user, the straightforward answer to this is that using "type": "module"
is a better developer experience than having to explicitly use .mjs
in every single JavaScript file in your project if you're going to have a non-trivial number of files.
Using "type": "module"
is often going to be a better developer experience for maintainers for a number of reasons:
"type": "module"
and converting all the CommonJS code to use the .cjs
file extension.package.json
) are only supported behind the --experimental-json-modules
flag. It does seem that necessary proposals to streamline this seem to be making pretty decent progress through the relevant standards processes.