Day 21: Modules, npm & Modern Tooling
✅ Today you will learn
- Why one huge script.js becomes unmaintainable
- ES modules: named and default export/import
- Modules in the browser (type="module") and in Node.js
- npm, package.json, dependencies and scripts
- Vite for dev and build; ESLint + Prettier for quality
🏢 In the real world
Our shop code now has products, a cart class, formatting helpers, validation rules and API calls – 900 lines in one file. Two developers editing it at the same time overwrite each other’s work, and nobody can find anything. Every company splits code into modules and uses npm tools. This is the setup you will see on day one of a job.
From one file to modules
sharma-shop/
├── index.html
├── package.json
└── src/
├── main.js ← entry point: wires everything together
├── api.js ← fetchJSON, getProducts, placeOrder
├── cart.js ← export class Cart
├── format.js ← export rupees(), formatDate()
├── validate.js ← export rules, validate()
└── ui/
├── productGrid.js
└── cartPanel.jsexport and import
export const GST_RATE = 0.05;
export function rupees(amount) {
return new Intl.NumberFormat('en-IN', { style: 'currency', currency: 'INR', maximumFractionDigits: 0 }).format(amount);
}
export const withGst = (amount) => Math.round(amount * (1 + GST_RATE));import { withGst } from './format.js';
export default class Cart {
#items = [];
add(product, qty = 1) { this.#items.push({ ...product, qty }); return this; }
get total() { return withGst(this.#items.reduce((s, i) => s + i.price * i.qty, 0)); }
}import Cart from './cart.js'; // default import – any name you like
import { rupees, GST_RATE } from './format.js'; // named imports – exact names in { }
import * as format from './format.js'; // everything as one object
const cart = new Cart().add({ name: 'Kaju Katli', price: 900 }, 2);
console.log(`Total with ${GST_RATE * 100}% GST: ${rupees(cart.total)}`);
console.log(format.withGst(1000));// The same idea running in Node.js: an inline module demo using data: URLs
const formatModule = `
export const rupees = n => '₹' + n.toLocaleString('en-IN');
export default function gst(n) { return Math.round(n * 1.05); }
`;
const url = 'data:text/javascript,' + encodeURIComponent(formatModule);
import(url).then(({ default: gst, rupees }) => { // dynamic import() returns a promise
console.log(rupees(gst(180000)));
});| Type | Export | Import | Use for |
|---|---|---|---|
| Named | export function rupees() {} | import { rupees } from './format.js' | Several helpers per file (most common) |
| Default | export default class Cart {} | import Cart from './cart.js' | One main thing per file (components, classes) |
| Rename | export { a as b } | import { rupees as inr } from … | Avoid name clashes |
| Namespace | — | import * as fmt from './format.js' | Grouping helpers |
| Dynamic | — | const m = await import('./chart.js') | Load heavy code only when needed |
Modules in the browser and in Node
<!-- type="module" = deferred automatically, strict mode, own scope, import/export allowed --> <script type="module" src="src/main.js"></script>
Opening index.html by double-click (file://) blocks module imports for security. Use VS Code Live Server, npx serve or Vite. Also always include the .js extension in browser imports.
| Browser | Node.js | |
|---|---|---|
| Enable ES modules | <script type="module"> | "type": "module" in package.json, or .mjs files |
| Older format | Global scripts | CommonJS: require() / module.exports |
| npm packages | Through a bundler (Vite) or CDN URLs | import express from "express" |
| Top-level await | Yes | Yes (in ES modules) |
npm and package.json
# 1. Install Node.js LTS from nodejs.org (includes npm), then check: node -v npm -v # 2. Create a project mkdir sharma-shop && cd sharma-shop npm init -y # creates package.json # 3. Add packages npm install dayjs # dependency – shipped with your app npm install -D vite eslint prettier # devDependencies – tools for developers only # 4. Run a script defined in package.json npm run dev
{
"name": "sharma-shop",
"version": "1.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"lint": "eslint src",
"format": "prettier --write src"
},
"dependencies": {
"dayjs": "^1.11.13"
},
"devDependencies": {
"eslint": "^9.15.0",
"prettier": "^3.3.3",
"vite": "^5.4.11"
}
}| File / folder | Commit to Git? | Why |
|---|---|---|
| package.json | Yes | Lists what the project needs |
| package-lock.json | Yes | Exact versions – same install for every developer and server |
| node_modules/ | No – add to .gitignore | Huge; recreated with npm install / npm ci |
| dist/ | Usually no | Build output; created by npm run build |
Semantic versioning: MAJOR.MINOR.PATCH. ^ allows newer minor and patch versions (1.12.0 yes, 2.0.0 no) because major versions may break your code.
Vite: dev server and production build
- 1npm create vite@latest sharma-shop -- --template vanilla
- 2cd sharma-shop && npm install
- 3npm run dev → open http://localhost:5173 – saves reload instantly (hot module replacement)
- 4Write modules in src/, import npm packages by name
- 5npm run build → optimised, minified files in dist/
- 6Upload dist/ to Hostinger, Netlify or GitHub Pages
ESLint and Prettier: automatic code review
ESLint finds problems
- Unused variables and imports
- Using a variable before defining it
- == instead of ===
- Missing await / unreachable code
Prettier formats code
- Same indentation and quotes for everyone
- Line breaks at a fixed width
- Runs on save in VS Code
- No more style arguments in code review
import js from '@eslint/js';
import globals from 'globals';
export default [
js.configs.recommended,
{
languageOptions: { ecmaVersion: 'latest', sourceType: 'module', globals: globals.browser },
rules: {
eqeqeq: 'error',
'no-unused-vars': 'warn',
'prefer-const': 'error',
},
},
];🏅 Job skills unlocked today
Practice tasks
🏋️ Practice – do it yourself
Quick quiz
🧠 Quick Quiz – test yourself
Interview corner
💼 Interview Corner
ES modules vs CommonJS?
ESM uses static import/export, is the standard in browsers and modern Node, supports tree-shaking and top-level await, and loads asynchronously. CommonJS uses require/module.exports, is synchronous and was Node’s original format; interop exists but mixing needs care.
Why use a bundler like Vite?
It serves modules quickly in development with hot reload, resolves npm imports, and for production bundles, minifies, tree-shakes, splits code and fingerprints file names for caching.
Summary
- One responsibility per module; export what others need.
- Named exports for helpers, default for the main thing; type="module" in HTML.
- npm + package.json + lock file; never commit node_modules.
- Vite for dev/build; ESLint finds bugs, Prettier formats.
Day 22: useful browser APIs – IntersectionObserver, clipboard, share, geolocation, URL parameters and history.
