Skip to main content

与 Hermes 共享床单

Hermes 是一个用 C++编写的嵌入式 JS 引擎。

¥Hermes is an embeddable JS engine written in C++.

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

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

该演示使用 Hermes 和 SheetJS 从电子表格中提取数据并打印 CSV 行。我们将探讨如何在 Hermes 上下文中加载 SheetJS 并从 C++ 程序处理电子表格。

¥This demo uses Hermes and SheetJS to pull data from a spreadsheet and print CSV rows. We'll explore how to load SheetJS in a Hermes context and process spreadsheets from a C++ program.

"集成示例" 部分包括一个完整的命令行工具,用于从文件中读取数据。

¥The "Integration Example" section includes a complete command-line tool for reading data from files.

集成详情

¥Integration Details

Hermes 的许多功能都没有文档记录。该解释已针对提交 15b323d 进行了验证。

¥Many Hermes functions are not documented. The explanation was verified against commit 15b323d.

Hermes 的主要目标是 React Native。在撰写本文时,还没有关于在 C++ 程序中嵌入 Hermes 引擎的官方文档。

¥The main target for Hermes is React Native. At the time of writing, there was no official documentation for embedding the Hermes engine in C++ programs.

初始化 Hermes

¥Initialize Hermes

使用 facebook::hermes::makeHermesRuntime 创建 Hermes 引擎实例:

¥A Hermes engine instance is created with facebook::hermes::makeHermesRuntime:

std::unique_ptr<facebook::jsi::Runtime> rt(facebook::hermes::makeHermesRuntime());

基本对象

¥Essential Objects

Hermes 不公开 consoleglobal 变量,但它们可以在运行时从 JS 代码合成:

¥Hermes does not expose a console or global variable, but they can be synthesized from JS code in the runtime:

  • global 可以通过未绑定函数中对 this 的引用获得:

    ¥global can be obtained from a reference to this in an unbound function:

/* create global object */
var global = (function(){ return this; }).call(null);
  • console.log 可以由内置 print 函数构造:

    ¥console.log can be constructed from the builtin print function:

/* create a fake `console` from the hermes `print` builtin */
var console = { log: function(x) { print(x); } };

代码可以存储在 C 字符串中,并使用 prepareJavascript 来准备代码并使用 evaluatePreparedJavascript 来评估:

¥The code can be stored in a C string and evaluated using prepareJavascript to prepare code and evaluatePreparedJavascript to evaluate:

const char *init_code =
/* create global object */
"var global = (function(){ return this; }).call(null);"
/* create a fake `console` from the hermes `print` builtin */
"var console = { log: function(x) { print(x); } };"
;
auto src = std::make_shared<facebook::jsi::StringBuffer>(init_code);
auto js = rt->prepareJavaScript(src, std::string("<eval>"));
rt->evaluatePreparedJavaScript(js);
异常处理

Hermes 集成代码中使用标准 C++ 异常处理模式。Hermes 异常的基类是 facebook::jsi::JSIException

¥Standard C++ exception handling patterns are used in Hermes integration code. The base class for Hermes exceptions is facebook::jsi::JSIException:

try {
const char *init_code = "...";
auto src = std::make_shared<facebook::jsi::StringBuffer>(init_code);
auto js = rt->prepareJavaScript(src, std::string("<eval>"));
rt->evaluatePreparedJavaScript(js);
} catch (const facebook::jsi::JSIException &e) {
std::cerr << "JavaScript exception: " << e.what() << std::endl;
return 1;
}

加载 SheetJS 脚本

¥Load SheetJS Scripts

SheetJS 独立脚本 可以在 Hermes 上下文中进行解析和评估。

¥SheetJS Standalone scripts can be parsed and evaluated in a Hermes context.

可以通过从文件系统读取脚本并在 Hermes 上下文中进行评估来加载主库。

¥The main library can be loaded by reading the script from the file system and evaluating in the Hermes context.

有一些非标准技巧可以将整个脚本嵌入到二进制文件中。有诸如 #embed 之类的语言提案(反映了 C23 中的相同功能)。

¥There are nonstandard tricks to embed the entire script in the binary. There are language proposals such as #embed (mirroring the same feature in C23).

为简单起见,示例从文件系统读取脚本文件。

¥For simplicity, the examples read the script file from the filesystem.

从文件系统读取脚本

¥Reading scripts from the filesystem

出于本演示的目的,使用标准 C <stdio.h> 方法:

¥For the purposes of this demo, the standard C <stdio.h> methods are used:

static char *read_file(const char *filename, size_t *sz) {
FILE *f = fopen(filename, "rb");
if(!f) return NULL;
long fsize; { fseek(f, 0, SEEK_END); fsize = ftell(f); fseek(f, 0, SEEK_SET); }
char *buf = (char *)malloc(fsize * sizeof(char));
*sz = fread((void *) buf, 1, fsize, f);
fclose(f);
return buf;
}

// ...
/* read SheetJS library from filesystem */
size_t sz; char *xlsx_full_min_js = read_file("xlsx.full.min.js", &sz);

对于 Windows 应用,字符串必须以 null 结尾:

¥For Windows applications, the string must be null-terminated:

/* Hermes-Windows requires the null terminator */
static char *read_file_null(const char *filename, size_t *sz) {
FILE *f = fopen(filename, "rb");
if(!f) return NULL;
long fsize; { fseek(f, 0, SEEK_END); fsize = ftell(f) + 1; fseek(f, 0, SEEK_SET); }
char *buf = (char *)malloc(fsize * sizeof(char));
*sz = fread((void *) buf, 1, fsize, f);
buf[fsize - 1] = 0;
fclose(f);
return buf;
}

// ...
/* read SheetJS library from filesystem */
size_t sz; char *xlsx_full_min_js = read_file_null("xlsx.full.min.js", &sz);

Hermes 封装纸

¥Hermes Wrapper

Hermes 没有提供一种友好的方式来准备存储在标准堆分配的 C 字符串中的 JavaScript 代码。幸运的是,可以创建一个封装器:

¥Hermes does not provide a friendly way to prepare JavaScript code stored in a standard heap-allocated C string. Fortunately a wrapper can be created:

/* Unfortunately the library provides no C-friendly Buffer classes */
class CBuffer : public facebook::jsi::Buffer {
public:
CBuffer(const uint8_t *data, size_t size) : buf(data), sz(size) {}
size_t size() const override { return sz; }
const uint8_t *data() const override { return buf; }

private:
const uint8_t *buf;
size_t sz;
};

// ...
/* load SheetJS library */
auto src = std::make_shared<CBuffer>(CBuffer((uint8_t *)xlsx_full_min_js, sz));

评估 SheetJS 库代码

¥Evaluating SheetJS Library Code

代码封装器可以是 "prepared" 与 prepareJavascript 以及 "evaluated" 与 evaluatePreparedJavascript

¥The code wrapper can be "prepared" with prepareJavascript and "evaluated" with evaluatePreparedJavascript.

preparedJavascript 的第二个参数是保存源 URL 的 C++ std::string。通常,像 xlsx.full.min.js 这样的名称有助于将 SheetJS 库异常与应用的其他部分区分开来。

¥The second argument to preparedJavascript is a C++ std::string that holds the source URL. Typically a name like xlsx.full.min.js helps distinguish SheetJS library exceptions from other parts of the application.

  auto js = rt->prepareJavaScript(src, std::string("xlsx.full.min.js"));
rt->evaluatePreparedJavaScript(js);

测试

¥Testing

如果加载了库,XLSX.version 将是一个字符串。可以将该字符串拉入主 C++ 程序中。

¥If the library is loaded, XLSX.version will be a string. This string can be pulled into the main C++ program.

evaluatePreparedJavascript 方法返回代表结果的 facebook::jsi::Value 对象:

¥The evaluatePreparedJavascript method returns a facebook::jsi::Value object that represents the result:

/* evaluate XLSX.version and capture the result */
auto src = std::make_shared<facebook::jsi::StringBuffer>("XLSX.version");
auto js = rt->prepareJavaScript(src, std::string("<eval>"));
facebook::jsi::Value jsver = rt->evaluatePreparedJavaScript(js);

getString 方法提取字符串值并返回内部字符串对象 (facebook::jsi::String)。给定该字符串对象,utf8 方法返回可以打印的正确 C++ std::string

¥The getString method extracts the string value and returns an internal string object (facebook::jsi::String). Given that string object, the utf8 method returns a proper C++ std::string that can be printed:

/* pull the version string into C++ code and print */
facebook::jsi::String jsstr = jsver.getString(*rt);
std::string cppver = jsstr.utf8(*rt);
std::cout << "SheetJS version " << cppver << std::endl;

读取文件

¥Reading Files

通常,C++ 代码将读取文件,而 Hermes 会将 JS 引擎中的数据投影为 ArrayBuffer。SheetJS 库可以解析 ArrayBuffer 数据。

¥Typically C++ code will read files and Hermes will project the data in the JS engine as an ArrayBuffer. SheetJS libraries can parse ArrayBuffer data.

标准 SheetJS 操作可以选择第一个工作表并从该工作表生成 CSV 字符串数据。Hermes 提供了将 JS 字符串转换回 std::string 对象的方法,以便在 C++ 中进一步处理。

¥Standard SheetJS operations can pick the first worksheet and generate CSV string data from the worksheet. Hermes provides methods to convert the JS strings back to std::string objects for further processing in C++.

强烈建议创建一个存根函数来在 JS 代码中执行整个工作流程并将最终结果传递回 C++。

¥It is strongly recommended to create a stub function to perform the entire workflow in JS code and pass the final result back to C++.

Hermes 封装纸

¥Hermes Wrapper

Hermes 支持 ArrayBuffer,但没有简单的辅助程序来读取原始内存。库预计实现 MutableBuffer

¥Hermes supports ArrayBuffer but has no simple helper to read raw memory. Libraries are expected to implement MutableBuffer:

/* ArrayBuffer constructor expects MutableBuffer */
class CMutableBuffer : public facebook::jsi::MutableBuffer {
public:
CMutableBuffer(uint8_t *data, size_t size) : buf(data), sz(size) {}
size_t size() const override { return sz; }
uint8_t *data() override { return buf; }

private:
uint8_t *buf;
size_t sz;
};

可以使用封装器创建 facebook::jsi::ArrayBuffer 对象:

¥A facebook::jsi::ArrayBuffer object can be created using the wrapper:

/* load payload as ArrayBuffer */
size_t sz; char *data = read_file("pres.xlsx", &sz);
auto payload = std::make_shared<CMutableBuffer>(CMutableBuffer((uint8_t *)data, sz));
auto ab = facebook::jsi::ArrayBuffer(*rt, payload);

SheetJS 操作

¥SheetJS Operations

在此示例中,目标是提取第一个工作表并生成 CSV 行。

¥In this example, the goal is to pull the first worksheet and generate CSV rows.

XLSX.read[^1] 解析 ArrayBuffer 并返回一个 SheetJS 工作簿对象:

¥XLSX.read[^1] parses the ArrayBuffer and returns a SheetJS workbook object:

var wb = XLSX.read(buf);

SheetNames 属性 [^2] 是工作簿中工作表名称的数组。第一个工作表名称可以通过以下 JS 片段获取:

¥The SheetNames property[^2] is an array of the sheet names in the workbook. The first sheet name can be obtained with the following JS snippet:

var first_sheet_name = wb.SheetNames[0];

Sheets 属性 [^3] 是一个对象,其键是工作表名称,其对应的值是工作表对象。

¥The Sheets property[^3] is an object whose keys are sheet names and whose corresponding values are worksheet objects.

var first_sheet = wb.Sheets[first_sheet_name];

sheet_to_csv 实用函数 [^4] 从工作表生成 CSV 字符串:

¥The sheet_to_csv utility function[^4] generates a CSV string from the sheet:

var csv = XLSX.utils.sheet_to_csv(first_sheet);

C++集成代码

¥C++ integration code

存根函数将传递一个 ArrayBuffer 对象:

¥The stub function will be passed an ArrayBuffer object:

function(buf) {
/* `buf` will be an ArrayBuffer */
var wb = XLSX.read(buf);
return XLSX.utils.sheet_to_csv(wb.Sheets[wb.SheetNames[0]]);
}

评估存根后的结果是 facebook::jsi::Value 对象:

¥The result after evaluating the stub is a facebook::jsi::Value object:

/* define stub function to read and convert first sheet to CSV */
auto src = std::make_shared<facebook::jsi::StringBuffer>(
"(function(buf) {"
"var wb = XLSX.read(buf);"
"return XLSX.utils.sheet_to_csv(wb.Sheets[wb.SheetNames[0]]);"
"})"
);
auto js = rt->prepareJavaScript(src, std::string("<eval>"));
facebook::jsi::Value funcval = rt->evaluatePreparedJavaScript(js);

要调用此函数,必须将不透明的 Value 转换为 Function

¥To call this function, the opaque Value must be converted to a Function:

facebook::jsi::Function func = func.asObject(*rt).asFunction(*rt);

Function 公开了 call 方法来执行函数调用。存根接受 ArrayBuffer 参数:

¥The Function exposes a call method to perform the function invocation. The stub accepts an ArrayBuffer argument:

/* call stub function and capture result */
facebook::jsi::Value csv = func.call(*rt, ab);

以与将库版本字符串提取到 C++ 代码中的方式相同的方式,可以使用 getStringutf8 方法捕获 CSV 数据:

¥In the same way the library version string was pulled into C++ code, the CSV data can be captured using getString and utf8 methods:

/* interpret as utf8 */
std::string str = csv.getString(*rt).utf8(*rt);
std::cout << str << std::endl;

完整示例

¥Complete Example

"集成示例" 涵盖了 C++ 应用中的传统集成,而 "CLI 测试" 使用 hermes CLI 工具演示了其他概念。

¥The "Integration Example" covers a traditional integration in a C++ application, while the "CLI Test" demonstrates other concepts using the hermes CLI tool.

集成示例

¥Integration Example

测试部署

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

¥This demo was tested in the following deployments:

架构Git 提交日期
darwin-x64d070c742024-04-25
darwin-armd070c742024-05-23
linux-x64d217af82024-03-21
linux-armd070c742024-05-25

Hermes 主要源代码树不支持 Windows。为 Windows 版 React Native 提供支持的 hermes-windows 分支确实具有内置支持 [^5]

¥The main Hermes source tree does not have Windows support. The hermes-windows fork, which powers React Native for Windows, does have built-in support[^5]

架构Git 提交日期
win10-x64240573e2024-03-24
win11-arm240573e2024-06-20

"Windows 示例" 覆盖 hermes-windows

¥The "Windows Example" covers hermes-windows.

  1. 安装 dependencies

    ¥Install dependencies

Installation Notes (click to show)

The official guidance[^6] has been verified in macOS and HoloOS (Linux).

On macOS:

brew install icu4c cmake ninja

On HoloOS (and other Arch Linux distros):

sudo pacman -Syu cmake git ninja icu python zip readline

On Debian and Ubuntu:

sudo apt install cmake git ninja-build libicu-dev python zip libreadline-dev

When using virtual machines, Linux builds require at least 8 GB memory.

  1. 建立一个项目目录:

    ¥Make a project directory:

mkdir sheetjs-hermes
cd sheetjs-hermes
  1. 下载 Makefile

    ¥Download the Makefile:

curl -LO https://xlsx.nodejs.cn/hermes/Makefile
  1. 下载 sheetjs-hermes.cpp

    ¥Download sheetjs-hermes.cpp:

curl -LO https://xlsx.nodejs.cn/hermes/sheetjs-hermes.cpp
  1. 构建库(这是 init 目标):

    ¥Build the library (this is the init target):

make init

在某些测试运行中,由于 Ninja 问题,构建失败:

¥In some test runs, the build failed due to Ninja issues:

CMake Error at CMakeLists.txt:64 (project):
Running

'/usr/local/lib/depot_tools/ninja' '--version'

failed with:

depot_tools/ninja.py: Could not find Ninja in the third_party of the current project, nor in your PATH.

这是由于与 depot_tools 附带的 Ninja 版本发生冲突。

¥This is due to a conflict with the Ninja version that ships with depot_tools.

由于 depot_tools 通常添加在系统 PATH 变量中的其他文件夹之前,因此强烈建议重命名 ninja 二进制文件、构建 Hermes 库并恢复 ninja 二进制文件:

¥Since depot_tools typically is added before other folders in the system PATH variable, it is strongly recommended to rename the ninja binary, build the Hermes libraries, and restore the ninja binary:

# Rename `ninja`
mv /usr/local/lib/depot_tools/ninja /usr/local/lib/depot_tools/ninja_tmp
# Build Hermes
make init
# Restore `ninja`
mv /usr/local/lib/depot_tools/ninja_tmp /usr/local/lib/depot_tools/ninja

在某些测试中,构建失败,并显示一条引用丢失标头的消息:

¥In some tests, the build failed with a message referencing a missing header:

hermes/API/hermes/inspector/chrome/tests/SerialExecutor.cpp:34:16: note: ‘std::runtime_error’ is defined in header ‘<stdexcept>’; did you forget to ‘#include <stdexcept>’?

此错误影响 Hermes 官方发布!

¥This error affects the official Hermes releases!

修复方法是在相应的头文件中手动添加 #include 语句(repo 中的 API/hermes/inspector/chrome/tests/SerialExecutor.h):

¥The fix is to manually add a #include statement in the corresponding header file (API/hermes/inspector/chrome/tests/SerialExecutor.h in the repo):

hermes/API/hermes/inspector/chrome/tests/SerialExecutor.h (add highlighted line)
#include <memory>
#include <mutex>
#if !defined(_WINDOWS) && !defined(__EMSCRIPTEN__)
#include <stdexcept>
#include <pthread.h>
#else
#include <thread>
  1. 构建应用:

    ¥Build the application:

make sheetjs-hermes
  1. 下载 SheetJS Standalone 脚本和测试文件。将这两个文件保存在项目目录中:

    ¥Download the SheetJS Standalone script and the test file. Save both files in the project directory:

curl -LO https://cdn.sheetjs.com/xlsx-0.20.3/package/dist/xlsx.full.min.js
curl -LO https://xlsx.nodejs.cn/pres.numbers
  1. libhermeslibjsi 库复制到当前文件夹中:

    ¥Copy the libhermes and libjsi libraries into the current folder:

cp ./build_release/API/hermes/libhermes.so .
cp ./build_release/jsi/libjsi.so .
  1. 运行应用:

    ¥Run the application:

./sheetjs-hermes pres.numbers

如果成功,程序将打印库版本号和第一张表的内容作为 CSV 行。

¥If successful, the program will print the library version number and the contents of the first sheet as CSV rows.

Windows 示例

¥Windows Example

在 ARM64 上,命令必须在 "ARM64 原生工具命令提示符" 中运行。

¥On ARM64, the commands must be run in a "ARM64 Native Tools Command Prompt".

  1. 安装依赖。

    ¥Install dependencies.

Installation Notes (click to show)

The build sequence requires Python, which can be installed from the official Windows installer[^7].

Visual Studio with "Desktop development with C++" workload and Cmake must be installed[^8]. In addition, the following Spectre-mitigated libs must be added:

  • MSVC C++ x64/x86 Spectre-mitigated libs (Latest)
  • C++ ATL for latest build tools with Spectre Mitigations (x86 & x64)
  • C++ MFC for latest build tools with Spectre Mitigations (x86 & x64)

The easiest way to install is to select "Individual components" and search for "spectre latest" (no quotation marks). Pick each option for the relevant CPU.

  1. 设置 depot_tools

    ¥Set up depot_tools.

必须下载 depot_tools.zip 并将其解压到 c:\src\depot_tools\

¥depot_tools.zip must be downloaded and extracted to c:\src\depot_tools\.

该 ZIP 有许多隐藏文件和文件夹(包括 .git),应与普通文件一起提取。

¥This ZIP has a number of hidden files and folders (including .git) which should be extracted along with the normal files.

将路径 c:\src\depot_tools\ 添加到用户 PATH 环境变量中

¥Add the path c:\src\depot_tools\ to the User PATH environment variable

Environment Variable Setup (click to show)

Type env in the search bar and select "Edit the system environment variables".

In the new window, click the "Environment Variables..." button.

In the new window, look for the "User variables" section. Select "Path" in the list and click "Edit".

In the new window, click "New" and type c:\src\depot_tools and press Enter.

Select the row and repeatedly click "Move Up" until it is the first entry.

Click "OK" in each window (3 windows) and restart your computer.

  1. 删除 c:\src\depot_tools\ninja(如果存在),然后下载 官方 Windows 版本,并将 ninja.exe 移动到 c:\src\depot_tools。如果文件夹中存在 ninja.exe,则替换现有程序。

    ¥Delete c:\src\depot_tools\ninja if it exists, then download the official Windows release and move the ninja.exe into c:\src\depot_tools. If a ninja.exe exists in the folder, replace the existing program.

  2. 建立一个项目目录:

    ¥Make a project directory:

mkdir sheetjs-hermes
cd sheetjs-hermes
  1. 克隆 hermes-windows 存储库:

    ¥Clone the hermes-windows repo:

git clone https://github.com/microsoft/hermes-windows
cd hermes-windows
git checkout 240573e
cd ..

如果出现与 SSL 或证书或 CApath 相关的错误,请在 Git 中暂时禁用 SSL:

¥If there are errors related to SSL or certificates or CApath, temporarily disable SSL in Git:

git config --global http.sslVerify false
git clone https://github.com/microsoft/hermes-windows
git config --global http.sslVerify true
  1. 构建库:

    ¥Build the library:

cd hermes-windows
.\.ado\scripts\cibuild.ps1 -AppPlatform win32 -Platform x64 -ToolsPlatform x64
cd ..

该脚本可能会失败并显示以下消息:

¥The script may fail with the message:

无法加载,因为运行脚本在此系统上被禁用

¥cannot be loaded because running scripts is disabled on this system

在 "以管理员身份运行" powershell 窗口中,运行以下命令:

¥In a "Run as Administrator" powershell window, run the following command:

Set-ExecutionPolicy -ExecutionPolicy RemoteSigned

在某些测试运行中,该命令在尝试复制 hermes.exe 时失败:

¥In some test runs, the command failed when trying to copy hermes.exe:

Copy-Item: C:\Users\Me\Documents\hermes-windows\.ado\scripts\cibuild.ps1:331
Line |
331 | Copy-Item "$compilerAndToolsBuildPath\bin\hermes.exe" -Destinatio …
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| Cannot find path 'C:\Users\Me\Documents\hermes-windows\workspace\build\tools\bin\hermes.exe'
| because it does not exist.

首先构建库,嵌入 Hermes 时不需要独立的二进制文件,因此可以安全地忽略错误消息。

¥The libraries are built first and the standalone binary is not needed when embedding Hermes, so the error message can be safely ignored.

  1. 将每个生成的 .lib.dll 文件复制到主文件夹中:

    ¥Copy every generated .lib and .dll file into the main folder:

dir -r -Path .\hermes-windows\workspace\build\win32-x64-debug\ -Filter "*.dll" | Copy-Item -Destination .\
dir -r -Path .\hermes-windows\workspace\build\win32-x64-debug\ -Filter "*.lib" | Copy-Item -Destination .\
  1. 下载 sheetjs-hermes.cpp

    ¥Download sheetjs-hermes.cpp:

curl -o sheetjs-hermesw.cpp https://xlsx.nodejs.cn/hermes/sheetjs-hermesw.cpp
  1. 构建应用:

    ¥Build the application:

cl /MDd sheetjs-hermesw.cpp DbgHelp.lib *.lib /I hermes-windows\API /I hermes-windows\include /I hermes-windows\public\ /I hermes-windows\API\jsi

如果没有找到 cl,则运行 "原生工具命令提示符" 中的命令

¥If cl is not found, run the command in the "Native Tools Command Prompt"

  1. 下载 SheetJS Standalone 脚本和测试文件。将这两个文件保存在项目目录中:

    ¥Download the SheetJS Standalone script and the test file. Save both files in the project directory:

curl -o xlsx.full.min.js https://cdn.sheetjs.com/xlsx-0.20.3/package/dist/xlsx.full.min.js
curl -o pres.numbers https://xlsx.nodejs.cn/pres.numbers
  1. 运行应用:

    ¥Run the application:

.\sheetjs-hermesw.exe pres.numbers

如果成功,程序将打印库版本号和第一张表的内容作为 CSV 行。

¥If successful, the program will print the library version number and the contents of the first sheet as CSV rows.

CLI 测试

¥CLI Test

测试部署

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

¥This demo was tested in the following deployments:

架构Hermes日期
darwin-x640.12.02024-03-13

由于独立二进制文件的限制,该演示将测试文件编码为 Base64 字符串,并将其直接添加到合并脚本中。

¥Due to limitations of the standalone binary, this demo will encode a test file as a Base64 string and directly add it to an amalgamated script.

安装命令行接口

¥Install CLI

  1. 安装 Hermes 命令行工具:

    ¥Install the Hermes command line tools:

npx jsvu hermes@0.12.0

出现提示时,选择适当的操作系统。

¥When prompted, select the appropriate operating system.

  1. 检查安装程序的输出。查找 "安装二进制文件" 行:

    ¥Inspect the output of the installer. Look for "Installing binary" lines:

❯ Extracting…
Installing binary to ~/.jsvu/engines/hermes-0.12.0/hermes-0.12.0…
Installing symlink at ~/.jsvu/bin/hermes-0.12.0 pointing to ~/.jsvu/engines/hermes-0.12.0/hermes-0.12.0…
Installing binary to ~/.jsvu/engines/hermes-0.12.0/hermes-0.12.0-compiler…
Installing symlink at ~/.jsvu/bin/hermes-0.12.0-compiler pointing to ~/.jsvu/engines/hermes-0.12.0/hermes-0.12.0-compiler…

第一 "安装二进制文件" 行提到了 hermes 工具的路径。

¥The first "Installing binary" line mentions the path to the hermes tool.

设置项目

¥Setup Project

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

    ¥Create a new project folder:

mkdir sheetjs-hermes-cli
cd sheetjs-hermes-cli
  1. 将步骤 1 中的二进制文件复制到当前文件夹中。例如,在 macOS 上:

    ¥Copy the binary from Step 1 into the current folder. For example, on macOS:

cp ~/.jsvu/engines/hermes-0.12.0/hermes-0.12.0 .

创建脚本

¥Create Script

  1. 下载 SheetJS Standalone 脚本和测试文件。将这两个文件保存在项目目录中:

    ¥Download the SheetJS Standalone script and the test file. Save both files in the project directory:

curl -LO https://cdn.sheetjs.com/xlsx-0.20.3/package/dist/xlsx.full.min.js
curl -LO https://xlsx.nodejs.cn/pres.numbers
  1. 打包测试文件并创建 payload.js

    ¥Bundle the test file and create payload.js:

node -e "fs.writeFileSync('payload.js', 'var payload = \"' + fs.readFileSync('pres.numbers').toString('base64') + '\";')"
  1. 创建支持脚本:

    ¥Create support scripts:

  • global.js 创建一个 global 变量并定义一个假 console

    ¥global.js creates a global variable and defines a fake console:

global.js
var global = (function(){ return this; }).call(null);
var console = { log: function(x) { print(x); } };
  • hermes.js 将调用 XLSX.readXLSX.utils.sheet_to_csv

    ¥hermes.js will call XLSX.read and XLSX.utils.sheet_to_csv:

hermes.js
var wb = XLSX.read(payload, {type:'base64'});
console.log(XLSX.utils.sheet_to_csv(wb.Sheets[wb.SheetNames[0]]));
  1. 创建合并 sheetjs.hermes.js

    ¥Create the amalgamation sheetjs.hermes.js:

cat global.js xlsx.full.min.js payload.js hermes.js > sheetjs.hermes.js

最终脚本在加载独立库之前定义了 global。准备就绪后,它将读取打包的测试数据并将内容打印为 CSV。

¥The final script defines global before loading the standalone library. Once ready, it will read the bundled test data and print the contents as CSV.

测试

¥Testing

  1. 使用 Hermes 独立二进制文件运行脚本:

    ¥Run the script using the Hermes standalone binary:

./hermes-0.12.0 sheetjs.hermes.js

如果成功,脚本将从测试文件中打印 CSV 数据。

¥If successful, the script will print CSV data from the test file.

[^1]: 见 read 于 "读取文件"

¥See read in "Reading Files"

[^2]: 见 "工作簿对象"

¥See "Workbook Object"

[^3]: 见 "工作簿对象"

¥See "Workbook Object"

[^4]: 见 sheet_to_csv 于 "实用工具"

¥See sheet_to_csv in "Utilities"

[^5]: 请参阅 GitHub 上的 microsoft/hermes-windows

¥See microsoft/hermes-windows on GitHub

[^6]: 请参阅 Hermes 文档中的 "依赖" 于 "构建和运行"

¥See "Dependencies" in "Building and Running" in the Hermes Documentation

[^7]: 请参阅 Python 网站中的 "下载 Python"

¥See "Download Python" in the Python website.

[^8]: 请参阅 Visual Studio 网站 下载链接。

¥See the Visual Studio website for download links.