Docs
14.3-CommonJS-AMD
14.3 CommonJS & AMD
š Introduction
While ES Modules are the modern standard, understanding CommonJS and AMD is crucial for working with Node.js projects, npm packages, and legacy codebases. This section provides deep coverage of both systems.
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā COMMONJS vs AMD COMPARISON ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā COMMONJS ā ā AMD ā ā
ā ā (CJS) ā ā (Asynchronous Module Def) ā ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāā⤠āāāāāāāāāāāāāāāāāāāāāāāāāāāāāā⤠ā
ā ā ā ā ā ā
ā ā Created: 2009 ā ā Created: 2010 ā ā
ā ā For: Node.js (Server) ā ā For: Browsers ā ā
ā ā Loading: Synchronous ā ā Loading: Asynchronous ā ā
ā ā ā ā ā ā
ā ā require('./module') ā ā define(['dep'], fn) ā ā
ā ā module.exports = {} ā ā require(['dep'], fn) ā ā
ā ā ā ā ā ā
ā ā ā Simple syntax ā ā ā Non-blocking ā ā
ā ā ā Large npm ecosystem ā ā ā Browser optimized ā ā
ā ā ā Sync = blocks in browser ā ā ā Verbose syntax ā ā
ā ā ā ā ā Less common today ā ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā
ā Both are being replaced by ES Modules, but remain in legacy code ā
ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
šÆ Learning Objectives
By the end of this section, you will:
- ā¢ā Master CommonJS module patterns
- ā¢ā Understand the module wrapper function
- ā¢ā Learn AMD define/require patterns
- ā¢ā Configure RequireJS for browser modules
- ā¢ā Convert between module formats
- ā¢ā Handle interoperability issues
1ļøā£ CommonJS Deep Dive
How CommonJS Works Internally
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// What Node.js does behind the scenes
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// Your code in math.js:
const PI = 3.14159;
function add(a, b) {
return a + b;
}
module.exports = { PI, add };
// Node.js WRAPS it in a function:
(function (exports, require, module, __filename, __dirname) {
// Your code runs inside this wrapper
const PI = 3.14159;
function add(a, b) {
return a + b;
}
module.exports = { PI, add };
// Return module.exports
});
// That's why you have access to:
// - exports (shortcut to module.exports)
// - require (function to load other modules)
// - module (info about current module)
// - __filename (absolute path to this file)
// - __dirname (absolute path to directory)
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā COMMONJS MODULE WRAPPER ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā ā
ā Every CommonJS file is wrapped in a function: ā
ā ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā (function(exports, require, module, __filename, __dirname) ā ā
ā ā { ā ā
ā ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā ā
ā ā ā ā ā ā
ā ā ā YOUR CODE HERE ā ā ā
ā ā ā ā ā ā
ā ā ā const x = 1; ā ā ā
ā ā ā module.exports = { x }; ā ā ā
ā ā ā ā ā ā
ā ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā ā
ā ā }); ā ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā
ā Parameters Available: ā
ā āāāāāāāāāāāāāāāāā¬āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā exports ā Shortcut to module.exports ā ā
ā ā require ā Function to import modules ā ā
ā ā module ā Reference to current module ā ā
ā ā __filename ā /home/user/project/src/file.js ā ā
ā ā __dirname ā /home/user/project/src ā ā
ā āāāāāāāāāāāāāāāāā“āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
CommonJS Export Patterns
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// Pattern 1: Exporting an Object
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// math.js
const PI = 3.14159;
const E = 2.71828;
function add(a, b) {
return a + b;
}
function subtract(a, b) {
return a - b;
}
// Export as object
module.exports = {
PI,
E,
add,
subtract,
};
// Usage
const math = require('./math');
console.log(math.PI); // 3.14159
console.log(math.add(2, 3)); // 5
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// Pattern 2: Exporting a Function
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// logger.js
function createLogger(prefix) {
return {
log: (msg) => console.log(`[${prefix}] ${msg}`),
error: (msg) => console.error(`[${prefix}] ERROR: ${msg}`),
};
}
module.exports = createLogger;
// Usage
const createLogger = require('./logger');
const logger = createLogger('App');
logger.log('Started');
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// Pattern 3: Exporting a Class
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// User.js
class User {
constructor(name, email) {
this.name = name;
this.email = email;
}
greet() {
return `Hello, I'm ${this.name}`;
}
}
module.exports = User;
// Usage
const User = require('./User');
const user = new User('Alice', 'alice@example.com');
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// Pattern 4: Using exports Shortcut
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// utils.js
exports.capitalize = (str) => str.charAt(0).toUpperCase() + str.slice(1);
exports.lowercase = (str) => str.toLowerCase();
exports.VERSION = '1.0.0';
// ā ļø CAUTION: Don't reassign exports!
exports = { something: 'new' }; // ā BREAKS the reference!
module.exports = { something: 'new' }; // ā
Correct way
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// Pattern 5: Augmenting exports and module.exports
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// api.js
// Add individual exports
exports.get = (url) => fetch(url);
exports.post = (url, data) => fetch(url, { method: 'POST', body: data });
// Also set a default function
module.exports = function (baseUrl) {
return {
get: (path) => exports.get(baseUrl + path),
post: (path, data) => exports.post(baseUrl + path, data),
};
};
// Attach the individual methods to the function
module.exports.get = exports.get;
module.exports.post = exports.post;
// Usage (both work):
const createApi = require('./api');
const api = createApi('https://api.example.com');
const { get, post } = require('./api');
The require() Algorithm
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// How require() resolves modules
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// 1. Core modules (built-in)
const fs = require('fs'); // Built-in, highest priority
const path = require('path'); // No path = core module
// 2. File modules (relative/absolute paths)
const myModule = require('./myModule'); // Relative
const other = require('../lib/other'); // Parent directory
const abs = require('/home/user/module'); // Absolute
// 3. Node modules (node_modules directory)
const lodash = require('lodash'); // Looks in node_modules
const express = require('express'); // Climbs up directory tree
// 4. Resolution order for require('./myModule'):
// a. ./myModule.js
// b. ./myModule.json
// c. ./myModule.node (native addon)
// d. ./myModule/index.js
// e. ./myModule/index.json
// f. ./myModule/package.json ā main field
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā require() RESOLUTION ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā ā
ā require('X') ā
ā ā ā
ā āāā¶ Is X a core module? (fs, path, http...) ā
ā ā YES ā Return core module ā
ā ā ā
ā āāā¶ Does X start with './' or '../' or '/'? ā
ā ā YES ā Load as FILE or DIRECTORY ā
ā ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā ā TRY IN ORDER: ā ā
ā ā ā 1. X.js ā ā
ā ā ā 2. X.json ā ā
ā ā ā 3. X.node ā ā
ā ā ā 4. X/index.js ā ā
ā ā ā 5. X/package.json ā main ā ā
ā ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā ā
ā āāā¶ Otherwise, search node_modules ā
ā Start from current directory, climb up ā
ā ./node_modules/X ā
ā ../node_modules/X ā
ā ../../node_modules/X ā
ā ... until root ā
ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā ā
ā CACHING: ā
ā ā
ā require('X') āāā¶ CACHE HIT? āāā¶ YES āāā¶ Return cached ā
ā ā ā
ā NO ā
ā ā ā
ā ā¼ ā
ā Load module ā
ā Execute code ā
ā Cache result ā
ā Return exports ā
ā ā
ā // Same module loaded once, result reused ā
ā require('./a'); // Loads and executes ā
ā require('./a'); // Returns cached ā
ā require('./a'); // Returns cached ā
ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Module Caching
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// Understanding CommonJS caching
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// counter.js
console.log('Module loaded!');
let count = 0;
module.exports = {
increment: () => ++count,
getCount: () => count,
};
// app.js
const counter1 = require('./counter'); // "Module loaded!" (first time)
const counter2 = require('./counter'); // (nothing - cached!)
const counter3 = require('./counter'); // (nothing - cached!)
console.log(counter1 === counter2); // true (same object!)
console.log(counter2 === counter3); // true
counter1.increment();
console.log(counter2.getCount()); // 1 (shared state!)
// Clearing cache (useful for testing):
delete require.cache[require.resolve('./counter')];
const freshCounter = require('./counter'); // "Module loaded!" (reloaded)
2ļøā£ AMD Deep Dive
AMD with RequireJS
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// AMD Module Definition
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// Simple module (no dependencies)
// math.js
define('math', [], function () {
var PI = 3.14159;
function add(a, b) {
return a + b;
}
function subtract(a, b) {
return a - b;
}
// Return public API
return {
PI: PI,
add: add,
subtract: subtract,
};
});
// Module with dependencies
// calculator.js
define('calculator', ['math', 'logger'], function (math, logger) {
// math and logger are injected as arguments
function calculate(a, b, operation) {
logger.log('Calculating...');
switch (operation) {
case '+':
return math.add(a, b);
case '-':
return math.subtract(a, b);
default:
throw new Error('Unknown operation');
}
}
return {
calculate: calculate,
};
});
// Anonymous module (name inferred from filename)
// utils.js
define(['jquery'], function ($) {
function showMessage(msg) {
$('body').append('<div class="message">' + msg + '</div>');
}
return { showMessage: showMessage };
});
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā AMD STRUCTURE ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā ā
ā define( ā
ā 'moduleName', ā Optional: Module ID ā
ā ['dep1', 'dep2'], ā Dependencies array ā
ā function(dep1, dep2) { ā Factory function ā
ā // Module code ā
ā return { ... }; ā Public API ā
ā } ā
ā ); ā
ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā ā
ā LOADING SEQUENCE: ā
ā ā
ā define('C', ['A', 'B'], factory) ā
ā ā ā
ā ā¼ ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā 1. Parse dependency array ā ā
ā ā 2. Load A.js (async) ā ā
ā ā 3. Load B.js (async, parallel) ā ā
ā ā 4. Wait for both to complete ā ā
ā ā 5. Execute factory(A, B) ā ā
ā ā 6. Cache and return result ā ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Requiring AMD Modules
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// Using require() in AMD
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// Main entry point - app.js
require(['jquery', 'calculator', 'utils'], function ($, calculator, utils) {
// All dependencies loaded
$(document).ready(function () {
var result = calculator.calculate(5, 3, '+');
utils.showMessage('Result: ' + result);
});
});
// Conditional loading
define(['require'], function (require) {
function loadFeature(name, callback) {
require(['features/' + name], function (feature) {
callback(feature);
});
}
return { loadFeature: loadFeature };
});
// CommonJS-style syntax (supported in AMD)
define(function (require, exports, module) {
var $ = require('jquery');
var math = require('./math');
exports.doSomething = function () {
// Use $ and math
};
});
RequireJS Configuration
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// requirejs.config() - Configure paths, shims, and more
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
requirejs.config({
// Base URL for all module lookups
baseUrl: '/js',
// Path shortcuts
paths: {
jquery: 'lib/jquery-3.6.0.min',
lodash: 'lib/lodash.min',
backbone: 'lib/backbone-min',
text: 'plugins/text', // Text plugin
json: 'plugins/json', // JSON plugin
},
// Shim non-AMD libraries
shim: {
backbone: {
deps: ['lodash', 'jquery'], // Load these first
exports: 'Backbone', // Global variable to export
},
'jquery.plugin': {
deps: ['jquery'],
exports: 'jQuery.fn.plugin',
},
'legacy-lib': {
exports: 'LegacyLib',
init: function () {
// Custom initialization
return this.LegacyLib.noConflict();
},
},
},
// Map module IDs to other IDs
map: {
'some/module': {
jquery: 'jquery-private', // Use private jQuery
},
},
// Require.js plugins config
config: {
text: {
useXhr: function (url, protocol, hostname, port) {
return true; // Always use XHR
},
},
},
// Wait time before timeout (milliseconds)
waitSeconds: 15,
// URL arguments for cache busting
urlArgs: 'bust=' + new Date().getTime(),
});
// Start the app
require(['app/main']);
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā REQUIREJS CONFIG OPTIONS ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā baseUrl: '/js' ā ā
ā ā ā ā
ā ā All module lookups start from this path ā ā
ā ā require(['app']) ā loads /js/app.js ā ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā paths: { 'jquery': 'lib/jquery-3.6.0' } ā ā
ā ā ā ā
ā ā Aliases for module paths (no .js extension!) ā ā
ā ā require(['jquery']) ā loads /js/lib/jquery-3.6.0.js ā ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā shim: { 'backbone': { deps: [...], exports: '...' }} ā ā
ā ā ā ā
ā ā Configure non-AMD scripts: ā ā
ā ā ⢠deps: modules to load first ā ā
ā ā ⢠exports: global variable the script creates ā ā
ā ā ⢠init: custom initialization function ā ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā urlArgs: 'v=1.0.0' ā ā
ā ā ā ā
ā ā Append to all URLs for cache busting ā ā
ā ā /js/app.js?v=1.0.0 ā ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
3ļøā£ HTML Integration
Loading AMD Modules
<!-- āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā -->
<!-- AMD with RequireJS in HTML -->
<!-- āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā -->
<!DOCTYPE html>
<html>
<head>
<title>AMD Example</title>
</head>
<body>
<!-- Single script tag loads RequireJS and starts app -->
<script data-main="js/main" src="js/lib/require.js"></script>
<!--
data-main="js/main" tells RequireJS:
1. Set baseUrl to "js/"
2. Load "js/main.js" as entry point
-->
</body>
</html>
<!-- āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā -->
<!-- Alternative: Inline configuration -->
<!-- āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā -->
<!DOCTYPE html>
<html>
<head>
<script src="js/lib/require.js"></script>
<script>
requirejs.config({
baseUrl: 'js',
paths: {
jquery: 'lib/jquery.min',
},
});
require(['app/main'], function (main) {
main.init();
});
</script>
</head>
<body>
<div id="app"></div>
</body>
</html>
4ļøā£ Interoperability
CommonJS in AMD
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// Using CommonJS style in AMD
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// AMD supports "Simplified CommonJS Wrapping"
define(function (require, exports, module) {
// CommonJS-style requires
var $ = require('jquery');
var _ = require('lodash');
var myModule = require('./myModule');
// CommonJS-style exports
exports.doSomething = function () {
return _.map([1, 2, 3], (x) => x * 2);
};
// Or use module.exports
module.exports = {
doSomething: function () {
/* ... */
},
};
});
AMD in Node.js
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// Using AMD modules in Node.js with amdefine
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// Install: npm install amdefine
// myModule.js
if (typeof define !== 'function') {
var define = require('amdefine')(module);
}
define(['./dependency'], function (dep) {
// This works in both AMD (browser) and CommonJS (Node.js)
return {
doSomething: function () {
return dep.helper();
},
};
});
5ļøā£ Migration Patterns
CommonJS to ES Modules
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// Converting CommonJS to ES Modules
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// āāā BEFORE: CommonJS āāā
// math.js
const PI = 3.14159;
function add(a, b) {
return a + b;
}
function subtract(a, b) {
return a - b;
}
module.exports = { PI, add, subtract };
// app.js
const math = require('./math');
const { add, PI } = require('./math');
// āāā AFTER: ES Modules āāā
// math.js
export const PI = 3.14159;
export function add(a, b) {
return a + b;
}
export function subtract(a, b) {
return a - b;
}
// app.js
import { PI, add, subtract } from './math.js';
// or
import * as math from './math.js';
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā CONVERSION CHEAT SHEET ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā ā
ā CommonJS ES Modules ā
ā āāāāāāāā āāāāāāāāāā ā
ā ā
ā module.exports = x; export default x; ā
ā ā
ā module.exports = { a, b }; export { a, b }; ā
ā ā
ā module.exports.x = y; export const x = y; ā
ā ā
ā exports.x = y; export const x = y; ā
ā ā
ā const x = require('./m'); import x from './m.js'; ā
ā ā
ā const { a } = require('./m'); import { a } from './m.js'; ā
ā ā
ā const m = require('./m'); import * as m from './m.js'; ā
ā ā
ā require('./m'); import './m.js'; ā
ā ā
ā const m = require(path); const m = await import(path); ā
ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā ā
ā ā ļø DIFFERENCES TO WATCH: ā
ā ā
ā ⢠Add .js extension (required in browsers/Node ESM) ā
ā ⢠No __dirname/__filename (use import.meta.url) ā
ā ⢠No require.resolve (use import.meta.resolve) ā
ā ⢠No dynamic require(var) (use await import(var)) ā
ā ⢠ESM is strict mode by default ā
ā ⢠ESM has top-level await ā
ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Dual Package Support
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// package.json - Supporting both CJS and ESM
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
{
"name": "my-package",
"version": "1.0.0",
// Main entry for CommonJS
"main": "./dist/index.cjs",
// Entry for ES Modules
"module": "./dist/index.mjs",
// Conditional exports (Node.js 12.7+)
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.cjs",
"default": "./dist/index.cjs"
},
"./utils": {
"import": "./dist/utils.mjs",
"require": "./dist/utils.cjs"
}
},
// TypeScript types
"types": "./dist/index.d.ts",
// Files to include in package
"files": ["dist"]
}
6ļøā£ Summary Comparison
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā COMPLETE COMPARISON ā
āāāāāāāāāāāāāāā¬āāāāāāāāāāāāāāāāāāāāāāāāā¬āāāāāāāāāāāāāāāāāāāāā¬āāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā Feature ā CommonJS ā AMD ā ES Modules ā
āāāāāāāāāāāāāāā¼āāāāāāāāāāāāāāāāāāāāāāāāā¼āāāāāāāāāāāāāāāāāāāāā¼āāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā Year ā 2009 ā 2010 ā 2015 ā
āāāāāāāāāāāāāāā¼āāāāāāāāāāāāāāāāāāāāāāāāā¼āāāāāāāāāāāāāāāāāāāāā¼āāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā Designed ā Node.js ā Browsers ā Everywhere ā
ā For ā (Server) ā ā ā
āāāāāāāāāāāāāāā¼āāāāāāāāāāāāāāāāāāāāāāāāā¼āāāāāāāāāāāāāāāāāāāāā¼āāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā Syntax ā require() ā define() ā import/export ā
ā ā module.exports ā require() ā ā
āāāāāāāāāāāāāāā¼āāāāāāāāāāāāāāāāāāāāāāāāā¼āāāāāāāāāāāāāāāāāāāāā¼āāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā Loading ā Synchronous ā Asynchronous ā Async (browser) ā
ā ā ā ā Both (Node.js) ā
āāāāāāāāāāāāāāā¼āāāāāāāāāāāāāāāāāāāāāāāāā¼āāāāāāāāāāāāāāāāāāāāā¼āāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā Analysis ā Dynamic ā Dynamic ā Static ā
ā ā (runtime) ā (runtime) ā (compile-time) ā
āāāāāāāāāāāāāāā¼āāāāāāāāāāāāāāāāāāāāāāāāā¼āāāāāāāāāāāāāāāāāāāāā¼āāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā Tree- ā ā No ā ā No ā ā
Yes ā
ā Shaking ā ā ā ā
āāāāāāāāāāāāāāā¼āāāāāāāāāāāāāāāāāāāāāāāāā¼āāāāāāāāāāāāāāāāāāāāā¼āāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā Circular ā Partial support ā Supported ā Live bindings ā
ā Deps ā (can be undefined) ā ā (better handling) ā
āāāāāāāāāāāāāāā¼āāāāāāāāāāāāāāāāāāāāāāāāā¼āāāāāāāāāāāāāāāāāāāāā¼āāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā Current ā Legacy Node.js ā Rare ā ā
Standard ā
ā Usage ā Many npm packages ā (mostly dead) ā Recommended ā
āāāāāāāāāāāāāāā¼āāāāāāāāāāāāāāāāāāāāāāāāā¼āāāāāāāāāāāāāāāāāāāāā¼āāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā Tools ā Node.js native ā RequireJS ā Native browsers ā
ā ā Bundlers ā ā Node.js, bundlers ā
āāāāāāāāāāāāāāā“āāāāāāāāāāāāāāāāāāāāāāāāā“āāāāāāāāāāāāāāāāāāāāā“āāāāāāāāāāāāāāāāāāāāāāāāāā
š Key Takeaways
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā KEY TAKEAWAYS ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā ā
ā COMMONJS: ā
ā ⢠require() / module.exports ā
ā ⢠Synchronous loading (server-side) ā
ā ⢠Still used in Node.js, many npm packages ā
ā ⢠Gradually migrating to ESM ā
ā ā
ā AMD: ā
ā ⢠define() / require() ā
ā ⢠Asynchronous loading (browser-side) ā
ā ⢠Mostly deprecated, replaced by ESM ā
ā ⢠May encounter in legacy codebases ā
ā ā
ā BEST PRACTICES: ā
ā ⢠New projects: Use ES Modules ā
ā ⢠Libraries: Build both ESM and CJS ā
ā ⢠Use package.json "exports" for dual support ā
ā ⢠Bundlers handle conversion between formats ā
ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
š Next Steps
Continue to 14.4 Bundlers & Build Tools to learn:
- ā¢Webpack configuration
- ā¢Rollup for libraries
- ā¢Vite for development
- ā¢esbuild for speed
- ā¢Tree-shaking optimization