There are a number of different components of Node.js that have been intentionally structured to allow you to use standard (as in, how the ECMAScript Specification defines it) ESM by default and extend/augment that experience if you'd like to.
Today, I want to get into one of the baisc elements of ESM in Node.js: the
The straightforward answer to this is that having different file extensions allows you to be explicit in how you want to run your code -
.mjs will always be run as ESM,
.cjs will always be run as CommonJS.
Because of the differences in how ESM and CommonJS work, Node.js runs them differently by default. This results in the runtime needing an indicator about which way you want to run your code - as ESM or as CommonJS.
There's three different ways that this indicator could be expressed: explicitly, implicitly, and by default.
To not break over a decade of projects and over a million modules that are expecting to Just Work, the default the project landed on was CommonJS - sensible, especially when you consider millions of lines of code and multitudes of applications already running in this way.
The way to explicitly assert that the code you're running is ESM and should be run as such is to just use the
.mjs file extension (which, if you're concerned, is also supported in web browsers as long as the
When you use
.cjs - Node.js knows that