Однофайловые исполняемые приложения
[История]
Версия | Изменения |
---|---|
v20.6.0 | Добавлена поддержка "useSnapshot". |
v20.6.0 | Добавлена поддержка "useCodeCache". |
v19.7.0, v18.16.0 | Добавлено в: v19.7.0, v18.16.0 |
[Стабильность: 1 - Экспериментально]
Стабильность: 1 Стабильность: 1.1 - Активная разработка
Исходный код: src/node_sea.cc
Эта функция позволяет удобно распространять приложение Node.js на системы, где Node.js не установлен.
Node.js поддерживает создание однофайловых исполняемых приложений, позволяя внедрять подготовленный Node.js блок данных, который может содержать собранный скрипт, в двоичный файл node
. При запуске программа проверяет, был ли что-либо внедрено. Если блок данных найден, он выполняет скрипт из блока. В противном случае Node.js работает в обычном режиме.
В настоящее время функция однофайлового исполняемого приложения поддерживает только запуск одного встроенного скрипта с использованием системы модулей CommonJS.
Пользователи могут создавать однофайловое исполняемое приложение из своего собранного скрипта, используя сам двоичный файл node
и любой инструмент, способный внедрять ресурсы в двоичный файл.
Вот шаги по созданию однофайлового исполняемого приложения с использованием одного из таких инструментов, postject:
Генерация блоков подготовки однофайлового исполняемого приложения
Блоки подготовки однофайлового исполняемого приложения, которые внедряются в приложение, могут быть сгенерированы с использованием флага --experimental-sea-config
двоичного файла Node.js, который будет использоваться для сборки однофайлового исполняемого файла. Он принимает путь к конфигурационному файлу в формате JSON. Если переданный ему путь не является абсолютным, Node.js будет использовать путь относительно текущего рабочего каталога.
Конфигурация в настоящее время читает следующие поля верхнего уровня:
{
"main": "/path/to/bundled/script.js",
"output": "/path/to/write/the/generated/blob.blob",
"disableExperimentalSEAWarning": true, // По умолчанию: false
"useSnapshot": false, // По умолчанию: false
"useCodeCache": true, // По умолчанию: false
"assets": {
// Необязательно
"a.dat": "/path/to/a.dat",
"b.txt": "/path/to/b.txt"
}
}
Если пути не являются абсолютными, Node.js будет использовать путь относительно текущего рабочего каталога. Версия двоичного файла Node.js, используемая для создания блока данных, должна совпадать с той, в которую будет внедрён блок данных.
Примечание: При генерации кроссплатформенных SEA (например, генерация SEA для linux-x64
на darwin-arm64
), useCodeCache
и useSnapshot
должны быть установлены в false, чтобы избежать генерации несовместимых исполняемых файлов. Поскольку кэш кода и снимки могут быть загружены только на той же платформе, где они были скомпилированы, сгенерированный исполняемый файл может аварийно завершить работу при запуске при попытке загрузить кэш кода или снимки, созданные на другой платформе.
Ресурсы
Пользователи могут включать ресурсы, добавляя словарь с ключом-путем в конфигурацию в качестве поля assets
. Во время сборки Node.js будет считывать ресурсы из указанных путей и включать их в подготавливаемый blob. В сгенерированном исполняемом файле пользователи могут получать доступ к ресурсам с помощью API sea.getAsset()
и 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"
}
}
Автономное приложение может получать доступ к ресурсам следующим образом:
const { getAsset, getAssetAsBlob, getRawAsset } = require('node:sea')
// Возвращает копию данных в ArrayBuffer.
const image = getAsset('a.jpg')
// Возвращает строку, декодированную из ресурса как UTF8.
const text = getAsset('b.txt', 'utf8')
// Возвращает Blob, содержащий ресурс.
const blob = getAssetAsBlob('a.jpg')
// Возвращает ArrayBuffer, содержащий необработанный ресурс без копирования.
const raw = getRawAsset('a.jpg')
См. документацию API sea.getAsset()
, sea.getAssetAsBlob()
и sea.getRawAsset()
для получения дополнительной информации.
Поддержка снимков запуска
Поле useSnapshot
может использоваться для включения поддержки снимков запуска. В этом случае основной скрипт не будет выполняться при запуске окончательного исполняемого файла. Вместо этого он будет выполнен при генерации подготавливаемого blob автономного приложения на машине сборки. Сгенерированный подготавливаемый blob будет затем включать снимок, захватывающий состояния, инициализированные основным скриптом. Окончательный исполняемый файл с внедренным подготавливаемым blob будет десериализовывать снимок во время выполнения.
Когда useSnapshot
имеет значение true, основной скрипт должен вызывать API v8.startupSnapshot.setDeserializeMainFunction()
для настройки кода, который необходимо выполнить при запуске окончательного исполняемого файла пользователями.
Типичный шаблон для использования снимка в автономном приложении:
Общие ограничения скриптов снимка запуска также применяются к главному скрипту, когда он используется для создания снимка для автономного приложения, и главный скрипт может использовать API v8.startupSnapshot
для адаптации к этим ограничениям. См. документацию о поддержке снимков запуска в Node.js.
Поддержка кэша кода V8
Когда в конфигурации параметр useCodeCache
установлен в true
, во время генерации единого исполняемого файла подготовки, Node.js будет компилировать основной скрипт (main
) для генерации кэша кода V8. Сгенерированный кэш кода будет частью файла подготовки и будет внедрён в окончательный исполняемый файл. При запуске единого исполняемого приложения, вместо компиляции основного скрипта (main
) с нуля, Node.js будет использовать кэш кода для ускорения компиляции, а затем выполнит скрипт, что улучшит производительность запуска.
Примечание: import()
не работает, когда useCodeCache
равен true
.
Во встроенном основном скрипте
API единого исполняемого приложения
Встроенный модуль node:sea
позволяет взаимодействовать с единым исполняемым приложением из основного JavaScript-скрипта, встроенного в исполняемый файл.
sea.isSea()
Добавлено в: v21.7.0, v20.12.0
- Возвращает: <boolean> Выполняется ли этот скрипт внутри единого исполняемого приложения.
sea.getAsset(key[, encoding])
Добавлено в: v21.7.0, v20.12.0
Этот метод может использоваться для извлечения ресурсов, настроенных для включения в единое исполняемое приложение во время сборки. Если подходящий ресурс не найден, возникает ошибка.
key
<string> ключ для ресурса в словаре, указанном полемassets
в конфигурации единого исполняемого приложения.encoding
<string> Если указано, ресурс будет декодирован как строка. Поддерживается любая кодировка, поддерживаемаяTextDecoder
. Если не указано, вместо этого будет возвращенArrayBuffer
, содержащий копию ресурса.- Возвращает: <string> | <ArrayBuffer>
sea.getAssetAsBlob(key[, options])
Добавлено в: v21.7.0, v20.12.0
Аналогично sea.getAsset()
, но возвращает результат в виде Blob
. Если соответствующий ресурс не найден, возникает ошибка.
key
<string> ключ ресурса в словаре, указанном полемassets
в конфигурации однофайлового приложения.options
<Object>type
<string> Необязательный тип MIME для blob.
Возвращает: <Blob>
sea.getRawAsset(key)
Добавлено в: v21.7.0, v20.12.0
Этот метод используется для извлечения ресурсов, настроенных для включения в однофайловое приложение во время сборки. Если соответствующий ресурс не найден, возникает ошибка.
В отличие от sea.getAsset()
или sea.getAssetAsBlob()
, этот метод не возвращает копию. Вместо этого он возвращает необработанный ресурс, встроенный в исполняемый файл.
На данный момент пользователям следует избегать записи в возвращаемый буфер массива. Если внедренный раздел не помечен как доступный для записи или не выровнен должным образом, запись в возвращаемый буфер массива может привести к сбою.
key
<string> ключ ресурса в словаре, указанном полемassets
в конфигурации однофайлового приложения.- Возвращает: <ArrayBuffer>
require(id)
во внедрённом основном скрипте не основан на файлах
require()
во внедрённом основном скрипте не является тем же самым, что и require()
, доступный модулям, которые не внедрены. Он также не имеет никаких свойств, которыми обладает не внедрённый require()
, за исключением require.main
. Он может использоваться только для загрузки встроенных модулей. Попытка загрузить модуль, который можно найти только в файловой системе, приведёт к ошибке.
Вместо использования require()
, основанного на файлах, пользователи могут собрать своё приложение в автономный JavaScript-файл для внедрения в исполняемый файл. Это также обеспечивает более детерминированный граф зависимостей.
Однако, если всё же необходим require()
, основанный на файлах, это также можно сделать:
const { createRequire } = require('node:module')
require = createRequire(__filename)
__filename
и module.filename
во встраиваемом основном скрипте
Значения __filename
и module.filename
во встраиваемом основном скрипте равны process.execPath
.
__dirname
во встраиваемом основном скрипте
Значение __dirname
во встраиваемом основном скрипте равно имени каталога process.execPath
.
Примечания
Процесс создания единого исполняемого приложения
Инструмент, предназначенный для создания единого исполняемого приложения Node.js, должен внедрить содержимое подготовленного с помощью --experimental-sea-config"
блока в:
- ресурс с именем
NODE_SEA_BLOB
, если бинарный файлnode
является файлом PE - секцию с именем
NODE_SEA_BLOB
в сегментеNODE_SEA
, если бинарный файлnode
является файлом Mach-O - заметку с именем
NODE_SEA_BLOB
, если бинарный файлnode
является файлом ELF
Найдите в бинарном файле строку NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2:0
fuse и измените последний символ на 1
, чтобы указать, что ресурс был внедрён.
Поддержка платформ
Поддержка единого исполняемого файла регулярно тестируется в CI только на следующих платформах:
- Windows
- macOS
- Linux (все дистрибутивы, поддерживаемые Node.js, за исключением Alpine, и все архитектуры, поддерживаемые Node.js, за исключением s390x)
Это связано с отсутствием лучших инструментов для генерации единых исполняемых файлов, которые можно было бы использовать для тестирования этой функции на других платформах.
Предложения по другим инструментам/рабочим процессам внедрения ресурсов приветствуются. Начните обсуждение по адресу https://github.com/nodejs/single-executable/discussions, чтобы помочь нам задокументировать их.