Modules — import/export, ESM vs CommonJS
আসল project কীভাবে সংগঠিত হয়
1. Why Modules?
Without modules, all your JS shares one giant global scope — naming collisions, mystery dependencies, and 5,000-line files. A module is a single .js file whose top-level declarations are private unless you explicitly export them.
.js ফাইল যেখানে top-level সব private; যা export করবেন সেটাই বাইরে দেখা যাবে।2. ES Module Syntax
math.js (exports)
// named exports
export const PI = 3.14;
export function add(a, b) { return a + b; }
export class Vec { /* ... */ }
// default export — only ONE per file
export default function double(n) {
return n * 2;
}
app.js (imports)
import double, { PI, add, Vec } from "./math.js";
import { add as plus } from "./math.js"; // rename
import * as math from "./math.js"; // namespace
console.log(double(5), PI, math.add(1, 2));
3. Loading Modules in HTML
<!-- Modules need type="module" -->
<script type="module" src="app.js"></script>
<!-- Modules are deferred by default and run in strict mode -->
<!-- They are loaded once per URL — no duplicate execution -->
this = undefined, single-execution semantics, top-level await, and a clean dependency graph.
4. Re-exports
// utils/index.js — barrel re-export
export { add, sub } from "./math.js";
export * from "./string.js";
export { default as User } from "./user.js";
// app.js
import { add, User } from "./utils/index.js";
5. Dynamic import()
Static imports are great for a fixed graph. When you want to load a heavy chunk only when needed (a route, a modal, a rarely-used feature), use dynamic import() — it returns a Promise.
// In the runner sandbox we just simulate the shape.
async function loadEditor() {
// Real code: const { initEditor } = await import("./editor.js");
const mod = await Promise.resolve({
initEditor(name) { return `Editor for ${name} ready`; }
});
console.log(mod.initEditor("hello.txt"));
}
loadEditor();
6. CommonJS — The Old Way (Still Common in Node)
// math.cjs — CommonJS
const PI = 3.14;
function add(a, b) { return a + b; }
module.exports = { PI, add };
// app.cjs
const { PI, add } = require("./math.cjs");
console.log(add(1, 2), PI);
| Feature | ESM | CommonJS |
|---|---|---|
| Syntax | import / export | require / module.exports |
| Resolution | Static, async | Synchronous, runtime |
| Tree-shaking | Yes | Hard |
| Top-level await | Yes | No |
| Browser-native | Yes | No |
| File ext (Node) | .mjs or "type":"module" | .cjs or default |
import/export ব্যবহার করুন। require পুরোনো Node.js কোডে এখনো প্রচলিত — কিন্তু নতুন project-এ ESM-ই উপযুক্ত।7. ESM in Node.js
// package.json
{
"name": "my-app",
"type": "module",
"main": "src/index.js"
}
// All .js files in this package now use ESM by default.
// Use .cjs for any CommonJS files you still need.
8. Glossary (শব্দকোষ)
| Term | Meaning | বাংলায় |
|---|---|---|
| Module | A single file whose top-level declarations are private unless exported. | একটি ফাইল — top-level declaration export না করলে private। |
| ESM | ECMAScript Modules — the standard import/export system. | আধুনিক standard import/export system। |
| CommonJS | Node's older require/module.exports system. | Node-এর পুরোনো require/module.exports system। |
| Named export | export const x — imported by exact name. | export const x — নাম মিলিয়ে import হয়। |
| Default export | One unnamed export per file — imported by any name. | প্রতিটি ফাইলে একটি — যেকোনো নামে import করা যায়। |
| Re-export | export * from "./x.js" — forwards exports through a barrel. | Barrel-এর মাধ্যমে অন্য module-এর export forward করা। |
| Dynamic import | import() — async, returns a Promise; for code-splitting. | import() — async; lazy-load chunk। |
| Tree-shaking | Bundler removes unused exports from the final output. | Bundler অপ্রয়োজনীয় export বাদ দেয়। |
| Top-level await | Using await at module top — only inside ESM. | Module-এর top-level-এ await ব্যবহার। |
import/export (ESM) — static, async, browser-native, tree-shake-friendly এবং strict mode by default। require পুরোনো Node-এ এখনো প্রচলিত, কিন্তু নতুন project-এ ESM-ই বেছে নিন। বড় bundle ছোট রাখতে চাইলে dynamic import() দিয়ে heavy chunk lazy-load করুন।
9. Practice Problems
- Show the export shape of a small math module (PI, add, default double).
✨ Show Answer
export const PI = 3.14; export function add(a, b) { return a + b; } export default n => n * 2; - Write the import for the same module that uses default + named.
✨ Show Answer
import double, { PI, add } from "./math.js"; - Rename an imported function with
as.✨ Show Answer
import { add as plus } from "./math.js"; plus(2, 3); - Import everything as a namespace.
✨ Show Answer
import * as math from "./math.js"; math.add(1, 2); - Simulate a dynamic import using a Promise (so it runs in this sandbox).
✨ Show Answer
a5.js(async () => { const mod = await Promise.resolve({ hello: () => "hi" }); console.log(mod.hello()); })(); - Show how to set a Node project to ESM mode.
✨ Show Answer
Add
"type": "module"topackage.json. All.jsfiles in the package now useimport/export; rename CommonJS files to.cjsif you keep any. - Compare ESM and CommonJS in two sentences.
✨ Show Answer
Answer: ESM is the standard browser-native format with static, async resolution that supports tree-shaking and top-level await. CommonJS is Node's older synchronous
require-based format; it still works but lacks tree-shaking and isn't natively understood by browsers. - Why does
"use strict"add no value inside an ES module?✨ Show Answer
Modules are strict by default — no extra directive needed. Silent globals throw,
thisat the top level isundefined, and reserved words can't be reused. - Build a "barrel"
index.jsthat re-exports from three files.✨ Show Answer
// utils/index.js export * from "./math.js"; export * from "./str.js"; export { default as Logger } from "./log.js"; - Why can a module's exports change later, but a CommonJS
module.exportssnapshot doesn't?✨ Show Answer
Answer: ESM imports are live bindings — the importer sees the current value of an exported variable. CommonJS hands the requirer a copy of
module.exportsat the momentrequirereturned, so subsequent reassignments inside the module don't propagate. - Lazy-load a chart library on a button click (sketch).
✨ Show Answer
btn.addEventListener("click", async () => { const { drawChart } = await import("./chart.js"); drawChart(data); }); - Run an async IIFE inside a module to use top-level await.
✨ Show Answer
Inside an ES module, you can simply write:
// app.mjs const data = await fetch("/api/feed").then(r => r.json()); console.log(data.length);
Summary — Module 18
Use import / export in modern code. ES Modules are static, async, browser-native, tree-shakable, and strict by default. CommonJS is still alive in older Node but has no place in new code. Reach for dynamic import() when you want to lazily load chunks.
import()।