Aufwärmen · Aufgabe 1 von 7
// B2.1 · ca. 34 Min. · Einstieg
package.json und npm-Skripte
Nach dieser Lektion können Sie eine package.json anlegen und lesen, ihre Skripte mit npm ausführen und entscheiden, wann node --run genügt.
Lektion 1 von 5 in B2 npm und Pakete
Danach können Sie
- package.json mit npm init -y anlegen und name, version, private, type, dependencies und devDependencies lesen
- Skripte mit npm run, npm test und npm start ausführen, Argumente nach -- übergeben und pre- und post-Skripte vorhersagen
- Zwischen npm run und node --run wählen und wissen, was node --run absichtlich weglässt
Vorhersagen · Aufgabe 2 von 7
Sagen Sie es vorher, bevor Sie weiterlesen. Das ist die package.json, und step.js gibt die Wörter aus, die es erhält. Sie führen npm run -s build aus (-s blendet nur die eigenen >-Zeilen von npm aus). Was erscheint, Zeile für Zeile?
{ "name": "demo", "version": "1.0.0", "scripts": { "prebuild": "node step.js pre", "build": "node step.js build", "postbuild": "node step.js post" } }Üben · Aufgabe 3 von 7
Das Skript "greet" ist node greet.js. Füllen Sie die Lücke so, dass greet.js --name Ada erhält und npm --name nicht für eine eigene Option hält.
npm run greet ____ --name Adanpm run greet --name AdaÜben · Aufgabe 4 von 7
Ordnen Sie jedem Eintrag der package.json zu, was er bewirkt.
Üben · Aufgabe 5 von 7
In diesem Projekt tippen Sie npm run dev. Was passiert?
{ "name": "demo", "version": "1.0.0", "scripts": { "start": "node main.js", "test": "node --test" } }Denksport · Aufgabe 6 von 7
Knobelaufgabe. Dasselbe Projekt wie vorhin: "prebuild", "build" und "postbuild" führen jeweils step.js aus. Was gibt dieser Befehl aus?
node --run buildAnwenden · Aufgabe 7 von 7
Mini-Aufgabe. Führen Sie in einem neuen Ordner npm init -y aus. Ändern Sie in der package.json "type" auf "module", ergänzen Sie "private": true, ersetzen Sie das Skript "test" durch node --test und fügen Sie ein Skript "pretest" hinzu, das echo checking ausführt. Schreiben Sie sum.js mit einer exportierten Funktion sum und sum.test.js mit einem Test. Führen Sie npm test aus, dann node --run test, und vergleichen Sie die Ausgaben.
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
npm run und node --run nebeneinander
Dieses Programm spielt Ihr Terminal. Es schreibt ein kleines Projekt in einen temporären Ordner: ein Skript "build" mit "prebuild" und "postbuild", die alle step.js ausführen; step.js gibt die erhaltenen Wörter und die Variable npm_package_version aus. Dann führt es denselben Befehl mit npm run und mit node --run aus und gibt aus, was jeweils auf stdout erschien. Sie brauchen Node.js und npm, aber kein Netzwerk.
main.js
// Plays the part of your terminal: makes a small project in a temporary
// folder and runs the same script with npm run and with node --run.
import {execSync} from "node:child_process";
import {mkdtempSync, writeFileSync} from "node:fs";
import {tmpdir} from "node:os";
import {join} from "node:path";
const dir = mkdtempSync(join(tmpdir(), "scripts-"));
const pkg = {
name: "demo",
version: "1.0.0",
private: true,
scripts: {
prebuild: "node step.js prebuild",
build: "node step.js build",
postbuild: "node step.js postbuild"
}
};
writeFileSync(join(dir, "package.json"), JSON.stringify(pkg, null, 2));
// step.js prints the words it got and the version that npm passes on.
writeFileSync(join(dir, "step.js"), 'console.log(process.argv.slice(2).join(" "), "| version:", process.env.npm_package_version);\n');
for (const command of ["npm run build -- --fast", "node --run build -- --fast"]) {
console.log("$ " + command);
console.log(execSync(command, {cwd: dir, encoding: "utf8"}).trim());
}
Ausführen mit
node main.jsAusgabe
$ npm run build -- --fast
> demo@1.0.0 prebuild
> node step.js prebuild
prebuild | version: 1.0.0
> demo@1.0.0 build
> node step.js build --fast
build --fast | version: 1.0.0
> demo@1.0.0 postbuild
> node step.js postbuild
postbuild | version: 1.0.0
$ node --run build -- --fast
build --fast | version: undefined- npm run gibt vor jedem Skript > demo@1.0.0 und den Befehl aus; npm run -s würde diese Zeilen ausblenden.
- --fast erreicht nur build: npm gibt Argumente nach -- an das genannte Skript weiter, nicht an prebuild oder postbuild.
- node --run hat nur build ausgeführt, und npm_package_version ist dort undefined: Es setzt keine npm_-Variablen.
- Beide Befehle haben --fast an denselben Eintrag "build" der package.json angehängt.
Übungen
Übung 1 von 2
Was npm run ausführen wird
Schreiben Sie runOrder(pkg, name). pkg ist eine eingelesene package.json. Geben Sie die Skriptnamen zurück, die npm run <name> ausführt, in Reihenfolge: pre<name>, falls vorhanden, dann <name>, dann post<name>, falls vorhanden. Fehlt <name> in "scripts" oder gibt es gar kein "scripts", werfen Sie einen Error mit der Meldung Missing script: "<name>", wie npm. Die letzte Zeile von main.js gibt die Reihenfolge für ein kleines Projekt aus.
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
Verwenden Sie pkg.scripts ?? {}, damit eine package.json ohne "scripts" Ihren Code nicht abstürzen lässt.
Hinweis 2
name in scripts sagt Ihnen, ob ein Skript existiert. Werfen Sie zuerst, wenn nicht.
Hinweis 3
Bilden Sie die drei möglichen Namen "pre" + name, name und "post" + name, und behalten Sie nur die, die in scripts stehen.
Eine Lösung zeigen
Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.
// The script names that npm run <name> runs, in order.
export function runOrder(pkg, name) {
const scripts = pkg.scripts ?? {};
if (!(name in scripts)) throw new Error('Missing script: "' + name + '"');
return ["pre" + name, name, "post" + name].filter((script) => script in scripts);
}
const pkg = {scripts: {prebuild: "node clean.js", build: "node build.js", postbuild: "echo done"}};
console.log(runOrder(pkg, "build"));
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 script names that npm run <name> runs, in order.
export function runOrder(pkg, name) {
return [name];
}
const pkg = {scripts: {prebuild: "node clean.js", build: "node build.js", postbuild: "echo done"}};
console.log(runOrder(pkg, "build"));
main.test.js
import {test} from 'node:test';
import assert from 'node:assert/strict';
import {runOrder} from './main.js';
const pkg = {
name: 'demo',
scripts: {prebuild: 'node clean.js', build: 'node build.js', postbuild: 'echo done', test: 'node --test', posttest: 'echo tested', start: 'node main.js'}
};
test('führt prebuild, build und postbuild in dieser Reihenfolge aus', () => {
const got = runOrder(pkg, 'build');
assert.deepEqual(got, ['prebuild', 'build', 'postbuild'], `runOrder(pkg, "build") lieferte ${JSON.stringify(got)}`);
});
test('lässt ein pre- oder post-Skript weg, das es nicht gibt', () => {
const test = runOrder(pkg, 'test');
assert.deepEqual(test, ['test', 'posttest'], `runOrder(pkg, "test") lieferte ${JSON.stringify(test)}`);
const start = runOrder(pkg, 'start');
assert.deepEqual(start, ['start'], `runOrder(pkg, "start") lieferte ${JSON.stringify(start)}`);
});
test('wirft Missing script für ein Skript, das es nicht gibt', () => {
assert.throws(() => runOrder(pkg, 'dev'), {message: 'Missing script: "dev"'}, 'runOrder(pkg, "dev") sollte einen Error mit der Meldung Missing script: "dev" werfen');
});
test('wirft auch, wenn package.json gar kein "scripts" hat', () => {
assert.throws(() => runOrder({name: 'empty'}, 'test'), {message: 'Missing script: "test"'}, 'runOrder({name: "empty"}, "test") sollte Missing script: "test" 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 2 von 2
Prüfen, was npm init -y geliefert hat
Schreiben Sie checkPackage(pkg), das für eine eingelesene package.json eine Liste von Problemen liefert, in dieser Reihenfolge: 'type: set it to "module"', wenn "type" nicht "module" ist; 'private: set it to true', wenn "private" nicht true ist; 'test: write a real test script', wenn es kein Skript "test" gibt oder es noch der Platzhalter von npm init -y ist, der in PLACEHOLDER steht. Eine package.json ohne Probleme ergibt [].
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 die drei Regeln nacheinander mit if und fügen Sie mit push die Meldung jeder verletzten Regel hinzu.
Hinweis 2
pkg.private !== true ist auch dann wahr, wenn "private" fehlt.
Hinweis 3
pkg.scripts?.test ist undefined, wenn es kein Objekt "scripts" gibt; !test deckt also beide fehlenden Fälle ab.
Eine Lösung zeigen
Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.
// The "test" script that npm init -y writes.
const PLACEHOLDER = 'echo "Error: no test specified" && exit 1';
export function checkPackage(pkg) {
const problems = [];
if (pkg.type !== "module") problems.push('type: set it to "module"');
if (pkg.private !== true) problems.push("private: set it to true");
const test = pkg.scripts?.test;
if (!test || test === PLACEHOLDER) problems.push("test: write a real test script");
return problems;
}
console.log(checkPackage({name: "demo", version: "1.0.0", type: "commonjs", scripts: {test: PLACEHOLDER}}));
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 "test" script that npm init -y writes.
const PLACEHOLDER = 'echo "Error: no test specified" && exit 1';
export function checkPackage(pkg) {
const problems = [];
return problems;
}
console.log(checkPackage({name: "demo", version: "1.0.0", type: "commonjs", scripts: {test: PLACEHOLDER}}));
main.test.js
import {test} from 'node:test';
import assert from 'node:assert/strict';
import {checkPackage} from './main.js';
// Exactly what npm init -y wrote with npm 11.19.0.
const fromInit = {
name: 'demo',
version: '1.0.0',
description: '',
main: 'index.js',
scripts: {test: 'echo "Error: no test specified" && exit 1'},
keywords: [],
author: '',
license: 'ISC',
type: 'commonjs'
};
test('findet alle drei Probleme in dem, was npm init -y schreibt', () => {
const got = checkPackage(fromInit);
assert.deepEqual(got, ['type: set it to "module"', 'private: set it to true', 'test: write a real test script'], `checkPackage(fromInit) lieferte ${JSON.stringify(got)}`);
});
test('liefert eine leere Liste für eine fertige package.json', () => {
const got = checkPackage({name: 'demo', version: '1.0.0', private: true, type: 'module', scripts: {test: 'node --test'}});
assert.deepEqual(got, [], `eine fertige package.json ergab ${JSON.stringify(got)}`);
});
test('behandelt ein fehlendes Skript test wie den Platzhalter', () => {
const got = checkPackage({private: true, type: 'module'});
assert.deepEqual(got, ['test: write a real test script'], `eine package.json ohne scripts ergab ${JSON.stringify(got)}`);
});
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
Ein Komma nach dem letzten Eintrag der package.json
// package.json as it was saved, with a comma after the last script.
const text = `{
"name": "demo",
"scripts": {
"start": "node main.js",
}
}`;
const pkg = JSON.parse(text);
console.log(pkg.scripts.start);
Was Node.js ausgibt
SyntaxError: Expected double-quoted property name in JSON at position 66 (line 5 column 3)Warum, und die Lösung
package.json ist JSON, kein JavaScript: kein Komma nach dem letzten Eintrag und keine Kommentare. JSON.parse lehnt diesen Text ab, und npm ebenso: npm start bricht mit npm error code EJSONPARSE ab, mit dem Hinweis, dass package.json echtes JSON sein muss. Löschen Sie das Komma nach "node main.js".
Sich unter node --run auf ein pre-Skript verlassen
// main.js, started by the "build" script. The "prebuild" script writes
// version.txt first, but only npm run build runs prebuild.
import {readFileSync} from "node:fs";
const version = readFileSync("version.txt", "utf8").trim();
console.log("building version", version);
Was Node.js ausgibt
Error: ENOENT: no such file or directory, open 'version.txt'Warum, und die Lösung
npm run build führt vor build das Skript prebuild aus, das version.txt schreibt. node --run build führt nur build aus, also fehlt die Datei, genau wie bei node main.js von Hand. Starten Sie ein solches Projekt mit npm run build, oder machen Sie den Schritt in einem Skript sichtbar: "build": "node version.js && node main.js".
npm_package_version unter node --run lesen
// main.js, started by "start": "node main.js". npm start sets
// npm_package_version; node --run start and node main.js do not.
const [major] = process.env.npm_package_version.split(".");
console.log("major version", major);
Was Node.js ausgibt
TypeError: Cannot read properties of undefined (reading 'split')Warum, und die Lösung
npm setzt für seine Skripte npm_package_name, npm_package_version und npm_lifecycle_event. node --run lässt sie absichtlich weg und setzt nur NODE_RUN_SCRIPT_NAME und NODE_RUN_PACKAGE_JSON_PATH, daher ist die Variable undefined. Damit das Programm unabhängig vom Startweg funktioniert, lesen Sie die Version aus der package.json selbst statt aus der Umgebung.
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.