We have seen that Node.js supports files with the extension .js and .mjs. If it seems confusing to you, hold on tight because it’s about to get bumpy I’ll explain it right now.
CommonJS and ECMAScript Modules (ESM) are two module systems, used in JavaScript to organize and reuse code across different files.
When Node.js appeared (2009), JavaScript did not have a native module system. Node.js decided to create its own, CommonJS. A decision we are still dealing with today.
Later, JavaScript created its “official” file system, ECMAScript Modules (ESM) (2015). They were running a bit behind, poor things. But the official syntax had many improvements over the previous ones.
You might say, well… “Perfect! Let’s drop CommonJS and use ESM!”. By that time, there was so much code that it has taken years to replace them.
In fact, Node.js didn’t become compatible with ESM until its version 12 (2019). That’s 10 long years of CommonJS. Therefore, many tools and libraries still haven’t been ported.
That’s why there are two extensions:
.jsworks with CommonJS modules 🤔.mjsworks with ECMAScript Modules (ESM) 👍
Whenever possible, use ECMAScript Modules (ESM). They are the standard solution and are meant to replace CommonJS.
Using Modules in JavaScript
Let’s see how to use CommonJS and ECMAScript Modules. As said, unless some library lacks compatibility, or there’s another compelling reason, we will prefer ECMAScript.
But we are going to see the syntax of both, because you will find half of the documentation on the internet, libraries, or projects you work on, using each system.
CommonJS Legacy
CommonJS is a module system that was developed for use in server environments, like Node.js. It uses the keywords require and module.exports to import and export modules.
Here’s an example of its use.
// Import a module
const anotherModule = require('./anotherModule');
// Export a module
module.exports = {
foo: 'bar'
};
Node.js has used CommonJS as its primary module system for a long time.
ECMAScript Modules (ESM) Standard
ECMAScript Modules are the JavaScript module standard defined in the ECMAScript standard (the underlying standard of JavaScript). It uses the keywords import and export to import and export modules, respectively.
Example of its use:
// Import a module
import anotherModule from './anotherModule';
// Export a module
export const foo = 'bar';
ECMAScript modules are designed to work both in modern web browsers and server environments (like Node.js), which means they are more portable.
Furthermore, ECMAScript modules are loaded asynchronously, which can be more efficient in certain scenarios.
JS, CJS, and MJS Extensions
Before the adoption of ECMAScript modules in Node.js, JavaScript files were written with the .js extension and used CommonJS for code import and export.
Starting with Node.js v12, support for ECMAScript modules was introduced, allowing the use of the same import/export syntax used in modern web browsers.
Since the “good” extension was already taken, they had to call it .mjs. To differentiate them, some (not many) started calling files containing CommonJS modules .cjs, which they previously called .js.
But, on the other hand, a .js file can be treated as an ESM module if its package.json file has the type set to “module”. Like this.
{ “name”: “tu_modulo”, “type”: “module”, “version”: “1.0.0” …more }
That is, if they tried to make it more confusing, they couldn’t even if they wanted to 😅
How to Use CommonJS Modules in ESM
In general, you should try to use ESM modules. They are the standard and you should always use them… and the world is wonderful and full of unicorns and rainbows 🦄.
No, the truth is that having two systems is a hassle, and you will have to learn to use both, and live with both. Using one or the other is too difficult, but when you have to mix them… you might pull your hair out sometimes.
In principle, in your projects you should use ESM modules. Node.js’s own documentation has migrated to using ESM. But there are still many libraries that haven’t been ported yet, and some that never will be.
So let’s see how to import a CommonJS module in ESM. Which, fortunately, is very simple.
import { createRequire } from 'node:module';
const require = createRequire(import.meta.url);
const your_functions = require('your_commonjs_module');
Download the Code
All the code for this post is available for download on Github
