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 permettant 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 d’habitude.
La fonctionnalité d’application exécutable unique prend actuellement uniquement en charge l’exécution d’un seul script intégré à l’aide du système de modules 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 pour créer une application exécutable unique à l’aide d’un tel outil, postject :
Générer des 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 utilise le chemin relatif au répertoire de travail actuel.
La configuration lit actuellement les champs de niveau supérieur suivants :
{
"main": "/chemin/vers/script/regroupé.js",
"output": "/chemin/vers/ecriture/du/blob/généré.blob",
"disableExperimentalSEAWarning": true, // Valeur par défaut : false
"useSnapshot": false, // Valeur par défaut : false
"useCodeCache": true, // Valeur par défaut : false
"assets": {
// Optionnel
"a.dat": "/chemin/vers/a.dat",
"b.txt": "/chemin/vers/b.txt"
}
}
Si les chemins ne sont pas absolus, Node.js utilise le chemin 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’une 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 sur laquelle 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 autre plateforme.
Ressources
Les utilisateurs peuvent inclure des ressources en ajoutant un dictionnaire clé-chemin à la configuration en tant que champ assets
. Au moment de la construction, Node.js lira les ressources à partir des chemins spécifiés et les regroupera dans le blob de préparation. Dans l'exécutable généré, les utilisateurs peuvent récupérer les ressources en utilisant les API sea.getAsset()
et sea.getAssetAsBlob()
.
{
"main": "/chemin/vers/le/script/groupé.js",
"output": "/chemin/vers/l'écriture/du/blob/généré.blob",
"assets": {
"a.jpg": "/chemin/vers/a.jpg",
"b.txt": "/chemin/vers/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 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é lors du 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 ensuite 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é lors 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 d'une application pour utiliser un instantané dans une application mono-exécutable est le suivant :
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, pendant la génération du blob de préparation de l'exécutable unique, Node.js compilera le script main
pour générer le cache de code V8. Le cache de code généré ferait partie du blob de préparation et serait 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 utiliserait le cache de code pour accélérer la compilation, puis exécuterait le script, ce qui améliorerait les performances de démarrage.
Remarque : import()
ne fonctionne pas lorsque useCodeCache
est 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é dans l'exécutable.
sea.isSea()
Ajouté dans : v21.7.0, v20.12.0
- Retourne : <boolean> Indique si ce script est exécuté dans 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 éléments configurés pour être regroupés dans l'application exécutable unique au moment de la construction. Une erreur est levée lorsqu'aucun élément correspondant n'est trouvé.
key
<string> la clé de l'élément dans le dictionnaire spécifié par le champassets
dans la configuration de l'application exécutable unique.encoding
<string> S'il est spécifié, l'élément sera décodé en tant que chaîne. Tout encodage pris en charge parTextDecoder
est accepté. S'il n'est pas spécifié, unArrayBuffer
contenant une copie de l'élément sera renvoyé à la place.- Retourne : <string> | <ArrayBuffer>
sea.getAssetAsBlob(key[, options])
Ajouté dans : v21.7.0, v20.12.0
Semblable à sea.getAsset()
, mais retourne le résultat dans un Blob
. Une erreur est levée lorsqu'aucun actif correspondant n'est trouvé.
key
<string> La clé de l'actif 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 actifs configurés pour être intégrés dans l'application à exécutable unique au moment de la construction. Une erreur est levée lorsqu'aucun actif correspondant n'est trouvé.
Contrairement à sea.getAsset()
ou sea.getAssetAsBlob()
, cette méthode ne retourne pas une copie. Au lieu de cela, elle retourne l'actif brut intégré à 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 inscriptible ou n'est pas correctement alignée, les écritures dans le tampon de tableau renvoyé sont susceptibles d'entraîner un plantage.
key
<string> La clé de l'actif 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 également aucune des propriétés que le 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 se fier à 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épendance 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'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 de l'exécutable unique est testée régulièrement sur l'IC 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 d'outils plus performants pour générer des exécutables uniques qui peuvent être utilisés pour tester cette fonctionnalité sur d'autres plateformes.
Les suggestions d'autres outils/flux de travail d'injection de ressources sont les bienvenues. Veuillez démarrer une discussion sur https://github.com/nodejs/single-executable/discussions pour nous aider à les documenter.