Перейти к содержанию

Готовые исполняемые приложения

v20.x.x

Стабильность: 1 – Экспериментальная

Эта функция находится в стадии разработки и будет меняться.

Эта возможность позволяет удобно распространять приложение Node.js в системе, на которой не установлен Node.js.

Node.js поддерживает создание одноисполняемых приложений, позволяя внедрять подготовленный Node.js блоб, который может содержать пакетный скрипт, в двоичный файл node. При запуске программа проверяет, не было ли что-либо инжектировано. Если блоб найден, она выполняет скрипт, содержащийся в блобе. В противном случае Node.js работает как обычно.

Функция одноисполняемого приложения в настоящее время поддерживает только запуск одного встроенного сценария с использованием системы модулей CommonJS.

Пользователи могут создать одноисполняемое приложение из своего встроенного скрипта с помощью самого бинарного файла node и любого инструмента, который может внедрять ресурсы в бинарный файл.

Создание одноисполняемого приложения

Ниже приведены шаги по созданию одноисполняемого приложения с помощью одного из таких инструментов, postject:

1. Создайте файл JavaScript

Создайте файл JavaScript:

1
$ echo 'console.log(`Hello, ${process.argv[2]}!`);' > hello.js

2. Создайте файл конфигурации

Создайте файл конфигурации, построив блоб, который можно внедрить в одноисполняемое приложение:

1
$ echo '{ "main": "hello.js", "output": "sea-prep.blob" }' > sea-config.json

3. Создайте блоб

Создайте блоб для инъекции:

1
$ node --experimental-sea-config sea-config.json

4. Создайте копию исполняемого файла

Создайте копию исполняемого файла node и назовите его в соответствии с вашими потребностями:

1
$ cp $(command -v node) hello

5. Удалите подпись

Удалите подпись двоичного файла:

  • На macOS:
1
$ codesign --remove-signature hello
  • На Windows (опционально):

signtool можно использовать из установленного Windows SDK. Если этот шаг пропущен, игнорируйте все предупреждения postject, связанные с подписью.

1
$ signtool remove /s hello

6. Внедрите блоб

Внедрите блоб в скопированный двоичный файл, запустив postject со следующими опциями:

  • hello - Имя копии исполняемого файла node, созданного на шаге 2.
  • NODE_SEA_BLOB - Имя ресурса / заметки / секции в бинарном файле, где будет храниться содержимое блоба.
  • sea-prep.blob - Имя блоба, созданного на шаге 1.
  • --sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2 - fuse, используемый проектом Node.js для обнаружения инъекции файла.
  • --macho-segment-name NODE_SEA (нужен только на macOS) - Имя сегмента в бинарном файле, где будет храниться содержимое блоба.

Подводя итог, вот необходимая команда для каждой платформы:

  • В системах, отличных от macOS:
1
2
$ npx postject hello NODE_SEA_BLOB sea-prep.blob \
    --sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2
  • На macOS:
1
2
3
$ npx postject hello NODE_SEA_BLOB sea-prep.blob \
    --sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2 \
    --macho-segment-name NODE_SEA

7. Подпишите бинарный файл

Подпишите бинарный файл:

  • На macOS:
1
$ codesign --sign - hello
  • На Windows (опционально):

    Для этого необходимо наличие сертификата. Однако неподписанный двоичный файл все равно можно будет запустить.

1
$ signtool sign /fd SHA256 hello

8. Запустите бинарный файл

Запустите бинарный файл:

1
2
$ ./hello world
Hello, world!

Генерация подготовительных блобов для одноисполняемого файла

Подготовительные блобы для одноисполняемого файла, которые внедряются в приложение, могут быть сгенерированы с помощью флага --experimental-sea-config в бинарном файле Node.js, который будет использоваться для сборки одноисполняемого файла. Он принимает путь к конфигурационному файлу в формате JSON. Если переданный ему путь не абсолютный, Node.js будет использовать путь относительно текущего рабочего каталога.

В настоящее время конфигурация считывает следующие поля верхнего уровня:

1
2
3
4
{
    "main": "/path/to/bundled/script.js",
    "output": "/path/to/write/the/generated/blob.blob"
}

Если пути не абсолютные, Node.js будет использовать путь относительно текущего рабочего каталога. Версия бинарного файла Node.js, используемого для создания блоба, должна быть такой же, как и версия, в которую будет внедрен блоб.

Примечания

require(id) в инжектируемом модуле не основан на файлах

require() в инжектируемом модуле - это не то же самое, что require(), доступное для модулей, которые не инжектируются. Он также не обладает ни одним из свойств, которыми обладает неинжектированный require(), кроме require.main. Его можно использовать только для загрузки встроенных модулей. Попытка загрузить модуль, который может быть найден только в файловой системе, приведет к ошибке.

Вместо того, чтобы полагаться на файловый require(), пользователи могут упаковать свое приложение в отдельный JavaScript-файл для внедрения в исполняемый файл. Это также обеспечивает более детерминированный граф зависимостей.

Однако, если все же требуется require() на основе файла, это также может быть достигнуто:

1
2
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 только на следующих платформах:

Это связано с отсутствием лучших инструментов для генерации одиночных исполняемых файлов, которые можно было бы использовать для тестирования этой функции на других платформах.

Предложения по другим инструментам/рабочим процессам инъекции ресурсов приветствуются. Пожалуйста, начните обсуждение по адресу https://github.com/nodejs/single-executable/discussions, чтобы помочь нам документировать их.

Комментарии