Applications exécutables uniques
[Historique]
Version | Modifications |
---|---|
v20.6.0 | Ajout de la prise en charge de "useSnapshot". |
v20.6.0 | Ajout de la prise en charge de "useCodeCache". |
v19.7.0, v18.16.0 | Ajouté dans : v19.7.0, v18.16.0 |
[Stable : 1 - Expérimental]
Stable : 1 Stabilité : 1.1 - Développement actif
Code source : src/node_sea.cc
Cette fonctionnalité permet de distribuer facilement une application Node.js à un système sur lequel Node.js n’est pas installé.
Node.js prend en charge la création d’applications exécutables uniques en autorisant l’injection d’un blob préparé par Node.js, qui peut contenir un script regroupé, dans le binaire node
. Au démarrage, le programme vérifie si quelque chose a été injecté. Si le blob est trouvé, il exécute le script dans le blob. Sinon, Node.js fonctionne comme il le fait normalement.
La fonctionnalité d’application exécutable unique ne prend actuellement en charge que l’exécution d’un seul script intégré à l’aide du système de module CommonJS.
Les utilisateurs peuvent créer une application exécutable unique à partir de leur script regroupé avec le binaire node
lui-même et tout outil capable d’injecter des ressources dans le binaire.
Voici les étapes à suivre pour créer une application exécutable unique à l’aide d’un de ces outils, postject :
Génération de blobs de préparation exécutables uniques
Les blobs de préparation exécutables uniques qui sont injectés dans l’application peuvent être générés à l’aide de l’indicateur --experimental-sea-config
du binaire Node.js qui sera utilisé pour créer l’exécutable unique. Il prend un chemin d’accès à un fichier de configuration au format JSON. Si le chemin qui lui est transmis n’est pas absolu, Node.js utilisera le chemin d’accès relatif au répertoire de travail actuel.
La configuration lit actuellement les champs de niveau supérieur suivants :
{
"main": "/path/to/bundled/script.js",
"output": "/path/to/write/the/generated/blob.blob",
"disableExperimentalSEAWarning": true, // Default: false
"useSnapshot": false, // Default: false
"useCodeCache": true, // Default: false
"assets": { // Optional
"a.dat": "/path/to/a.dat",
"b.txt": "/path/to/b.txt"
}
}
Si les chemins d’accès ne sont pas absolus, Node.js utilisera le chemin d’accès relatif au répertoire de travail actuel. La version du binaire Node.js utilisée pour produire le blob doit être la même que celle dans laquelle le blob sera injecté.
Remarque : lors de la génération de SEA multiplateformes (par exemple, la génération d’un SEA pour linux-x64
sur darwin-arm64
), useCodeCache
et useSnapshot
doivent être définis sur false pour éviter de générer des exécutables incompatibles. Étant donné que le cache de code et les instantanés ne peuvent être chargés que sur la même plateforme où ils sont compilés, l’exécutable généré peut planter au démarrage lors de la tentative de chargement du cache de code ou des instantanés créés sur une plateforme différente.
Ressources
Les utilisateurs peuvent inclure des ressources en ajoutant un dictionnaire clé-chemin à la configuration sous le champ assets
. Au moment de la compilation, Node.js lirait les ressources à partir des chemins spécifiés et les regrouperait dans le blob de préparation. Dans l'exécutable généré, les utilisateurs peuvent récupérer les ressources à l'aide des API sea.getAsset()
et 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'application mono-exécutable peut accéder aux ressources comme suit :
const { getAsset, getAssetAsBlob, getRawAsset } = require('node:sea');
// Renvoie une copie des données dans un ArrayBuffer.
const image = getAsset('a.jpg');
// Renvoie une chaîne décodée à partir de la ressource en tant qu'UTF8.
const text = getAsset('b.txt', 'utf8');
// Renvoie un Blob contenant la ressource.
const blob = getAssetAsBlob('a.jpg');
// Renvoie un ArrayBuffer contenant la ressource brute sans copie.
const raw = getRawAsset('a.jpg');
Consultez la documentation des API sea.getAsset()
, sea.getAssetAsBlob()
et sea.getRawAsset()
pour plus d'informations.
Prise en charge des instantanés de démarrage
Le champ useSnapshot
peut être utilisé pour activer la prise en charge des instantanés de démarrage. Dans ce cas, le script main
ne serait pas exécuté au lancement de l'exécutable final. Au lieu de cela, il serait exécuté lorsque le blob de préparation de l'application mono-exécutable est généré sur la machine de construction. Le blob de préparation généré inclurait alors un instantané capturant les états initialisés par le script main
. L'exécutable final avec le blob de préparation injecté désérialiserait l'instantané au moment de l'exécution.
Lorsque useSnapshot
est vrai, le script principal doit invoquer l'API v8.startupSnapshot.setDeserializeMainFunction()
pour configurer le code qui doit être exécuté lorsque l'exécutable final est lancé par les utilisateurs.
Le modèle typique pour qu'une application utilise un instantané dans une application mono-exécutable est :
Les contraintes générales des scripts d'instantané de démarrage s'appliquent également au script principal lorsqu'il est utilisé pour créer un instantané pour l'application mono-exécutable, et le script principal peut utiliser l'API v8.startupSnapshot
pour s'adapter à ces contraintes. Consultez la documentation sur la prise en charge des instantanés de démarrage dans Node.js.
Prise en charge du cache de code V8
Lorsque useCodeCache
est défini sur true
dans la configuration, lors de la génération du blob de préparation de l'exécutable unique, Node.js compile le script main
pour générer le cache de code V8. Le cache de code généré fera partie du blob de préparation et sera injecté dans l'exécutable final. Lorsque l'application exécutable unique est lancée, au lieu de compiler le script main
à partir de zéro, Node.js utilise le cache de code pour accélérer la compilation, puis exécute le script, ce qui améliore les performances de démarrage.
Remarque : import()
ne fonctionne pas lorsque useCodeCache
est défini sur true
.
Dans le script principal injecté
API d'application à exécutable unique
Le module intégré node:sea
permet d'interagir avec l'application à exécutable unique à partir du script principal JavaScript intégré à l'exécutable.
sea.isSea()
Ajouté dans : v21.7.0, v20.12.0
- Retourne : <boolean> Indique si ce script s'exécute à l'intérieur d'une application à exécutable unique.
sea.getAsset(key[, encoding])
Ajouté dans : v21.7.0, v20.12.0
Cette méthode peut être utilisée pour récupérer les ressources configurées pour être regroupées dans l’application à exécutable unique au moment de la construction. Une erreur est renvoyée si aucune ressource correspondante n’est trouvée.
key
<string> : la clé de la ressource dans le dictionnaire spécifié par le champassets
dans la configuration de l’application à exécutable unique.encoding
<string> : si spécifié, la ressource sera décodée en tant que chaîne. Tout encodage pris en charge par leTextDecoder
est accepté. Si non spécifié, unArrayBuffer
contenant une copie de la ressource sera renvoyé à la place.- Retourne : <string> | <ArrayBuffer>
sea.getAssetAsBlob(key[, options])
Ajouté dans : v21.7.0, v20.12.0
Similaire à sea.getAsset()
, mais renvoie le résultat dans un Blob
. Une erreur est levée si aucun asset correspondant n'est trouvé.
key
<string> : la clé de l'asset dans le dictionnaire spécifié par le champassets
dans la configuration de l'application à exécutable unique.options
<Object>type
<string> Un type mime optionnel pour le blob.
Retourne : <Blob>
sea.getRawAsset(key)
Ajouté dans : v21.7.0, v20.12.0
Cette méthode peut être utilisée pour récupérer les assets configurés pour être regroupés dans l'application à exécutable unique au moment de la construction. Une erreur est levée si aucun asset correspondant n'est trouvé.
Contrairement à sea.getAsset()
ou sea.getAssetAsBlob()
, cette méthode ne renvoie pas une copie. Au lieu de cela, elle renvoie l'asset brut regroupé à l'intérieur de l'exécutable.
Pour l'instant, les utilisateurs doivent éviter d'écrire dans le tampon de tableau renvoyé. Si la section injectée n'est pas marquée comme accessible en écriture ou n'est pas alignée correctement, les écritures dans le tampon de tableau renvoyé sont susceptibles d'entraîner un crash.
key
<string> : la clé de l'asset dans le dictionnaire spécifié par le champassets
dans la configuration de l'application à exécutable unique.- Retourne : <ArrayBuffer>
require(id)
dans le script principal injecté n'est pas basé sur un fichier
require()
dans le script principal injecté n'est pas le même que le require()
disponible pour les modules qui ne sont pas injectés. Il ne possède pas non plus les propriétés que require()
non injecté possède, à l'exception de require.main
. Il ne peut être utilisé que pour charger des modules intégrés. Tenter de charger un module qui ne peut être trouvé que dans le système de fichiers lèvera une erreur.
Au lieu de s'appuyer sur un require()
basé sur un fichier, les utilisateurs peuvent regrouper leur application dans un fichier JavaScript autonome à injecter dans l'exécutable. Cela garantit également un graphe de dépendances plus déterministe.
Cependant, si un require()
basé sur un fichier est toujours nécessaire, cela peut également être réalisé :
const { createRequire } = require('node:module');
require = createRequire(__filename);
__filename
et module.filename
dans le script principal injecté
Les valeurs de __filename
et module.filename
dans le script principal injecté sont égales à process.execPath
.
__dirname
dans le script principal injecté
La valeur de __dirname
dans le script principal injecté est égale au nom du répertoire de process.execPath
.
Notes
Processus de création d'une application exécutable unique
Un outil visant à créer une application Node.js exécutable unique doit injecter le contenu du blob préparé avec --experimental-sea-config"
dans :
- une ressource nommée
NODE_SEA_BLOB
si le binairenode
est un fichier PE - une section nommée
NODE_SEA_BLOB
dans le segmentNODE_SEA
si le binairenode
est un fichier Mach-O - une note nommée
NODE_SEA_BLOB
si le binairenode
est un fichier ELF
Recherchez dans le binaire la chaîne fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2:0
et remplacez le dernier caractère par 1
pour indiquer qu'une ressource a été injectée.
Prise en charge de la plateforme
La prise en charge des exécutables uniques est testée régulièrement sur CI uniquement sur les plateformes suivantes :
- Windows
- macOS
- Linux (toutes les distributions prises en charge par Node.js à l'exception d'Alpine et toutes les architectures prises en charge par Node.js à l'exception de s390x)
Cela est dû à un manque de meilleurs outils pour générer des exécutables uniques qui peuvent être utilisés pour tester cette fonctionnalité sur d'autres plateformes.
Les suggestions concernant d'autres outils/workflows d'injection de ressources sont les bienvenues. Veuillez lancer une discussion sur https://github.com/nodejs/single-executable/discussions pour nous aider à les documenter.