Learn the key differences between .js and .mjs files in Node.js and how they impact module resolution and code execution.
JavaScript initially lacked a formal module system, leading developers to use various workarounds. Node.js introduced CommonJS modules using the .cjs
extension, while ECMAScript, the language standard, later established its own module system with the .mjs
extension. This article clarifies the differences between .js
, .mjs
, and .cjs
files in JavaScript, explaining their historical context, syntax, and how they are treated in Node.js and browsers. It also discusses the reasons behind the confusion surrounding these extensions and provides insights into when to use each one.
JavaScript has seen the rise of different module systems, leading to different file extensions like .js
, .mjs
, and .cjs
. Here's a breakdown:
1. The Era Before Modules:
.js
files.2. CommonJS (.cjs
): The Node.js Way
.cjs
files explicitly signal that they use CommonJS modules.// module1.cjs
module.exports = {
sayHello: function() {
console.log("Hello from module1!");
}
};
// app.cjs
const module1 = require('./module1.cjs');
module1.sayHello();
3. ECMAScript Modules (.mjs
): The Standard
.mjs
files clearly indicate the use of ECMAScript modules.// module2.mjs
export function sayHi() {
console.log("Hi from module2!");
}
// app.mjs
import { sayHi } from './module2.mjs';
sayHi();
4. The Role of .js
.js
was the default for all JavaScript files..js
files based on your project's configuration:
"type": "module"
is set in your package.json
, .js
files are treated as ECMAScript modules.<script>
tag's type="module"
attribute.Why Use .mjs
?
.mjs
removes ambiguity, making it instantly clear that a file uses ECMAScript modules..mjs
files.Why the Confusion?
.js
, can lead to confusion.In Summary:
.cjs
: CommonJS modules (primarily Node.js)..mjs
: ECMAScript modules (the standard, supported in modern environments)..js
: Can be either CommonJS or ESM in Node.js, depending on configuration. In browsers, the <script>
tag's type
attribute is more relevant.While .mjs
promotes clarity, the best practice for your project depends on your environment, dependencies, and whether you prioritize explicitness or maintaining compatibility.
This code provides examples of using both CommonJS and ECMAScript modules in a JavaScript project. It showcases how to export and import functions using module.exports
and require
for CommonJS, and export
and import
for ECMAScript modules. The project is structured with separate files for each module system and a package.json
file that defines the entry points and module type for Node.js. The code includes examples of running both CommonJS and ECMAScript modules using npm
scripts.
This example demonstrates the different module systems in JavaScript.
Project Structure:
module-example/
โโโ module1.cjs
โโโ module2.mjs
โโโ app.cjs
โโโ app.mjs
โโโ package.json
1. CommonJS (module1.cjs, app.cjs):
// module1.cjs
module.exports = {
sayHello: function() {
console.log("Hello from module1.cjs!");
}
};
// app.cjs
const module1 = require('./module1.cjs');
module1.sayHello(); // Output: Hello from module1.cjs!
2. ECMAScript Modules (module2.mjs, app.mjs):
// module2.mjs
export function sayHi() {
console.log("Hi from module2.mjs!");
}
// app.mjs
import { sayHi } from './module2.mjs';
sayHi(); // Output: Hi from module2.mjs!
3. package.json (for Node.js):
{
"name": "module-example",
"version": "1.0.0",
"description": "Example of different JavaScript module systems",
"main": "app.cjs",
"type": "module", // This line makes .js files ESM by default
"scripts": {
"start": "node app.cjs",
"start-esm": "node app.mjs"
}
}
Explanation:
module.exports
exports functionality, and require
imports it.export
keywords export functionality, and import
keywords import it."type": "module"
signifies that .js
files in this project are treated as ECMAScript modules.npm start
for CommonJS and npm run start-esm
for ESM.Important Notes:
<script type="module">
tag for ESM.General:
.mjs
offers clarity, using it might require adjustments in your build process or configuration, especially if you rely on tools that are not fully ESM-aware.CommonJS:
ECMAScript Modules:
Choosing a Module System:
<script type="module">
tag is the standard approach for working with modules.Beyond File Extensions:
Future:
This table summarizes the different JavaScript module file extensions and their uses:
File Extension | Module System | Description | Used In |
---|---|---|---|
.js |
None (Historically) | Original extension for all JavaScript files, before modules. | Everywhere |
.js |
CommonJS or ESM | Can be either module system in Node.js, depending on package.json configuration. |
Node.js |
.js |
N/A | Module type determined by <script type="module"> in browsers. |
Browsers |
.cjs |
CommonJS | Explicitly indicates a CommonJS module. | Node.js |
.mjs |
ECMAScript Modules (ESM) | Explicitly indicates an ECMAScript module. | Node.js, Modern Browsers |
Key Takeaways:
.cjs
and .mjs
offer clarity by explicitly signaling the module system used..js
remains flexible but can lead to confusion due to its context-dependent interpretation in Node.js.The evolution of JavaScript modules brought both progress and confusion. While the initial lack of a module system led to workarounds, Node.js introduced CommonJS (.cjs
) and later, ECMAScript standardized modules (.mjs
). This dual system, along with the flexible role of .js
, often causes uncertainty. Choosing between .cjs
and .mjs
depends on your project's environment and priorities. While .mjs
offers clarity and aligns with modern practices, .cjs
remains relevant in Node.js. Ultimately, understanding these module systems is crucial for navigating the JavaScript landscape. As the language continues to evolve, embracing the standardized module system will lead to more maintainable and scalable JavaScript applications.