Applicazioni eseguibili singole
[Cronologia]
Versione | Modifiche |
---|---|
v20.6.0 | Aggiunto supporto per "useSnapshot". |
v20.6.0 | Aggiunto supporto per "useCodeCache". |
v19.7.0, v18.16.0 | Aggiunto in: v19.7.0, v18.16.0 |
[Stabile: 1 - Sperimentale]
Stabile: 1 Stabilità: 1.1 - Sviluppo attivo
Codice sorgente: src/node_sea.cc
Questa funzionalità consente la distribuzione di un'applicazione Node.js in modo conveniente su un sistema che non ha Node.js installato.
Node.js supporta la creazione di applicazioni eseguibili singole consentendo l'iniezione di un blob preparato da Node.js, che può contenere uno script raggruppato, nel binario node
. Durante l'avvio, il programma controlla se è stato iniettato qualcosa. Se viene trovato il blob, esegue lo script nel blob. Altrimenti Node.js funziona come al solito.
La funzionalità di applicazione eseguibile singola attualmente supporta solo l'esecuzione di un singolo script incorporato utilizzando il sistema di moduli CommonJS.
Gli utenti possono creare un'applicazione eseguibile singola dal loro script raggruppato con il binario node
stesso e qualsiasi strumento che può iniettare risorse nel binario.
Ecco i passaggi per creare un'applicazione eseguibile singola utilizzando uno di questi strumenti, postject:
Generazione di blob di preparazione per eseguibili singoli
I blob di preparazione per eseguibili singoli che vengono iniettati nell'applicazione possono essere generati utilizzando il flag --experimental-sea-config
del binario Node.js che verrà utilizzato per costruire l'eseguibile singolo. Accetta un percorso a un file di configurazione in formato JSON. Se il percorso passato non è assoluto, Node.js userà il percorso relativo alla directory di lavoro corrente.
La configurazione legge attualmente i seguenti campi di livello superiore:
{
"main": "/path/to/bundled/script.js",
"output": "/path/to/write/the/generated/blob.blob",
"disableExperimentalSEAWarning": true, // Predefinito: false
"useSnapshot": false, // Predefinito: false
"useCodeCache": true, // Predefinito: false
"assets": {
// Opzionale
"a.dat": "/path/to/a.dat",
"b.txt": "/path/to/b.txt"
}
}
Se i percorsi non sono assoluti, Node.js userà il percorso relativo alla directory di lavoro corrente. La versione del binario Node.js utilizzata per produrre il blob deve essere la stessa a cui il blob verrà iniettato.
Nota: quando si generano SEA multipiattaforma (ad esempio, generando una SEA per linux-x64
su darwin-arm64
), useCodeCache
e useSnapshot
devono essere impostati su false per evitare di generare eseguibili incompatibili. Poiché la cache del codice e gli snapshot possono essere caricati solo sulla stessa piattaforma in cui sono compilati, l'eseguibile generato potrebbe bloccarsi all'avvio quando si tenta di caricare la cache del codice o gli snapshot creati su una piattaforma diversa.
Risorse
Gli utenti possono includere risorse aggiungendo un dizionario di percorsi chiave alla configurazione come campo assets
. Al momento della build, Node.js leggerà le risorse dai percorsi specificati e le includerà nel blob di preparazione. Nell'eseguibile generato, gli utenti possono recuperare le risorse utilizzando le API sea.getAsset()
e sea.getAssetAsBlob()
.
{
"main": "/path/to/bundled/script.js",
"output": "/path/to/write/the/generated/blob.blob",
"assets": {
"a.jpg": "/path/to/a.jpg",
"b.txt": "/path/to/b.txt"
}
}
L'applicazione single-executable può accedere alle risorse come segue:
const { getAsset, getAssetAsBlob, getRawAsset } = require('node:sea')
// Restituisce una copia dei dati in un ArrayBuffer.
const image = getAsset('a.jpg')
// Restituisce una stringa decodificata dall'asset come UTF8.
const text = getAsset('b.txt', 'utf8')
// Restituisce un Blob contenente l'asset.
const blob = getAssetAsBlob('a.jpg')
// Restituisce un ArrayBuffer contenente l'asset grezzo senza copia.
const raw = getRawAsset('a.jpg')
Vedi la documentazione delle API sea.getAsset()
, sea.getAssetAsBlob()
e sea.getRawAsset()
per maggiori informazioni.
Supporto snapshot di avvio
Il campo useSnapshot
può essere utilizzato per abilitare il supporto snapshot di avvio. In questo caso, lo script main
non verrebbe eseguito quando viene lanciato l'eseguibile finale. Invece, verrebbe eseguito quando viene generato il blob di preparazione dell'applicazione single-executable sulla macchina di build. Il blob di preparazione generato includerebbe quindi uno snapshot che cattura gli stati inizializzati dallo script main
. L'eseguibile finale con il blob di preparazione iniettato deserializzerebbe lo snapshot al momento dell'esecuzione.
Quando useSnapshot
è true, lo script principale deve invocare l'API v8.startupSnapshot.setDeserializeMainFunction()
per configurare il codice che deve essere eseguito quando l'eseguibile finale viene lanciato dagli utenti.
Lo schema tipico per un'applicazione che utilizza snapshot in un'applicazione single-executable è:
I vincoli generali degli script snapshot di avvio si applicano anche allo script principale quando viene utilizzato per creare snapshot per l'applicazione single-executable, e lo script principale può utilizzare l'API v8.startupSnapshot
per adattarsi a questi vincoli. Vedi documentazione sul supporto snapshot di avvio in Node.js.
Supporto della cache del codice V8
Quando useCodeCache
è impostato su true
nella configurazione, durante la generazione del blob di preparazione dell'eseguibile singolo, Node.js compilerà lo script main
per generare la cache del codice V8. La cache del codice generata farà parte del blob di preparazione e verrà iniettata nell'eseguibile finale. Quando viene avviata l'applicazione eseguibile singola, invece di compilare lo script main
da zero, Node.js utilizzerà la cache del codice per accelerare la compilazione, quindi eseguirà lo script, migliorando le prestazioni di avvio.
Nota: import()
non funziona quando useCodeCache
è true
.
Nello script principale iniettato
API dell'applicazione eseguibile singola
Il built-in node:sea
consente l'interazione con l'applicazione eseguibile singola dallo script principale JavaScript incorporato nell'eseguibile.
sea.isSea()
Aggiunto in: v21.7.0, v20.12.0
- Restituisce: <boolean> Se questo script è in esecuzione all'interno di un'applicazione eseguibile singola.
sea.getAsset(key[, encoding])
Aggiunto in: v21.7.0, v20.12.0
Questo metodo può essere utilizzato per recuperare le risorse configurate per essere raggruppate nell'applicazione eseguibile singola al momento della compilazione. Viene generato un errore quando non viene trovata alcuna risorsa corrispondente.
key
<string> la chiave per la risorsa nel dizionario specificato dal campoassets
nella configurazione dell'applicazione eseguibile singola.encoding
<string> Se specificato, la risorsa verrà decodificata come stringa. È accettata qualsiasi codifica supportata daTextDecoder
. Se non specificato, verrà invece restituito unArrayBuffer
contenente una copia della risorsa.- Restituisce: <string> | <ArrayBuffer>
sea.getAssetAsBlob(key[, options])
Aggiunto in: v21.7.0, v20.12.0
Simile a sea.getAsset()
, ma restituisce il risultato come Blob
. Viene sollevato un errore quando non viene trovata alcuna risorsa corrispondente.
key
<string> la chiave per la risorsa nel dizionario specificato dal campoassets
nella configurazione dell'applicazione single-executable.options
<Object>type
<string> Un tipo mime facoltativo per il blob.
Restituisce: <Blob>
sea.getRawAsset(key)
Aggiunto in: v21.7.0, v20.12.0
Questo metodo può essere utilizzato per recuperare le risorse configurate per essere raggruppate nell'applicazione single-executable al momento della compilazione. Viene sollevato un errore quando non viene trovata alcuna risorsa corrispondente.
A differenza di sea.getAsset()
o sea.getAssetAsBlob()
, questo metodo non restituisce una copia. Invece, restituisce la risorsa grezza inclusa nell'eseguibile.
Per ora, gli utenti dovrebbero evitare di scrivere nel buffer di array restituito. Se la sezione iniettata non è contrassegnata come scrivibile o non è allineata correttamente, la scrittura nel buffer di array restituito potrebbe causare un arresto anomalo.
key
<string> la chiave per la risorsa nel dizionario specificato dal campoassets
nella configurazione dell'applicazione single-executable.- Restituisce: <ArrayBuffer>
require(id)
nello script principale iniettato non è basato su file
require()
nello script principale iniettato non è lo stesso di require()
disponibile per i moduli che non sono iniettati. Non ha nemmeno nessuna delle proprietà che require()
non iniettato ha, eccetto require.main
. Può essere utilizzato solo per caricare moduli integrati. Tentare di caricare un modulo che può essere trovato solo nel file system solleverà un errore.
Invece di affidarsi a un require()
basato su file, gli utenti possono raggruppare la propria applicazione in un file JavaScript autonomo da iniettare nell'eseguibile. Ciò garantisce anche un grafo di dipendenze più deterministico.
Tuttavia, se è ancora necessario un require()
basato su file, ciò può anche essere ottenuto:
const { createRequire } = require('node:module')
require = createRequire(__filename)
__filename
e module.filename
nello script principale iniettato
I valori di __filename
e module.filename
nello script principale iniettato sono uguali a process.execPath
.
__dirname
nello script principale iniettato
Il valore di __dirname
nello script principale iniettato è uguale al nome della directory di process.execPath
.
Note
Processo di creazione di un'applicazione singola eseguibile
Uno strumento che mira a creare un'applicazione Node.js singola eseguibile deve iniettare il contenuto del blob preparato con --experimental-sea-config"
in:
- una risorsa denominata
NODE_SEA_BLOB
se il binarionode
è un file PE - una sezione denominata
NODE_SEA_BLOB
nel segmentoNODE_SEA
se il binarionode
è un file Mach-O - una nota denominata
NODE_SEA_BLOB
se il binarionode
è un file ELF
Cercare nel binario la stringa NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2:0
fuse e capovolgere l'ultimo carattere in 1
per indicare che è stata iniettata una risorsa.
Supporto della piattaforma
Il supporto per i singoli eseguibili viene testato regolarmente su CI solo sulle seguenti piattaforme:
- Windows
- macOS
- Linux (tutte le distribuzioni supportate da Node.js eccetto Alpine e tutte le architetture supportate da Node.js eccetto s390x)
Ciò è dovuto alla mancanza di strumenti migliori per generare singoli eseguibili che possono essere utilizzati per testare questa funzionalità su altre piattaforme.
Sono benvenuti suggerimenti per altri strumenti/workflow di iniezione di risorse. Si prega di avviare una discussione su https://github.com/nodejs/single-executable/discussions per aiutarci a documentarli.