Node.js v18.20.0 文档


单个可执行应用#

¥Single executable applications

稳定性: 1 - 实验:此功能正在设计中并将更改。

¥Stability: 1 - Experimental: This feature is being designed and will change.

源代码: src/node_sea.cc

此功能允许将 Node.js 应用方便地分发到未安装 Node.js 的系统。

¥This feature allows the distribution of a Node.js application conveniently to a system that does not have Node.js installed.

Node.js 通过允许将 JavaScript 文件注入 node 二进制文件来支持 单个可执行应用 的创建。在启动过程中,程序会检查是否注入了任何东西。如果找到脚本,它将执行其内容。否则 Node.js 会像往常一样运行。

¥Node.js supports the creation of single executable applications by allowing the injection of a JavaScript file into the node binary. During start up, the program checks if anything has been injected. If the script is found, it executes its contents. Otherwise Node.js operates as it normally does.

单个可执行应用功能仅支持运行单个嵌入式 CommonJS 文件。

¥The single executable application feature only supports running a single embedded CommonJS file.

可以使用任何可以将资源注入 node 二进制文件的工具将打包的 JavaScript 文件转换为单个可执行应用。

¥A bundled JavaScript file can be turned into a single executable application with any tool which can inject resources into the node binary.

以下是使用此类工具 postject 创建单个可执行应用的步骤:

¥Here are the steps for creating a single executable application using one such tool, postject:

  1. 创建一个 JavaScript 文件:

    ¥Create a JavaScript file:

    $ echo 'console.log(`Hello, ${process.argv[2]}!`);' > hello.js 
  2. 创建 node 可执行文件的副本并根据需要命名:

    ¥Create a copy of the node executable and name it according to your needs:

    $ cp $(command -v node) hello 
  3. 删除二进制文件的签名:

    ¥Remove the signature of the binary:

    • 在 macOS 上:

      ¥On macOS:

    $ codesign --remove-signature hello 
    • 在 Windows 上(可选):

      ¥On Windows (optional):

    signtool 可以从已安装的 Windows SDK 使用。如果跳过此步骤,请忽略来自 postject 的任何与签名相关的警告。

    ¥signtool can be used from the installed Windows SDK. If this step is skipped, ignore any signature-related warning from postject.

    $ signtool remove /s hello 
  4. 通过使用以下选项运行 postject,将 JavaScript 文件注入到复制的二进制文件中:

    ¥Inject the JavaScript file into the copied binary by running postject with the following options:

    • hello - 在步骤 2 中创建的 node 可执行文件副本的名称。

      ¥hello - The name of the copy of the node executable created in step 2.

    • NODE_JS_CODE - 二进制文件中将存储 JavaScript 文件内容的资源/注释/部分的名称。

      ¥NODE_JS_CODE - The name of the resource / note / section in the binary where the contents of the JavaScript file will be stored.

    • hello.js - 在步骤 1 中创建的 JavaScript 文件的名称。

      ¥hello.js - The name of the JavaScript file created in step 1.

    • --sentinel-fuse NODE_JS_FUSE_fce680ab2cc467b6e072b8b5df1996b2 - Node.js 项目用来检测文件是否被注入的 fuse

      ¥--sentinel-fuse NODE_JS_FUSE_fce680ab2cc467b6e072b8b5df1996b2 - The fuse used by the Node.js project to detect if a file has been injected.

    • --macho-segment-name NODE_JS(仅在 macOS 上需要) - 二进制文件中将存储 JavaScript 文件内容的段的名称。

      ¥--macho-segment-name NODE_JS (only needed on macOS) - The name of the segment in the binary where the contents of the JavaScript file will be stored.

    总而言之,这是每个平台所需的命令:

    ¥To summarize, here is the required command for each platform:

    • 在 macOS 以外的系统上:

      ¥On systems other than macOS:

      $ npx postject hello NODE_JS_CODE hello.js \
          --sentinel-fuse NODE_JS_FUSE_fce680ab2cc467b6e072b8b5df1996b2 
    • 在 macOS 上:

      ¥On macOS:

      $ npx postject hello NODE_JS_CODE hello.js \
          --sentinel-fuse NODE_JS_FUSE_fce680ab2cc467b6e072b8b5df1996b2 \
          --macho-segment-name NODE_JS 
  5. 签署二进制文件:

    ¥Sign the binary:

    • 在 macOS 上:

      ¥On macOS:

    $ codesign --sign - hello 
    • 在 Windows 上(可选):

      ¥On Windows (optional):

    需要有证书才能工作。但是,未签名的二进制文件仍然可以运行。

    ¥A certificate needs to be present for this to work. However, the unsigned binary would still be runnable.

    $ signtool sign /fd SHA256 hello 
  6. 运行二进制文件:

    ¥Run the binary:

    $ ./hello world
    Hello, world! 

注意事项#

¥Notes

注入模块中的 require(id) 不是基于文件的#

¥require(id) in the injected module is not file based

注入模块中的 require() 与未注入模块可用的 require() 不同。除 require.main 外,它也不具有非注入 require() 所具有的任何属性。它只能用于加载内置模块。尝试加载只能在文件系统中找到的模块将引发错误。

¥require() in the injected module is not the same as the require() available to modules that are not injected. It also does not have any of the properties that non-injected require() has except require.main. It can only be used to load built-in modules. Attempting to load a module that can only be found in the file system will throw an error.

用户可以将他们的应用打包到一个独立的 JavaScript 文件中以注入可执行文件,而不是依赖于基于 require() 的文件。这也确保了更具确定性的依赖图。

¥Instead of relying on a file based require(), users can bundle their application into a standalone JavaScript file to inject into the executable. This also ensures a more deterministic dependency graph.

但是,如果仍然需要基于 require() 的文件,也可以实现:

¥However, if a file based require() is still needed, that can also be achieved:

const { createRequire } = require('node:module');
require = createRequire(__filename); 

注入模块中的 __filenamemodule.filename#

¥__filename and module.filename in the injected module

注入模块中的 __filenamemodule.filename 的值等于 process.execPath

¥The values of __filename and module.filename in the injected module are equal to process.execPath.

注入模块中的 __dirname#

¥__dirname in the injected module

注入模块中 __dirname 的值等于 process.execPath 的目录名。

¥The value of __dirname in the injected module is equal to the directory name of process.execPath.

单个可执行应用创建过程#

¥Single executable application creation process

旨在创建单个可执行 Node.js 应用的工具必须将 JavaScript 文件的内容注入到:

¥A tool aiming to create a single executable Node.js application must inject the contents of a JavaScript file into:

  • 如果 node 二进制文件是 PE 文件,则名为 NODE_JS_CODE 的资源

    ¥a resource named NODE_JS_CODE if the node binary is a PE file

  • 如果 node 二进制文件是 Mach-O 文件,则 NODE_JS 段中名为 NODE_JS_CODE 的部分

    ¥a section named NODE_JS_CODE in the NODE_JS segment if the node binary is a Mach-O file

  • 如果 node 二进制文件是 ELF 文件,则名为 NODE_JS_CODE 的注释

    ¥a note named NODE_JS_CODE if the node binary is an ELF file

在二进制文件中搜索 NODE_JS_FUSE_fce680ab2cc467b6e072b8b5df1996b2:0 fuse 字符串并将最后一个字符翻转为 1 以指示已注入资源。

¥Search the binary for the NODE_JS_FUSE_fce680ab2cc467b6e072b8b5df1996b2:0 fuse string and flip the last character to 1 to indicate that a resource has been injected.

平台支持#

¥Platform support

仅在以下平台上的 CI 上定期测试单一可执行支持:

¥Single-executable support is tested regularly on CI only on the following platforms:

这是由于缺乏更好的工具来生成可用于在其他平台上测试此功能的单一可执行文件。

¥This is due to a lack of better tools to generate single-executables that can be used to test this feature on other platforms.

欢迎对其他资源注入工具/工作流程提出建议。请在 https://github.com/nodejs/single-executable/discussions 开始讨论以帮助我们记录它们。

¥Suggestions for other resource injection tools/workflows are welcomed. Please start a discussion at https://github.com/nodejs/single-executable/discussions to help us document them.