Shell-Skripting mit Node.js
Sie können die Offline-Version dieses Buches (HTML, PDF, EPUB, MOBI) kaufen und damit die kostenlose Online-Version unterstützen.
(Werbung, bitte nicht blockieren.)

15 Plattformübergreifende Aufgaben mit npm-Paket-Skripten ausführen



package.json hat die Eigenschaft "scripts", mit der wir Paket-Skripte definieren können, kleine Shell-Skripte, die Paket-bezogene Aufgaben wie das Kompilieren von Artefakten oder das Ausführen von Tests durchführen. Dieses Kapitel erklärt sie und wie wir sie so schreiben können, dass sie sowohl unter Windows als auch unter Unix (macOS, Linux usw.) funktionieren.

15.1 npm-Paket-Skripte

npm-Paket-Skripte werden über die Eigenschaft "scripts" von package.json definiert.

{
  ···
  "scripts": {
    "tsc": "tsc",
    "tscwatch": "tsc --watch",
    "tscclean": "shx rm -rf ./dist/*"
  },
  ···
}

Der Wert von "scripts" ist ein Objekt, bei dem jede Eigenschaft ein Paket-Skript definiert.

Wenn wir eingeben

npm run <script-name>

dann führt npm das Skript mit dem Namen script-name in einer Shell aus. Zum Beispiel können wir verwenden

npm run tscwatch

um den folgenden Befehl in einer Shell auszuführen

tsc --watch

In diesem Kapitel werden wir gelegentlich die npm-Option -s verwenden, die eine Abkürzung für --silent ist und npm run anweist, weniger Ausgabe zu erzeugen.

npm -s run <script-name>

Diese Option wird im Abschnitt über die Protokollierung ausführlicher behandelt (siehe Abschnitt über die Protokollierung).

15.1.1 Kürzere npm-Befehle zum Ausführen von Paket-Skripten

Einige Paket-Skripte können über kürzere npm-Befehle ausgeführt werden.

Befehle Äquivalent
npm test, npm t npm run test
npm start npm run start
npm stop npm run stop
npm restart npm run restart

15.1.2 Welche Shell wird zum Ausführen von Paket-Skripten verwendet?

Standardmäßig führt npm Paket-Skripte unter Windows über cmd.exe und unter Unix über /bin/sh aus. Dies kann über die npm-Konfigurationseinstellung script-shell geändert werden.

Dies ist jedoch selten eine gute Idee: Viele bestehende plattformübergreifende Skripte sind für sh und cmd.exe geschrieben und würden dann nicht mehr funktionieren.

15.1.3 Verhindern, dass Paket-Skripte automatisch ausgeführt werden

Einige Skriptnamen sind für Lebenszyklus-Skripte reserviert, die npm ausführt, wann immer wir bestimmte npm-Befehle ausführen.

Zum Beispiel führt npm das Skript "postinstall" aus, wenn wir npm install (ohne Argumente) ausführen. Lebenszyklus-Skripte werden später ausführlicher behandelt.

Wenn die Konfigurationseinstellung ignore-scripts auf true gesetzt ist, führt npm niemals Skripte automatisch aus, sondern nur, wenn wir sie direkt aufrufen.

15.1.4 Tab-Vervollständigung für Paket-Skripte unter Unix erhalten

Unter Unix unterstützt npm die Tab-Vervollständigung für Befehle und Paket-Skriptnamen über npm completion. Wir können sie installieren, indem wir diese Zeile zu unserer .profile / .zprofile / .bash_profile / etc. hinzufügen.

. <(npm completion)

Wenn Sie Tab-Vervollständigung für Nicht-Unix-Plattformen benötigen, suchen Sie im Web nach z. B. „npm tab completion PowerShell“.

15.1.5 Paket-Skripte auflisten und organisieren

npm run ohne Namen listet die verfügbaren Skripte auf. Wenn die folgenden Skripte vorhanden sind

"scripts": {
  "tsc": "tsc",
  "tscwatch": "tsc --watch",
  "serve": "serve ./site/"
}

Dann werden sie wie folgt aufgelistet

% npm run
Scripts available via `npm run-script`:
  tsc
    tsc
  tscwatch
    tsc --watch
  serve
    serve ./site/
15.1.5.1 Trennzeichen hinzufügen

Wenn es viele Paket-Skripte gibt, können wir Skriptnamen als Trennzeichen missbrauchen (das Skript "help" wird im nächsten Unterabschnitt erklärt)

  "scripts": {
    "help": "scripts-help -w 40",
    "\n========== Building ==========": "",
    "tsc": "tsc",
    "tscwatch": "tsc --watch",
    "\n========== Serving ==========": "",
    "serve": "serve ./site/"
  },

Jetzt werden die Skripte wie folgt aufgelistet

% npm run
Scripts available via `npm run-script`:
  help
    scripts-help -w 40

========== Building ==========

  tsc
    tsc
  tscwatch
    tsc --watch
  
========== Serving ==========

  serve
    serve ./site/

Beachten Sie, dass der Trick, neue Zeilen (\n) voranzustellen, unter Unix und unter Windows funktioniert.

15.1.5.2 Hilfeinformationen ausgeben

Das Paket-Skript "help" gibt Hilfeinformationen über das Bin-Skript scripts-help aus dem Paket @rauschma/scripts-help aus. Wir stellen Beschreibungen über die package.json-Eigenschaft "scripts-help" bereit (der Wert von "tscwatch" ist abgekürzt, damit er auf eine einzige Zeile passt)

"scripts-help": {
  "tsc": "Compile the TypeScript to JavaScript.",
  "tscwatch": "Watch the TypeScript source code [...]",
  "serve": "Serve the generated website via a local server."
}

So sehen die Hilfeinformationen aus

% npm -s run help
Package “demo”

╔══════╤══════════════════════════╗
║ help │ scripts-help -w 40       ║
╚══════╧══════════════════════════╝

Building

╔══════════╤══════════════════════════════════════════╗
║ tsc      │ Compile the TypeScript to JavaScript.    ║
╟──────────┼──────────────────────────────────────────╢
║ tscwatch │ Watch the TypeScript source code and     ║
║          │ compile it incrementally when and if     ║
║          │ there are changes.                       ║
╚══════════╧══════════════════════════════════════════╝

Serving

╔═══════╤══════════════════════════════════════════╗
║ serve │ Serve the generated website via a local  ║
║       │ server.                                  ║
╚═══════╧══════════════════════════════════════════╝

15.2 Arten von Paket-Skripten

Wenn bestimmte Namen für Skripte verwendet werden, werden diese in einigen Situationen automatisch ausgeführt.

Alle anderen Skripte werden als direkt ausgeführte Skripte bezeichnet.

15.2.1 Pre- und Post-Skripte

Immer wenn npm ein Paket-Skript PS ausführt, führt es automatisch die folgenden Skripte aus – falls vorhanden

Die folgenden Skripte enthalten das Pre-Skript prehello und das Post-Skript posthello.

"scripts": {
  "hello": "echo hello",
  "prehello": "echo BEFORE",
  "posthello": "echo AFTER"
},

Dies geschieht, wenn wir hello ausführen.

% npm -s run hello
BEFORE
hello
AFTER

15.2.2 Lebenszyklus-Skripte

npm führt Lebenszyklus-Skripte während npm-Befehlen wie diesen aus:

Wenn eines der Lebenszyklus-Skripte fehlschlägt, stoppt der gesamte Befehl sofort mit einem Fehler.

Was sind Anwendungsfälle für Lebenszyklus-Skripte?

Dies sind die wichtigsten Lebenszyklus-Skripte (für detaillierte Informationen zu allen Lebenszyklus-Skripten siehe die npm-Dokumentation)

Die folgende Tabelle fasst zusammen, wann diese Lebenszyklus-Skripte ausgeführt werden:

prepublishOnly prepack prepare install
npm publish
npm pack
npm install
globale Installation
Installation über git, Pfad

Vorsicht: Automatische Ausführung ist immer etwas knifflig. Ich befolge normalerweise diese Regeln:

15.3 Die Shell-Umgebung, in der Paket-Skripte ausgeführt werden

In diesem Abschnitt werden wir gelegentlich verwenden:

node -p <expr>

was den JavaScript-Code in expr ausführt und das Ergebnis auf dem Terminal ausgibt – zum Beispiel:

% node -p "'hello everyone!'.toUpperCase()" 
HELLO EVERYONE!

15.3.1 Das aktuelle Verzeichnis

Wenn ein Paket-Skript ausgeführt wird, ist das aktuelle Verzeichnis immer das Paketverzeichnis, unabhängig davon, wo wir uns im Verzeichnisbaum befinden, dessen Wurzel es ist. Dies können wir bestätigen, indem wir das folgende Skript zu package.json hinzufügen:

"cwd": "node -p \"process.cwd()\""

Versuchen wir cwd unter Unix:

% cd /Users/robin/new-package/src/util 
% npm -s run cwd
/Users/robin/new-package

Das Ändern des aktuellen Verzeichnisses auf diese Weise hilft beim Schreiben von Paket-Skripten, da wir Pfade verwenden können, die relativ zum Paketverzeichnis sind.

15.3.2 Der Shell-PATH

Wenn ein Modul M aus einem Modul importiert, dessen Spezifizierer mit dem Namen eines Pakets P beginnt, durchsucht Node.js node_modules-Verzeichnisse, bis es das Verzeichnis von P findet.

Das bedeutet, dass M die node_modules-Verzeichnisse seiner Vorfahren erbt.

Eine ähnliche Art von Vererbung tritt bei Bin-Skripten auf, die in node_modules/.bin gespeichert werden, wenn wir ein Paket installieren. npm run fügt dem Shell-PATH-Variablen ($PATH unter Unix, %Path% unter Windows) temporär Einträge hinzu.

Um diese Ergänzungen zu sehen, können wir das folgende Paket-Skript verwenden:

"bin-dirs": "node -p \"JS\""

JS steht für eine einzelne Zeile mit diesem JavaScript-Code:

(process.env.PATH ?? process.env.Path)
.split(path.delimiter)
.filter(p => p.includes('.bin'))

Unter Unix erhalten wir die folgende Ausgabe, wenn wir bin-dirs ausführen:

% npm -s run bin-dirs
[
  '/Users/robin/new-package/node_modules/.bin',
  '/Users/robin/node_modules/.bin',
  '/Users/node_modules/.bin',
  '/node_modules/.bin'
]

Unter Windows erhalten wir:

>npm -s run bin-dirs
[
  'C:\\Users\\charlie\\new-package\\node_modules\\.bin',
  'C:\\Users\\charlie\\node_modules\\.bin',
  'C:\\Users\\node_modules\\.bin',
  'C:\\node_modules\\.bin'
]

15.4 Umgebungsvariablen in Paket-Skripten verwenden

In Task-Runnern wie Make, Grunt und Gulp sind Variablen wichtig, da sie helfen, Redundanz zu reduzieren. Leider haben Paket-Skripte keine eigenen Variablen, aber wir können diese Einschränkung umgehen, indem wir Umgebungsvariablen (die auch Shell-Variablen genannt werden) verwenden.

Wir können die folgenden Befehle verwenden, um plattformspezifische Umgebungsvariablen aufzulisten:

Unter macOS sieht das Ergebnis wie folgt aus:

TERM_PROGRAM=Apple_Terminal
SHELL=/bin/zsh
TMPDIR=/var/folders/ph/sz0384m11vxf5byk12fzjms40000gn/T/
USER=robin
PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
PWD=/Users/robin/new-package
HOME=/Users/robin
LOGNAME=robin
···

In der Windows-Kommandozeile sieht das Ergebnis wie folgt aus:

Path=C:\Windows;C:\Users\charlie\AppData\Roaming\npm;···
PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC
PROMPT=$P$G
TEMP=C:\Users\charlie\AppData\Local\Temp
TMP=C:\Users\charlie\AppData\Local\Temp
USERNAME=charlie
USERPROFILE=C:\Users\charlie
···

Zusätzlich fügt npm vor dem Ausführen eines Paket-Skripts temporär weitere Umgebungsvariablen hinzu. Um zu sehen, wie das Endergebnis aussieht, können wir den folgenden Befehl verwenden:

npm run env

Dieser Befehl ruft ein integriertes Paket-Skript auf. Probieren wir es für diese package.json aus:

{
  "name": "@my-scope/new-package",
  "version": "1.0.0",
  "bin": {
    "hello": "./hello.mjs"
  },
  "config": {
    "stringProp": "yes",
    "arrayProp": ["a", "b", "c"],
    "objectProp": {
      "one": 1,
      "two": 2
    }
  }
}

Die Namen aller temporären npm-Variablen beginnen mit npm_. Lassen Sie uns nur diese in alphabetischer Reihenfolge ausgeben:

npm run env | grep npm_ | sort

Die npm_-Variablen haben eine hierarchische Struktur. Unter npm_lifecycle_ finden wir den Namen und die Definition des aktuell ausgeführten Paket-Skripts.

npm_lifecycle_event: 'env',
npm_lifecycle_script: 'env',

Unter Windows würde npm_lifecycle_script in diesem Fall SET lauten.

Unter dem Präfix npm_config_ können wir einige der npm-Konfigurationseinstellungen sehen (die in der npm-Dokumentation beschrieben sind). Hier einige Beispiele:

npm_config_cache: '/Users/robin/.npm',
npm_config_global_prefix: '/usr/local',
npm_config_globalconfig: '/usr/local/etc/npmrc',
npm_config_local_prefix: '/Users/robin/new-package',
npm_config_prefix: '/usr/local'
npm_config_user_agent: 'npm/8.15.0 node/v18.7.0 darwin arm64 workspaces/false',
npm_config_userconfig: '/Users/robin/.npmrc',

Das Präfix npm_package_ gibt uns Zugriff auf den Inhalt von package.json. Seine oberste Ebene sieht so aus:

npm_package_json: '/Users/robin/new-package/package.json',
npm_package_name: '@my-scope/new-package',
npm_package_version: '1.0.0',

Unter npm_package_bin_ finden wir die Eigenschaften der package.json-Eigenschaft "bin".

npm_package_bin_hello: 'hello.mjs',

Die Einträge npm_package_config_ geben uns Zugriff auf die Eigenschaften von "config".

npm_package_config_arrayProp: 'a\n\nb\n\nc',
npm_package_config_objectProp_one: '1',
npm_package_config_objectProp_two: '2',
npm_package_config_stringProp: 'yes',

Das bedeutet, dass "config" uns erlaubt, Variablen einzurichten, die wir in Paket-Skripten verwenden können. Der nächste Unterabschnitt befasst sich damit genauer.

Beachten Sie, dass das Objekt in „verschachtelte“ Einträge umgewandelt wurde (Zeile 2 und Zeile 3), während das Array (Zeile 1) und die Zahlen (Zeile 2 und Zeile 3) in Zeichenfolgen umgewandelt wurden.

Dies sind die verbleibenden npm_-Umgebungsvariablen:

npm_command: 'run-script',
npm_execpath: '/usr/local/lib/node_modules/npm/bin/npm-cli.js',
npm_node_execpath: '/usr/local/bin/node',

15.4.1 Umgebungsvariablen abrufen und setzen

Die folgende package.json demonstriert, wie wir Variablen abrufen können, die über "config" in Paket-Skripten definiert wurden:

{
  "scripts": {
    "hi:unix": "echo $​npm_package_config_hi",
    "hi:windows": "echo %​npm_package_config_hi%"
  },
  "config": {
    "hi": "HELLO"
  }
}

Leider gibt es keine integrierte plattformübergreifende Methode, um von Paket-Skripten aus auf Umgebungsvariablen zuzugreifen.

Es gibt jedoch Pakete mit Bin-Skripten, die uns helfen können.

Das Paket env-var ermöglicht es uns, Umgebungsvariablen abzurufen.

"scripts": {
  "hi": "env-var echo {{npm_package_config_hi}}"
}

Das Paket cross-env ermöglicht es uns, Umgebungsvariablen zu setzen.

"scripts": {
  "build": "cross-env FIRST=one SECOND=two node ./build.mjs"
}

15.4.2 Umgebungsvariablen über .env-Dateien einrichten

Es gibt auch Pakete, die es uns ermöglichen, Umgebungsvariablen über .env-Dateien einzurichten. Diese Dateien haben das folgende Format:

# Comment
SECRET_HOST="https://example.com"
SECRET_KEY="123456789" # another comment

Die Verwendung einer Datei, die von package.json getrennt ist, ermöglicht es uns, diese Daten aus der Versionskontrolle herauszuhalten.

Dies sind Pakete, die .env-Dateien unterstützen:

15.5 Argumente für Paket-Skripte

Lassen Sie uns untersuchen, wie Argumente an Shell-Befehle übergeben werden, die wir über Paket-Skripte aufrufen. Wir verwenden die folgende package.json:

{
  ···
  "scripts": {
    "args": "log-args"
  },
  "dependencies": {
    "log-args": "^1.0.0"
  }
}

Das Bin-Skript log-args sieht wie folgt aus:

for (const [key,value] of Object.entries(process.env)) {
  if (key.startsWith('npm_config_arg')) {
    console.log(`${key}=${JSON.stringify(value)}`);
  }
}
console.log(process.argv.slice(2));

Positionsargumente funktionieren wie erwartet.

% npm -s run args three positional arguments
[ 'three', 'positional', 'arguments' ]

npm run verbraucht Optionen und erstellt Umgebungsvariablen für sie. Sie werden nicht zu process.argv hinzugefügt.

% npm -s run args --arg1='first arg' --arg2='second arg'
npm_config_arg2="second arg"
npm_config_arg1="first arg"
[]

Wenn wir möchten, dass Optionen in process.argv erscheinen, müssen wir den Optionsbegrenzer -- verwenden. Dieser Begrenzer wird normalerweise nach dem Namen des Paket-Skripts eingefügt.

% npm -s run args -- --arg1='first arg' --arg2='second arg' 
[ '--arg1=first arg', '--arg2=second arg' ]

Aber wir können ihn auch vor diesem Namen einfügen.

% npm -s run -- args --arg1='first arg' --arg2='second arg' 
[ '--arg1=first arg', '--arg2=second arg' ]

15.6 Der npm-Protokollierungspegel (wie viele Ausgaben produziert werden)

npm unterstützt die folgenden Protokollierungspegel:

Protokollierungspegel npm-Option Aliase
silent --loglevel silent -s --silent
error --loglevel error
warn --loglevel warn -q --quiet
notice --loglevel notice
http --loglevel http
timing --loglevel timing
info --loglevel info -d
verbose --loglevel verbose -dd --verbose
silly --loglevel silly -ddd

Protokollierung bezieht sich auf zwei Arten von Aktivitäten:

Die folgenden Unterabschnitte beschreiben:

15.6.1 Protokollierungspegel und Informationen, die auf dem Terminal ausgegeben werden

Standardmäßig sind Paket-Skripte relativ gesprächig, was die Terminalausgabe angeht. Nehmen wir zum Beispiel die folgende package.json-Datei:

{
  "name": "@my-scope/new-package",
  "version": "1.0.0",
  "scripts": {
    "hello": "echo Hello",
    "err": "more does-not-exist.txt"
  },
  ···
}

Dies geschieht, wenn der Protokollierungspegel höher als silent ist und das Paket-Skript ohne Fehler beendet wird:

% npm run hello

> @my-scope/new-package@1.0.0 hello
> echo Hello

Hello

Dies geschieht, wenn der Protokollierungspegel höher als silent ist und das Paket-Skript fehlschlägt:

% npm run err      

> @my-scope/new-package@1.0.0 err
> more does-not-exist.txt

does-not-exist.txt: No such file or directory

Mit dem Protokollierungspegel silent wird die Ausgabe weniger überladen:

% npm -s run hello
Hello

% npm -s run err
does-not-exist.txt: No such file or directory

Einige Fehler werden von -s verschluckt.

% npm -s run abc
%

Wir benötigen mindestens den Protokollierungspegel error, um sie zu sehen.

% npm --loglevel error run abc
npm ERR! Missing script: "abc"
npm ERR! 
npm ERR! To see a list of scripts, run:
npm ERR!   npm run

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/robin/.npm/_logs/2072-08-30T14_59_40_474Z-debug-0.log

Leider unterdrückt der Protokollierungspegel silent auch die Ausgabe von npm run (ohne Argumente).

% npm -s run
%

15.6.2 Protokollierungspegel und Informationen, die in das npm-Protokoll geschrieben werden

Standardmäßig werden die Protokolle in das npm-Cache-Verzeichnis geschrieben, dessen Pfad wir über npm config abrufen können:

% npm config get cache
/Users/robin/.npm

Der Inhalt des Protokollverzeichnisses sieht wie folgt aus:

% ls -1 /Users/robin/.npm/_logs
2072-08-28T11_44_38_499Z-debug-0.log
2072-08-28T11_45_45_703Z-debug-0.log
2072-08-28T11_52_04_345Z-debug-0.log

Jede Zeile in einem Protokoll beginnt mit einem Zeilenindex und einem Protokollierungspegel. Dies ist ein Beispiel für ein Protokoll, das mit dem Protokollierungspegel notice geschrieben wurde. Interessanterweise erscheinen selbst Protokollierungspegel, die „gesprächiger“ als notice sind (wie silly), darin:

0 verbose cli /usr/local/bin/node /usr/local/bin/npm
1 info using npm@8.15.0
···
33 silly logfile done cleaning log files
34 timing command:run Completed in 9ms
···

Wenn npm run mit einem Fehler zurückkehrt, endet das entsprechende Protokoll wie folgt:

34 timing command:run Completed in 7ms
35 verbose exit 1
36 timing npm Completed in 28ms
37 verbose code 1

Wenn kein Fehler auftritt, endet das entsprechende Protokoll wie folgt:

34 timing command:run Completed in 7ms
35 verbose exit 0
36 timing npm Completed in 26ms
37 info ok

15.6.3 Protokollierung konfigurieren

npm config list --long gibt Standardwerte für verschiedene Einstellungen aus. Dies sind die Standardwerte für Protokollierungs-bezogene Einstellungen:

% npm config list --long | grep log
loglevel = "notice"
logs-dir = null
logs-max = 10

Wenn der Wert von logs-dir null ist, verwendet npm das Verzeichnis _logs innerhalb des npm-Cache-Verzeichnisses (wie bereits erwähnt).

Um diese Einstellungen dauerhaft zu ändern, verwenden wir ebenfalls npm config – zum Beispiel:

Wir können Einstellungen auch vorübergehend über Befehlszeilenoptionen ändern – zum Beispiel:

npm --loglevel silent run build

Andere Möglichkeiten zur Änderung von Einstellungen (wie die Verwendung von Umgebungsvariablen) werden von der npm-Dokumentation erklärt.

15.6.4 Ausgabe von Lebenszyklus-Skripten, die während npm install ausgeführt werden

Die Ausgabe von Lebenszyklus-Skripten, die während npm install (ohne Argumente) ausgeführt werden, ist verborgen. Wir können dies ändern, indem wir foreground-scripts (vorübergehend oder dauerhaft) auf true setzen.

15.6.5 Beobachtungen zur Funktionsweise der npm-Protokollierung

15.7 Plattformübergreifendes Shell-Skripting

Die beiden am häufigsten für Paket-Skripte verwendeten Shells sind:

In diesem Abschnitt untersuchen wir Konstrukte, die in beiden Shells funktionieren.

15.7.1 Pfade und Anführungszeichen

Tipps

15.7.2 Befehle verketten

Es gibt zwei Möglichkeiten, Befehle zu verketten, die auf beiden Plattformen funktionieren:

Das Verketten unter Ignorieren des Exit-Codes unterscheidet sich zwischen den Plattformen:

Die folgende Interaktion demonstriert, wie && und || unter Unix funktionieren (unter Windows würden wir dir anstelle von ls verwenden):

% ls unknown && echo "SUCCESS" || echo "FAILURE"
ls: unknown: No such file or directory
FAILURE

% ls package.json && echo "SUCCESS" || echo "FAILURE"
package.json
SUCCESS

15.7.3 Die Exit-Codes von Paket-Skripten

Der Exit-Code kann über eine Shell-Variable abgerufen werden:

npm run kehrt mit demselben Exit-Code zurück wie das zuletzt ausgeführte Shell-Skript.

{
  ···
  "scripts": {
    "hello": "echo Hello",
    "err": "more does-not-exist.txt"
  }
}

Die folgende Interaktion findet unter Unix statt:

% npm -s run hello ; echo $?
Hello
0
% npm -s run err ; echo $?
does-not-exist.txt: No such file or directory
1

15.7.4 Ein- und Ausgabe leiten und umleiten

15.7.5 Befehle, die auf beiden Plattformen funktionieren

Die folgenden Befehle existieren auf beiden Plattformen (unterscheiden sich aber bei den Optionen):

15.7.6 Bin-Skripte und paketinterne Module ausführen

Die folgende package.json demonstriert drei Möglichkeiten, Bin-Skripte in Abhängigkeiten aufzurufen:

{
  "scripts": {
    "hi1": "./node_modules/.bin/cowsay Hello",
    "hi2": "cowsay Hello",
    "hi3": "npx cowsay Hello"
  },
  "dependencies": {
    "cowsay": "^1.5.0"
  }
}

Erläuterungen

Unter Unix können wir paketlokale Skripte direkt aufrufen – wenn sie Hashbangs haben und ausführbar sind. Dies funktioniert jedoch nicht unter Windows, weshalb es besser ist, sie über node aufzurufen.

"build": "node ./build.mjs"

15.7.7 node --eval und node --print

Wenn die Funktionalität eines Paket-Skripts zu komplex wird, ist es oft eine gute Idee, sie über ein Node.js-Modul zu implementieren – was die plattformübergreifende Codeerstellung erleichtert.

Wir können jedoch auch den node-Befehl verwenden, um kleine JavaScript-Snippets auszuführen, was nützlich ist, um kleine Aufgaben plattformübergreifend auszuführen. Die relevanten Optionen sind:

Die folgenden Befehle funktionieren sowohl unter Unix als auch unter Windows (nur die Kommentare sind Unix-spezifisch):

# Print a string to the terminal (cross-platform echo)
node -p "'How are you?'"

# Print the value of an environment variable
# (Alas, we can’t change variables via `process.env`)
node -p process.env.USER # only Unix
node -p process.env.USERNAME # only Windows
node -p "process.env.USER ?? process.env.USERNAME"

# Print all environment variables
node -p process.env

# Print the current working directory
node -p "process.cwd()"

# Print the path of the current home directory
node -p "os.homedir()"

# Print the path of the current temporary directory
node -p "os.tmpdir()"

# Print the contents of a text file
node -p "fs.readFileSync('package.json', 'utf-8')"

# Write a string to a file
node -e "fs.writeFileSync('file.txt', 'Text content', 'utf-8')"

Wenn wir plattformspezifische Zeilenabschlüsse benötigen, können wir os.EOL verwenden – zum Beispiel könnten wir 'Text content' im vorherigen Befehl ersetzen durch:

`line 1${os.EOL}line2${os.EOL}`

Beobachtungen

15.8 Hilfspakete für gängige Operationen

15.8.1 Paket-Skripte von einer Kommandozeile ausführen

npm-quick-run bietet ein Bin-Skript nr, mit dem wir Abkürzungen verwenden können, um Paket-Skripte auszuführen – zum Beispiel:

15.8.2 Mehrere Skripte gleichzeitig oder nacheinander ausführen

Shell-Skripte gleichzeitig ausführen

Die folgenden beiden Pakete bieten uns plattformübergreifende Optionen dafür und für verwandte Funktionalitäten:

15.8.3 Dateisystemoperationen

Das Paket shx ermöglicht es uns, „Unix-Syntax“ zu verwenden, um verschiedene Dateisystemoperationen auszuführen. Alles, was es tut, funktioniert unter Unix und Windows.

Ein Verzeichnis erstellen

"create-asset-dir": "shx mkdir ./assets"

Ein Verzeichnis löschen

"remove-asset-dir": "shx rm -rf ./assets"

Ein Verzeichnis leeren (doppelte Anführungszeichen zur Sicherheit bezüglich des Wildcard-Symbols *).

"tscclean": "shx rm -rf \"./dist/*\""

Eine Datei kopieren

"copy-index": "shx cp ./html/index.html ./out/index.html"

Eine Datei löschen

"remove-index": "shx rm ./out/index.html"

shx basiert auf der JavaScript-Bibliothek ShellJS, deren Repository alle unterstützten Befehle auflistet. Zusätzlich zu den bereits bekannten Unix-Befehlen emuliert es auch: cat, chmod, echo, find, grep, head, ln, ls, mv, pwd, sed, sort, tail, touch, uniq und andere.

15.8.4 Dateien oder Verzeichnisse in den Papierkorb legen

Paket trash-cli funktioniert unter macOS (10.12+), Linux und Windows (8+). Es legt Dateien und Verzeichnisse in den Papierkorb und unterstützt Pfade und Glob-Muster. Dies sind Beispiele für die Verwendung

trash tmp-file.txt
trash tmp-dir
trash "*.jpg"

15.8.5 Dateistrukturen kopieren

Paket copyfiles ermöglicht es uns, Dateistrukturen zu kopieren.

Der folgende Anwendungsfall beschreibt die Verwendung von copyfiles: In TypeScript können wir Nicht-Code-Assets wie CSS und Bilder importieren. Der TypeScript-Compiler kompiliert den Code in ein "dist" (Ausgabe)-Verzeichnis, ignoriert aber Nicht-Code-Assets. Dieser plattformübergreifende Shell-Befehl kopiert sie in das dist-Verzeichnis

copyfiles --up 1 "./ts/**/*.{css,png,svg,gif}" ./dist

TypeScript kompiliert

my-pkg/ts/client/picker.ts  -> my-pkg/dist/client/picker.js

copy-assets kopiert

my-pkg/ts/client/picker.css -> my-pkg/dist/client/picker.css
my-pkg/ts/client/icon.svg   -> my-pkg/dist/client/icon.svg

15.8.6 Dateien überwachen

Paket onchange überwacht Dateien und führt bei jeder Änderung einen Shell-Befehl aus – zum Beispiel

onchange 'app/**/*.js' 'test/**/*.js' -- npm test

Eine gängige Alternative (unter vielen anderen)

15.8.7 Verschiedene Funktionalitäten

15.8.8 HTTP-Server

Während der Entwicklung ist es oft nützlich, einen HTTP-Server zu haben. Die folgenden Pakete (unter vielen anderen) können dabei helfen

15.9 Erweiterung der Funktionen von Paket-Skripten

15.9.1 per-env: Umschalten zwischen Skripten, abhängig von $NODE_ENV

Das Binärskript per-env ermöglicht uns, ein Paket-Skript SCRIPT auszuführen und automatisch zwischen (z. B.) SCRIPT:development, SCRIPT:staging und SCRIPT:production umzuschalten, abhängig vom Wert der Umgebungsvariable NODE_ENV

{
  "scripts": {
    // If NODE_ENV is missing, the default is "development"
    "build": "per-env",

    "build:development": "webpack -d --watch",
    "build:staging": "webpack -p",
    "build:production": "webpack -p"
  },
  // Processes spawned by `per-env` inherit environment-specific
  // variables, if defined.
  "per-env": {
    "production": {
      "DOCKER_USER": "my",
      "DOCKER_REPO": "project"
    }
  }
}

15.9.2 Betriebssystemspezifische Skripte definieren

Das Binärskript cross-os schaltet zwischen Skripten um, abhängig vom aktuellen Betriebssystem.

{
  "scripts": {
    "user": "cross-os user"
  },
  "cross-os": {
    "user": {
      "darwin": "echo $USER",
      "win32": "echo %USERNAME%",
      "linux": "echo $USER"
    }
  },
  ···
}

Unterstützte Eigenschaftswerte sind: darwin, freebsd, linux, sunos, win32.

15.10 Quellen dieses Kapitels