hermes
This commit is contained in:
parent
ba3ffafe47
commit
fecf228fed
@ -1006,7 +1006,7 @@ When the app is loaded, the data will be displayed in rows.
|
||||
[^4]: See ["Workbook Object"](/docs/csf/book)
|
||||
[^5]: See ["Workbook Object"](/docs/csf/book)
|
||||
[^6]: See ["Sheet Objects"](/docs/csf/sheet)
|
||||
[^7]: See [`sheet_to_csv` in "Utilities"](/docs/api/utilities/csv#csv-output)
|
||||
[^7]: See [`sheet_to_html` in "Utilities"](/docs/api/utilities/html#html-table-output)
|
||||
[^8]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
|
||||
[^9]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
|
||||
[^10]: See [`!merges` in "Sheet Objects"](/docs/csf/sheet#worksheet-object)
|
||||
|
@ -1,13 +1,11 @@
|
||||
---
|
||||
title: NetSuite
|
||||
sidebar_title: NetSuite
|
||||
title: Spreadsheets in NetSuite SuiteScripts
|
||||
sidebar_label: NetSuite
|
||||
description: Automate the NetSuite ERP platform with SuiteScripts. Effortlessly read and write spreadsheets using SheetJS. Modernize Excel-powered business processes with confidence.
|
||||
pagination_prev: demos/local/index
|
||||
pagination_next: demos/extensions/index
|
||||
---
|
||||
|
||||
# Spreadsheets in NetSuite SuiteScripts
|
||||
|
||||
import current from '/version.js';
|
||||
import CodeBlock from '@theme/CodeBlock';
|
||||
|
||||
@ -29,6 +27,7 @@ This demo was verified by NetSuite consultants in the following deployments:
|
||||
| ScheduledScript | 2.1 | 2023-03-09 |
|
||||
| Restlet | 2.1 | 2023-04-20 |
|
||||
| Suitelet | 2.1 | 2023-07-21 |
|
||||
| MapReduceScript | 2.1 | 2023-07-31 |
|
||||
|
||||
:::
|
||||
|
||||
|
@ -1,13 +1,11 @@
|
||||
---
|
||||
title: C + QuickJS
|
||||
title: Data Processing with QuickJS
|
||||
sidebar_label: C + QuickJS
|
||||
description: Process structured data in C programs. Seamlessly integrate spreadsheets into your program by pairing QuickJS and SheetJS. Supercharge programs with modern data tools.
|
||||
pagination_prev: demos/bigdata/index
|
||||
pagination_next: solutions/input
|
||||
---
|
||||
|
||||
# Data Processing with QuickJS
|
||||
|
||||
import current from '/version.js';
|
||||
import CodeBlock from '@theme/CodeBlock';
|
||||
|
||||
|
@ -1,5 +1,7 @@
|
||||
---
|
||||
title: C++ + Hermes
|
||||
title: Sharing Sheets with Hermes
|
||||
sidebar_label: C++ + Hermes
|
||||
description: Process structured data in C++ programs. Seamlessly integrate spreadsheets into your program by pairing Hermes and SheetJS. Handle the most complex files without breaking a sweat.
|
||||
pagination_prev: demos/bigdata/index
|
||||
pagination_next: solutions/input
|
||||
---
|
||||
@ -7,62 +9,115 @@ pagination_next: solutions/input
|
||||
import current from '/version.js';
|
||||
import CodeBlock from '@theme/CodeBlock';
|
||||
|
||||
Hermes is an embeddable JS engine written in C++. With some light shims, it can
|
||||
run the standalone browser scripts.
|
||||
[Hermes](https://hermesengine.dev/) is an embeddable JS engine written in C++.
|
||||
|
||||
The [Standalone scripts](/docs/getting-started/installation/standalone) can be
|
||||
parsed and evaluated in a Hermes context.
|
||||
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
|
||||
data from spreadsheets.
|
||||
|
||||
:::caution Here be Dragons
|
||||
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"](#integration-example) section includes a complete
|
||||
command-line tool for reading data from files.
|
||||
|
||||
## Integration Details
|
||||
|
||||
:::info pass
|
||||
|
||||
Many Hermes functions are not documented. The explanation was verified against
|
||||
commit `70af78b`.
|
||||
|
||||
:::
|
||||
|
||||
:::warning pass
|
||||
|
||||
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.
|
||||
|
||||
:::
|
||||
|
||||
## Integration Details
|
||||
### Initialize Hermes
|
||||
|
||||
_Initialize Hermes_
|
||||
|
||||
The runtime can be initialized in one line:
|
||||
A Hermes engine instance is created with `facebook::hermes::makeHermesRuntime`:
|
||||
|
||||
```cpp
|
||||
std::unique_ptr<facebook::jsi::Runtime> rt(facebook::hermes::makeHermesRuntime());
|
||||
```
|
||||
|
||||
Hermes does not expose a `console` or `global` variable, but those can be
|
||||
synthesized from JS code in the runtime.
|
||||
_Essential Objects_
|
||||
|
||||
:::note pass
|
||||
Hermes does not expose a `console` or `global` variable, but they can be
|
||||
synthesized from JS code in the runtime:
|
||||
|
||||
The JavaScript code is shown below:
|
||||
- `global` can be obtained from a reference to `this` in an unbound function:
|
||||
|
||||
```js
|
||||
/* create global object */
|
||||
var global = (function(){ return this; }).call(null);
|
||||
```
|
||||
|
||||
- `console.log` can be constructed from the builtin `print` function:
|
||||
|
||||
```js
|
||||
/* create a fake `console` from the hermes `print` builtin */
|
||||
var console = { log: function(x) { print(x); } };
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
The code can be stored in a C string and evaluated after creating a runtime:
|
||||
The code can be stored in a C string and evaluated using `prepareJavascript` to
|
||||
prepare code and `evaluatePreparedJavascript` to evaluate:
|
||||
|
||||
```cpp
|
||||
auto src = std::make_shared<facebook::jsi::StringBuffer>(
|
||||
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);
|
||||
```
|
||||
|
||||
_Load SheetJS Scripts_
|
||||
:::info Exception handling
|
||||
|
||||
Standard C++ exception handling patterns are used in Hermes integration code.
|
||||
The base class for Hermes exceptions is `facebook::jsi::JSIException`:
|
||||
|
||||
```cpp
|
||||
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;
|
||||
}
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
### Load SheetJS Scripts
|
||||
|
||||
[SheetJS Standalone scripts](/docs/getting-started/installation/standalone) can
|
||||
be parsed and evaluated in a Hermes context.
|
||||
|
||||
The main library can be loaded by reading the script from the file system and
|
||||
evaluating in the Hermes context:
|
||||
evaluating in the Hermes context.
|
||||
|
||||
:::tip pass
|
||||
|
||||
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_
|
||||
|
||||
For the purposes of this demo, the standard C `<stdio.h>` methods are used:
|
||||
|
||||
```cpp
|
||||
static char *read_file(const char *filename, size_t *sz) {
|
||||
@ -75,6 +130,17 @@ static char *read_file(const char *filename, size_t *sz) {
|
||||
return buf;
|
||||
}
|
||||
|
||||
// ...
|
||||
/* read SheetJS library from filesystem */
|
||||
size_t sz; char *xlsx_full_min_js = read_file("xlsx.full.min.js", &sz);
|
||||
```
|
||||
|
||||
_Hermes Wrapper_
|
||||
|
||||
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:
|
||||
|
||||
```cpp
|
||||
/* Unfortunately the library provides no C-friendly Buffer classes */
|
||||
class CBuffer : public facebook::jsi::Buffer {
|
||||
public:
|
||||
@ -89,24 +155,67 @@ class CBuffer : public facebook::jsi::Buffer {
|
||||
|
||||
// ...
|
||||
/* load SheetJS library */
|
||||
size_t sz; char *xlsx_full_min_js = read_file("xlsx.full.min.js", &sz);
|
||||
auto src = std::make_shared<CBuffer>(CBuffer((uint8_t *)xlsx_full_min_js, sz));
|
||||
```
|
||||
|
||||
_Evaluating SheetJS Library Code_
|
||||
|
||||
The code wrapper can be "prepared" with `prepareJavascript` and "evaluated" with
|
||||
`evaluatePreparedJavascript`.
|
||||
|
||||
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.
|
||||
|
||||
```cpp
|
||||
auto js = rt->prepareJavaScript(src, std::string("xlsx.full.min.js"));
|
||||
rt->evaluatePreparedJavaScript(js);
|
||||
```
|
||||
|
||||
To confirm the library is loaded, `XLSX.version` can be printed to the console:
|
||||
_Testing_
|
||||
|
||||
If the library is loaded, `XLSX.version` will be a string. This string can be
|
||||
pulled into the main C++ program.
|
||||
|
||||
The `evaluatePreparedJavascript` method returns a `facebook::jsi::Value` object
|
||||
that represents the result:
|
||||
|
||||
```cpp
|
||||
auto src = std::make_shared<facebook::jsi::StringBuffer>(
|
||||
"console.log('SheetJS Library Version: ' + XLSX.version)"
|
||||
);
|
||||
/* 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>"));
|
||||
rt->evaluatePreparedJavaScript(js);
|
||||
facebook::jsi::Value jsver = rt->evaluatePreparedJavaScript(js);
|
||||
```
|
||||
|
||||
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:
|
||||
|
||||
```cpp
|
||||
/* 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
|
||||
|
||||
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.
|
||||
|
||||
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++.
|
||||
|
||||
:::note
|
||||
|
||||
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 Wrapper_
|
||||
|
||||
Hermes supports `ArrayBuffer` but has no simple helper to read raw memory.
|
||||
Libraries are expected to implement `MutableBuffer`:
|
||||
|
||||
@ -122,15 +231,48 @@ class CMutableBuffer : public facebook::jsi::MutableBuffer {
|
||||
uint8_t *buf;
|
||||
size_t sz;
|
||||
};
|
||||
// ...
|
||||
/* load payload as ArrayBuffer */
|
||||
size_t sz; char *data = read_file(argv[1], &sz);
|
||||
auto payload = std::make_shared<CMutableBuffer>(CMutableBuffer((uint8_t *)data, sz));
|
||||
auto ab = facebook::jsi::ArrayBuffer(*rt, payload);
|
||||
```
|
||||
|
||||
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++.
|
||||
A `facebook::jsi::ArrayBuffer` object can be created using the wrapper:
|
||||
|
||||
```cpp
|
||||
/* 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 Operations_
|
||||
|
||||
In this example, the goal is to pull the first worksheet and generate CSV rows.
|
||||
|
||||
`XLSX.read`[^1] parses the `ArrayBuffer` and returns a SheetJS workbook object:
|
||||
|
||||
```js
|
||||
var wb = XLSX.read(buf);
|
||||
```
|
||||
|
||||
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:
|
||||
|
||||
```js
|
||||
var first_sheet_name = wb.SheetNames[0];
|
||||
```
|
||||
|
||||
The `Sheets` property[^3] is an object whose keys are sheet names and whose
|
||||
corresponding values are worksheet objects.
|
||||
|
||||
```js
|
||||
var first_sheet = wb.Sheets[first_sheet_name];
|
||||
```
|
||||
|
||||
The `sheet_to_csv` utility function[^4] generates a CSV string from the sheet:
|
||||
|
||||
```js
|
||||
var csv = XLSX.utils.sheet_to_csv(first_sheet);
|
||||
```
|
||||
|
||||
_C++ integration code_
|
||||
|
||||
:::note pass
|
||||
|
||||
@ -146,24 +288,41 @@ function(buf) {
|
||||
|
||||
:::
|
||||
|
||||
_C++ integration code_
|
||||
The result after evaluating the stub is a `facebook::jsi::Value` object:
|
||||
|
||||
```cpp
|
||||
/* 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>"));
|
||||
auto func = rt->evaluatePreparedJavaScript(js);
|
||||
/* 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);
|
||||
```
|
||||
|
||||
/* call stub function and capture result */
|
||||
auto csv = func.asObject(*rt).asFunction(*rt).call(*rt, ab);
|
||||
To call this function, the opaque `Value` must be converted to a `Function`:
|
||||
|
||||
/* interpret as utf8 and print to stdout */
|
||||
std::string str = csv.getString(*rt).utf8(*rt);
|
||||
```cpp
|
||||
facebook::jsi::Function func = func.asObject(*rt).asFunction(*rt);
|
||||
```
|
||||
|
||||
The `Function` exposes a `call` method to perform the function invocation. The
|
||||
stub accepts an `ArrayBuffer` argument:
|
||||
|
||||
```cpp
|
||||
/* call stub function and capture result */
|
||||
facebook::jsi::Value csv = func.call(*rt, ab);
|
||||
```
|
||||
|
||||
In the same way the library version string was pulled into C++ code, the CSV
|
||||
data can be captured using `getString` and `utf8` methods:
|
||||
|
||||
```cpp
|
||||
/* interpret as utf8 */
|
||||
std::string str = csv.getString(*rt).utf8(*rt);
|
||||
std::cout << str << std::endl;
|
||||
```
|
||||
|
||||
## Complete Example
|
||||
@ -179,7 +338,7 @@ This demo was tested in the following deployments:
|
||||
|
||||
| Architecture | Git Commit | Date |
|
||||
|:-------------|:-----------|:-----------|
|
||||
| `darwin-x64` | `9f8603b` | 2023-07-24 |
|
||||
| `darwin-x64` | `70af78b` | 2023-08-02 |
|
||||
| `darwin-arm` | `869312f` | 2023-06-05 |
|
||||
|
||||
:::
|
||||
@ -304,4 +463,9 @@ ready, it will read the bundled test data and print the contents as CSV.
|
||||
hermes xlsx.hermes.js
|
||||
```
|
||||
|
||||
If successful, the script will print CSV data from the test file
|
||||
If successful, the script will print CSV data from the test file.
|
||||
|
||||
[^1]: See [`read` in "Reading Files"](/docs/api/parse-options)
|
||||
[^2]: See ["Workbook Object"](/docs/csf/book)
|
||||
[^3]: See ["Workbook Object"](/docs/csf/book)
|
||||
[^4]: See [`sheet_to_csv` in "Utilities"](/docs/api/utilities/csv#csv-output)
|
||||
|
@ -37,7 +37,7 @@ var global = (function(){ return this; }).call(null);
|
||||
**Console**
|
||||
|
||||
Some engines do not provide a `console` object. `console.log` can be shimmed
|
||||
using the engine functionality. For example, `hermes` provides `print()`:
|
||||
using the engine functionality. For example, `hermes`[^1] provides `print()`:
|
||||
|
||||
```js
|
||||
var console = { log: function(x) { print(x); } };
|
||||
@ -235,3 +235,7 @@ NodeJS and Deno, Adobe UXP and other platforms.
|
||||
|
||||
This demo has been moved [to a dedicated page](/docs/demos/engines/v8).
|
||||
The demo includes examples in C++ and Rust.
|
||||
|
||||
The ["Python + Pandas" demo](/docs/demos/engines/pandas) uses V8 with Python.
|
||||
|
||||
[^1]: See ["Initialize Hermes"](/docs/demos/engines/hermes#initialize-hermes) in the Hermes demo.
|
@ -1,10 +1,10 @@
|
||||
# Note: The official Hermes documentation includes zero guidance on embedding.
|
||||
# Tested against commit 9f8603b9886c957e0ccead61fe4380616188bbb4
|
||||
# Tested against commit 70af78ba69391645749b40a3674d7321c4d6177a
|
||||
|
||||
.PHONY: doit
|
||||
doit: sheetjs-hermes
|
||||
curl -LO https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js
|
||||
curl -LO https://sheetjs.com/pres.numbers
|
||||
if [ ! -e xlsx.full.min.js ]; then curl -LO https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js; fi
|
||||
if [ ! -e pres.numbers ]; then curl -LO https://sheetjs.com/pres.numbers; fi
|
||||
./sheetjs-hermes pres.numbers
|
||||
|
||||
.PHONY: clean
|
||||
@ -53,5 +53,5 @@ sheetjs-hermes: sheetjs-hermes.cpp init
|
||||
|
||||
.PHONY: init
|
||||
init:
|
||||
if [ ! -e hermes ]; then git clone https://github.com/facebook/hermes.git; cd hermes; git checkout 9f8603b9886c957e0ccead61fe4380616188bbb4; cd ..; fi
|
||||
if [ ! -e hermes ]; then git clone https://github.com/facebook/hermes.git; cd hermes; git checkout 70af78ba69391645749b40a3674d7321c4d6177a; cd ..; fi
|
||||
if [ ! -e build_release ]; then cmake -S hermes -B build_release -G Ninja -DCMAKE_BUILD_TYPE=Release; cmake --build ./build_release; fi
|
||||
|
Loading…
Reference in New Issue
Block a user