Warm-up · Activity 1 of 7
Warm-up from lesson I3.2. What does main.js print?
// tools.js
export default function shout(text) {
return text.toUpperCase();
}
// main.js
import loud from "./tools.js";
console.log(loud("hi"));// I3.3 · ~34 min · Intermediate
After this lesson you know why Node.js treats a file as an ES module or as CommonJS, and you replace require, __dirname and JSON loading with their ES module versions.
You will be able to
Warm-up · Activity 1 of 7
// tools.js
export default function shout(text) {
return text.toUpperCase();
}
// main.js
import loud from "./tools.js";
console.log(loud("hi"));Predict · Activity 2 of 7
// package.json
{"type": "commonjs"}
// main.js
import {platform} from "node:os";
console.log(typeof platform());Practice · Activity 3 of 7
import {readFileSync} from "____:fs";Practice · Activity 4 of 7
Practice · Activity 5 of 7
// main.js
const fs = require("node:fs");
console.log(typeof fs.readFileSync);Brain teaser · Activity 6 of 7
// app/data.txt
hello
// app/main.js
import {readFileSync} from "node:fs";
console.log(readFileSync("data.txt", "utf8").trim());Apply · Activity 7 of 7
Check your work against this list
Read the worked example, then write the exercises. Your code runs in your browser or on your computer and is never uploaded.
Worked example
main.js is an ES module, because the project’s package.json says "type": "module". It imports built-ins with node:, a CommonJS file whose module.exports arrives as the default export, and a JSON file with an import attribute. It reads notes.txt next to itself through import.meta.dirname, and loads node:os only when it needs it, with a dynamic import.
main.js
import {readFileSync} from "node:fs";
import {join} from "node:path";
import legacy from "./legacy.cjs"; // a CommonJS file: its module.exports
import settings from "./settings.json" with {type: "json"};
console.log(typeof require, typeof __dirname); // CommonJS only
console.log(import.meta.filename.endsWith("main.js"));
// A file next to this module, wherever node is started from:
const notes = readFileSync(join(import.meta.dirname, "notes.txt"), "utf8");
console.log(notes.trim());
console.log(legacy.greet("Ada"));
console.log(settings.theme);
const os = await import("node:os"); // a dynamic import
console.log(typeof os.platform());
legacy.cjs
module.exports = {
greet(name) {
return "Hello, " + name;
}
};
settings.json
{"theme": "dark"}
notes.txt
Remember: import, not require.
Run it with
node main.jsOutput
undefined undefined
true
Remember: import, not require.
Hello, Ada
dark
stringExercise 1 of 2
loadWords() should read words.txt, which sits next to main.js, and return its lines as an array. The starter builds the path with __dirname, which does not exist in an ES module, so the call throws. Use import.meta.dirname instead.
This exercise needs Node.js on your computer (the browser version cannot run it). The files and commands are below.
Run the tests: ReferenceError: __dirname is not defined in ES module scope. What replaces it?
import.meta.dirname is the folder of the current module.
join(import.meta.dirname, "words.txt")
One way to solve it. Yours can look different and still pass the checks.
import {readFileSync} from "node:fs";
import {join} from "node:path";
export function loadWords() {
const text = readFileSync(join(import.meta.dirname, "words.txt"), "utf8");
return text.trim().split("\n");
}
Install ECMAScript 2026 or newer. Save these files in one folder, open a terminal in that folder, and run the commands below.
main.js
import {readFileSync} from "node:fs";
import {join} from "node:path";
export function loadWords() {
const text = readFileSync(join(__dirname, "words.txt"), "utf8");
return text.trim().split("\n");
}
main.test.js
import {test} from 'node:test';
import assert from 'node:assert/strict';
import {loadWords} from './main.js';
test('loadWords reads the three words', () => {
const words = loadWords();
assert.deepEqual(words, ['apple', 'banana', 'cherry'], `loadWords gave ${JSON.stringify(words)}`);
});
words.txt
apple
banana
cherry
package.json
{
"type": "module"
}
package.json tells Node.js that the .js files are modules; keep it in the folder.
Run the program:
node main.jsRun the checks (needs learnrun.js in the same folder):
node --testDownload learnrun.jsExercise 2 of 2
legacy.cjs is an old CommonJS file with toCelsius and toFahrenheit on module.exports. convert(value, to) in main.js should use them: to "C" converts from Fahrenheit, to "F" from Celsius. The starter calls require inside the function, which an ES module does not have. Import legacy.cjs at the top instead.
This exercise needs Node.js on your computer (the browser version cannot run it). The files and commands are below.
Run the tests: require is not defined in ES module scope. How does an ES module load another file?
A CommonJS file’s module.exports arrives as the default export.
import temperature from "./legacy.cjs"; at the top of main.js
One way to solve it. Yours can look different and still pass the checks.
import temperature from "./legacy.cjs";
export function convert(value, to) {
return to === "C" ? temperature.toCelsius(value) : temperature.toFahrenheit(value);
}
Install ECMAScript 2026 or newer. Save these files in one folder, open a terminal in that folder, and run the commands below.
main.js
export function convert(value, to) {
const temperature = require("./legacy.cjs");
return to === "C" ? temperature.toCelsius(value) : temperature.toFahrenheit(value);
}
main.test.js
import {test} from 'node:test';
import assert from 'node:assert/strict';
import {convert} from './main.js';
test('212 Fahrenheit is 100 Celsius', () => {
const got = convert(212, 'C');
assert.equal(got, 100, `convert(212, 'C') gave ${got}`);
});
test('20 Celsius is 68 Fahrenheit', () => {
const got = convert(20, 'F');
assert.equal(got, 68, `convert(20, 'F') gave ${got}`);
});
legacy.cjs
module.exports = {
toCelsius: (f) => Math.round(((f - 32) * 5) / 9),
toFahrenheit: (c) => Math.round((c * 9) / 5 + 32)
};
package.json
{
"type": "module"
}
package.json tells Node.js that the .js files are modules; keep it in the folder.
Run the program:
node main.jsRun the checks (needs learnrun.js in the same folder):
node --testDownload learnrun.jsconst {readFileSync} = require("node:fs");
console.log(readFileSync("main.js", "utf8").length > 0);
What Node.js prints
ReferenceError: require is not defined in ES module scope, you can use import insteadWhy, and the fix
A .js file in a package with "type": "module" is an ES module, where require does not exist. Write import {readFileSync} from "node:fs"; at the top. Only if you really need CommonJS, name the file .cjs.
import settings from "./settings.json";
console.log(settings.theme);
What Node.js prints
TypeError [ERR_IMPORT_ATTRIBUTE_MISSING]Why, and the fix
Node.js only imports JSON when the import says what it expects, so that a file can never run as code by mistake. Add the import attribute: import settings from "./settings.json" with {type: "json"};. Or read the file with readFileSync and JSON.parse.
import {greet} from "./legacy.cjs";
console.log(greet("Ada"));
What Node.js prints
SyntaxError: Named export 'greet' not found. The requested module './legacy.cjs' is a CommonJS module, which may not support all module.exports as named exports.Why, and the fix
Node.js can only guess named exports from some CommonJS code, and here it cannot. The default export always works, because it is module.exports itself: import legacy from "./legacy.cjs"; then legacy.greet("Ada"), or const {greet} = legacy;.
JavaScript in the browser: your browser’s own engine, in a sandboxed worker. Syntax errors are located with acorn 8.18.0, MIT. Licence and source
5 questions, no hints. Score 80% or more to complete the lesson.
Finish every activity above to unlock the exit ticket.