Node.js v20.20.6 文档


单个可执行应用#>

【Single executable applications】

稳定性: 1.1 - 处于活跃开发中

源代码: 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 支持通过允许注入由 Node.js 准备的 blob 来创建 单个可执行应用,该 blob 可以包含打包的脚本,注入到 node 二进制文件中。在启动期间,程序会检查是否有任何内容被注入。如果找到了 blob,它会执行 blob 中的脚本。否则,Node.js 会像平常一样运行。

【Node.js supports the creation of single executable applications by allowing the injection of a blob prepared by Node.js, which can contain a bundled script, into the node binary. During start up, the program checks if anything has been injected. If the blob is found, it executes the script in the blob. Otherwise Node.js operates as it normally does.】

单个可执行应用功能目前仅支持使用 CommonJS 模块系统运行单个嵌入脚本。

【The single executable application feature currently only supports running a single embedded script using the CommonJS module system.】

用户可以使用 node 可执行文件本身以及任何可以将资源注入二进制文件的工具,从他们打包的脚本创建单个可执行应用。

【Users can create a single executable application from their bundled script with the node binary itself and any tool which can inject resources into the binary.】

以下是使用其中一个工具 后投 创建单个可执行应用的步骤:

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

  1. 创建一个 JavaScript 文件:

    echo 'console.log(`Hello, ${process.argv[2]}!`);' > hello.js 
  2. 创建一个配置文件,用于构建可以注入到单个可执行应用中的二进制数据块(详情见 生成单个可执行准备 blob):

    echo '{ "main": "hello.js", "output": "sea-prep.blob" }' > sea-config.json 
  3. 生成要注入的 blob:

    node --experimental-sea-config sea-config.json 
  4. 创建 node 可执行文件的副本,并根据需要命名:

    • 在非 Windows 系统上:
    cp $(command -v node) hello 
    • 在 Windows 上:
    node -e "require('fs').copyFileSync(process.execPath, 'hello.exe')" 

    .exe 扩展名是必要的。

  5. 移除二进制文件的签名(仅适用于 macOS 和 Windows):

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

    签名工具 可以从已安装的 Windows 软件开发工具包 使用。如果跳过此步骤,请忽略来自 postject 的任何与签名相关的警告。

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

    • hello / hello.exe - 在第4步中创建的 node 可执行文件的副本名称。

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

    • sea-prep.blob - 第1步中创建的 blob 的名称。

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

    • --macho-segment-name NODE_SEA(仅在 macOS 上需要)- 存储 blob 内容的二进制段名称。

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

    • 在 Linux 上:

      npx postject hello NODE_SEA_BLOB sea-prep.blob \
          --sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2 
    • 在 Windows 上 - PowerShell:

      npx postject hello.exe NODE_SEA_BLOB sea-prep.blob `
          --sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2 
    • 在 Windows 上 - 命令提示符:

      npx postject hello.exe NODE_SEA_BLOB sea-prep.blob ^
          --sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2 
    • 在 macOS 上:

      npx postject hello NODE_SEA_BLOB sea-prep.blob \
          --sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2 \
          --macho-segment-name NODE_SEA 
  7. 对二进制文件进行签名(仅限 macOS 和 Windows):

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

    要使其工作,需要提供证书。然而,未签名的二进制文件仍然可以运行。

    signtool sign /fd SHA256 hello.exe 
  8. 运行二进制文件:

    • 在非 Windows 系统上
    $ ./hello world
    Hello, world! 
    • 在 Windows 上
    $ .\hello.exe world
    Hello, world! 

生成单个可执行准备 blob#>

【Generating single executable preparation blobs】

可以使用要用于构建单个可执行文件的 Node.js 二进制文件的 --experimental-sea-config 标志生成注入到应用中的单个可执行文件准备 blob。该标志接收一个 JSON 格式的配置文件路径。如果传入的路径不是绝对路径,Node.js 将使用相对于当前工作目录的路径。

【Single executable preparation blobs that are injected into the application can be generated using the --experimental-sea-config flag of the Node.js binary that will be used to build the single executable. It takes a path to a configuration file in JSON format. If the path passed to it isn't absolute, Node.js will use the path relative to the current working directory.】

该配置当前读取以下顶层字段:

【The configuration currently reads the following top-level fields:】

{
  "main": "/path/to/bundled/script.js",
  "output": "/path/to/write/the/generated/blob.blob",
  "disableExperimentalSEAWarning": true, // Default: false
  "useSnapshot": false,  // Default: false
  "useCodeCache": true, // Default: false
  "assets": {  // Optional
    "a.dat": "/path/to/a.dat",
    "b.txt": "/path/to/b.txt"
  }
} 

如果路径不是绝对路径,Node.js 将使用相对于当前工作目录的路径。用于生成二进制数据的 Node.js 二进制版本必须与将注入二进制数据的版本相同。

【If the paths are not absolute, Node.js will use the path relative to the current working directory. The version of the Node.js binary used to produce the blob must be the same as the one to which the blob will be injected.】

注意:在生成跨平台 SEA(例如,在 darwin-arm64 上为 linux-x64 生成 SEA)时,必须将 useCodeCacheuseSnapshot 设置为 false,以避免生成不兼容的可执行文件。由于代码缓存和快照只能在编译它们的平台上加载,当尝试加载在不同平台上构建的代码缓存或快照时,生成的可执行文件可能会在启动时崩溃。

【Note: When generating cross-platform SEAs (e.g., generating a SEA for linux-x64 on darwin-arm64), useCodeCache and useSnapshot must be set to false to avoid generating incompatible executables. Since code cache and snapshots can only be loaded on the same platform where they are compiled, the generated executable might crash on startup when trying to load code cache or snapshots built on a different platform.】

资源#>

【Assets】

用户可以通过在配置中添加键-路径字典作为 assets 字段来包含资源。在构建时,Node.js 会从指定路径读取资源并将它们打包到准备好的 blob 中。在生成的可执行文件中,用户可以使用 sea.getAsset()sea.getAssetAsBlob() API 检索这些资源。

【Users can include assets by adding a key-path dictionary to the configuration as the assets field. At build time, Node.js would read the assets from the specified paths and bundle them into the preparation blob. In the generated executable, users can retrieve the assets using the sea.getAsset() and sea.getAssetAsBlob() APIs.】

{
  "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"
  }
} 

单一可执行应用可以按如下方式访问资源:

【The single-executable application can access the assets as follows:】

const { getAsset, getAssetAsBlob, getRawAsset } = require('node:sea');
// Returns a copy of the data in an ArrayBuffer.
const image = getAsset('a.jpg');
// Returns a string decoded from the asset as UTF8.
const text = getAsset('b.txt', 'utf8');
// Returns a Blob containing the asset.
const blob = getAssetAsBlob('a.jpg');
// Returns an ArrayBuffer containing the raw asset without copying.
const raw = getRawAsset('a.jpg'); 

有关更多信息,请参阅 sea.getAsset()sea.getAssetAsBlob()sea.getRawAsset() API 的文档。

【See documentation of the sea.getAsset(), sea.getAssetAsBlob() and sea.getRawAsset() APIs for more information.】

启动快照支持#>

【Startup snapshot support】

useSnapshot 字段可以用来启用启动快照支持。在这种情况下,当最终可执行文件启动时,main 脚本不会运行。相反,它会在构建机器上生成单个可执行应用准备 blob 时运行。生成的准备 blob 会包含一个快照,该快照捕获了由 main 脚本初始化的状态。注入准备 blob 的最终可执行文件将在运行时反序列化该快照。

【The useSnapshot field can be used to enable startup snapshot support. In this case the main script would not be when the final executable is launched. Instead, it would be run when the single executable application preparation blob is generated on the building machine. The generated preparation blob would then include a snapshot capturing the states initialized by the main script. The final executable with the preparation blob injected would deserialize the snapshot at run time.】

useSnapshot 为 true 时,主脚本必须调用 v8.startupSnapshot.setDeserializeMainFunction() API 来配置在最终可执行文件被用户启动时需要运行的代码。

【When useSnapshot is true, the main script must invoke the v8.startupSnapshot.setDeserializeMainFunction() API to configure code that needs to be run when the final executable is launched by the users.】

单个可执行应用使用快照的典型模式是:

【The typical pattern for an application to use snapshot in a single executable application is:】

  1. 在构建时,在构建机器上,会运行主脚本以将堆初始化为准备好接受用户输入的状态。该脚本还应配置一个带有 v8.startupSnapshot.setDeserializeMainFunction() 的主函数。这个函数将被编译并序列化到快照中,但在构建时不会被调用。
  2. 在运行时,主函数将在用户机器上已反序列化的堆之上运行,以处理用户输入并生成输出。

启动快照脚本的一般约束也适用于用于为单一可执行应用构建快照的主脚本,主脚本可以使用 v8.startupSnapshot API 来适应这些约束。详见 关于 Node.js 启动快照支持的文档

【The general constraints of the startup snapshot scripts also apply to the main script when it's used to build snapshot for the single executable application, and the main script can use the v8.startupSnapshot API to adapt to these constraints. See documentation about startup snapshot support in Node.js.】

V8 代码缓存支持#>

【V8 code cache support】

当在配置中将 useCodeCache 设置为 true 时,在生成单个可执行文件准备 Blob 的过程中,Node.js 会编译 main 脚本以生成 V8 代码缓存。生成的代码缓存将成为准备 Blob 的一部分,并注入到最终的可执行文件中。当启动单个可执行应用时,Node.js 不会从头编译 main 脚本,而是使用代码缓存来加快编译速度,然后执行脚本,从而提高启动性能。

【When useCodeCache is set to true in the configuration, during the generation of the single executable preparation blob, Node.js will compile the main script to generate the V8 code cache. The generated code cache would be part of the preparation blob and get injected into the final executable. When the single executable application is launched, instead of compiling the main script from scratch, Node.js would use the code cache to speed up the compilation, then execute the script, which would improve the startup performance.】

注意:useCodeCachetrue 时,import() 不起作用。

在注入的主脚本中#>

【In the injected main script】

单个可执行应用 API#>

【Single-executable application API】

node:sea 内置模块允许从嵌入在可执行文件中的 JavaScript 主脚本与单一可执行应用进行交互。

【The node:sea builtin allows interaction with the single-executable application from the JavaScript main script embedded into the executable.】

sea.isSea()#>
  • 返回值:<boolean> 指示此脚本是否在单文件可执行程序中运行。

sea.getAsset(key[, encoding])#>

此方法可用于检索在构建时配置为打包到单一可执行应用中的资源。找不到匹配的资源时会抛出错误。

【This method can be used to retrieve the assets configured to be bundled into the single-executable application at build time. An error is thrown when no matching asset can be found.】

  • key <string> 在单可执行程序配置中由 assets 字段指定的字典中资源的键。
  • encoding <string> 如果指定,资源将作为字符串进行解码。接受 TextDecoder 支持的任何编码。 如果未指定,则返回包含资源副本的 ArrayBuffer
  • 返回: <string> | <ArrayBuffer>

sea.getAssetAsBlob(key[, options])#>

类似于 sea.getAsset(),但返回的是 Blob 中的结果。找不到匹配的资源时会抛出错误。

【Similar to sea.getAsset(), but returns the result in a Blob. An error is thrown when no matching asset can be found.】

  • key <string> 在单一可执行应用配置的 assets 字段中指定的字典中资源的键。
  • options <Object>
    • type <string> Blob 的可选 MIME 类型。
  • 返回: <Blob>

sea.getRawAsset(key)#>

此方法可用于检索在构建时配置为打包到单一可执行应用中的资源。找不到匹配的资源时会抛出错误。

【This method can be used to retrieve the assets configured to be bundled into the single-executable application at build time. An error is thrown when no matching asset can be found.】

sea.getAsset()sea.getAssetAsBlob() 不同,这个方法不会返回副本。相反,它返回打包在可执行文件中的原始资源。

【Unlike sea.getAsset() or sea.getAssetAsBlob(), this method does not return a copy. Instead, it returns the raw asset bundled inside the executable.】

目前,用户应避免写入返回的数组缓冲区。如果注入的部分未标记为可写或未正确对齐,写入返回的数组缓冲区很可能会导致崩溃。

【For now, users should avoid writing to the returned array buffer. If the injected section is not marked as writable or not aligned properly, writes to the returned array buffer is likely to result in a crash.】

  • key <string> 在单文件可执行应用配置中由 assets 字段指定的字典中资源的键。
  • 返回: <ArrayBuffer>

注入的主脚本中的 require(id) 不是基于文件的#>

require(id) in the injected main script is not file based】

require() 在注入的主脚本中与非注入模块可用的 require() 不同。它也没有非注入 require() 的任何属性,除了 require.main。它只能用于加载内置模块。尝试加载只能在文件系统中找到的模块将会抛出错误。

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

【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 main script】

注入的主脚本中 __filenamemodule.filename 的值等于 process.execPath

【The values of __filename and module.filename in the injected main script are equal to process.execPath.】

注入的主脚本中的 __dirname#>

__dirname in the injected main script】

注入的主脚本中 __dirname 的值等于 process.execPath 的目录名称。

【The value of __dirname in the injected main script is equal to the directory name of process.execPath.】

注意事项#>

【Notes】

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

【Single executable application creation process】

旨在创建单个可执行 Node.js 应用的工具必须将使用 --experimental-sea-config 准备的 blob 内容注入到:

【A tool aiming to create a single executable Node.js application must inject the contents of the blob prepared with --experimental-sea-config" into:】

  • 如果 node 二进制文件是 体育 文件,则有一个名为 NODE_SEA_BLOB 的资源
  • 如果 node 二进制文件是 Mach-O 文件,则在 NODE_SEA 段中有一个名为 NODE_SEA_BLOB 的节
  • 如果 node 二进制文件是 精灵 文件,则有一个名为 NODE_SEA_BLOB 的注释

在二进制文件中搜索 NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2:0 保险丝 字符串,并将最后一个字符改为 1,以表示资源已被注入。

【Search the binary for the NODE_SEA_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 发起讨论,以帮助我们记录它们。

Node.js 中文网 - 粤ICP备13048890号