Modules — import/export, ESM vs CommonJS

আসল project কীভাবে সংগঠিত হয়

~30 min Intermediate 12 practice problems Live runner

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.

Module ছাড়া পুরো JS একটি global scope share করে — নামের সংঘর্ষ এবং বিশাল ফাইল। একটি module মানে একটি .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 -->
What modules give you for free Strict mode, deferred loading, top-level 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.

dynamic.js
// 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);
FeatureESMCommonJS
Syntaximport / exportrequire / module.exports
ResolutionStatic, asyncSynchronous, runtime
Tree-shakingYesHard
Top-level awaitYesNo
Browser-nativeYesNo
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 (শব্দকোষ)

TermMeaningবাংলায়
ModuleA single file whose top-level declarations are private unless exported.একটি ফাইল — top-level declaration export না করলে private।
ESMECMAScript Modules — the standard import/export system.আধুনিক standard import/export system।
CommonJSNode's older require/module.exports system.Node-এর পুরোনো require/module.exports system।
Named exportexport const x — imported by exact name.export const x — নাম মিলিয়ে import হয়।
Default exportOne unnamed export per file — imported by any name.প্রতিটি ফাইলে একটি — যেকোনো নামে import করা যায়।
Re-exportexport * from "./x.js" — forwards exports through a barrel.Barrel-এর মাধ্যমে অন্য module-এর export forward করা।
Dynamic importimport() — async, returns a Promise; for code-splitting.import() — async; lazy-load chunk।
Tree-shakingBundler removes unused exports from the final output.Bundler অপ্রয়োজনীয় export বাদ দেয়।
Top-level awaitUsing 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

  1. 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;
  2. Write the import for the same module that uses default + named.
    ✨ Show Answer
    import double, { PI, add } from "./math.js";
  3. Rename an imported function with as.
    ✨ Show Answer
    import { add as plus } from "./math.js";
    plus(2, 3);
  4. Import everything as a namespace.
    ✨ Show Answer
    import * as math from "./math.js";
    math.add(1, 2);
  5. 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());
    })();
  6. Show how to set a Node project to ESM mode.
    ✨ Show Answer

    Add "type": "module" to package.json. All .js files in the package now use import/export; rename CommonJS files to .cjs if you keep any.

  7. 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.

  8. 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, this at the top level is undefined, and reserved words can't be reused.

  9. Build a "barrel" index.js that 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";
  10. Why can a module's exports change later, but a CommonJS module.exports snapshot 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.exports at the moment require returned, so subsequent reassignments inside the module don't propagate.

  11. Lazy-load a chart library on a button click (sketch).
    ✨ Show Answer
    btn.addEventListener("click", async () => {
        const { drawChart } = await import("./chart.js");
        drawChart(data);
    });
  12. 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.

আধুনিক কোডে ESM ব্যবহার করুন; CommonJS কেবল legacy। Lazy loading-এ dynamic import()।

Next Module → Midterm Project — Build a real interactive browser app।