Node.js kompiliert

Die JavaScript-Laufzeitumgebung Node.js verwendet intern zwar Googles V8 als Compiler, kann aber trotzdem keine Binärpakete erstellen. Das von dem Unternehmen Zeit entwickelte Modul pkg ändert das. Wie funktioniert das Modul, und was leistet es?

In Pocket speichern vorlesen Druckansicht 14 Kommentare lesen
Lesezeit: 4 Min.
Von
  • Golo Roden
Inhaltsverzeichnis

Die JavaScript-Laufzeitumgebung Node.js verwendet intern zwar Googles V8 als Compiler, kann aber trotzdem keine Binärpakete erstellen. Das von dem Unternehmen Zeit entwickelte Modul pkg ändert das. Wie funktioniert das Modul, und was leistet es?

Die Installation von pkg erfolgt, wie bei Werkzeugen für Node.js üblich, mithilfe der Paketverwaltung npm. Dabei gilt es, den Parameter -g für eine globale Installation anzugeben:

$ npm install -g pkg

Anschließend lässt sich das Modul über die Kommandozeile aufrufen. Auf dem Weg kann man beispielsweise die Hilfe anzeigen:

$ pkg --help

Das Kompilieren einer Anwendung fällt ebenso leicht. Dazu muss dem Aufruf von pkg lediglich die Datei als Parameter übergeben werden, die den Haupteinstiegspunkt der Anwendung enthält. Daher sieht der Aufruf von pkg häufig wie folgt aus:

$ pkg app.js

Ohne weitere Parameter erzeugt der Aufruf Binärpakete für macOS, Linux und Windows, durch Angabe von ---targets lassen sich aber auch gezielt Zielplattformen angeben, für die Binärpakete erzeugt werden sollen. Beispiele für gültige Werte finden sich in der Dokumentation, beispielsweise node6-macos-x64 und node4-linux-armv6.

Abhängigkeiten löst pkg automatisch auf, was auch für npm-Module funktioniert. Beispielsweise funktioniert das Verwenden von Modulen wie express und processenv wie erwartet. Der Code

'use strict';

const http = require('http');

const express = require('express'),
processenv = require('processenv');

const port = processenv('PORT') || 3000;

const app = express();

app.get('/', (req, res) => {
res.send('Hallo Node.js!');
});

const server = http.createServer(app);

server.listen(port, () => {
console.log(`Server started on port ${port}.`);
});

führt durch den Aufruf von

$ pkg app.js

tatsächlich zu einem ausführbaren Binärpaket, das sich ohne installierte Laufzeitumgebung aufrufen lässt und das einen Webserver auf Port 3000 (oder dem durch die Umgebungsvariable PORT vorgegebenen Port) startet.

Der gleiche Mechanismus funktioniert sogar für .json-Dateien, sodass sich zum Beispiel auch Konfigurationsdaten verwenden lassen.

Schwieriger wird es, wenn Abhängigkeiten dynamisch aufzulösen sind, weil beispielsweise der zu ladende Dateiname erst zur Laufzeit zusammengefügt wird, wie das Beispiel der folgenden parametrisierten Route zeigt:

app.get('/:filename', (req, res) => {
res.send(fs.readFileSync(filename, { encoding: 'utf8' }));
});

Ruft man nun beispielsweise die Route http://localhost:3000/app.js auf, gibt die Anwendung als Ergebnis nicht den Quellcode der Datei app.js zurück, sondern eine Fehlermeldung, da die angeforderte Datei nicht gefunden wurde.

Um das Beispiel lauffähig zu machen, ist die Datei app.js dem Binärpaket als sogenanntes Asset hinzuzufügen. Das erfolgt über einen neuen Abschnitt innerhalb der Datei package.json:

"pkg": {
"assets": [ "app.js" ]
}

Bei Bedarf lassen sich innerhalb der Einträge der Liste auch Platzhalter wie * und ** verwenden, um mehrere Dateien und Verzeichnisse, gegebenenfalls sogar rekursiv, einzubinden.

Innerhalb des Binärpakets stehen weiterhin die beiden Variablen __dirname und __filename zur Verfügung, die den vollqualifizierten Pfad des aktuellen Verzeichnisses beziehungsweise der aktuellen Datei enthalten. Beide Variablen werden von pkg allerdings mit dem Präfix /snapshot versehen.

Die Details hierzu finden sich in der Dokumentation von pkg unter dem Stichwort "Snapshot filesystem". Außerdem stellt pkg die zusätzlichen Variablen entrypoint und defaultEntrypoint unter process.pkg zur Verfügung, über die man den Einstiegspunkt der Anwendung abfragen kann.

pkg ist ein bemerkenswert gut funktionierendes und zugleich einfach zu bedienendes Werkzeug. Es ermöglicht Entwicklern Node.js-basierter Anwendungen, Binärpakete zu erzeugen. Für diese Aufgabe musste bislang entweder auf andere Sprachen wie Go oder auf Werkzeuge wie nexe zurückgegriffen werden, was allerdings deutlich umständlicher war.

Abzuwarten bleibt, wie gut pkg bei großen Anwendungen funktioniert, insbesondere wenn Mechanismen zum Einsatz kommen, die vom handelsüblichen require abweichen, beispielsweise um im großen Stil auf dynamische Art Module nachzuladen. Der Anfang sieht aber vielversprechend aus.

tl;dr: pkg ermöglicht das Erstellen von Binärpaketen für Node.js-basierte Anwendungen. Es erzeugt Ausgaben für verschiedene Plattformen, unter anderem macOS, Linux und Windows. ()