Aufwärmen · Aufgabe 1 von 7
// B3.1 · ca. 38 Min. · Einstieg
Pfade und Datei-URLs
Nach dieser Lektion bauen Sie Pfade, die auf jedem System und aus jedem Ordner funktionieren, und wandeln Pfade in Datei-URLs um und zurück.
Lektion 1 von 5 in B3 Dateien und Pfade
Danach können Sie
- Pfade mit node:path bauen und zerlegen und mit path.posix oder path.win32 überall dasselbe Ergebnis bekommen
- process.cwd() von import.meta.dirname unterscheiden und Dateien neben einem Modul aus jedem Ordner erreichen
- Datei-URLs nutzen: new URL mit import.meta.url, fileURLToPath und pathToFileURL
Vorhersagen · Aufgabe 2 von 7
Sagen Sie es voraus, bevor Sie weiterlesen. Sie sind in /home/ada und führen node shop/src/main.js aus. Was gibt es aus?
// /home/ada/shop/src/main.js import path from "node:path"; console.log(path.resolve("data.json"));Üben · Aufgabe 3 von 7
Setzen Sie die Funktion ein, sodass die Zeile notes.txt liefert, wo immer Sie sie ausführen.
path.posix.____("docs", "..", "notes.txt") // "notes.txt"path.posix.("docs", "..", "notes.txt")Üben · Aufgabe 4 von 7
Ordnen Sie jedem Aufruf mit "/home/ada/notes/todo.txt" zu, was er liefert.
Üben · Aufgabe 5 von 7
Ein Windows-Pfad kommt in einem Programm an, das unter Linux läuft und path.posix nutzt. Was gibt es aus?
import path from "node:path"; console.log(path.posix.basename("C:\\temp\\myfile.html"));Denksport · Aufgabe 6 von 7
Knobelaufgabe. Ein Nutzer gibt für einen Upload den Dateinamen "/etc/passwd" ein. Was geben die beiden Zeilen aus?
import path from "node:path"; const name = "/etc/passwd"; // typed by a user console.log(path.posix.join("/srv/uploads", name)); console.log(path.posix.resolve("/srv/uploads", name));Anwenden · Aufgabe 7 von 7
Mini-Aufgabe, lokal. Legen Sie in einem Ordner shop mit "type": "module" die Datei data/prices.json mit {"tea": 2.5} an und schreiben Sie src/main.js. Es baut den Pfad von data/prices.json aus import.meta.dirname, liest und parst die Datei und gibt den Pfad, seine Datei-URL (pathToFileURL) und den Preis von tea aus. Starten Sie es in shop mit node src/main.js, dann aus einem anderen Ordner mit dem vollen Pfad zu main.js: Die Ausgabe muss gleich sein.
Prüfen Sie Ihr Ergebnis anhand dieser Liste
Selbst programmieren
Lesen Sie das ausgearbeitete Beispiel und lösen Sie dann die Übungen. Ihr Code läuft in Ihrem Browser oder auf Ihrem Computer und wird nie hochgeladen.
Ausgearbeitetes Beispiel
Pfade für eine Notiz-App
main.js arbeitet auf drei Arten mit Pfaden: reine String-Arbeit mit path.posix und path.win32, der eigene Ort des Moduls mit import.meta und eine Datei daneben, gefunden über Pfad und über URL. data.json liegt neben main.js. Führen Sie node main.js aus; die Ausgabe ist aus jedem Ordner gleich.
main.js
// Paths for a notes app: string work with node:path, then the module's own place.
import path from "node:path";
import {readFileSync} from "node:fs";
import {fileURLToPath, pathToFileURL} from "node:url";
// 1. String work. path.posix gives the same result on every system.
const note = "/home/ada/notes/2026/todo.txt";
console.log(path.posix.dirname(note));
console.log(path.posix.parse(note).name, path.posix.extname(note));
console.log(path.posix.join("/home/ada/notes", "..", "photos", "cat.png"));
console.log(path.posix.relative("/home/ada/notes", note));
console.log(path.win32.join("C:\\Users\\ada", "notes", "todo.txt"));
// 2. Where is this module? The same from any folder you run node in.
console.log(path.basename(import.meta.filename));
console.log(import.meta.filename === fileURLToPath(import.meta.url));
console.log(import.meta.dirname === path.dirname(import.meta.filename));
// 3. Two ways to the file next to this module.
const byPath = path.join(import.meta.dirname, "data.json");
const byUrl = new URL("./data.json", import.meta.url);
console.log(byUrl.protocol, fileURLToPath(byUrl) === byPath);
console.log(JSON.parse(readFileSync(byUrl, "utf8")).title);
// 4. In a URL, a space becomes %20.
console.log(pathToFileURL("/home/ada/my notes/todo.txt", {windows: false}).href);
data.json
{"title": "My notes"}
Ausführen mit
node main.jsAusgabe
/home/ada/notes/2026
todo .txt
/home/ada/photos/cat.png
2026/todo.txt
C:\Users\ada\notes\todo.txt
main.js
true
true
file: true
My notes
file:///home/ada/my%20notes/todo.txt- path.posix und path.win32 berechnen unter Linux, macOS und Windows dieselben Strings, also ändern sich diese Zeilen nie.
- import.meta.filename ist fileURLToPath(import.meta.url), und import.meta.dirname ist dessen dirname: drei Blicke auf einen Ort.
- readFileSync nimmt das URL-Objekt direkt an; der mit join gebaute Pfad führt zur selben Datei.
- {windows: false} verlangt einen POSIX-Pfad, also ist die URL auch unter Windows dieselbe.
Übungen
Übung 1 von 3
Ihr eigenes extname
Schreiben Sie extname(p) für POSIX-Pfade, wie path.posix.extname es macht: Nehmen Sie den letzten Teil nach dem letzten / und geben Sie alles ab seinem letzten Punkt zurück. Hat dieser Teil keinen Punkt, oder ist sein einziger Punkt das erste Zeichen (".env"), geben Sie "" zurück. "archive.tar.gz" ergibt ".gz", "index." ergibt ".". Reine String-Arbeit, läuft also auch im Browser.
Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.
Beim ersten Ausführen lädt Ihr Browser den JavaScript-Runner herunter (bis zu 0.1 MB) und speichert ihn im Cache. Ihr Code läuft in der eigenen Engine Ihres Browsers und bleibt auf Ihrem Gerät.
Hinweise
Hinweis 1
p.lastIndexOf("/") findet das letzte Trennzeichen; slice danach liefert den letzten Teil.
Hinweis 2
In diesem Teil findet lastIndexOf(".") den letzten Punkt.
Hinweis 3
Nur ein Punkt nach Position 0 beginnt eine Endung: return dot > 0 ? base.slice(dot) : "".
Eine Lösung zeigen
Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.
// The extension of the last part of a POSIX path: "todo.txt" -> ".txt".
export function extname(p) {
const base = p.slice(p.lastIndexOf("/") + 1);
const dot = base.lastIndexOf(".");
return dot > 0 ? base.slice(dot) : "";
}
console.log(extname("/home/ada/notes/todo.txt"));
Auf dem eigenen Computer ausführen
Installieren Sie Node.js 24 LTS oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.
main.js
// The extension of the last part of a POSIX path: "todo.txt" -> ".txt".
export function extname(p) {
return p.slice(p.indexOf("."));
}
console.log(extname("/home/ada/notes/todo.txt"));
main.test.js
import {test} from 'node:test';
import assert from 'node:assert/strict';
import {extname} from './main.js';
const check = (p, want) => assert.equal(extname(p), want, `extname(${JSON.stringify(p)}) lieferte ${JSON.stringify(extname(p))}, erwartet ${JSON.stringify(want)}`);
test('ein einfacher Dateiname', () => {
check('todo.txt', '.txt');
check('/home/ada/notes/todo.txt', '.txt');
});
test('nur der letzte Punkt zählt', () => {
check('archive.tar.gz', '.gz');
});
test('ein Punkt im Ordnernamen zählt nicht', () => {
check('/home/ada/v1.2/notes', '');
});
test('keine Endung: kein Punkt oder nur ein Punkt am Anfang', () => {
check('README', '');
check('/home/ada/.env', '');
});
test('ein Punkt am Ende ist eine eigene Endung', () => {
check('index.', '.');
});
package.json
{
"type": "module"
}
package.json sagt Node.js, dass die .js-Dateien Module sind; lassen Sie die Datei im Ordner.
Programm ausführen:
node main.jsPrüfungen ausführen (learnrun.js muss im selben Ordner liegen):
node --testlearnrun.js herunterladenÜbung 2 von 3
Ihr eigenes POSIX-join
Schreiben Sie joinPosix(...parts) wie path.posix.join: Leere Teile fallen weg, der Rest wird mit / verbunden und dann aufgeräumt. Mehrere Schrägstriche werden einer, "."-Teile fallen weg, ".." entfernt den Teil davor oder bleibt am Anfang eines relativen Pfads stehen; an der Wurzel bleibt ".." an der Wurzel. Ein Pfad, der mit / beginnt, bleibt absolut, und ein leeres Ergebnis ist ".". Werfen Sie einen TypeError, wenn ein Teil kein String ist. Einen Schrägstrich ganz am Ende dürfen Sie ignorieren. Reine String-Arbeit, läuft also auch im Browser.
Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.
Beim ersten Ausführen lädt Ihr Browser den JavaScript-Runner herunter (bis zu 0.1 MB) und speichert ihn im Cache. Ihr Code läuft in der eigenen Engine Ihres Browsers und bleibt auf Ihrem Gerät.
Hinweise
Hinweis 1
Prüfen Sie zuerst jeden Teil; dann ergibt parts.filter((part) => part !== "").join("/") einen String.
Hinweis 2
Teilen Sie ihn an "/" und gehen Sie die Segmente durch: "" und "." überspringen, bei ".." das letzte behaltene Segment mit pop entfernen.
Hinweis 3
Merken Sie sich, ob der String mit / begann. Behalten Sie ".." nur in einem relativen Pfad ohne etwas zum Entfernen, und geben Sie "." zurück, wenn nichts übrig ist.
Eine Lösung zeigen
Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.
// Joins POSIX path parts and tidies the result, like path.posix.join.
export function joinPosix(...parts) {
for (const part of parts) {
if (typeof part !== "string") throw new TypeError("every part must be a string, got " + typeof part);
}
const joined = parts.filter((part) => part !== "").join("/");
if (joined === "") return ".";
const absolute = joined.startsWith("/");
const kept = [];
for (const segment of joined.split("/")) {
if (segment === "" || segment === ".") continue;
if (segment === "..") {
if (kept.length > 0 && kept.at(-1) !== "..") kept.pop();
else if (!absolute) kept.push("..");
} else {
kept.push(segment);
}
}
const body = kept.join("/");
return absolute ? "/" + body : body || ".";
}
console.log(joinPosix("docs", "..", "notes.txt"));
Auf dem eigenen Computer ausführen
Installieren Sie Node.js 24 LTS oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.
main.js
// Joins POSIX path parts and tidies the result, like path.posix.join.
export function joinPosix(...parts) {
return parts.join("/");
}
console.log(joinPosix("docs", "..", "notes.txt"));
main.test.js
import {test} from 'node:test';
import assert from 'node:assert/strict';
import {joinPosix} from './main.js';
const check = (parts, want) => {
const got = joinPosix(...parts);
assert.equal(got, want, `joinPosix(${parts.map((p) => JSON.stringify(p)).join(', ')}) lieferte ${JSON.stringify(got)}, erwartet ${JSON.stringify(want)}`);
};
test('Teile werden mit / verbunden', () => {
check(['/home/ada', 'notes', 'todo.txt'], '/home/ada/notes/todo.txt');
});
test('.. entfernt den Teil davor', () => {
check(['docs', '..', 'notes.txt'], 'notes.txt');
check(['/home/ada/shop/src', '..', 'data', 'prices.json'], '/home/ada/shop/data/prices.json');
});
test('mehrere Schrägstriche und .-Teile werden aufgeräumt', () => {
check(['a//', 'b'], 'a/b');
check(['./a', './b'], 'a/b');
});
test('ein führender Schrägstrich bei einem späteren Teil wird angehängt', () => {
check(['/srv/uploads', '/etc/passwd'], '/srv/uploads/etc/passwd');
});
test('.. am Anfang eines relativen Pfads bleibt, an der Wurzel endet es', () => {
check(['..', 'a'], '../a');
check(['/', '..', 'etc'], '/etc');
});
test('bleibt nichts übrig, ergibt das .', () => {
check([], '.');
check(['a', '..'], '.');
});
test('ein Teil, der kein String ist, wirft einen TypeError', () => {
assert.throws(() => joinPosix('notes', 2026), TypeError, 'joinPosix("notes", 2026) sollte einen TypeError werfen');
});
package.json
{
"type": "module"
}
package.json sagt Node.js, dass die .js-Dateien Module sind; lassen Sie die Datei im Ordner.
Programm ausführen:
node main.jsPrüfungen ausführen (learnrun.js muss im selben Ordner liegen):
node --testlearnrun.js herunterladenÜbung 3 von 3
Dateien neben dem Modul
notes.txt liegt neben main.js. Schreiben Sie drei Funktionen, die aus jedem Ordner funktionieren, in dem Sie node starten. here(name) liefert den absoluten Pfad von name im Ordner von main.js, gebaut mit path.join und import.meta.dirname. hereUrl(name) liefert ein URL-Objekt für dieselbe Datei, gebaut mit new URL und import.meta.url. readHere(name) liest diese Datei über hereUrl als UTF-8-Text. Führen Sie die Tests mit node --test aus.
Diese Übung braucht Node.js auf Ihrem Computer (die Browser-Version kann sie nicht ausführen). Dateien und Befehle stehen unten.
Hinweise
Hinweis 1
path.join(import.meta.dirname, name) liefert den Pfad; join setzt das Trennzeichen dazwischen.
Hinweis 2
new URL(name, import.meta.url) löst name gegen die URL von main.js auf.
Hinweis 3
readFileSync nimmt ein URL-Objekt an: readFileSync(hereUrl(name), "utf8").
Eine Lösung zeigen
Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.
import path from "node:path";
import {readFileSync} from "node:fs";
export function here(name) {
return path.join(import.meta.dirname, name);
}
export function hereUrl(name) {
return new URL(name, import.meta.url);
}
export function readHere(name) {
return readFileSync(hereUrl(name), "utf8");
}
Auf dem eigenen Computer ausführen
Installieren Sie Node.js 24 LTS oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.
main.js
import path from "node:path";
import {readFileSync} from "node:fs";
export function here(name) {
return name;
}
export function hereUrl(name) {
return new URL(name);
}
export function readHere(name) {
return readFileSync(name, "utf8");
}
main.test.js
import {test} from 'node:test';
import assert from 'node:assert/strict';
import {here, hereUrl, readHere} from './main.js';
test('here() ist die Datei im Ordner von main.js', () => {
const got = here('notes.txt');
const folder = import.meta.dirname;
assert.ok(got.startsWith(folder) && got.endsWith('notes.txt') && got.length === folder.length + 'notes.txt'.length + 1, `here('notes.txt') lieferte ${JSON.stringify(got)}; erwartet war notes.txt in ${folder}`);
});
test('hereUrl() ist eine file:-URL neben main.js', () => {
const got = hereUrl('notes.txt');
assert.ok(got instanceof URL, 'hereUrl() sollte ein URL-Objekt liefern');
assert.equal(got.href, new URL('./notes.txt', import.meta.url).href, `hereUrl('notes.txt') lieferte ${got.href}`);
});
test('readHere() liest den Text von notes.txt', () => {
assert.equal(readHere('notes.txt'), 'buy tea\ncall Ben\n', `readHere('notes.txt') lieferte ${JSON.stringify(readHere('notes.txt'))}`);
});
notes.txt
buy tea
call Ben
package.json
{
"type": "module"
}
package.json sagt Node.js, dass die .js-Dateien Module sind; lassen Sie die Datei im Ordner.
Programm ausführen:
node main.jsPrüfungen ausführen (learnrun.js muss im selben Ordner liegen):
node --testlearnrun.js herunterladenHäufige Fehler
Einen Pfad mit + zusammenkleben
// The folder as import.meta.dirname gives it: no slash at the end.
import {readFileSync} from "node:fs";
const folder = "/home/ada/shop/src";
console.log(readFileSync(folder + "notes.txt", "utf8"));
Was Node.js ausgibt
Error: ENOENT: no such file or directory, open '/home/ada/shop/srcnotes.txt'Warum, und die Lösung
import.meta.dirname hat am Ende keinen Schrägstrich, also klebt + die Namen zusammen, und die Datei wird unter einem Namen gesucht, den es nicht gibt. path.join(import.meta.dirname, "notes.txt") setzt das Trennzeichen des Systems ein und verdoppelt auch keines.
Eine Zahl als Pfadteil
import path from "node:path";
const year = 2026;
console.log(path.join("notes", year, "todo.txt"));
Was Node.js ausgibt
TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string. Received type number (2026)Warum, und die Lösung
Jeder Teil von path.join muss ein String sein; node:path wandelt Zahlen nicht für Sie um. Schreiben Sie String(year) oder ein Template-Literal: path.join("notes", `${year}`, "todo.txt").
Ein Ordnerpfad als Basis einer URL
// Looks for data.json next to this module, but with the wrong base.
const url = new URL("data.json", import.meta.dirname);
console.log(url.href);
Was Node.js ausgibt
TypeError: Invalid URLWarum, und die Lösung
Die Basis von new URL muss selbst eine URL sein, und import.meta.dirname ist ein Pfad wie /home/ada/shop/src. Nehmen Sie import.meta.url als Basis: new URL("data.json", import.meta.url). Oder bleiben Sie bei Pfaden: path.join(import.meta.dirname, "data.json").
JavaScript im Browser: die eigene Engine Ihres Browsers in einem abgeschotteten Worker. Syntaxfehler findet acorn 8.18.0, MIT. Lizenz und Quellcode
Abschlussquiz
5 Fragen, ohne Hinweise. Ab 80 % ist die Lektion abgeschlossen.
Erledigen Sie zuerst alle Aufgaben oben, um das Abschlussquiz freizuschalten.