Aplicaciones ejecutables únicas
[Historial]
Versión | Cambios |
---|---|
v20.6.0 | Se agregó soporte para "useSnapshot". |
v20.6.0 | Se agregó soporte para "useCodeCache". |
v19.7.0, v18.16.0 | Agregado en: v19.7.0, v18.16.0 |
[Estable: 1 - Experimental]
Estable: 1 Estabilidad: 1.1 - Desarrollo activo
Código fuente: src/node_sea.cc
Esta característica permite la distribución de una aplicación Node.js convenientemente a un sistema que no tiene Node.js instalado.
Node.js admite la creación de aplicaciones ejecutables únicas al permitir la inyección de un blob preparado por Node.js, que puede contener un script empaquetado, en el binario node
. Durante el inicio, el programa comprueba si se ha inyectado algo. Si se encuentra el blob, ejecuta el script en el blob. De lo contrario, Node.js funciona como lo hace normalmente.
La función de aplicación ejecutable única actualmente solo admite la ejecución de un único script incrustado utilizando el sistema de módulos CommonJS.
Los usuarios pueden crear una aplicación ejecutable única a partir de su script empaquetado con el propio binario node
y cualquier herramienta que pueda inyectar recursos en el binario.
Aquí están los pasos para crear una aplicación ejecutable única utilizando una de esas herramientas, postject:
Generando blobs de preparación ejecutables únicos
Los blobs de preparación ejecutables únicos que se inyectan en la aplicación se pueden generar utilizando el flag --experimental-sea-config
del binario Node.js que se utilizará para construir el ejecutable único. Toma una ruta a un archivo de configuración en formato JSON. Si la ruta que se le pasa no es absoluta, Node.js usará la ruta relativa al directorio de trabajo actual.
La configuración actualmente lee los siguientes campos de nivel superior:
{
"main": "/ruta/a/script/empaquetado.js",
"output": "/ruta/para/escribir/el/blob/generado.blob",
"disableExperimentalSEAWarning": true, // Predeterminado: false
"useSnapshot": false, // Predeterminado: false
"useCodeCache": true, // Predeterminado: false
"assets": { // Opcional
"a.dat": "/ruta/a/a.dat",
"b.txt": "/ruta/a/b.txt"
}
}
Si las rutas no son absolutas, Node.js usará la ruta relativa al directorio de trabajo actual. La versión del binario Node.js utilizada para producir el blob debe ser la misma que aquella en la que se inyectará el blob.
Nota: Cuando se generan SEAs multiplataforma (por ejemplo, generar un SEA para linux-x64
en darwin-arm64
), useCodeCache
y useSnapshot
deben establecerse en false para evitar la generación de ejecutables incompatibles. Dado que el caché de código y las instantáneas solo se pueden cargar en la misma plataforma donde se compilan, el ejecutable generado podría fallar al inicio al intentar cargar el caché de código o las instantáneas creadas en una plataforma diferente.
Activos
Los usuarios pueden incluir activos añadiendo un diccionario de clave-ruta a la configuración como el campo assets
. En tiempo de compilación, Node.js leería los activos de las rutas especificadas y los empaquetaría en el blob de preparación. En el ejecutable generado, los usuarios pueden recuperar los activos usando las APIs sea.getAsset()
y 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"
}
}
La aplicación de un solo ejecutable puede acceder a los activos de la siguiente manera:
const { getAsset, getAssetAsBlob, getRawAsset } = require('node:sea');
// Devuelve una copia de los datos en un ArrayBuffer.
const image = getAsset('a.jpg');
// Devuelve una cadena decodificada del activo como UTF8.
const text = getAsset('b.txt', 'utf8');
// Devuelve un Blob que contiene el activo.
const blob = getAssetAsBlob('a.jpg');
// Devuelve un ArrayBuffer que contiene el activo en bruto sin copiar.
const raw = getRawAsset('a.jpg');
Consulte la documentación de las APIs sea.getAsset()
, sea.getAssetAsBlob()
y sea.getRawAsset()
para obtener más información.
Soporte para instantáneas de inicio
El campo useSnapshot
se puede utilizar para habilitar el soporte de instantáneas de inicio. En este caso, el script main
no se ejecutaría cuando se inicie el ejecutable final. En cambio, se ejecutaría cuando el blob de preparación de la aplicación de un solo ejecutable se genere en la máquina de compilación. El blob de preparación generado incluiría entonces una instantánea que capturara los estados inicializados por el script main
. El ejecutable final con el blob de preparación inyectado deserializaría la instantánea en tiempo de ejecución.
Cuando useSnapshot
es verdadero, el script principal debe invocar la API v8.startupSnapshot.setDeserializeMainFunction()
para configurar el código que debe ejecutarse cuando el ejecutable final sea lanzado por los usuarios.
El patrón típico para que una aplicación use instantáneas en una aplicación de un solo ejecutable es:
Las restricciones generales de los scripts de instantáneas de inicio también se aplican al script principal cuando se utiliza para construir una instantánea para la aplicación de un solo ejecutable, y el script principal puede utilizar la API v8.startupSnapshot
para adaptarse a estas restricciones. Consulte la documentación sobre el soporte de instantáneas de inicio en Node.js.
Soporte de caché de código V8
Cuando useCodeCache
se establece en true
en la configuración, durante la generación del blob de preparación del ejecutable único, Node.js compilará el script main
para generar la caché de código V8. La caché de código generada formaría parte del blob de preparación y se inyectaría en el ejecutable final. Cuando se inicia la aplicación ejecutable única, en lugar de compilar el script main
desde cero, Node.js usaría la caché de código para acelerar la compilación y luego ejecutaría el script, lo que mejoraría el rendimiento de inicio.
Nota: import()
no funciona cuando useCodeCache
es true
.
En el script principal inyectado
API de aplicación de un solo ejecutable
El módulo incorporado node:sea
permite la interacción con la aplicación de un solo ejecutable desde el script principal de JavaScript incrustado en el ejecutable.
sea.isSea()
Agregado en: v21.7.0, v20.12.0
- Devuelve: <boolean> Si este script se está ejecutando dentro de una aplicación de un solo ejecutable.
sea.getAsset(key[, encoding])
Agregado en: v21.7.0, v20.12.0
Este método se puede utilizar para recuperar los activos configurados para ser incluidos en la aplicación de un solo ejecutable en tiempo de compilación. Se genera un error cuando no se encuentra ningún activo coincidente.
key
<string> la clave del activo en el diccionario especificado por el campoassets
en la configuración de la aplicación de un solo ejecutable.encoding
<string> Si se especifica, el activo se decodificará como una cadena. Se acepta cualquier codificación admitida por elTextDecoder
. Si no se especifica, se devolverá unArrayBuffer
que contiene una copia del activo.- Devuelve: <string> | <ArrayBuffer>
sea.getAssetAsBlob(key[, options])
Añadido en: v21.7.0, v20.12.0
Similar a sea.getAsset()
, pero devuelve el resultado en un Blob
. Se produce un error cuando no se puede encontrar ningún activo coincidente.
key
<string> la clave para el activo en el diccionario especificado por el campoassets
en la configuración de la aplicación de un solo ejecutable.options
<Object>type
<string> Un tipo mime opcional para el blob.
Devuelve: <Blob>
sea.getRawAsset(key)
Añadido en: v21.7.0, v20.12.0
Este método se puede utilizar para recuperar los activos configurados para ser incluidos en la aplicación de un solo ejecutable en tiempo de compilación. Se produce un error cuando no se puede encontrar ningún activo coincidente.
A diferencia de sea.getAsset()
o sea.getAssetAsBlob()
, este método no devuelve una copia. En cambio, devuelve el activo sin procesar incluido dentro del ejecutable.
Por ahora, los usuarios deben evitar escribir en el búfer de matriz devuelto. Si la sección inyectada no está marcada como grabable o no está alineada correctamente, es probable que las escrituras en el búfer de matriz devuelto provoquen un bloqueo.
key
<string> la clave para el activo en el diccionario especificado por el campoassets
en la configuración de la aplicación de un solo ejecutable.- Devuelve: <ArrayBuffer>
require(id)
en el script principal inyectado no está basado en archivos
require()
en el script principal inyectado no es lo mismo que require()
disponible para módulos que no están inyectados. Tampoco tiene ninguna de las propiedades que no están inyectadas require()
tiene excepto require.main
. Solo se puede utilizar para cargar módulos integrados. Intentar cargar un módulo que solo se puede encontrar en el sistema de archivos arrojará un error.
En lugar de depender de un require()
basado en archivos, los usuarios pueden agrupar su aplicación en un archivo JavaScript independiente para inyectar en el ejecutable. Esto también asegura un gráfico de dependencia más determinista.
Sin embargo, si todavía se necesita un require()
basado en archivos, también se puede lograr:
const { createRequire } = require('node:module');
require = createRequire(__filename);
__filename
y module.filename
en el script principal inyectado
Los valores de __filename
y module.filename
en el script principal inyectado son iguales a process.execPath
.
__dirname
en el script principal inyectado
El valor de __dirname
en el script principal inyectado es igual al nombre del directorio de process.execPath
.
Notas
Proceso de creación de una aplicación ejecutable única
Una herramienta destinada a crear una aplicación Node.js ejecutable única debe inyectar el contenido del blob preparado con --experimental-sea-config"
en:
- un recurso llamado
NODE_SEA_BLOB
si el binarionode
es un archivo PE - una sección llamada
NODE_SEA_BLOB
en el segmentoNODE_SEA
si el binarionode
es un archivo Mach-O - una nota llamada
NODE_SEA_BLOB
si el binarionode
es un archivo ELF
Busque en el binario la cadena fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2:0
y cambie el último carácter a 1
para indicar que se ha inyectado un recurso.
Compatibilidad de plataforma
La compatibilidad con un solo ejecutable se prueba regularmente en CI solo en las siguientes plataformas:
- Windows
- macOS
- Linux (todas las distribuciones compatibles con Node.js excepto Alpine y todas las arquitecturas compatibles con Node.js excepto s390x)
Esto se debe a la falta de mejores herramientas para generar ejecutables únicos que se puedan usar para probar esta función en otras plataformas.
Las sugerencias para otras herramientas/flujos de trabajo de inyección de recursos son bienvenidas. Inicie una discusión en https://github.com/nodejs/single-executable/discussions para ayudarnos a documentarlos.