Einzelne ausführbare Anwendungen
[Geschichte]
Version | Änderungen |
---|---|
v20.6.0 | Unterstützung für "useSnapshot" hinzugefügt. |
v20.6.0 | Unterstützung für "useCodeCache" hinzugefügt. |
v19.7.0, v18.16.0 | Hinzugefügt in: v19.7.0, v18.16.0 |
[Stabil: 1 - Experimentell]
Stabil: 1 Stabilität: 1.1 - Aktive Entwicklung
Quellcode: src/node_sea.cc
Diese Funktion ermöglicht die bequeme Verteilung einer Node.js-Anwendung an ein System, auf dem Node.js nicht installiert ist.
Node.js unterstützt die Erstellung von einzelnen ausführbaren Anwendungen, indem es die Injektion eines von Node.js vorbereiteten Blobs, das ein gebündeltes Skript enthalten kann, in die node
-Binärdatei ermöglicht. Beim Start prüft das Programm, ob etwas injiziert wurde. Wenn der Blob gefunden wird, führt es das Skript im Blob aus. Andernfalls arbeitet Node.js wie gewohnt.
Die Funktion für einzelne ausführbare Anwendungen unterstützt derzeit nur die Ausführung eines einzelnen eingebetteten Skripts mit dem CommonJS-Modulsystem.
Benutzer können eine einzelne ausführbare Anwendung aus ihrem gebündelten Skript mit der node
-Binärdatei selbst und jedem Tool erstellen, das Ressourcen in die Binärdatei injizieren kann.
Hier sind die Schritte zum Erstellen einer einzelnen ausführbaren Anwendung mit einem solchen Tool, postject:
Generieren von Vorbereitungsblobs für einzelne ausführbare Dateien
Vorbereitungsblobs für einzelne ausführbare Dateien, die in die Anwendung injiziert werden, können mit dem Flag --experimental-sea-config
der Node.js-Binärdatei generiert werden, die zum Erstellen der einzelnen ausführbaren Datei verwendet wird. Es benötigt einen Pfad zu einer Konfigurationsdatei im JSON-Format. Wenn der an sie übergebene Pfad nicht absolut ist, verwendet Node.js den Pfad relativ zum aktuellen Arbeitsverzeichnis.
Die Konfiguration liest derzeit die folgenden Top-Level-Felder:
{
"main": "/pfad/zu/gebündeltem/skript.js",
"output": "/pfad/zum/schreiben/des/generierten/blobs.blob",
"disableExperimentalSEAWarning": true, // Standard: false
"useSnapshot": false, // Standard: false
"useCodeCache": true, // Standard: false
"assets": {
// Optional
"a.dat": "/pfad/zu/a.dat",
"b.txt": "/pfad/zu/b.txt"
}
}
Wenn die Pfade nicht absolut sind, verwendet Node.js den Pfad relativ zum aktuellen Arbeitsverzeichnis. Die Version der Node.js-Binärdatei, die zum Erstellen des Blobs verwendet wird, muss dieselbe sein wie diejenige, in die der Blob injiziert wird.
Hinweis: Beim Generieren von plattformübergreifenden SEAs (z. B. Generieren einer SEA für linux-x64
auf darwin-arm64
) müssen useCodeCache
und useSnapshot
auf false gesetzt werden, um das Generieren inkompatibler ausführbarer Dateien zu vermeiden. Da der Code-Cache und Snapshots nur auf derselben Plattform geladen werden können, auf der sie kompiliert werden, kann die generierte ausführbare Datei beim Start abstürzen, wenn versucht wird, einen Code-Cache oder Snapshots zu laden, die auf einer anderen Plattform erstellt wurden.
Assets
Benutzer können Assets einbinden, indem sie der Konfiguration ein Schlüssel-Pfad-Wörterbuch als assets
-Feld hinzufügen. Zur Build-Zeit würde Node.js die Assets von den angegebenen Pfaden lesen und sie in den Vorbereitungs-Blob bündeln. In der generierten ausführbaren Datei können Benutzer die Assets mithilfe der APIs sea.getAsset()
und sea.getAssetAsBlob()
abrufen.
{
"main": "/pfad/zum/gebündelten/skript.js",
"output": "/pfad/zum/schreiben/des/generierten/blob.blob",
"assets": {
"a.jpg": "/pfad/zu/a.jpg",
"b.txt": "/pfad/zu/b.txt"
}
}
Die Single-Executable-Anwendung kann wie folgt auf die Assets zugreifen:
const { getAsset, getAssetAsBlob, getRawAsset } = require('node:sea')
// Gibt eine Kopie der Daten in einem ArrayBuffer zurück.
const image = getAsset('a.jpg')
// Gibt eine aus dem Asset als UTF8 dekodierte Zeichenkette zurück.
const text = getAsset('b.txt', 'utf8')
// Gibt ein Blob mit dem Asset zurück.
const blob = getAssetAsBlob('a.jpg')
// Gibt einen ArrayBuffer zurück, der das Roh-Asset ohne Kopieren enthält.
const raw = getRawAsset('a.jpg')
Weitere Informationen finden Sie in der Dokumentation der APIs sea.getAsset()
, sea.getAssetAsBlob()
und sea.getRawAsset()
.
Unterstützung für Startup-Snapshots
Das Feld useSnapshot
kann verwendet werden, um die Unterstützung für Startup-Snapshots zu aktivieren. In diesem Fall würde das main
-Skript nicht beim Starten der endgültigen ausführbaren Datei ausgeführt. Stattdessen würde es ausgeführt, wenn der Vorbereitungs-Blob der Single-Executable-Anwendung auf dem Build-Rechner generiert wird. Der generierte Vorbereitungs-Blob würde dann einen Snapshot enthalten, der die vom main
-Skript initialisierten Zustände erfasst. Die endgültige ausführbare Datei mit dem eingefügten Vorbereitungs-Blob würde den Snapshot zur Laufzeit deserialisieren.
Wenn useSnapshot
true ist, muss das Hauptskript die API v8.startupSnapshot.setDeserializeMainFunction()
aufrufen, um Code zu konfigurieren, der ausgeführt werden muss, wenn die endgültige ausführbare Datei von den Benutzern gestartet wird.
Das typische Muster für eine Anwendung, um einen Snapshot in einer Single-Executable-Anwendung zu verwenden, ist:
Die allgemeinen Einschränkungen der Startup-Snapshot-Skripte gelten auch für das Hauptskript, wenn es zum Erstellen von Snapshots für die Single-Executable-Anwendung verwendet wird, und das Hauptskript kann die v8.startupSnapshot
-API verwenden, um sich an diese Einschränkungen anzupassen. Siehe Dokumentation zur Unterstützung von Startup-Snapshots in Node.js.
V8-Code-Cache-Unterstützung
Wenn useCodeCache
in der Konfiguration auf true
gesetzt ist, kompiliert Node.js während der Generierung des einzelnen ausführbaren Vorbereitungs-Blobs das main
-Skript, um den V8-Code-Cache zu generieren. Der generierte Code-Cache wäre Teil des Vorbereitungs-Blobs und würde in die endgültige ausführbare Datei injiziert. Wenn die einzelne ausführbare Anwendung gestartet wird, würde Node.js anstatt das main
-Skript von Grund auf neu zu kompilieren, den Code-Cache verwenden, um die Kompilierung zu beschleunigen, und dann das Skript ausführen, was die Startleistung verbessern würde.
Hinweis: import()
funktioniert nicht, wenn useCodeCache
true
ist.
Im injizierten Hauptskript
API für einzelne ausführbare Anwendungen
Das integrierte node:sea
ermöglicht die Interaktion mit der einzelnen ausführbaren Anwendung über das in die ausführbare Datei eingebettete JavaScript-Hauptskript.
sea.isSea()
Hinzugefügt in: v21.7.0, v20.12.0
- Gibt zurück: <boolean> Ob dieses Skript innerhalb einer einzelnen ausführbaren Anwendung ausgeführt wird.
sea.getAsset(key[, encoding])
Hinzugefügt in: v21.7.0, v20.12.0
Diese Methode kann verwendet werden, um die Assets abzurufen, die zur Build-Zeit in die einzelne ausführbare Anwendung gebündelt werden sollen. Es wird ein Fehler ausgelöst, wenn kein passendes Asset gefunden werden kann.
key
<string> Der Schlüssel für das Asset im Dictionary, das durch das Feldassets
in der Konfiguration der einzelnen ausführbaren Anwendung angegeben wird.encoding
<string> Wenn angegeben, wird das Asset als Zeichenkette dekodiert. Jede vonTextDecoder
unterstützte Kodierung wird akzeptiert. Wenn nicht angegeben, wird stattdessen einArrayBuffer
zurückgegeben, das eine Kopie des Assets enthält.- Gibt zurück: <string> | <ArrayBuffer>
sea.getAssetAsBlob(key[, options])
Hinzugefügt in: v21.7.0, v20.12.0
Ähnlich wie sea.getAsset()
, gibt das Ergebnis jedoch als Blob
zurück. Es wird ein Fehler geworfen, wenn kein passendes Asset gefunden werden kann.
key
<string> Der Schlüssel für das Asset im Wörterbuch, das durch das Feldassets
in der Konfiguration der einzelnen ausführbaren Anwendung angegeben wird.options
<Object>type
<string> Ein optionaler MIME-Typ für den Blob.
Gibt zurück: <Blob>
sea.getRawAsset(key)
Hinzugefügt in: v21.7.0, v20.12.0
Diese Methode kann verwendet werden, um die Assets abzurufen, die so konfiguriert wurden, dass sie zur Build-Zeit in die einzelne ausführbare Anwendung gebündelt werden. Es wird ein Fehler geworfen, wenn kein passendes Asset gefunden werden kann.
Im Gegensatz zu sea.getAsset()
oder sea.getAssetAsBlob()
gibt diese Methode keine Kopie zurück. Stattdessen gibt sie das rohe Asset zurück, das in die ausführbare Datei gebündelt ist.
Im Moment sollten Benutzer es vermeiden, in den zurückgegebenen Array-Puffer zu schreiben. Wenn der eingefügte Abschnitt nicht als beschreibbar markiert oder nicht richtig ausgerichtet ist, führen Schreibvorgänge in den zurückgegebenen Array-Puffer wahrscheinlich zu einem Absturz.
key
<string> Der Schlüssel für das Asset im Wörterbuch, das durch das Feldassets
in der Konfiguration der einzelnen ausführbaren Anwendung angegeben wird.- Gibt zurück: <ArrayBuffer>
require(id)
im injizierten Hauptskript ist nicht dateibasiert
require()
im injizierten Hauptskript ist nicht dasselbe wie das require()
, das Modulen zur Verfügung steht, die nicht injiziert werden. Es hat auch keine der Eigenschaften, die nicht injizierte require()
hat, außer require.main
. Es kann nur zum Laden integrierter Module verwendet werden. Der Versuch, ein Modul zu laden, das nur im Dateisystem zu finden ist, führt zu einem Fehler.
Anstatt sich auf ein dateibasiertes require()
zu verlassen, können Benutzer ihre Anwendung in eine eigenständige JavaScript-Datei bündeln, die in die ausführbare Datei injiziert werden soll. Dies gewährleistet auch einen deterministischeren Abhängigkeitsgraphen.
Wenn jedoch ein dateibasiertes require()
dennoch benötigt wird, kann dies auch erreicht werden:
const { createRequire } = require('node:module')
require = createRequire(__filename)
__filename
und module.filename
im injizierten Hauptskript
Die Werte von __filename
und module.filename
im injizierten Hauptskript sind gleich process.execPath
.
__dirname
im injizierten Hauptskript
Der Wert von __dirname
im injizierten Hauptskript entspricht dem Verzeichnisnamen von process.execPath
.
Hinweise
Prozess zur Erstellung einer einzelnen ausführbaren Anwendung
Ein Tool, das darauf abzielt, eine einzelne ausführbare Node.js-Anwendung zu erstellen, muss den Inhalt des mit --experimental-sea-config
vorbereiteten Blobs injizieren in:
- eine Ressource mit dem Namen
NODE_SEA_BLOB
, wenn dienode
-Binärdatei eine PE-Datei ist - einen Abschnitt mit dem Namen
NODE_SEA_BLOB
imNODE_SEA
-Segment, wenn dienode
-Binärdatei eine Mach-O-Datei ist - eine Notiz mit dem Namen
NODE_SEA_BLOB
, wenn dienode
-Binärdatei eine ELF-Datei ist
Suchen Sie in der Binärdatei nach dem Fuse-String NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2:0
und ändern Sie das letzte Zeichen in 1
, um anzuzeigen, dass eine Ressource injiziert wurde.
Plattformunterstützung
Die Unterstützung für Single-Executables wird in der CI regelmäßig nur auf den folgenden Plattformen getestet:
- Windows
- macOS
- Linux (alle von Node.js unterstützten Distributionen außer Alpine und alle von Node.js unterstützten Architekturen außer s390x)
Dies ist auf einen Mangel an besseren Werkzeugen zur Erstellung von Single-Executables zurückzuführen, die verwendet werden können, um diese Funktion auf anderen Plattformen zu testen.
Vorschläge für andere Werkzeuge/Workflows zur Ressourceninjektion sind willkommen. Bitte starten Sie eine Diskussion unter https://github.com/nodejs/single-executable/discussions, um uns bei der Dokumentation zu helfen.