8.1 — Modules: import & export
Every program you’ve written fits in one file. No real project does: a Playwright test suite is dozens of files — tests, helpers, configuration, page models — working together. Split carelessly and nothing can find anything; the language’s answer is modules: one file = one sealed module, sharing only what it explicitly exports.
Two new keywords, export and import — and one new picture, the dependency graph — and you can read the folder structure of any professional codebase.
// ---- math.js ----------------
export const TAX = 0.25;
export function withTax(price) {
return price * (1 + TAX);
}
const secret = "internal only";
// ---- cart.js ----------------
import { withTax } from "./math.js";
console.log(withTax(100));First, the problem. Real projects are thousands of lines; one file becomes a junk drawer where every variable can see every other variable, and two far-apart lines can silently fight over one name. The fix has a name: MODULES.
The deeper story, with the real names for things — this part is what turns “I saw it” into “I can explain it.”
Precision on what travels: a named import is not a copy — it’s a live binding to the exporting module’s variable. If math.js reassigns an exported let counter, every importer sees the new value. You read through the door; you don’t take a photocopy home.
Each module runs once, no matter how many files import it. The first import executes the file and caches the result; every later import receives that same cached module. That’s why a module is a safe place for shared setup — and why test frameworks lean on this so heavily.
“Static” buys real things: because the graph is known before execution, the engine can report a misspelled import name immediately, and build tools can drop exports nobody imports (you’ll hear this called tree-shaking).
Job note: every Playwright test file you will ever write starts with import { test, expect } from "@playwright/test" — a named import from an installed package instead of a ./file path. Installing packages is exactly the next lesson.
⌨️ build the import machine
The sandbox runs a single file, so build the mechanism yourself: a module is a box of named things, and importing means fetching one OUT of the box by its name.
requirements:
- Create
TAXset to0.25, a functionwithTax(price)returningprice * (1 + TAX), and an objectmathModuleholding both under those exact names. - Write
importFrom(module, name): it returns whatever the module holds under that name — the name arrives as a string variable, so pick the right property syntax (4.4 knows). - Use it twice: fetch
"withTax"into a variable and print calling it with100; then print fetching"TAX"directly.
when you press RUN, the console must show exactly:
✏️ Quick check 1
cart.js runs console.log(withTax(100)) with TAX = 0.25. What prints?
✏️ Quick check 2
math.js has const secret = "internal only" — with no export. Can cart.js import secret? Type yes or no.
✏️ Quick check 3
True or false: an import line may sit inside an if block, so you only load the module sometimes.
🗣️ Now teach it back
Explain modules to a friend: why one giant file breaks down, what export and import each do, what a module can see of another module, and what the dependency graph is.
Write it as if your friend is sitting next to you. Saved to your journal — future-you will use these notes to teach others.