Modules
Modules in a proper sense emerged in JavaScript with arrival of libraries like CommonJS and Asynchronous Module Definitions (AMD).
CommonJS Modules
require()
andmodule.exports
- Asynchronous
Exporting
// myModule.js
function greet(name) {
return `Hello, ${name}!`;
}
module.exports = greet;
For multiple exports
// myModule.js
exports.greet = function(name) {
return `Hello, ${name}!`;
};
exports.farewell = function(name) {
return `Goodbye, ${name}!`;
};
Importing
// main.js
const greet = require('./myModule.js');
console.log(greet('Alice')); // Output: Hello, Alice!
// Or, if using multiple exports:
const myModule = require('./myModule.js');
console.log(myModule.greet('Alice')); // Output: Hello, Alice!
console.log(myModule.farewell('Alice')); // Output: Goodbye, Alice!
Selectively loading modules
In NodeJS, you can load certain parts of modules selectively.
const { Buffer } = require('node:buffer');
ES6 Modules
With arrival of ES6, modules were introduced natively in JS.
The existing require
syntax was not used since require
is asynchronous.
Exporting
Named exports
// utils.js
export const add = (x, y) => x + y;
export const multiply = (x, y) => x * y;
Default export
// greeting.js
export default function greet(name) {
return `Hello, ${name}!`;
}
Importing
Named import
// main.js
import { add, multiply } from './utils.js';
console.log(add(2, 3)); // Output: 5
console.log(multiply(2, 3)); // Output: 6
Default import
// main.js
import greet from './greeting.js';
console.log(greet('Alice')); // Output: Hello, Alice!
Selectively loading
// main.js
import { add } from './utils.js';
console.log(add(2, 3)); // Output: 5
Es6 vs CommonJS
Synchronicity and systems.char.performance.i.throughput (Private)
Loading is synchronous(step by step) for require on the other hand import can be asynchronous(without waiting for previous import) so it can perform a little better than require.
Compatibility
ES modules are compatible with Common JS modules. That is, you can import commonJS modules if you're using ES modules.
But you can't import ES modules if you're using CommonJS modules. A workaround is to use import()
syntax.
Since the modules are not compatible you have to be careful while shipping your packages.
Dual Package Hazard
Its a situation in which two versions of the same package get loaded within the same runtime environment. Of course, an application or package wouldn't do this intentionally, but it is common that the application itself loads one version, while a dependency of the application loads another version.
Which module system?
Default
Browsers didn't have module systems early on. Although, Bundlers can process them. Even today, most modern browsers support only ES modules and have limited support for CommonJS modules.
NodeJS treats all JS code as CommonJS modules.
Why though? => Backward Compatibility
Many existing projects used commonJS modules, since it came first and nodeJS also traditionally supported CommonJS. Keeping ES as default would mean rewriting or transpiling all that code. Keeping CommonJS as default maintains compatiblity with existing projects.
This allows the developers to gradually adopt the newer spec and stick with CommonJS until the benefits of migration outweigh the compatibility concerns.
Configuration
- package.json:
"type":"module"
indicates that the project uses ES modules. - File extensions (optional)
.cjs
: CommonJS module (Even in ES module project).mjs
: ES Module (Differentiates ES modules from default.js
CommonJS modules)
Final Outcome of bundler
Even if you use an ES module system in your project, if your Bundler emits commonJS code, then you are not really shipping an ES package.
Interoperability
There are certain workarounds to ensure interoperability of both module systems in your project.
- Bundler: Bundlers can be configured to handle both modules. It takes care of dependencies and compatibility.
- Use
import()
syntax to import ES modules in CommonJS modules.import
statement works the same for ES and CommonJS modules in an ES module.
References
- import() - MDN
- Dual Package Hazard - GeoffreyBooth/dual-package-hazard - Github: Example illustrating the hazard posed by dual CommonJS/ES module packages
- JS Modules - GFG
- Migrating from CommonJS - Pure ESM package
- Import vs Require - The Biggest JavaScript Divide - Matt Pocock - YouTube
- The difference between "require(x)" and "import x"
Backlinks