Skip to main content

NodeJS SEA

NodeJS "单一可执行应用"[^1] 是独立的 CLI 工具,它将打包的脚本嵌入到 NodeJS 二进制文件的特殊独立副本中。

¥NodeJS "Single Executable Applications"[^1] are standalone CLI tools that embed bundled scripts in a special standalone copy of the NodeJS binary.

SheetJS 是一个用于从电子表格读取和写入数据的 JavaScript 库。

¥SheetJS is a JavaScript library for reading and writing data from spreadsheets.

该演示使用 NodeJS SEA 和 SheetJS 创建一个独立的 CLI 工具,用于解析电子表格和生成 CSV 行。

¥This demo uses NodeJS SEA and SheetJS to create a standalone CLI tool for parsing spreadsheets and generating CSV rows.

强烈建议在命令行工具中使用 SheetJS 库的系统上安装 NodeJS。仅当认为需要独立的二进制文件时才应考虑此解决方法。

¥It is strongly recommended to install NodeJS on systems using SheetJS libraries in command-line tools. This workaround should only be considered if a standalone binary is considered desirable.

NodeJS SEA 支持被认为是实验性的。

优秀的开源软件会随着用户测试和报告而不断成长。任何问题都应报告给 NodeJS 单一可执行项目以进行进一步诊断。

¥Great open source software grows with user tests and reports. Any issues should be reported to the NodeJS single-executable project for further diagnosis.

集成详情

¥Integration Details

NodeJS SEA 基本脚本可能需要 SheetJS NodeJS 模块

¥The SheetJS NodeJS module can be required from NodeJS SEA base scripts.

NodeJS SEA 不支持 ECMAScript 模块!

¥NodeJS SEA does not support ECMAScript Modules!

CommonJS 脚本方便地包含在 SheetJS NodeJS 模块包中。

¥A CommonJS script is conveniently included in the SheetJS NodeJS module package.

在较高层次上,单一可执行应用的构建分为四个步骤:

¥At a high level, single-executable applications are constructed in four steps:

  1. 预处理现有 NodeJS 脚本,创建 SEA 包。

    ¥Pre-process an existing NodeJS script, creating a SEA bundle.

  2. 复制 NodeJS 二进制文件并删除所有签名。

    ¥Copy the NodeJS binary and remove any signatures.

  3. 将 SEA 包注入未签名的 NodeJS 二进制文件中。

    ¥Inject the SEA bundle into the unsigned NodeJS binary.

  4. 重新签署二进制文件。

    ¥Re-sign the binary.

macOS 和 Windows 强制实现数字签名。如果签名的程序被修改,两种操作系统都会警告用户。

¥macOS and Windows enforce digital signatures. Both operating systems will warn users if a signed program is modified.

在注入 SEA 包之前应删除现有签名。注入 SEA 包后,二进制文件应该被重新签名。

¥Existing signatures should be removed before injecting the SEA bundle. After injecting the SEA bundle, the binary should be resigned.

脚本要求

¥Script Requirements

专门使用 SheetJS 库和 NodeJS 内置模块的脚本可以使用 NodeJS SEA 进行打包。由于 SEA 打包器的限制,必须手动创建特殊的 require 函数:

¥Scripts that exclusively use SheetJS libraries and NodeJS built-in modules can be bundled using NodeJS SEA. Due to limitations in the SEA bundler, a special require function must be created manually:

const { createRequire } = require('node:module');
require = createRequire(__filename);
const { readFile, utils } = require("xlsx");

例如,以下脚本接受一个命令行参数,使用 SheetJS readFile 方法 [^2] 解析指定文件,使用 sheet_to_csv[^3] 从第一个工作表生成 CSV 文本,然后打印到终端:

¥For example, the following script accepts one command line argument, parses the specified file using the SheetJS readFile method[^2], generates CSV text from the first worksheet using sheet_to_csv[^3], and prints to terminal:

sheet2csv.js
// For NodeJS SEA, the CommonJS `require` must be used
const { createRequire } = require('node:module');
require = createRequire(__filename);
const { readFile, utils } = require("xlsx");

// argv[2] is the first argument to the script
const filename = process.argv[2];

// read file
const wb = readFile(filename);

// generate CSV of first sheet
const ws = wb.Sheets[wb.SheetNames[0]];
const csv = utils.sheet_to_csv(ws);

// print to terminal
console.log(csv);

SEA 打包

¥SEA Bundles

SEA Bundles 是代表脚本和支持库的 blob。

¥SEA Bundles are blobs that represent the script and supporting libraries.

配置

¥Configuration

SEA 配置是使用特殊的 JSON 文件指定的。假设脚本没有打包任何特殊资源,则有两个相关字段:

¥SEA configuration is specified using a special JSON file. Assuming no special assets are bundled with the script, there are two relevant fields:

  • main 是入口脚本的相对路径。

    ¥main is a relative path to the entry script.

  • output 是输出文件的相对路径(通常以 .blob 结尾)

    ¥output is a relative path to the output file (typically ending in .blob)

例如,以下配置指定 sheet2csv.js 作为入口脚本,sheet2csv.blob 作为输出 blob:

¥For example, the following configuration specifies sheet2csv.js as the entry script and sheet2csv.blob as the output blob:

sheet2csv.json
{
"main": "sheet2csv.js",
"output": "sheet2csv.blob"
}

构建

¥Construction

node 程序,带有命令行标志 --experimental-sea-config,将生成一个 SEA 包:

¥The main node program, with the command-line flag --experimental-sea-config, will generate a SEA bundle:

node --experimental-sea-config sheet2csv.json

该打包包将写入 SEA 配置文件的 output 字段中指定的文件。

¥The bundle will be written to the file specified in the output field of the SEA configuration file.

注入

¥Injection

使用特殊的 postject 实用程序将 SEA 包添加到 NodeJS 二进制文件中。具体命令取决于操作系统。

¥A special postject utility is used to add the SEA bundle to the NodeJS binary. The specific command depends on the operating system.

在 macOS 上,假设 NodeJS 二进制文件的副本名为 sheet2csv,SEA 包名为 sheet2csv.blob,则以下命令将注入该包:

¥On macOS, assuming the copy of the NodeJS binary is named sheet2csv and the SEA bundle is named sheet2csv.blob, the following command injects the bundle:

npx -y postject --sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2 --macho-segment-name NODE_SEA sheet2csv NODE_SEA_BLOB sheet2csv.blob

完整示例

¥Complete Example

测试部署

该演示在以下部署中进行了测试:

¥This demo was tested in the following deployments:

架构NodeJS日期
darwin-x6422.2.02024-05-28
darwin-arm22.2.02024-05-29
win10-x6420.12.02024-03-26
win11-x6420.13.12024-05-22
win11-arm20.14.02024-06-11
linux-x6420.11.12024-03-18
linux-arm20.14.02024-06-10

ARM 上的 Windows 上的 NodeJS 使用 X64 兼容层。它不会生成原生 ARM64 二进制文件!

¥NodeJS on Windows on ARM uses the X64 compatibility layer. It does not generate a native ARM64 binary!

  1. 确保已安装 NodeJS 版本 20 或更高版本。

    ¥Ensure NodeJS version 20 or later is installed.

要显示当前版本,请运行以下命令:

¥To display the current version, run the following command:

node --version

主版本号从 v 之后开始,到第一个 . 之前结束

¥The major version number starts after the v and ends before the first .

如果版本号为 19 或更早版本,请先升级 NodeJS,然后再继续。

¥If the version number is 19 or earlier, upgrade NodeJS before proceeding.

项目设置

¥Project Setup

  1. 创建一个新的项目文件夹:

    ¥Create a new project folder:

mkdir sheetjs-sea
cd sheetjs-sea
npm init -y
  1. sheet2csv.js 代码块的内容sheet2csv.js 保存在项目文件夹中。

    ¥Save the contents of the sheet2csv.js code block to sheet2csv.js in the project folder.

  2. 安装 SheetJS 依赖:

    ¥Install the SheetJS dependency:

yarn add https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz

脚本测试

¥Script Test

在构建独立应用之前,应使用本地 NodeJS 平台测试基本脚本。

¥Before building the standalone app, the base script should be tested using the local NodeJS platform.

  1. 下载测试文件 https://xlsx.nodejs.cn/pres.numbers

    ¥Download the test file https://xlsx.nodejs.cn/pres.numbers:

curl -o pres.numbers https://xlsx.nodejs.cn/pres.numbers
  1. 运行脚本并传递 pres.numbers 作为第一个参数:

    ¥Run the script and pass pres.numbers as the first argument:

node sheet2csv.js pres.numbers

该脚本应显示第一张工作表中的 CSV 内容:

¥The script should display CSV contents from the first sheet:

Expected Output
Name,Index
Bill Clinton,42
GeorgeW Bush,43
Barack Obama,44
Donald Trump,45
Joseph Biden,46

SEA 打包

¥SEA Bundle

  1. sheet2csv.json 代码块的内容sheet2csv.json 保存在项目文件夹中。

    ¥Save the contents of the sheet2csv.json code block to sheet2csv.json in the project folder.

  2. 生成 SEA 包:

    ¥Generate the SEA bundle:

node --experimental-sea-config sheet2csv.json

SEA 注入

¥SEA Injection

  1. 创建 NodeJS 二进制文件的本地副本:

    ¥Create a local copy of the NodeJS binary:

cp `which node` sheet2csv
  1. 删除代码签名。

    ¥Remove the code signature.

codesign --remove-signature ./sheet2csv
  1. 注入 SEA 打包包。

    ¥Inject the SEA bundle.

npx -y postject --sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2 --macho-segment-name NODE_SEA sheet2csv NODE_SEA_BLOB sheet2csv.blob
  1. 放弃二进制文件。以下命令执行 macOS 即席签名:

    ¥Resign the binary. The following command performs macOS ad-hoc signing:

codesign -s - ./sheet2csv

独立测试

¥Standalone Test

  1. 运行命令并传递 pres.numbers 作为第一个参数:

    ¥Run the command and pass pres.numbers as the first argument:

./sheet2csv pres.numbers

该程序应显示与脚本相同的 CSV 内容(来自步骤 5)

¥The program should display the same CSV contents as the script (from step 5)

  1. 验证二进制签名:

    ¥Validate the binary signature:

codesign -dv ./sheet2csv

检查输出,以下行确认使用了临时签名:

¥Inspecting the output, the following line confirms ad-hoc signing was used:

Signature=adhoc

[^1]: 请参阅 NodeJS 文档中的 "单一可执行应用"

¥See "Single Executable Applications" in the NodeJS documentation.

[^2]: 见 readFile 于 "读取文件"

¥See readFile in "Reading Files"

[^3]: 见 sheet_to_csv 于 "CSV 和文本"

¥See sheet_to_csv in "CSV and Text"

[^4]: 有关 Windows 开发者中心文档中的 Windows SDK

¥See Windows SDK in the Windows Dev Center documentation.