{"id":"l-js-5-1","courseId":"javascript-fundamentals","lang":"en","kind":"lesson","summary":"Split a growing program into modules with export and import, load them in the browser with type=“module” from a dev server, and organise Pocket into files with clear jobs.","html":"<p>Pocket now has money helpers, validation, storage, rendering, event handlers and network code. In one file that is several hundred lines, every function can see every variable, and changing one part means scrolling past all the others. Real programs are split into files, each with one job, and JavaScript’s way of doing that is <strong>modules</strong>.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">A module is a file with its own scope</h2>\n<p>In a module, top-level variables and functions are private to that file. To share something, you <code dir=\"ltr\">export</code> it. To use something from another module, you <code dir=\"ltr\">import</code> it:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"js\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">src/money.js</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">export</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> function</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> toCents</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#953800;--shiki-dark:#FFA657\">dollars</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  return</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> Math.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">round</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(dollars </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">*</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 100</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">export</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> function</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> formatMoney</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#953800;--shiki-dark:#FFA657\">cents</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">symbol</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> \"$\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  return</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> `${</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">symbol</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">}${</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">(</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">cents</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> /</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 100</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">).</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">toFixed</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">2</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">)</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">}`</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> SECRET_ROUNDING_NOTE</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> \"not exported, so only this file can see it\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span></code></pre></div>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"js\" data-index=\"1\"><div class=\"code-head\"><span class=\"code-title\">src/main.js</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">import</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> { toCents, formatMoney } </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">from</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> \"./money.js\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">formatMoney</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">toCents</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">12.2</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)));</span></span></code></pre></div>\n<p>The curly braces list the <strong>named exports</strong> you want. The path starts with <code dir=\"ltr\">./</code>, meaning “relative to this file”, and in the browser it includes the <code dir=\"ltr\">.js</code> extension. <code dir=\"ltr\">SECRET_ROUNDING_NOTE</code> cannot be imported at all; that is the point. Each module decides what it offers, and everything else is hidden, much like the closures from section 2, at file level.</p>\n<p>A few more forms you will see:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"js\" data-index=\"2\"><div class=\"code-head\"><span class=\"code-title\">src/main.js</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">import</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> { formatMoney </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">as</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> fmt } </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">from</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> \"./money.js\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;   </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// rename on import</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">import</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> *</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> as</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> money </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">from</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> \"./money.js\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;                </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// everything, as money.toCents etc.</span></span></code></pre></div>\n<p>There is also a <strong>default export</strong>, one per file, imported without braces and under any name you like: <code dir=\"ltr\">export default function renderApp() {}</code> and then <code dir=\"ltr\">import renderApp from &quot;./ui.js&quot;</code>. Many teams prefer named exports, and so does this course: a named export has the same name in every file that uses it, so search and automatic renaming in your editor work reliably.</p>\n<h2 id=\"sec-2\" tabindex=\"-1\">Loading modules in the browser</h2>\n<p>The entry file is loaded with <code dir=\"ltr\">type=&quot;module&quot;</code>, and it imports everything else:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"html\" data-index=\"3\"><div class=\"code-head\"><span class=\"code-title\">index.html</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">script</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> type</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"module\"</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> src</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"./src/main.js\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">script</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span></code></pre></div>\n<p>Module scripts behave a little differently from the classic scripts you have used so far. They are deferred automatically, so the DOM is ready when they run. They run in strict mode, which turns some silent mistakes into errors. And each module runs <strong>once</strong>, however many files import it, so a module-level variable is shared by all its importers.</p>\n<aside class=\"callout callout-mistake\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\"/><path d=\"M12 9v4\"/><path d=\"M12 17h.01\"/></svg><span><span class=\"callout-kind\">Common mistake</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>Opening the page from the file system</span></p>\n<p>Double-clicking <code dir=\"ltr\">index.html</code> opens it from a <code dir=\"ltr\">file://</code> address, and browsers refuse to load module scripts from there: you get a CORS error in the console. Serve the folder over http instead. The usual way is a dev server: create a project with <code dir=\"ltr\">npm create vite@latest</code> and run <code dir=\"ltr\">npm run dev</code>, or run <code dir=\"ltr\">npx serve</code> in the folder.</p>\n</aside>\n<p>In practice, most projects use a build tool such as Vite. It serves your modules during development, lets you import packages from npm by name (<code dir=\"ltr\">import { something } from &quot;some-package&quot;</code>), and at build time bundles everything into a few optimised files. The <code dir=\"ltr\">import</code> and <code dir=\"ltr\">export</code> syntax you write is exactly the same.</p>\n<h2 id=\"sec-3\" tabindex=\"-1\">Modules in Node.js</h2>\n<p>Node.js supports the same syntax. A file is treated as an ES module when the nearest <code dir=\"ltr\">package.json</code> contains <code dir=\"ltr\">&quot;type&quot;: &quot;module&quot;</code>, or when the file name ends in <code dir=\"ltr\">.mjs</code>. (Recent Node.js versions also detect <code dir=\"ltr\">import</code> syntax in a plain <code dir=\"ltr\">.js</code> file, but setting <code dir=\"ltr\">&quot;type&quot;</code> explicitly is clearer.) You will also meet the older Node.js system, CommonJS, which uses <code dir=\"ltr\">require()</code> and <code dir=\"ltr\">module.exports</code>; it is still common in existing projects, but new code uses <code dir=\"ltr\">import</code> and <code dir=\"ltr\">export</code>.</p>\n<h2 id=\"sec-4\" tabindex=\"-1\">Organising Pocket</h2>\n<p>A good split follows jobs, and keeps the parts that touch the outside world separate from the pure logic:</p>\n<figure class=\"diagram\"><svg class=\"diagram-svg\" focusable=\"false\" viewBox=\"0 0 680 250\" role=\"img\" aria-labelledby=\"l-js-5-1-t1\"><defs><marker id=\"arrow-l-js-5-1-1\" viewBox=\"0 0 10 10\" refX=\"9\" refY=\"5\" markerWidth=\"7\" markerHeight=\"7\" orient=\"auto-start-reverse\"><path d=\"M0 0 L10 5 L0 10 z\" class=\"d-arrowhead\"/></marker></defs>\n  <title id=\"l-js-5-1-t1\">main.js imports from ui.js, storage.js and expenses.js. ui.js imports from money.js. expenses.js and money.js are pure logic that never touch the page or storage.</title>\n  <rect class=\"d-box-primary\" x=\"270\" y=\"16\" width=\"140\" height=\"48\" rx=\"10\"/>\n  <text class=\"d-code\" x=\"340\" y=\"46\" text-anchor=\"middle\">main.js</text>\n  <rect class=\"d-box-accent\" x=\"40\" y=\"110\" width=\"150\" height=\"48\" rx=\"10\"/>\n  <text class=\"d-code\" x=\"115\" y=\"140\" text-anchor=\"middle\">ui.js</text>\n  <rect class=\"d-box-accent\" x=\"265\" y=\"110\" width=\"150\" height=\"48\" rx=\"10\"/>\n  <text class=\"d-code\" x=\"340\" y=\"140\" text-anchor=\"middle\">storage.js</text>\n  <rect class=\"d-box-success\" x=\"490\" y=\"110\" width=\"150\" height=\"48\" rx=\"10\"/>\n  <text class=\"d-code\" x=\"565\" y=\"140\" text-anchor=\"middle\">expenses.js</text>\n  <rect class=\"d-box-success\" x=\"40\" y=\"196\" width=\"150\" height=\"48\" rx=\"10\"/>\n  <text class=\"d-code\" x=\"115\" y=\"226\" text-anchor=\"middle\">money.js</text>\n  <path class=\"d-arrow\" d=\"M300 64 L150 106\" marker-end=\"url(#arrow-l-js-5-1-1)\"/>\n  <path class=\"d-arrow\" d=\"M340 64 L340 106\" marker-end=\"url(#arrow-l-js-5-1-1)\"/>\n  <path class=\"d-arrow\" d=\"M380 64 L530 106\" marker-end=\"url(#arrow-l-js-5-1-1)\"/>\n  <path class=\"d-arrow\" d=\"M115 158 L115 192\" marker-end=\"url(#arrow-l-js-5-1-1)\"/>\n  <text class=\"d-label-muted\" x=\"420\" y=\"214\" text-anchor=\"middle\">green: pure logic, no DOM, no storage</text>\n</svg><figcaption>Pocket’s modules and what imports what</figcaption></figure>\n<ul>\n<li><code dir=\"ltr\">money.js</code>: <code dir=\"ltr\">toCents</code>, <code dir=\"ltr\">formatMoney</code>. Pure.</li>\n<li><code dir=\"ltr\">expenses.js</code>: <code dir=\"ltr\">addExpense</code>, <code dir=\"ltr\">removeExpense</code>, <code dir=\"ltr\">totalCents</code>, <code dir=\"ltr\">totalsByCategory</code>, <code dir=\"ltr\">validateExpense</code>. Pure: data in, data out.</li>\n<li><code dir=\"ltr\">storage.js</code>: <code dir=\"ltr\">loadExpenses</code>, <code dir=\"ltr\">saveExpenses</code>. The only file that knows about <code dir=\"ltr\">localStorage</code>.</li>\n<li><code dir=\"ltr\">ui.js</code>: <code dir=\"ltr\">renderExpenses</code> and other DOM code.</li>\n<li><code dir=\"ltr\">main.js</code>: the entry point. Loads data, connects event listeners, and calls the others.</li>\n</ul>\n<p>The rule behind the split: dependencies point from the outside in. <code dir=\"ltr\">main.js</code> knows about everything; <code dir=\"ltr\">expenses.js</code> knows about nothing but data. That means you can test, or reuse in Node.js, the core logic without a browser, and you can swap <code dir=\"ltr\">localStorage</code> for a server later by changing one file.</p>\n<aside class=\"callout callout-tip\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5\"/><path d=\"M9 18h6\"/><path d=\"M10 22h4\"/></svg><span><span class=\"callout-kind\">Tip</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>Watch for circular imports</span></p>\n<p>If <code dir=\"ltr\">a.js</code> imports from <code dir=\"ltr\">b.js</code> and <code dir=\"ltr\">b.js</code> imports from <code dir=\"ltr\">a.js</code>, one of them may run before the other has finished defining its exports, and you get confusing <code dir=\"ltr\">undefined</code> or “cannot access before initialization” errors. When that happens, move the shared code into a third module that both import.</p>\n</aside>\n<p>This course’s playground runs one file at a time, so the practice below is about reading module code. In the next lesson you will meet another way to organise code, classes, which group data with the functions that act on it.</p>\n","toc":[{"id":"sec-1","text":"A module is a file with its own scope","level":2},{"id":"sec-2","text":"Loading modules in the browser","level":2},{"id":"sec-3","text":"Modules in Node.js","level":2},{"id":"sec-4","text":"Organising Pocket","level":2}],"takeaways":["A module is a file with its own scope; nothing in it is visible to other files unless it is exported.","Prefer named exports (<code dir=\"ltr\">export function formatMoney</code>), which keep the same name everywhere and are easy to search for.","In the browser, load the entry file with <code dir=\"ltr\">&lt;script type=&quot;module&quot;&gt;</code>, use relative paths with the <code dir=\"ltr\">.js</code> extension, and serve the files over http.","In Node.js, files are ES modules when the nearest <code dir=\"ltr\">package.json</code> has <code dir=\"ltr\">&quot;type&quot;: &quot;module&quot;</code> or the file ends in <code dir=\"ltr\">.mjs</code>.","[object Object]"],"further":[{"title":"JavaScript modules (MDN)","url":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules"},{"title":"export (MDN)","url":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export"},{"title":"Modules: ECMAScript modules (Node.js)","url":"https://nodejs.org/api/esm.html"}],"quiz":[{"q":"<p>The browser console says <code dir=\"ltr\">Uncaught SyntaxError: Cannot use import statement outside a module</code>. What is the fix?</p>","options":[{"text":"Rename the file from <code dir=\"ltr\">.js</code> to <code dir=\"ltr\">.mjs</code>.","why":"The browser does not decide by file extension. It decides by how the script tag loads the file."},{"text":"Move the <code dir=\"ltr\">import</code> lines to the bottom of the file.","why":"Imports are allowed only in modules, wherever they appear. Moving them does not make the file a module."},{"text":"Wrap the imports in a function.","why":"Static <code dir=\"ltr\">import</code> statements must be at the top level of a module; inside a function they are a syntax error."},{"text":"Load the entry file with <code dir=\"ltr\">&lt;script type=&quot;module&quot; src=&quot;./main.js&quot;&gt;</code>.","why":"Correct. Without <code dir=\"ltr\">type=&quot;module&quot;</code>, the browser runs the file as a classic script, where <code dir=\"ltr\">import</code> is not allowed."}],"answer":3},{"q":"<p><code dir=\"ltr\">money.js</code> declares <code dir=\"ltr\">function toCents(d) { … }</code> without <code dir=\"ltr\">export</code>. What happens when <code dir=\"ltr\">main.js</code> runs <code dir=\"ltr\">import { toCents } from &quot;./money.js&quot;;</code>?</p>","options":[{"text":"It works, because all top-level functions are shared between modules.","why":"Each module has its own scope. Only exported names can be imported."},{"text":"Loading fails with a SyntaxError saying <code dir=\"ltr\">money.js</code> does not provide an export named <code dir=\"ltr\">toCents</code>.","why":"Correct. Imports are checked before any code runs, so the mistake is reported immediately."},{"text":"<code dir=\"ltr\">toCents</code> is imported as <code dir=\"ltr\">undefined</code> and the error appears when you call it.","why":"Named imports are linked before the code runs; a missing export stops the module graph from loading at all."}],"answer":1},{"q":"<p>You open <code dir=\"ltr\">index.html</code> by double-clicking it, and the module script fails to load with a CORS error. Why?</p>","options":[{"text":"Module scripts are blocked from <code dir=\"ltr\">file://</code> pages, so the files must be served over http, for example by a dev server.","why":"Correct. Browsers apply stricter loading rules to modules. Any local server, such as the one Vite starts, fixes it."},{"text":"Modules only work after the site is deployed to the internet.","why":"A local server on your own computer is enough. Deployment is not required."},{"text":"The browser is too old to support modules.","why":"Every current browser has supported modules for years. The problem is the <code dir=\"ltr\">file://</code> origin, not the browser."}],"answer":0}],"exercise":{"kind":"spot-bug","language":"js","code":"// src/money.js\nexport function formatMoney(cents) {\n  return `$${(cents / 100).toFixed(2)}`;\n}\nfunction toCents(dollars) {\n  return Math.round(dollars * 100);\n}\n\n// src/main.js  (loaded with <script type=\"module\" src=\"./src/main.js\">)\nimport { formatMoney, toCents } from \"./money\";\nconst amount = toCents(12.2);\nconsole.log(formatMoney(amount));","bugLines":[5,10],"title":"Why won't Pocket's modules load?","prompt":"<p>Pocket was split into two modules, and now the page shows nothing and the console reports errors. The HTML loads <code dir=\"ltr\">src/main.js</code> correctly with <code dir=\"ltr\">type=&quot;module&quot;</code> from a plain static file server (no build tool such as Vite). Select the <strong>two</strong> lines that stop these modules from working in the browser.</p>\n","hints":["One file uses a function from the other. Is it actually offered?","Look closely at the import path. In the browser, without a build tool, the path must point to the exact file."],"explanation":"<p><strong>Line 5:</strong> <code dir=\"ltr\">toCents</code> is not exported, so it is private to <code dir=\"ltr\">money.js</code>. The import fails before any code runs, with an error like <code dir=\"ltr\">The requested module './money.js' does not provide an export named 'toCents'</code>. Fix: <code dir=\"ltr\">export function toCents(dollars) {</code>.</p>\n<p><strong>Line 10:</strong> the browser requests exactly the URL you write, and <code dir=\"ltr\">./money</code> is not a file (the file is <code dir=\"ltr\">money.js</code>), so the request returns 404. Fix: <code dir=\"ltr\">import { formatMoney, toCents } from &quot;./money.js&quot;;</code>. Build tools such as Vite let you leave off the extension, but plain browser modules do not.</p>\n","checkLabels":{}},"words":710,"readMinutes":3}