Compare commits
14 Commits
Author | SHA1 | Date | |
---|---|---|---|
3b81f8931b | |||
8c4bd369c4 | |||
418f16a872 | |||
603b49a9fd | |||
89a8f00ff3 | |||
e5a1d470ad | |||
3134922c55 | |||
843441893b | |||
d35203cb2b | |||
d1efa326f6 | |||
93de5bce80 | |||
4ff62b8032 | |||
fd0d5a6ad0 | |||
22d9563f39 |
3
.gitignore
vendored
@ -4,5 +4,4 @@ package-lock.json
|
||||
pnpm-lock.yaml
|
||||
/docs
|
||||
node_modules
|
||||
.idea
|
||||
.vscode
|
||||
.idea
|
@ -352,7 +352,7 @@
|
||||
<Cell><Data ss:Type="String">Python</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String"></Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String"></Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String"></Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String"></Data></Cell>
|
||||
|
@ -118,20 +118,20 @@ importScripts("https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.mi
|
||||
|
||||
### Type Checker
|
||||
|
||||
:::danger VS Code Telemetry and Data Exfiltration
|
||||
:::danger VSCode Telemetry and Data Exfiltration
|
||||
|
||||
The official builds of Visual Studio Code ("VS Code" or "VSCode") embed
|
||||
telemetry and send information to Microsoft servers.
|
||||
The official Microsoft builds of Visual Studio Code ("VSCode") embed telemetry
|
||||
and send information to external servers.
|
||||
|
||||
**[VSCodium](https://vscodium.com/) is a telemetry-free fork of VS Code.**
|
||||
**[VSCodium](https://vscodium.com/) is a telemetry-free fork of VSCode.**
|
||||
|
||||
When writing code that may process personally identifiable information (PII),
|
||||
the SheetJS team strongly encourages building VS Code from source or using IDEs
|
||||
the SheetJS team strongly encourages building VSCode from source or using IDEs
|
||||
that do not exfiltrate data.
|
||||
|
||||
:::
|
||||
|
||||
The type checker integrated in VSCodium and VS Code does not currently provide
|
||||
The type checker integrated in VSCodium and VSCode does not currently provide
|
||||
type hints when using the standalone build. Using the JSDoc `@type` directive
|
||||
coupled with type imports, VSCodium will recognize the types:
|
||||
|
||||
@ -176,7 +176,7 @@ The `.d.ts` file extension must be omitted.
|
||||
|
||||
JSDoc types using the `@import` directive are not supported in `<script>` tags.
|
||||
|
||||
**This is a known bug with VS Code!**
|
||||
**This is a known bug with VSCode!**
|
||||
|
||||
:::
|
||||
|
||||
|
@ -644,7 +644,7 @@ After saving the file, run a local web server in the folder with the HTML file.
|
||||
For example, if NodeJS is installed:
|
||||
|
||||
```bash
|
||||
npx -y http-server .
|
||||
npx http-server .
|
||||
```
|
||||
|
||||
The server process will display a URL (typically `http://127.0.0.1:8080`). Open
|
||||
|
@ -489,20 +489,20 @@ function SheetJSAoAFilled() {
|
||||
### Select Data Rows
|
||||
|
||||
At this point, each data row will have the year in column `A` and dollar value
|
||||
in column `C`. The year (first value in the row) will be between 2007 and 2029.
|
||||
in column `C`. The year (first value in the row) will be between 2007 and 2024.
|
||||
The value (third value) will be positive. The following function tests a row
|
||||
against the requirements:
|
||||
|
||||
```js
|
||||
const is_valid_row = r =>
|
||||
r[0] >= 2007 && r[0] <= 2029 // year (column A) is between 2007 and 2029
|
||||
r[0] >= 2007 && r[0] <= 2024 // year (column A) is between 2007 and 2024
|
||||
&& r[2] > 0; // dollar value (column C) is positive
|
||||
```
|
||||
|
||||
`Array#filter`, using the previous test, can select the matching rows:
|
||||
|
||||
```js
|
||||
const rows = raw_data.filter(r => r[0] >= 2007 && r[0] <= 2029 && r[2] > 0);
|
||||
const rows = raw_data.filter(r => r[0] >= 2007 && r[0] <= 2024 && r[2] > 0);
|
||||
```
|
||||
|
||||
<details>
|
||||
@ -522,7 +522,7 @@ function SheetJSAoAFiltered() {
|
||||
var last_year = 0;
|
||||
raw_data.forEach(r => last_year = r[0] = (r[0] != null ? r[0] : last_year));
|
||||
/* select data rows */
|
||||
const rows = raw_data.filter(r => r[0] >= 2007 && r[0] <= 2029 && r[2] > 0);
|
||||
const rows = raw_data.filter(r => r[0] >= 2007 && r[0] <= 2024 && r[2] > 0);
|
||||
/* display data */
|
||||
setRows(rows);
|
||||
})(); }, []);
|
||||
@ -598,7 +598,7 @@ function SheetJSObjects() {
|
||||
var last_year = 0;
|
||||
raw_data.forEach(r => last_year = r[0] = (r[0] != null ? r[0] : last_year));
|
||||
/* select data rows */
|
||||
const rows = raw_data.filter(r => r[0] >= 2007 && r[0] <= 2029 && r[2] > 0);
|
||||
const rows = raw_data.filter(r => r[0] >= 2007 && r[0] <= 2024 && r[2] > 0);
|
||||
/* generate row objects */
|
||||
const objects = rows.map(r => ({FY: r[0], FQ: r[1], total: r[8]}));
|
||||
/* display data */
|
||||
@ -706,7 +706,7 @@ function StudentAidTotal() {
|
||||
raw_data.forEach(r => last_year = r[0] = (r[0] != null ? r[0] : last_year));
|
||||
|
||||
/* select data rows */
|
||||
const rows = raw_data.filter(r => r[0] >= 2007 && r[0] <= 2029 && r[2] > 0);
|
||||
const rows = raw_data.filter(r => r[0] >= 2007 && r[0] <= 2024 && r[2] > 0);
|
||||
|
||||
/* generate row objects */
|
||||
const objects = rows.map(r => ({FY: r[0], FQ: r[1], total: r[8]}));
|
||||
@ -761,7 +761,7 @@ Save the following script to `SheetJSStandaloneDemo.html`:
|
||||
raw_data.forEach(r => last_year = r[0] = (r[0] != null ? r[0] : last_year));
|
||||
\n\
|
||||
/* select data rows */
|
||||
const rows = raw_data.filter(r => r[0] >= 2007 && r[0] <= 2029 && r[2] > 0);
|
||||
const rows = raw_data.filter(r => r[0] >= 2007 && r[0] <= 2024 && r[2] > 0);
|
||||
\n\
|
||||
/* generate row objects */
|
||||
const objects = rows.map(r => ({FY: r[0], FQ: r[1], total: r[8]}));
|
||||
@ -781,7 +781,7 @@ After saving the file, run a local web server in the folder with the HTML file.
|
||||
For example, if NodeJS is installed:
|
||||
|
||||
```bash
|
||||
npx -y http-server .
|
||||
npx http-server .
|
||||
```
|
||||
|
||||
The server process will display a URL (typically `http://127.0.0.1:8080`). Open
|
||||
@ -827,7 +827,7 @@ const XLSX = require("xlsx");
|
||||
raw_data.forEach(r => last_year = r[0] = (r[0] != null ? r[0] : last_year));
|
||||
|
||||
/* select data rows */
|
||||
const rows = raw_data.filter(r => r[0] >= 2007 && r[0] <= 2029 && r[2] > 0);
|
||||
const rows = raw_data.filter(r => r[0] >= 2007 && r[0] <= 2024 && r[2] > 0);
|
||||
|
||||
/* generate row objects */
|
||||
const objects = rows.map(r => ({FY: r[0], FQ: r[1], total: r[8]}));
|
||||
@ -900,7 +900,7 @@ Save the following script to `SheetJSNW.html`:
|
||||
raw_data.forEach(r => last_year = r[0] = (r[0] != null ? r[0] : last_year));
|
||||
\n\
|
||||
/* select data rows */
|
||||
const rows = raw_data.filter(r => r[0] >= 2007 && r[0] <= 2029 && r[2] > 0);
|
||||
const rows = raw_data.filter(r => r[0] >= 2007 && r[0] <= 2024 && r[2] > 0);
|
||||
\n\
|
||||
/* generate row objects */
|
||||
const objects = rows.map(r => ({FY: r[0], FQ: r[1], total: r[8]}));
|
||||
@ -1001,7 +1001,7 @@ const App = () => {
|
||||
raw_data.forEach(r => last_year = r[0] = (r[0] != null ? r[0] : last_year));
|
||||
|
||||
/* select data rows */
|
||||
const rows = raw_data.filter(r => r[0] >= 2007 && r[0] <= 2029 && r[2] > 0);
|
||||
const rows = raw_data.filter(r => r[0] >= 2007 && r[0] <= 2024 && r[2] > 0);
|
||||
|
||||
/* generate row objects */
|
||||
const objects = rows.map(r => ({FY: r[0], FQ: r[1], total: r[8]}));
|
||||
|
@ -35,7 +35,6 @@ This demo was tested in the following configurations:
|
||||
|
||||
| Platform | Architecture | Date |
|
||||
|:------------------------------------------------------------------|:-------------|:-----------|
|
||||
| NVIDIA RTX 5090 (32 GB VRAM) + Ryzen Z1 Extreme (16 GB RAM) | `win11-x64` | 2025-05-17 |
|
||||
| NVIDIA RTX 4090 (24 GB VRAM) + Ryzen Z1 Extreme (16 GB RAM) | `win11-x64` | 2025-04-17 |
|
||||
| NVIDIA RTX 4090 (24 GB VRAM) + Ryzen Z1 Extreme (16 GB RAM) | `linux-x64` | 2025-01-28 |
|
||||
| AMD RX 7900 XTX (24 GB VRAM) + Ryzen Z1 Extreme (16 GB RAM) | `win11-x64` | 2025-01-12 |
|
||||
|
@ -42,7 +42,7 @@ This demo was tested in the following deployments:
|
||||
|:-------------|:----------------|:-------|:-------|:-----------|
|
||||
| `darwin-x64` | Duktape `2.7.0` | 2.2.3 | 3.13.1 | 2025-03-31 |
|
||||
| `darwin-arm` | Duktape `2.7.0` | 2.2.3 | 3.13.2 | 2025-03-30 |
|
||||
| `win11-x64` | Duktape `2.7.0` | 2.2.3 | 3.11.9 | 2025-04-28 |
|
||||
| `win11-x64` | Duktape `2.7.0` | 2.2.3 | 3.11.8 | 2024-12-21 |
|
||||
| `win11-arm` | Duktape `2.7.0` | 2.2.3 | 3.13.2 | 2025-02-23 |
|
||||
| `linux-x64` | Duktape `2.7.0` | 1.5.3 | 3.11.7 | 2025-01-01 |
|
||||
| `linux-arm` | Duktape `2.7.0` | 1.5.3 | 3.11.2 | 2025-02-16 |
|
||||
@ -203,7 +203,7 @@ DataFrame. The DataFrame will be exported to the binary XLSB spreadsheet format.
|
||||
:::note pass
|
||||
|
||||
The Windows build requires Visual Studio with "Desktop development with C++".
|
||||
**Commands must be run in a "Native Tools Command Prompt" session.**
|
||||
Commands must be run in a "Native Tools Command Prompt" session.
|
||||
|
||||
:::
|
||||
|
||||
@ -215,14 +215,6 @@ python3 -m pip install pandas
|
||||
|
||||
:::info pass
|
||||
|
||||
On Windows, Python may be available as `python.exe`:
|
||||
|
||||
```bash
|
||||
python.exe -m pip install pandas
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
On macOS and Linux, the install command may require root access:
|
||||
|
||||
```bash
|
||||
@ -313,13 +305,15 @@ cd ..
|
||||
</TabItem>
|
||||
<TabItem value="win11-x64" label="Windows">
|
||||
|
||||
- Download and extract the source tarball:
|
||||
- Download and extract the source tarball. Commands must be run in WSL `bash`:
|
||||
|
||||
```bash
|
||||
curl -LO https://duktape.org/duktape-2.7.0.tar.xz
|
||||
tar -xJf duktape-2.7.0.tar.xz
|
||||
```
|
||||
|
||||
(Run `bash`, then run the aforementioned commands, then run `exit` to exit WSL)
|
||||
|
||||
- Enter the source folder:
|
||||
|
||||
```bash
|
||||
@ -478,17 +472,6 @@ def eval_file(ctx, path):
|
||||
python3 SheetJSPandas.py pres.numbers
|
||||
```
|
||||
|
||||
:::info pass
|
||||
|
||||
On Windows, Python may be available as `python.exe`:
|
||||
|
||||
```bash
|
||||
python.exe SheetJSPandas.py pres.numbers
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
|
||||
If successful, the script will display DataFrame metadata:
|
||||
|
||||
```
|
||||
@ -546,7 +529,7 @@ This demo was tested in the following deployments:
|
||||
|:-------------|:----------------|:--------|:-------|:-----------|
|
||||
| `darwin-x64` | Duktape `2.7.0` | 1.26.0 | 3.13.1 | 2025-03-31 |
|
||||
| `darwin-arm` | Duktape `2.7.0` | 1.26.0 | 3.13.2 | 2025-03-30 |
|
||||
| `win11-x64` | Duktape `2.7.0` | 1.28.1 | 3.11.9 | 2025-04-28 |
|
||||
| `win11-x64` | Duktape `2.7.0` | 1.17.1 | 3.11.8 | 2024-12-21 |
|
||||
| `win11-arm` | Duktape `2.7.0` | 1.23.0 | 3.13.2 | 2025-02-23 |
|
||||
| `linux-x64` | Duktape `2.7.0` | 1.18.0 | 3.11.7 | 2025-01-01 |
|
||||
| `linux-arm` | Duktape `2.7.0` | 1.22.0 | 3.11.2 | 2025-02-16 |
|
||||
@ -616,18 +599,10 @@ python3 -m pip install polars
|
||||
|
||||
:::info pass
|
||||
|
||||
On Windows, Python may be available as `python.exe`:
|
||||
|
||||
```bash
|
||||
python.exe -m pip install polars
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
On macOS and Linux, the install command may require root access:
|
||||
|
||||
```bash
|
||||
sudo python3 -m pip install polars
|
||||
sudo python3 -m pip install pandas
|
||||
```
|
||||
|
||||
:::
|
||||
@ -687,16 +662,6 @@ cp ../libduktape.* ../SheetJSPandas.py ../sheetjs.py ../*.js ../*.numbers .
|
||||
python3 SheetJSPandas.py pres.numbers
|
||||
```
|
||||
|
||||
:::info pass
|
||||
|
||||
On Windows, Python may be available as `python.exe`:
|
||||
|
||||
```bash
|
||||
python.exe SheetJSPandas.py pres.numbers
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
:::note pass
|
||||
|
||||
If the virtual environment was configured in the previous step, run:
|
||||
@ -727,7 +692,7 @@ shape: (5, 2)
|
||||
It will also export the DataFrame to `SheetJSPolars.xlsb`. The file can be
|
||||
inspected with a spreadsheet editor that supports XLSB files.
|
||||
|
||||
[^1]: See ["JavaScript Engines"](/docs/demos/engines/) for more examples.
|
||||
[^1]: See ["Other Languages"](/docs/demos/engines/) for more examples.
|
||||
[^2]: See [`ctypes`](https://docs.python.org/3/library/ctypes.html) in the Python documentation.
|
||||
[^3]: See [`read` in "Reading Files"](/docs/api/parse-options)
|
||||
[^4]: See ["Workbook Object"](/docs/csf/book)
|
||||
|
@ -518,7 +518,7 @@ The generated site will be placed in the `dist` folder.
|
||||
9) Start a local web server:
|
||||
|
||||
```bash
|
||||
npx -y http-server dist
|
||||
npx http-server dist
|
||||
```
|
||||
|
||||
Access the displayed URL (typically `http://localhost:8080`) with a web browser
|
||||
@ -665,7 +665,7 @@ The generated site will be placed in the `dist` folder.
|
||||
9) Start a local web server:
|
||||
|
||||
```bash
|
||||
npx -y http-server dist
|
||||
npx http-server dist
|
||||
```
|
||||
|
||||
Access the displayed URL (typically `http://localhost:8080`) with a web browser
|
||||
|
@ -135,7 +135,7 @@ The SheetJS [`read`](/docs/api/parse-options) and [`sheet_to_json`](/docs/api/ut
|
||||
functions simplify state updates. They are best used in the function bodies of
|
||||
`useEffect`[^2] and `useCallback`[^3] hooks.
|
||||
|
||||
A `useEffect` hook can download and update state when the site is loaded:
|
||||
A `useEffect` hook can download and update state when a person loads the site:
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
@ -150,13 +150,12 @@ flowchart LR
|
||||
wb --> |wb.Sheets\nselect sheet| ws
|
||||
ws --> |sheet_to_json\n\n| aoo
|
||||
aoo --> |setPres\nfrom `setState`| state
|
||||
linkStyle 1,2,3 color:blue,stroke:blue;
|
||||
```
|
||||
|
||||
<Tabs groupId="lang">
|
||||
<TabItem name="JS" value="JavaScript">
|
||||
|
||||
```js title="In a useEffect hook, update state with data from a remote workbook"
|
||||
```js
|
||||
import { useEffect } from 'react';
|
||||
import { read, utils } from 'xlsx';
|
||||
|
||||
@ -183,7 +182,7 @@ useEffect(() => { (async() => {
|
||||
</TabItem>
|
||||
<TabItem name="TS" value="TypeScript" default>
|
||||
|
||||
```ts title="In a useEffect hook, update state with data from a remote workbook"
|
||||
```ts
|
||||
import { useEffect } from 'react';
|
||||
import { read, utils } from 'xlsx';
|
||||
|
||||
@ -253,10 +252,9 @@ flowchart LR
|
||||
state --> |json_to_sheet\n\n| ws
|
||||
ws --> |book_new\nbook_append_sheet| wb
|
||||
wb --> |writeFile\n\n| file
|
||||
linkStyle 0,1,2 color:blue,stroke:blue;
|
||||
```
|
||||
|
||||
```ts title="Export data from state to a new XLSX workbook"
|
||||
```ts
|
||||
import { useCallback } from 'react';
|
||||
import { utils, writeFile } from 'xlsx';
|
||||
|
||||
@ -334,7 +332,7 @@ This demo was tested in the following environments:
|
||||
|
||||
| ReactJS | ViteJS | Date |
|
||||
|:---------|:--------|:-----------|
|
||||
| `19.1.0` | `6.3.5` | 2025-05-11 |
|
||||
| `18.3.1` | `6.0.1` | 2024-12-12 |
|
||||
|
||||
:::
|
||||
|
||||
@ -371,7 +369,7 @@ The generated site will be placed in the `dist` folder.
|
||||
6) Start a local web server:
|
||||
|
||||
```bash
|
||||
npx -y http-server dist
|
||||
npx http-server dist
|
||||
```
|
||||
|
||||
Access the displayed URL (typically `http://localhost:8080`) with a web browser
|
||||
@ -386,14 +384,23 @@ This demo was tested in the following environments:
|
||||
|
||||
| ReactJS | CRA | Date |
|
||||
|:---------|:--------|:-----------|
|
||||
| `19.1.0` | `5.1.0` | 2025-05-11 |
|
||||
| `18.2.0` | `5.0.1` | 2024-12-12 |
|
||||
|
||||
:::
|
||||
|
||||
:::caution pass
|
||||
|
||||
CRA has known compatibility issues with React 19[^5]. CRA no longer receives
|
||||
updates and the ReactJS docs no longer recommend using CRA. For new projects, it
|
||||
is strongly recommended to use ViteJS with the `react` or `react-ts` templates.
|
||||
|
||||
:::
|
||||
|
||||
|
||||
1) Create a new site:
|
||||
|
||||
```bash
|
||||
npx -y create-react-app@5.1.0 sheetjs-react
|
||||
npx -y create-react-app@5.0.1 --scripts-version=5.0.1 sheetjs-react
|
||||
```
|
||||
|
||||
2) Install the SheetJS dependency and start the dev server:
|
||||
@ -401,7 +408,7 @@ npx -y create-react-app@5.1.0 sheetjs-react
|
||||
<CodeBlock language="bash">{`\
|
||||
cd sheetjs-react
|
||||
npm i
|
||||
npm i react@19.1.0 react-dom@19.1.0 web-vitals --save --save-exact
|
||||
npm i react@18.2.0 react-dom@18.2.0 web-vitals --save --save-exact
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz
|
||||
npm start`}
|
||||
</CodeBlock>
|
||||
@ -424,7 +431,7 @@ The generated site will be placed in the `build` folder.
|
||||
6) Start a local web server:
|
||||
|
||||
```bash
|
||||
npx -y http-server build
|
||||
npx http-server build
|
||||
```
|
||||
|
||||
Access the displayed URL (typically `http://localhost:8080`) with a web browser
|
||||
@ -439,7 +446,7 @@ This demo was tested in the following environments:
|
||||
|
||||
| ReactJS | NextJS | Date |
|
||||
|:---------|:---------|:-----------|
|
||||
| `19.1.0` | `15.3.2` | 2025-05-11 |
|
||||
| `19.0.0` | `15.1.0` | 2024-12-13 |
|
||||
|
||||
:::
|
||||
|
||||
@ -560,7 +567,7 @@ This demo was tested in the following environments:
|
||||
|
||||
| Preact | ViteJS | Date |
|
||||
|:----------|:----------|:-----------|
|
||||
| `10.26.6` | `5.4.19` | 2025-05-11 |
|
||||
| `10.22.1` | `5.3.3` | 2024-12-17 |
|
||||
|
||||
:::
|
||||
|
||||
@ -571,12 +578,7 @@ npm init preact sheetjs-preact
|
||||
```
|
||||
|
||||
This will initiate the project creation process. **Follow the on-screen prompts and
|
||||
press Enter to accept the default options:**
|
||||
|
||||
- `Project language:` JavaScript
|
||||
- `Use router?` No
|
||||
- `Prerender app (SSG)?` No
|
||||
- `Use ESLint?` No
|
||||
press Enter to accept the default options.**
|
||||
|
||||
2) Install the SheetJS dependency and start the dev server:
|
||||
|
||||
@ -618,7 +620,7 @@ The generated site will be placed in the `dist` folder.
|
||||
7) Start a local web server:
|
||||
|
||||
```bash
|
||||
npx -y http-server dist
|
||||
npx http-server dist
|
||||
```
|
||||
|
||||
Access the displayed URL (typically `http://localhost:8080`) with a web browser
|
||||
@ -639,149 +641,10 @@ The main disadvantage of the Array of Objects approach is the specific nature
|
||||
of the columns. For more general use, passing around an Array of Arrays works.
|
||||
However, this does not handle merge cells[^6] well!
|
||||
|
||||
HTML Tables support elements with `rowspan` and `colspan` attributes.
|
||||
|
||||
#### State
|
||||
|
||||
The state will be the serialized HTML string:
|
||||
|
||||
<Tabs groupId="lang">
|
||||
<TabItem name="JS" value="JavaScript">
|
||||
|
||||
```ts
|
||||
import { useState } from 'react';
|
||||
|
||||
/* the component state is a string */
|
||||
const [__html, setHtml] = useState("");
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem name="TS" value="TypeScript" default>
|
||||
|
||||
```ts
|
||||
import { useState } from 'react';
|
||||
|
||||
/* the component state is a string */
|
||||
const [__html, setHtml] = useState<string>("");
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
:::info Use of the variable name `__html`
|
||||
|
||||
Examples use the name `__html` due to the design of `dangerouslySetInnerHTML`.
|
||||
|
||||
`dangerouslySetInnerHTML` expects objects of the form `{ __html: "html code" }`.
|
||||
|
||||
For example, the following snippet assumes `html` is the variable name:
|
||||
|
||||
```jsx
|
||||
<div ref={tbl} dangerouslySetInnerHTML={{ __html: html }} />
|
||||
```
|
||||
|
||||
By using the name `__html`, the ES6 shorthand syntax simplifies the code:
|
||||
|
||||
```jsx
|
||||
<div ref={tbl} dangerouslySetInnerHTML={{ __html }} />
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
#### Updating State
|
||||
|
||||
The [`sheet_to_html`](/docs/api/utilities/html#html-table-output) function
|
||||
generates HTML that is aware of merges and other worksheet features.
|
||||
|
||||
A `useEffect` hook can download and update state when the site is loaded:
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
url[(Remote\nFile)]
|
||||
ab[(Data\nArrayBuffer)]
|
||||
wb(SheetJS\nWorkbook)
|
||||
ws(SheetJS\nWorksheet)
|
||||
html(HTML\nTABLE)
|
||||
state((component\nstate))
|
||||
url --> |fetch\n\n| ab
|
||||
ab --> |read\n\n| wb
|
||||
wb --> |wb.Sheets\nselect sheet| ws
|
||||
ws --> |sheet_to_html\n\n| html
|
||||
html --> |setHtml\nfrom `setState`| state
|
||||
linkStyle 1,2,3 color:blue,stroke:blue;
|
||||
```
|
||||
|
||||
```js title="In a useEffect hook, update state with HTML generated from a remote workbook"
|
||||
import { useEffect } from 'react';
|
||||
import { read, utils } from 'xlsx';
|
||||
|
||||
/* Fetch and update the state once */
|
||||
useEffect(() => { (async() => {
|
||||
/* Download from https://docs.sheetjs.com/pres.numbers */
|
||||
const f = await fetch("https://docs.sheetjs.com/pres.numbers");
|
||||
const ab = await f.arrayBuffer();
|
||||
|
||||
// highlight-start
|
||||
/* parse */
|
||||
const wb = read(ab);
|
||||
|
||||
/* generate HTML TABLE from first worksheet */
|
||||
const ws = wb.Sheets[wb.SheetNames[0]]; // get the first worksheet
|
||||
const data = utils.sheet_to_html(ws); // generate objects
|
||||
|
||||
/* update state */
|
||||
setHtml(data); // update state
|
||||
// highlight-end
|
||||
})(); }, []);
|
||||
```
|
||||
|
||||
#### Rendering Data
|
||||
|
||||
ReactJS `dangerouslySetInnerHTML`[^7] prop allows code to set the `innerHTML`
|
||||
attribute, effectively inserting the code into the page.
|
||||
|
||||
It is strongly recommended to set the `innerHTML` of a parent `DIV` container.
|
||||
By attaching a `ref`, callbacks will be able to access the live `TABLE` element.
|
||||
|
||||
```jsx title="Example JSX for displaying HTML TABLE code"
|
||||
<div ref={tbl} dangerouslySetInnerHTML={{ __html }} />
|
||||
```
|
||||
|
||||
#### Exporting Data
|
||||
|
||||
The [`writeFile`](/docs/api/write-options) and [`table_to_book`](/docs/api/utilities/html#html-table-input)
|
||||
functions simplify exporting data. They are best used in the function bodies of
|
||||
`useCallback`[^4] hooks attached to button or other elements.
|
||||
|
||||
A callback can generate a local file when a user clicks a button:
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
state((component\nstate))
|
||||
wb(SheetJS\nWorkbook)
|
||||
file[(XLSX\nexport)]
|
||||
state --> |table_to_book\n\n| wb
|
||||
wb --> |writeFile\n\n| file
|
||||
linkStyle 0,1 color:blue,stroke:blue;
|
||||
```
|
||||
|
||||
```ts title="Export data from HTML TABLE element to a new XLSX workbook"
|
||||
import { useCallback } from 'react';
|
||||
import { utils, writeFile } from 'xlsx';
|
||||
|
||||
/* get data from live HTML TABLE and export to XLSX */
|
||||
const exportFile = useCallback(() => {
|
||||
/* get live reference to HTML TABLE element */
|
||||
const elt = tbl.current.getElementsByTagName("TABLE")[0];
|
||||
/* generate workbook from element */
|
||||
// highlight-next-line
|
||||
const wb = utils.table_to_book(elt);
|
||||
/* export to XLSX */
|
||||
writeFile(wb, "SheetJSReactAoO.xlsx");
|
||||
}, [pres]);
|
||||
```
|
||||
|
||||
#### Complete Component
|
||||
generates HTML that is aware of merges and other worksheet features. ReactJS
|
||||
`dangerouslySetInnerHTML`[^7] prop allows code to set the `innerHTML` attribute,
|
||||
effectively inserting the code into the page.
|
||||
|
||||
In this example, the component attaches a `ref` to the `DIV` container. During
|
||||
export, the first `TABLE` child element can be parsed with [`table_to_book`](/docs/api/utilities/html#html-table-input) to
|
||||
@ -838,7 +701,7 @@ This demo was tested in the following environments:
|
||||
|
||||
| ReactJS | ViteJS | Date |
|
||||
|:---------|:--------|:-----------|
|
||||
| `19.1.0` | `6.3.5` | 2025-05-11 |
|
||||
| `18.3.1` | `6.0.1` | 2024-12-13 |
|
||||
|
||||
:::
|
||||
|
||||
@ -875,7 +738,7 @@ The generated site will be placed in the `dist` folder.
|
||||
6) Start a local web server:
|
||||
|
||||
```bash
|
||||
npx -y http-server dist
|
||||
npx http-server dist
|
||||
```
|
||||
|
||||
Access the displayed URL (typically `http://localhost:8080`) with a web browser
|
||||
@ -890,14 +753,22 @@ This demo was tested in the following environments:
|
||||
|
||||
| ReactJS | CRA | Date |
|
||||
|:---------|:--------|:-----------|
|
||||
| `19.1.0` | `5.1.0` | 2025-05-11 |
|
||||
| `18.2.0` | `5.0.1` | 2024-12-31 |
|
||||
|
||||
:::
|
||||
|
||||
:::caution pass
|
||||
|
||||
CRA has known compatibility issues with React 19[^5]. CRA no longer receives
|
||||
updates and the ReactJS docs no longer recommend using CRA. For new projects, it
|
||||
is strongly recommended to use ViteJS with the `react` or `react-ts` templates.
|
||||
|
||||
:::
|
||||
|
||||
1) Create a new site:
|
||||
|
||||
```bash
|
||||
npx -y create-react-app@5.1.0 sheetjs-react
|
||||
npx -y create-react-app@5.0.1 --scripts-version=5.0.1 sheetjs-react
|
||||
```
|
||||
|
||||
2) Install the SheetJS dependency and start the dev server:
|
||||
@ -905,7 +776,7 @@ npx -y create-react-app@5.1.0 sheetjs-react
|
||||
<CodeBlock language="bash">{`\
|
||||
cd sheetjs-react
|
||||
npm i
|
||||
npm i react@19.1.0 react-dom@19.1.0 web-vitals --save --save-exact
|
||||
npm i react@18.2.0 react-dom@18.2.0 web-vitals --save --save-exact
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz
|
||||
npm start`}
|
||||
</CodeBlock>
|
||||
@ -928,7 +799,7 @@ The generated site will be placed in the `build` folder.
|
||||
6) Start a local web server:
|
||||
|
||||
```bash
|
||||
npx -y http-server build
|
||||
npx http-server build
|
||||
```
|
||||
|
||||
Access the displayed URL (typically `http://localhost:8080`) with a web browser
|
||||
@ -941,9 +812,9 @@ and test the page.
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| Preact | ViteJS | Date |
|
||||
|:----------|:---------|:-----------|
|
||||
| `10.26.6` | `5.4.19` | 2025-05-11 |
|
||||
| Preact | ViteJS | Date |
|
||||
|:----------|:--------|:-----------|
|
||||
| `10.22.1` | `5.3.3` | 2024-12-17 |
|
||||
|
||||
:::
|
||||
|
||||
@ -952,15 +823,11 @@ npm init preact sheetjs-preact
|
||||
```
|
||||
|
||||
This will initiate the project creation process. **Follow the on-screen prompts and
|
||||
press Enter to accept the default options:**
|
||||
|
||||
- `Project language:` JavaScript
|
||||
- `Use router?` No
|
||||
- `Prerender app (SSG)?` No
|
||||
- `Use ESLint?` No
|
||||
press Enter to accept the default options.**
|
||||
|
||||
2) Install the SheetJS dependency and start the dev server:
|
||||
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
cd sheetjs-preact
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz
|
||||
@ -999,7 +866,7 @@ The generated site will be placed in the `dist` folder.
|
||||
7) Start a local web server:
|
||||
|
||||
```bash
|
||||
npx -y http-server dist
|
||||
npx http-server dist
|
||||
```
|
||||
|
||||
Access the displayed URL (typically `http://localhost:8080`) with a web browser
|
||||
@ -1046,8 +913,8 @@ const columns = Array.from({ length: range.e.c + 1 }, (_, i) => ({
|
||||
|
||||
## Legacy Deployments
|
||||
|
||||
[SheetJS Standalone Scripts](/docs/getting-started/installation/standalone) use
|
||||
simple `SCRIPT` tags and work with legacy deployments that do not use a bundler.
|
||||
[The Standalone Scripts](/docs/getting-started/installation/standalone) play nice
|
||||
with legacy deployments that do not use a bundler.
|
||||
|
||||
[The legacy demo](pathname:///react/index.html) shows a simple ReactJS component
|
||||
transpiled in the browser using Babel standalone library.
|
||||
|
@ -397,7 +397,7 @@ The generated site will be placed in the `dist` folder.
|
||||
6) Start a local web server:
|
||||
|
||||
```bash
|
||||
npx -y http-server dist
|
||||
npx http-server dist
|
||||
```
|
||||
|
||||
Access the displayed URL (typically `http://localhost:8080`) with a web browser
|
||||
@ -449,7 +449,7 @@ The generated site will be placed in the `dist` folder.
|
||||
6) Start a local web server:
|
||||
|
||||
```bash
|
||||
npx -y http-server .output/public/
|
||||
npx http-server .output/public/
|
||||
```
|
||||
|
||||
Access the displayed URL (typically `http://localhost:8080`) with a web browser
|
||||
@ -558,7 +558,7 @@ The generated site will be placed in the `dist` folder.
|
||||
6) Start a local web server:
|
||||
|
||||
```bash
|
||||
npx -y http-server dist
|
||||
npx http-server dist
|
||||
```
|
||||
|
||||
Access the displayed URL (typically `http://localhost:8080`) with a web browser
|
||||
@ -610,7 +610,7 @@ The generated site will be placed in the `dist` folder.
|
||||
6) Start a local web server:
|
||||
|
||||
```bash
|
||||
npx -y http-server .output/public/
|
||||
npx http-server .output/public/
|
||||
```
|
||||
|
||||
Access the displayed URL (typically `http://localhost:8080`) with a web browser
|
||||
|
@ -91,7 +91,7 @@ in the ["API Reference"](/docs/api/) section of the documentation.
|
||||
|
||||
<!-- The SheetJS Standalone script must be loaded before the UI5 bootstrap -->
|
||||
<script src="https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js"></script>
|
||||
|
||||
|
||||
<!-- UI5 bootstrap script -->
|
||||
<script
|
||||
id="sap-ui-bootstrap"
|
||||
@ -441,7 +441,7 @@ generated. The `dist` folder in this demo can be deployed on a static host.
|
||||
9) Start a local web server:
|
||||
|
||||
```bash
|
||||
npx -y http-server dist
|
||||
npx http-server dist
|
||||
```
|
||||
|
||||
Access the displayed URL (typically `http://localhost:8080`) with a web browser
|
||||
@ -611,7 +611,7 @@ generated. The `dist` folder in this demo can be deployed on a static host.
|
||||
9) Start a local web server:
|
||||
|
||||
```bash
|
||||
npx -y http-server dist
|
||||
npx http-server dist
|
||||
```
|
||||
|
||||
Access the displayed URL (typically `http://localhost:8080`) with a web browser
|
||||
|
@ -173,7 +173,7 @@ npx vite build
|
||||
7) Verify the new site by running a local web server in the `dist` folder:
|
||||
|
||||
```bash
|
||||
npx -y http-server dist
|
||||
npx http-server dist
|
||||
```
|
||||
|
||||
8) Access the displayed URL (typically `http://localhost:8080`) in a web browser
|
||||
|
@ -145,7 +145,7 @@ npx -y esbuild@0.19.8 esbrowser.js --bundle --outfile=esb.browser.js
|
||||
5) Start a local HTTP server:
|
||||
|
||||
```bash
|
||||
npx -y http-server .
|
||||
npx http-server .
|
||||
```
|
||||
|
||||
Access the displayed URL (typically `http://localhost:8080`) with a web browser.
|
||||
|
@ -322,7 +322,7 @@ npx webpack --mode=production
|
||||
6) Start a local HTTP server:
|
||||
|
||||
```bash
|
||||
npx -y http-server .
|
||||
npx http-server .
|
||||
```
|
||||
|
||||
7) Load the displayed URL (typically `http://localhost:8080/`) in a web browser.
|
||||
|
@ -177,7 +177,7 @@ npm install --save browserify@3.46.1
|
||||
5) Start a local HTTP server:
|
||||
|
||||
```bash
|
||||
npx -y http-server .
|
||||
npx http-server .
|
||||
```
|
||||
|
||||
6) Load the displayed URL (typically `http://localhost:8080/`) in a web browser.
|
||||
|
@ -209,7 +209,7 @@ uses normal functions and traditional Promise chains.
|
||||
3) Start a local HTTP server:
|
||||
|
||||
```bash
|
||||
npx -y http-server .
|
||||
npx http-server .
|
||||
```
|
||||
|
||||
4) Load the displayed URL (typically `http://localhost:8080/`) in a web browser.
|
||||
|
@ -169,7 +169,7 @@ This step will create `bundle.js`
|
||||
5) Start a local HTTP server:
|
||||
|
||||
```bash
|
||||
npx -y http-server .
|
||||
npx http-server .
|
||||
```
|
||||
|
||||
Access the displayed URL (typically `http://localhost:8080/`) in a web browser.
|
||||
|
@ -34,8 +34,8 @@ This demo was tested in the following environments:
|
||||
|
||||
| Version | Date |
|
||||
|:---------|:-----------|
|
||||
| `2.14.4` | 2025-05-07 |
|
||||
| `1.12.3` | 2025-05-07 |
|
||||
| `2.13.3` | 2024-12-31 |
|
||||
| `1.12.3` | 2024-12-31 |
|
||||
|
||||
:::
|
||||
|
||||
@ -239,7 +239,7 @@ The production site will be stored in the `dist` folder
|
||||
7) Start a local web server and serve the `dist` folder:
|
||||
|
||||
```bash
|
||||
npx -y http-server dist
|
||||
npx http-server dist
|
||||
```
|
||||
|
||||
Access the displayed URL (typically `http://localhost:8080/`) in a web browser.
|
||||
|
@ -195,7 +195,7 @@ This command will create the script `lib/web.js`
|
||||
6) Start a local HTTP server, then go to `http://localhost:8080/`
|
||||
|
||||
```bash
|
||||
npx -y http-server .
|
||||
npx http-server .
|
||||
```
|
||||
|
||||
Click on "Click here to export" to generate a file.
|
||||
|
@ -164,7 +164,7 @@ npx snowpack@3.8.8 build
|
||||
5) Start a local HTTP server:
|
||||
|
||||
```bash
|
||||
npx -y http-server build/
|
||||
npx http-server build/
|
||||
```
|
||||
|
||||
6) Open a web browser to the displayed URL (typically `http://localhost:8080/`).
|
||||
@ -290,7 +290,7 @@ npx wmr@3.8.0 build
|
||||
5) Start a local HTTP server:
|
||||
|
||||
```bash
|
||||
npx -y http-server dist/
|
||||
npx http-server dist/
|
||||
```
|
||||
|
||||
6) Open a web browser to the displayed URL (typically `http://localhost:8080/`).
|
||||
|
@ -36,13 +36,12 @@ the file can be downloaded or previewed in the browser.
|
||||
|
||||
This demo was tested in the following deployments:
|
||||
|
||||
| Platform | Version | Date |
|
||||
|:--------------|:---------|:-----------|
|
||||
| Chromium 136 | `1.9.0` | 2025-05-07 |
|
||||
| Safari 17.5 | `1.9.0` | 2025-05-07 |
|
||||
| Konqueror 22 | `1.9.0` | 2025-05-07 |
|
||||
| NodeJS 24.0.0 | `1.11.0` | 2025-05-07 |
|
||||
| BunJS 1.2.10 | `1.11.0` | 2025-05-07 |
|
||||
| Platform | Version | Date |
|
||||
|:-------------|:---------|:-----------|
|
||||
| Chromium 131 | `1.9.0` | 2024-12-22 |
|
||||
| Konqueror 22 | `1.9.0` | 2025-04-23 |
|
||||
| NodeJS 20 | `1.10.0` | 2024-12-22 |
|
||||
| BunJS 1.1 | `1.10.0` | 2024-12-22 |
|
||||
|
||||
:::
|
||||
|
||||
|
@ -377,7 +377,7 @@ This demo was tested in the following deployments:
|
||||
|:-------------|:--------|:-------|:-----------|
|
||||
| `darwin-x64` | 0.1.48 | 2.2.6 | 2025-03-31 |
|
||||
| `darwin-arm` | 0.1.48 | 2.2.12 | 2025-04-24 |
|
||||
| `win11-x64` | 0.1.48 | 2.2.12 | 2025-04-28 |
|
||||
| `win11-x64` | 0.1.48 | 2.0.4 | 2024-10-30 |
|
||||
| `win11-arm` | 0.1.48 | 2.2.1 | 2025-02-23 |
|
||||
| `linux-x64` | 0.1.48 | 2.0.5 | 2025-01-10 |
|
||||
| `linux-arm` | 0.1.48 | 2.1.10 | 2025-02-16 |
|
||||
@ -392,17 +392,17 @@ This demo was tested in the following deployments:
|
||||
2) Run the script with `--allow-net` and `--allow-write` entitlements:
|
||||
|
||||
```bash
|
||||
deno run --allow-net --allow-write --allow-import SheetJSDenoDOM.ts
|
||||
deno run --allow-net --allow-write SheetJSDenoDOM.ts
|
||||
```
|
||||
|
||||
The script will create a file `SheetJSDenoDOM.xlsx` that can be opened.
|
||||
|
||||
:::note pass
|
||||
:::caution pass
|
||||
|
||||
In older versions of Deno, the `--allow-import` flag must be omitted:
|
||||
Deno 2 additionally requires the `--allow-import` entitlement:
|
||||
|
||||
```bash
|
||||
deno run --allow-net --allow-write SheetJSDenoDOM.ts
|
||||
deno run --allow-net --allow-write --allow-import SheetJSDenoDOM.ts
|
||||
```
|
||||
|
||||
:::
|
||||
|
@ -231,7 +231,7 @@ This will create a static site in the `_site` folder
|
||||
8) Test the generated site by starting a web server:
|
||||
|
||||
```bash
|
||||
npx -y http-server _site
|
||||
npx http-server _site
|
||||
```
|
||||
|
||||
The program will display a URL (typically `http://localhost:8080`). Accessing
|
||||
|
@ -294,7 +294,7 @@ The final script will be saved to `out.js`
|
||||
7) Start a local web server to host the project folder:
|
||||
|
||||
```bash
|
||||
npx -y http-server .
|
||||
npx http-server .
|
||||
```
|
||||
|
||||
The command will print a list of URLs.
|
||||
|
@ -430,7 +430,7 @@ Save and refresh the page. A data table should be displayed
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
npx -y http-server dist/
|
||||
npx http-server dist/
|
||||
```
|
||||
|
||||
The terminal will display a URL, typically `http://127.0.0.1:8080` . Access
|
||||
@ -506,7 +506,7 @@ Save and refresh the page. A data table should be displayed
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
npx -y http-server dist/
|
||||
npx http-server dist/
|
||||
```
|
||||
|
||||
The terminal will display a URL, typically `http://127.0.0.1:8080` . Access
|
||||
@ -594,7 +594,7 @@ const data = utils.sheet_to_json<IPresident>(ws);
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
npx -y http-server dist/
|
||||
npx http-server dist/
|
||||
```
|
||||
|
||||
The terminal will display a URL ( `http://127.0.0.1:8080` ). Access that page
|
||||
|
@ -334,7 +334,7 @@ The final site will be placed in the `dist` folder.
|
||||
12) Start a local web server to host the `dist` folder:
|
||||
|
||||
```bash
|
||||
npx -y http-server dist
|
||||
npx http-server dist
|
||||
```
|
||||
|
||||
The command will print a list of URLs.
|
||||
|
@ -32,7 +32,6 @@ flowchart LR
|
||||
file --> |.eleventy.js\ncustom parser| buffer
|
||||
buffer --> |.eleventy.js\ncustom parser| aoo
|
||||
aoo --> |index.njk\ntemplate| html
|
||||
linkStyle 1 color:blue,stroke:blue;
|
||||
```
|
||||
|
||||
:::tip No Telemetry
|
||||
@ -115,11 +114,10 @@ accessed using the variable `pres` in a template:
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| Eleventy | Date |
|
||||
|:---------------|:-----------|
|
||||
| `2.0.1` | 2025-05-07 |
|
||||
| `3.0.0` | 2025-05-07 |
|
||||
| `3.1.0-beta.1` | 2025-05-07 |
|
||||
| Eleventy | Date |
|
||||
|:---------|:-----------|
|
||||
| `2.0.1` | 2024-12-23 |
|
||||
| `3.0.0` | 2024-12-23 |
|
||||
|
||||
:::
|
||||
|
||||
@ -214,7 +212,7 @@ Eleventy will place the generated site in the `_site` subfolder.
|
||||
9) Start a web server to host the static site:
|
||||
|
||||
```bash
|
||||
npx -y http-server _site
|
||||
npx http-server _site
|
||||
```
|
||||
|
||||
Open a web browser and access the displayed URL ( `http://localhost:8080` ).
|
||||
|
@ -49,8 +49,7 @@ This demo was tested in the following environments:
|
||||
| Nuxt Content | Nuxt | Date |
|
||||
|:-------------|:-----------|:-----------|
|
||||
| `1.15.1` | `2.18.1` | 2025-04-23 |
|
||||
| `2.13.4` | `3.17.2` | 2025-05-12 |
|
||||
| `3.5.1` | `3.17.3` | 2025-05-18 |
|
||||
| `2.13.4` | `3.14.159` | 2024-11-14 |
|
||||
|
||||
:::
|
||||
|
||||
@ -526,8 +525,8 @@ script files. The module script is expected to export a module configured with
|
||||
- Add the transformer to Nuxt Content in the `content:context` hook
|
||||
|
||||
```js title="sheetmodule.ts (Module)"
|
||||
import { resolve } from 'path';
|
||||
import { defineNuxtModule } from '@nuxt/kit';
|
||||
import { resolve } from 'path'
|
||||
import { defineNuxtModule } from '@nuxt/kit'
|
||||
|
||||
export default defineNuxtModule({
|
||||
/* module setup method */
|
||||
@ -550,7 +549,7 @@ The module must be loaded in `nuxt.config.ts` and added to the `modules` array:
|
||||
|
||||
```ts title="nuxt.config.ts"
|
||||
// highlight-next-line
|
||||
import SheetJSModule from './sheetmodule';
|
||||
import SheetJSModule from './sheetmodule'
|
||||
|
||||
export default defineNuxtConfig({
|
||||
// @ts-ignore
|
||||
@ -607,7 +606,7 @@ from the script setup will be shaped like the return value from the transformer.
|
||||
:::caution pass
|
||||
|
||||
For some older versions, parts of the Nuxt dependency tree did not support
|
||||
NodeJS version 20. If the `pnpm install` step fails with a message like
|
||||
NodeJS version 20. If the `yarn install` step fails with a message like
|
||||
|
||||
```
|
||||
error @nuxt/kit@3.4.1: The engine "node" is incompatible with this module.
|
||||
@ -620,18 +619,17 @@ The recommended solution is to switch to Node 18.
|
||||
1) Create a stock app and install dependencies:
|
||||
|
||||
```bash
|
||||
npx -y nuxi init -t content --packageManager pnpm --no-gitInit sheetjs-nc2 -M ,
|
||||
npx -y nuxi init -t content --packageManager yarn --no-gitInit sheetjs-nc2
|
||||
cd sheetjs-nc2
|
||||
npx -y pnpm install
|
||||
npx -y pnpm install @nuxt/content@2 --save
|
||||
npx -y pnpm install @types/node @nuxt/kit --save
|
||||
npx -y yarn install
|
||||
npx -y yarn add --dev @types/node
|
||||
```
|
||||
|
||||
2) Install the SheetJS library and start the server:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npx -y pnpm install --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz
|
||||
npx -y pnpm dev`}
|
||||
npx -y yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz
|
||||
npx -y yarn dev`}
|
||||
</CodeBlock>
|
||||
|
||||
|
||||
@ -641,7 +639,7 @@ When the build finishes, the terminal will display a URL like:
|
||||
> Local: http://localhost:3000/
|
||||
```
|
||||
|
||||
The server is listening on that URL. Open the link in a web browser.
|
||||
The server is listening on that URL. Open the link in a web browser.
|
||||
|
||||
3) Download https://docs.sheetjs.com/pres.xlsx and move to the `content` folder.
|
||||
|
||||
@ -651,12 +649,12 @@ curl -L -o content/pres.xlsx https://docs.sheetjs.com/pres.xlsx
|
||||
|
||||
4) Create the transformer. Two files must be saved at the root of the project:
|
||||
|
||||
- [`sheetformer.ts`](https://docs.sheetjs.com/nuxt/2/sheetformer.ts) (raw transformer module)
|
||||
- [`sheetformer.ts`](https://docs.sheetjs.com/nuxt/3/sheetformer.ts) (raw transformer module)
|
||||
- [`sheetmodule.ts`](https://docs.sheetjs.com/nuxt/3/sheetmodule.ts) (Nuxt configuration module)
|
||||
|
||||
```bash
|
||||
curl -O https://docs.sheetjs.com/nuxt/2/sheetformer.ts
|
||||
curl -O https://docs.sheetjs.com/nuxt/2/sheetmodule.ts
|
||||
curl -O https://docs.sheetjs.com/nuxt/3/sheetformer.ts
|
||||
curl -O https://docs.sheetjs.com/nuxt/3/sheetmodule.ts
|
||||
```
|
||||
|
||||
After creating the source files, the module must be added to `nuxt.config.ts`:
|
||||
@ -670,13 +668,14 @@ export default defineNuxtConfig({
|
||||
// @ts-ignore
|
||||
telemetry: false,
|
||||
// highlight-end
|
||||
devtools: { enabled: true },
|
||||
// highlight-start
|
||||
modules: [
|
||||
// highlight-next-line
|
||||
SheetJSModule,
|
||||
'@nuxt/content'
|
||||
],
|
||||
devtools: { enabled: true },
|
||||
// ...
|
||||
content: {}
|
||||
// highlight-end
|
||||
});
|
||||
```
|
||||
|
||||
@ -685,13 +684,33 @@ Stop the dev server (<kbd>CTRL</kbd>+<kbd>C</kbd>) and run the following:
|
||||
```bash
|
||||
npx -y nuxi clean
|
||||
npx -y nuxi cleanup
|
||||
npx -y pnpm run dev
|
||||
npx -y nuxi typecheck
|
||||
npx -y yarn run dev
|
||||
```
|
||||
|
||||
5) Download [`pres.vue`](pathname:///nuxt/2/pres.vue) and save to `app/pages`:
|
||||
Loading `http://localhost:3000/pres` should show some JSON data:
|
||||
|
||||
```json
|
||||
{
|
||||
// ...
|
||||
"data": {
|
||||
"_path": "/pres",
|
||||
// ...
|
||||
"_id": "content:pres.xlsx",
|
||||
"body": [
|
||||
{
|
||||
"name": "Sheet1", // <-- sheet name
|
||||
"data": [ // <-- array of data objects
|
||||
{
|
||||
"Name": "Bill Clinton",
|
||||
"Index": 42
|
||||
},
|
||||
```
|
||||
|
||||
5) Download [`pres.vue`](pathname:///nuxt/3/pres.vue) and save to `pages`:
|
||||
|
||||
```bash
|
||||
curl -o app/pages/pres.vue https://docs.sheetjs.com/nuxt/2/pres.vue
|
||||
curl -o pages/pres.vue https://docs.sheetjs.com/nuxt/3/pres.vue
|
||||
```
|
||||
|
||||
Stop the dev server (<kbd>CTRL</kbd>+<kbd>C</kbd>) and run the following:
|
||||
@ -699,247 +718,24 @@ Stop the dev server (<kbd>CTRL</kbd>+<kbd>C</kbd>) and run the following:
|
||||
```bash
|
||||
npx -y nuxi clean
|
||||
npx -y nuxi cleanup
|
||||
npx -y pnpm run dev
|
||||
npx -y yarn run dev
|
||||
```
|
||||
|
||||
6) From the browser window in step 2, access `/pres` from the site. For example,
|
||||
if the URL in step 2 was `http://localhost:3000/`, the new page should be
|
||||
`http://localhost:3000/pres`.
|
||||
The browser should now display an HTML table.
|
||||
|
||||
This page should now display an HTML table.
|
||||
|
||||
7) To verify that hot loading works, open `pres.xlsx` from the `content` folder
|
||||
6) To verify that hot loading works, open `pres.xlsx` from the `content` folder
|
||||
with a spreadsheet editor.
|
||||
|
||||
Set cell `A7` to "SheetJS Dev" and set `B7` to `47`. Save the spreadsheet.
|
||||
|
||||
The page should automatically refresh with the new content.
|
||||
|
||||
8) Stop the server (press <kbd>CTRL</kbd>+<kbd>C</kbd> in the terminal window).
|
||||
|
||||
9) Copy `app/pages/pres.vue` to `app/pages/index.vue`:
|
||||
|
||||
```bash
|
||||
cp app/pages/pres.vue app/pages/index.vue
|
||||
```
|
||||
|
||||
:::info pass
|
||||
|
||||
In older test runs, the Nuxt starter created a default `/` page. The most recent
|
||||
test did not create the index page, resulting in build errors. This step ensures
|
||||
some sort of index page exists.
|
||||
|
||||
:::
|
||||
|
||||
10) Build the static site:
|
||||
|
||||
```bash
|
||||
npx -y pnpm run generate
|
||||
```
|
||||
|
||||
This will create a static site in `.output/public`, which can be served with:
|
||||
|
||||
```bash
|
||||
npx -y http-server .output/public
|
||||
```
|
||||
|
||||
Access the displayed URL (typically `http://localhost:8080`) in a web browser.
|
||||
|
||||
To confirm that the spreadsheet data is added to the site, view the page source.
|
||||
|
||||
Searching for `Bill Clinton` reveals the following encoded HTML row:
|
||||
|
||||
```html
|
||||
<tr><td>Bill Clinton</td><td>42</td></tr>
|
||||
```
|
||||
|
||||
## Nuxt Content v3
|
||||
|
||||
:::danger pass
|
||||
|
||||
When this demo was last tested, the official Nuxt Content v3 custom transformers
|
||||
and custom collections examples did not work.
|
||||
|
||||
:::
|
||||
|
||||
[ViteJS modules](/docs/demos/static/vitejs) can be used in Nuxt v3.
|
||||
|
||||
### Installation
|
||||
|
||||
The [SheetJS NodeJS Module](/docs/getting-started/installation/nodejs) can be
|
||||
safely imported from `nuxt.config.ts` or transformer or module scripts. As long
|
||||
as the SheetJS modules are not imported in the various `.vue` pages, the library
|
||||
will not be added to the final page bundle!
|
||||
|
||||
### Configuration
|
||||
|
||||
The `vite` property in the NuxtJS config is passed to ViteJS. Plugins and other
|
||||
configuration options can be copied to the object. `vite.config.js` for the
|
||||
[Pure Data Plugin](/docs/demos/static/vitejs#pure-data-plugin) is shown below:
|
||||
|
||||
```js title="vite.config.js (raw ViteJS)"
|
||||
import { readFileSync } from 'fs';
|
||||
import { read, utils } from 'xlsx';
|
||||
import { defineConfig } from 'vite';
|
||||
|
||||
export default defineConfig({
|
||||
assetsInclude: ['**/*.xlsx'], // xlsx file should be treated as assets
|
||||
|
||||
plugins: [
|
||||
{ // this plugin handles ?sheetjs tags
|
||||
name: "vite-sheet",
|
||||
transform(_code, id) {
|
||||
if(!id.match(/\?sheetjs$/)) return;
|
||||
var wb = read(readFileSync(id.replace(/\?sheetjs$/, "")));
|
||||
var data = utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]);
|
||||
return `export default JSON.parse('${JSON.stringify(data).replace(/\\/g, "\\\\")}')`;
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
The `assetsInclude` and `plugins` properties should be added within the `vite`
|
||||
property in the object passed to `defineNuxtConfig`.
|
||||
|
||||
:::danger pass
|
||||
|
||||
NuxtJS does not properly honor the `?sheetjs` tag! As a result, the transform
|
||||
explicitly tests for the `.xlsx` extension.
|
||||
|
||||
:::
|
||||
|
||||
```ts title="nuxt.config.ts"
|
||||
import { readFileSync } from 'fs';
|
||||
import { read, utils } from 'xlsx';
|
||||
|
||||
export default defineNuxtConfig({
|
||||
// highlight-next-line
|
||||
vite: { // these options are passed to ViteJS
|
||||
assetsInclude: ['**/*.xlsx'], // xlsx file should be treated as assets
|
||||
|
||||
plugins: [
|
||||
{ // this plugin handles .xlsx
|
||||
name: "vite-sheet",
|
||||
transform(_code, id) {
|
||||
if(!id.match(/\.xlsx$/)) return;
|
||||
var wb = read(readFileSync(id.replace(/\?sheetjs$/, "")));
|
||||
var data = utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]);
|
||||
return `export default JSON.parse('${JSON.stringify(data).replace(/\\/g, "\\\\")}')`;
|
||||
}
|
||||
},
|
||||
],
|
||||
},
|
||||
// ...
|
||||
```
|
||||
|
||||
### Template Use
|
||||
|
||||
Pages can reference spreadsheets using a relative file reference. The ViteJS
|
||||
plugin will transform files with the `.xlsx` extension.
|
||||
|
||||
```js title="Script section of .vue VueJS page"
|
||||
import data from '../../pres.xlsx'; // data is an array of objects
|
||||
```
|
||||
|
||||
In the template, `data` is an array of objects that works with `v-for`[^4]:
|
||||
|
||||
```xml title="Template section of .vue VueJS page"
|
||||
<table>
|
||||
<thead><tr><th>Name</th><th>Index</th></tr></thead><tbody>
|
||||
<!-- loop over the rows of each worksheet -->
|
||||
<tr v-for="row in data" v-bind:key="row.Index">
|
||||
<!-- here `row` is a row object generated from sheet_to_json -->
|
||||
<td>{{ row.Name }}</td>
|
||||
<td>{{ row.Index }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
```
|
||||
|
||||
### Nuxt Content 3 Demo
|
||||
|
||||
1) Create a stock app and install dependencies:
|
||||
|
||||
```bash
|
||||
npx -y nuxi init -t content --packageManager pnpm --no-gitInit sheetjs-nc3 -M ,
|
||||
cd sheetjs-nc3
|
||||
npx -y pnpm install
|
||||
```
|
||||
|
||||
2) Install the SheetJS library and start the server:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npx -y pnpm install --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz
|
||||
npx -y pnpm dev`}
|
||||
</CodeBlock>
|
||||
|
||||
When the build finishes, the terminal will display a URL like:
|
||||
|
||||
```
|
||||
> Local: http://localhost:3000/
|
||||
```
|
||||
|
||||
The server is listening on that URL. Open the link in a web browser.
|
||||
|
||||
3) Download https://docs.sheetjs.com/pres.xlsx and move to the root folder:
|
||||
|
||||
```bash
|
||||
curl -o pres.xlsx https://docs.sheetjs.com/pres.xlsx
|
||||
```
|
||||
|
||||
4) Replace `nuxt.config.ts` with the following codeblock:
|
||||
|
||||
```ts title="nuxt.config.ts"
|
||||
import { readFileSync } from 'fs';
|
||||
import { read, utils } from 'xlsx';
|
||||
|
||||
export default defineNuxtConfig({
|
||||
vite: { // these options are passed to ViteJS
|
||||
assetsInclude: ['**/*.xlsx'], // xlsx file should be treated as assets
|
||||
|
||||
plugins: [
|
||||
{ // this plugin handles .xlsx
|
||||
name: "vite-sheet",
|
||||
transform(_code, id) {
|
||||
if(!id.match(/\.xlsx$/)) return;
|
||||
var wb = read(readFileSync(id.replace(/\?sheetjs$/, "")));
|
||||
var data = utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]);
|
||||
return `export default JSON.parse('${JSON.stringify(data).replace(/\\/g, "\\\\")}')`;
|
||||
}
|
||||
},
|
||||
],
|
||||
},
|
||||
modules: [
|
||||
'@nuxt/content',
|
||||
],
|
||||
devtools: { enabled: true },
|
||||
});
|
||||
```
|
||||
|
||||
5) Create a new file `app.vue` with the following contents:
|
||||
|
||||
```jsx title="app.vue (create new file)"
|
||||
<script setup>
|
||||
import data from '../../pres.xlsx'
|
||||
</script>
|
||||
<template>
|
||||
<table><thead><tr><th>Name</th><th>Index</th></tr></thead><tbody>
|
||||
<tr v-for="row in data" v-bind:key="row.Index">
|
||||
<td>{{ row.Name }}</td>
|
||||
<td>{{ row.Index }}</td>
|
||||
</tr>
|
||||
</tbody></table>
|
||||
</template>
|
||||
```
|
||||
|
||||
6) Refresh the browser window. This page should now display an HTML table.
|
||||
|
||||
7) Stop the server (press <kbd>CTRL</kbd>+<kbd>C</kbd> in the terminal window).
|
||||
|
||||
8) Build the static site:
|
||||
|
||||
```bash
|
||||
npx -y pnpm run generate
|
||||
npx -y yarn run generate
|
||||
```
|
||||
|
||||
This will create a static site in `.output/public`, which can be served with:
|
||||
|
@ -345,7 +345,7 @@ AstroJS will place the generated site in the `dist` subfolder.
|
||||
9) Start a web server to host the static site:
|
||||
|
||||
```bash
|
||||
npx -y http-server dist
|
||||
npx http-server dist
|
||||
```
|
||||
|
||||
Open a web browser and access the displayed URL ( `http://localhost:8080` ).
|
||||
|
@ -56,15 +56,15 @@ This demo was tested in the following environments:
|
||||
|
||||
| OS | Device | NS | Date |
|
||||
|:-----------|:--------------------|:---------|:-----------|
|
||||
| Android 30 | NVIDIA Shield | `8.9.2` | 2025-05-06 |
|
||||
| iOS 15.1 | iPad Pro | `8.9.2` | 2025-05-06 |
|
||||
| Android 30 | NVIDIA Shield | `8.7.2` | 2024-06-09 |
|
||||
| iOS 15.1 | iPad Pro | `8.7.2` | 2024-06-09 |
|
||||
|
||||
**Simulators**
|
||||
|
||||
| OS | Device | NS | Dev Platform | Date |
|
||||
|:-----------|:--------------------|:---------|:-------------|:-----------|
|
||||
| Android 35 | Pixel 9 Pro XL | `8.9.2` | `darwin-x64` | 2025-05-06 |
|
||||
| iOS 18.4 | iPhone 16 Pro Max | `8.9.2` | `darwin-x64` | 2025-05-06 |
|
||||
| Android 34 | Pixel 3a | `8.7.2` | `darwin-arm` | 2024-06-09 |
|
||||
| iOS 17.5 | iPhone SE (3rd gen) | `8.7.2` | `darwin-arm` | 2024-06-09 |
|
||||
| Android 35 | Pixel 9 | `8.8.3` | `win11-x64` | 2024-12-21 |
|
||||
| Android 35 | Pixel 9 | `8.8.3` | `linux-x64` | 2025-01-02 |
|
||||
|
||||
@ -78,15 +78,15 @@ NativeScript 8.6.1 split the telemetry into two parts: "usage" and "error". Both
|
||||
must be disabled separately:
|
||||
|
||||
```bash
|
||||
npx -y -p nativescript ns usage-reporting disable
|
||||
npx -y -p nativescript ns error-reporting disable
|
||||
npx -p nativescript ns usage-reporting disable
|
||||
npx -p nativescript ns error-reporting disable
|
||||
```
|
||||
|
||||
To verify telemetry was disabled:
|
||||
|
||||
```bash
|
||||
npx -y -p nativescript ns usage-reporting status
|
||||
npx -y -p nativescript ns error-reporting status
|
||||
npx -p nativescript ns usage-reporting status
|
||||
npx -p nativescript ns error-reporting status
|
||||
```
|
||||
|
||||
:::
|
||||
@ -120,22 +120,6 @@ for accessing data and are subject to change in future platform versions.
|
||||
<details>
|
||||
<summary><b>Technical Details</b> (click to show)</summary>
|
||||
|
||||
**iOS**
|
||||
|
||||
The following key/value pairs must be added to `Info.plist`:
|
||||
|
||||
```xml title="App_Resources/iOS/Info.plist (add highlighted lines)"
|
||||
<dict>
|
||||
<!-- highlight-start -->
|
||||
<key>UIFileSharingEnabled</key>
|
||||
<true/>
|
||||
<key>LSSupportsOpeningDocumentsInPlace</key>
|
||||
<true/>
|
||||
<!-- highlight-end -->
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Android**
|
||||
|
||||
Android security has evolved over the years. In newer Android versions, the
|
||||
@ -281,8 +265,8 @@ const wb = read(ab);
|
||||
0) Disable telemetry:
|
||||
|
||||
```bash
|
||||
npx -y -p nativescript ns usage-reporting disable
|
||||
npx -y -p nativescript ns error-reporting disable
|
||||
npx -p nativescript ns usage-reporting disable
|
||||
npx -p nativescript ns error-reporting disable
|
||||
```
|
||||
|
||||
1) Follow the official Environment Setup instructions[^8].
|
||||
@ -290,14 +274,14 @@ npx -y -p nativescript ns error-reporting disable
|
||||
:::caution pass
|
||||
|
||||
In previous test runs, NativeScript did not support the latest Android API.
|
||||
The error message from `npx -y -p nativescript ns doctor android` clearly stated
|
||||
The error message from `npx -p nativescript ns doctor android` clearly stated
|
||||
supported versions:
|
||||
|
||||
<pre>
|
||||
<span {...r}>✖</span> No compatible version of the Android SDK Build-tools are installed on your system. You can install any version in the following range: '>=23 <=33'.
|
||||
</pre>
|
||||
|
||||
If NativeScript does not properly support the latest API level, an older API
|
||||
If NativeScript does not properly supports the latest API level, a previous API
|
||||
version should be installed using Android Studio.
|
||||
|
||||
In a previous test run, the following packages were required:
|
||||
@ -306,14 +290,14 @@ In a previous test run, the following packages were required:
|
||||
- `Android SDK Build-Tools` Version `33.0.2`
|
||||
|
||||
It is recommended to install the SDK Platform and corresponding Android SDK
|
||||
Build-Tools for the latest supported API level.
|
||||
Build-Tools for the latest supported API level.ß
|
||||
|
||||
:::
|
||||
|
||||
2) Test the local system configuration for Android development:
|
||||
|
||||
```bash
|
||||
npx -y -p nativescript ns doctor android
|
||||
npx -p nativescript ns doctor android
|
||||
```
|
||||
|
||||
In the last macOS test, the following output was displayed:
|
||||
@ -332,7 +316,7 @@ In the last macOS test, the following output was displayed:
|
||||
<span {...g}>✔</span> Javac is installed and is configured properly.
|
||||
<span {...g}>✔</span> The Java Development Kit (JDK) is installed and is configured properly.
|
||||
<span {...g}>✔</span> Getting NativeScript components versions information...
|
||||
<span {...g}>✔</span> Component nativescript has 8.9.2 version and is up to date.
|
||||
<span {...g}>✔</span> Component nativescript has 8.7.2 version and is up to date.
|
||||
</pre>
|
||||
|
||||
</details>
|
||||
@ -340,7 +324,7 @@ In the last macOS test, the following output was displayed:
|
||||
3) Test the local system configuration for iOS development (macOS only):
|
||||
|
||||
```bash
|
||||
npx -y -p nativescript ns doctor ios
|
||||
npx -p nativescript ns doctor ios
|
||||
```
|
||||
|
||||
In the last macOS test, the following output was displayed:
|
||||
@ -359,9 +343,9 @@ In the last macOS test, the following output was displayed:
|
||||
<span {...g}>✔</span> CocoaPods are configured properly.
|
||||
<span {...g}>✔</span> Your current CocoaPods version is newer than 1.0.0.
|
||||
<span {...g}>✔</span> Python installed and configured correctly.
|
||||
<span {...g}>✔</span> Xcode version 16.3.0 satisfies minimum required version 10.
|
||||
<span {...g}>✔</span> Xcode version 15.4.0 satisfies minimum required version 10.
|
||||
<span {...g}>✔</span> Getting NativeScript components versions information...
|
||||
<span {...g}>✔</span> Component nativescript has 8.9.2 version and is up to date.
|
||||
<span {...g}>✔</span> Component nativescript has 8.7.2 version and is up to date.
|
||||
</pre>
|
||||
|
||||
</details>
|
||||
@ -371,21 +355,21 @@ In the last macOS test, the following output was displayed:
|
||||
4) Create a skeleton NativeScript + Angular app:
|
||||
|
||||
```bash
|
||||
npx -y -p nativescript ns create SheetJSNS --ng
|
||||
npx -p nativescript ns create SheetJSNS --ng
|
||||
```
|
||||
|
||||
5) Launch the app in the Android simulator to verify the app:
|
||||
5) Launch the app in the android simulator to verify the app:
|
||||
|
||||
```bash
|
||||
cd SheetJSNS
|
||||
npx -y -p nativescript ns run android
|
||||
npx -p nativescript ns run android
|
||||
```
|
||||
|
||||
(this may take a while)
|
||||
|
||||
Once the simulator launches and the test app is displayed, end the script by
|
||||
selecting the terminal and pressing <kbd>CTRL</kbd>+<kbd>C</kbd>. On Windows, if
|
||||
prompted to `Terminate batch job`, type <kbd>Y</kbd> and press Enter.
|
||||
prompted to `Terminate batch job`, type `y` and press Enter.
|
||||
|
||||
:::note pass
|
||||
|
||||
@ -397,23 +381,6 @@ Emulator start failed with: No emulator image available for device identifier 'u
|
||||
|
||||
:::
|
||||
|
||||
:::caution pass
|
||||
|
||||
In the most recent test, the build failed with an exception:
|
||||
|
||||
```
|
||||
WARNING: A restricted method in java.lang.System has been called
|
||||
WARNING: java.lang.System::load has been called by net.rubygrapefruit.platform.internal.NativeLibraryLoader in an unnamed module (file:/Users/sheetjs/.gradle/wrapper/dists/gradle-8.7-bin/bhs2wmbdwecv87pi65oeuq5iu/gradle-8.7/lib/native-platform-0.22-milestone-25.jar)
|
||||
WARNING: Use --enable-native-access=ALL-UNNAMED to avoid a warning for callers in this module
|
||||
WARNING: Restricted methods will be blocked in a future release unless native access is enabled
|
||||
```
|
||||
|
||||
**The NativeScript Gradle version is incompatible with Java 24!**
|
||||
|
||||
It is strongly recommended to roll back to Java 21.
|
||||
|
||||
:::
|
||||
|
||||
### Add SheetJS
|
||||
|
||||
:::note pass
|
||||
@ -439,10 +406,11 @@ import { Component, OnInit } from '@angular/core'
|
||||
// ...
|
||||
|
||||
export class ItemsComponent implements OnInit {
|
||||
items: Array<Item>
|
||||
// highlight-next-line
|
||||
version = `SheetJS - ${version}`;
|
||||
itemService = inject(ItemService)
|
||||
page = inject(Page)
|
||||
|
||||
constructor(private itemService: ItemService) {}
|
||||
// ...
|
||||
```
|
||||
|
||||
@ -460,7 +428,7 @@ in the title of the action bar:
|
||||
9) End the script and relaunch the app in the Android simulator:
|
||||
|
||||
```bash
|
||||
npx -y -p nativescript ns run android
|
||||
npx -p nativescript ns run android
|
||||
```
|
||||
|
||||
The title bar should show the version.
|
||||
@ -481,7 +449,7 @@ The title bar should show the version.
|
||||
<Button text="Export File" (tap)="export()" style="padding: 10px"></Button>
|
||||
</StackLayout>
|
||||
<!-- highlight-end -->
|
||||
<ListView [items]="itemService.items()">
|
||||
<ListView [items]="items">
|
||||
<!-- ... -->
|
||||
</ListView>
|
||||
<!-- highlight-next-line -->
|
||||
@ -490,15 +458,15 @@ The title bar should show the version.
|
||||
|
||||
11) Add the `import` and `export` methods in the component script:
|
||||
|
||||
```ts title="src/app/item/items.component.ts (add highlighted lines)"
|
||||
```ts title="src/app/item/items.component.ts"
|
||||
// highlight-start
|
||||
import { version, utils, read, write } from 'xlsx';
|
||||
import { Dialogs, getFileAccess } from '@nativescript/core';
|
||||
import { Folder, knownFolders, path } from '@nativescript/core/file-system';
|
||||
// highlight-end
|
||||
import { Component, NO_ERRORS_SCHEMA, inject } from '@angular/core'
|
||||
import { NativeScriptCommonModule, NativeScriptRouterModule } from '@nativescript/angular'
|
||||
import { Page } from '@nativescript/core'
|
||||
import { Component, OnInit } from '@angular/core'
|
||||
|
||||
import { Item } from './item'
|
||||
import { ItemService } from './item.service'
|
||||
|
||||
// highlight-start
|
||||
@ -508,12 +476,19 @@ function get_url_for_filename(filename: string): string {
|
||||
}
|
||||
// highlight-end
|
||||
|
||||
// ...
|
||||
@Component({
|
||||
selector: 'ns-items',
|
||||
templateUrl: './items.component.html',
|
||||
})
|
||||
export class ItemsComponent implements OnInit {
|
||||
items: Array<Item>
|
||||
version: string = `SheetJS - ${version}`;
|
||||
|
||||
export class ItemsComponent {
|
||||
version = `SheetJS - ${version}`;
|
||||
itemService = inject(ItemService)
|
||||
page = inject(Page)
|
||||
constructor(private itemService: ItemService) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.items = this.itemService.getItems()
|
||||
}
|
||||
|
||||
// highlight-start
|
||||
/* Import button */
|
||||
@ -524,14 +499,13 @@ export class ItemsComponent {
|
||||
async export() {
|
||||
}
|
||||
// highlight-end
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
12) End the script and relaunch the app in the Android simulator:
|
||||
|
||||
```bash
|
||||
npx -y -p nativescript ns run android
|
||||
npx -p nativescript ns run android
|
||||
```
|
||||
|
||||
Two buttons should appear just below the header:
|
||||
@ -560,7 +534,7 @@ Two buttons should appear just below the header:
|
||||
const ws = wb.Sheets[wsname];
|
||||
|
||||
/* update table */
|
||||
this.itemService.items.set(utils.sheet_to_json(ws));
|
||||
this.items = utils.sheet_to_json<Item>(ws);
|
||||
} catch(e) { await Dialogs.alert(e.message); }
|
||||
// highlight-end
|
||||
}
|
||||
@ -573,7 +547,7 @@ Two buttons should appear just below the header:
|
||||
|
||||
try {
|
||||
/* create worksheet from data */
|
||||
const ws = utils.json_to_sheet(this.itemService.items());
|
||||
const ws = utils.json_to_sheet(this.items);
|
||||
|
||||
/* create workbook from worksheet */
|
||||
const wb = utils.book_new();
|
||||
@ -595,7 +569,7 @@ Two buttons should appear just below the header:
|
||||
14) Launch the app in the Android Simulator:
|
||||
|
||||
```bash
|
||||
npx -y -p nativescript ns run android
|
||||
npx -p nativescript ns run android
|
||||
```
|
||||
|
||||
If the app does not automatically launch, manually open the `SheetJSNS` app.
|
||||
@ -637,8 +611,8 @@ $bytes = [Convert]::FromBase64String($b64)
|
||||
After the header row, insert a row and make the following assignments:
|
||||
|
||||
- Set cell `A2` to `0`
|
||||
- Set cell `B2` to `SheetJS` (type `'SheetJS` in the formula bar)
|
||||
- Set cell `C2` to `Library` (type `'Library` in the formula bar)
|
||||
- Set cell `B2` to `SheetJS` (type `'SheetJS` in the formula bar)
|
||||
- Set cell `C2` to `Library` (type `'Library` in the formula bar)
|
||||
|
||||
After making the changes, the worksheet should look like the following:
|
||||
|
||||
@ -646,6 +620,8 @@ After making the changes, the worksheet should look like the following:
|
||||
id | name | role
|
||||
# highlight-next-line
|
||||
0 | SheetJS | Library
|
||||
1 | Ter Stegen | Goalkeeper
|
||||
3 | Piqué | Defender
|
||||
...
|
||||
```
|
||||
|
||||
@ -695,7 +671,7 @@ Scroll down to ["Fetching Files"](#android-device) for Android device testing.
|
||||
20) Launch the app in the iOS Simulator:
|
||||
|
||||
```bash
|
||||
npx -y -p nativescript ns run ios
|
||||
npx -p nativescript ns run ios
|
||||
```
|
||||
|
||||
21) Tap "Export File". A dialog will print where the file was written.
|
||||
@ -705,8 +681,8 @@ npx -y -p nativescript ns run ios
|
||||
After the header row, insert a row and make the following assignments:
|
||||
|
||||
- Set cell `A2` to `0`
|
||||
- Set cell `B2` to `SheetJS` (type `'SheetJS` in the formula bar)
|
||||
- Set cell `C2` to `Library` (type `'Library` in the formula bar)
|
||||
- Set cell `B2` to `SheetJS` (type `'SheetJS` in the formula bar)
|
||||
- Set cell `C2` to `Library` (type `'Library` in the formula bar)
|
||||
|
||||
After making the changes, the worksheet should look like the following:
|
||||
|
||||
@ -714,6 +690,8 @@ After making the changes, the worksheet should look like the following:
|
||||
id | name | role
|
||||
# highlight-next-line
|
||||
0 | SheetJS | Library
|
||||
1 | Ter Stegen | Goalkeeper
|
||||
3 | Piqué | Defender
|
||||
...
|
||||
```
|
||||
|
||||
@ -726,21 +704,31 @@ The first item in the list will change:
|
||||
|
||||
### Fetching Files
|
||||
|
||||
25) Replace `item.service.ts` with the following:
|
||||
25) In `src/app/item/items.component.ts`, make `ngOnInit` asynchronous:
|
||||
|
||||
```ts title="src/app/item/items.component.ts (replace existing function)"
|
||||
async ngOnInit(): Promise<void> {
|
||||
this.items = await this.itemService.getItems()
|
||||
}
|
||||
```
|
||||
|
||||
26) Replace `item.service.ts` with the following:
|
||||
|
||||
```ts title="src/app/item/item.service.ts"
|
||||
import { read, utils } from 'xlsx';
|
||||
import { Injectable, signal, effect } from '@angular/core'
|
||||
import { knownFolders, path, getFileAccess } from '@nativescript/core';
|
||||
import { getFile } from '@nativescript/core/http';
|
||||
import { Item } from './item'
|
||||
import { Injectable } from '@angular/core'
|
||||
|
||||
import { knownFolders, path, getFileAccess } from '@nativescript/core'
|
||||
import { getFile } from '@nativescript/core/http';
|
||||
import { read, utils } from 'xlsx';
|
||||
|
||||
import { Item } from './item'
|
||||
interface IPresident { Name: string; Index: number };
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class ItemService {
|
||||
items = signal<Item[]>([]);
|
||||
constructor() { effect(() => { (async() => {
|
||||
private items: Array<Item>;
|
||||
|
||||
async getItems(): Promise<Array<Item>> {
|
||||
/* fetch https://docs.sheetjs.com/pres.xlsx */
|
||||
const temp: string = path.join(knownFolders.temp().path, "pres.xlsx");
|
||||
const ab = await getFile("https://docs.sheetjs.com/pres.xlsx", temp)
|
||||
@ -748,33 +736,32 @@ export class ItemService {
|
||||
const wb = read(await getFileAccess().readBufferAsync(ab.path));
|
||||
/* translate the first worksheet to the required Item type */
|
||||
const data = utils.sheet_to_json<IPresident>(wb.Sheets[wb.SheetNames[0]]);
|
||||
/* update state */
|
||||
this.items.set(data.map((pres, id) => ({id, name: pres.Name, role: ""+pres.Index} as Item)));
|
||||
})(); }); }
|
||||
return this.items = data.map((pres, id) => ({id, name: pres.Name, role: ""+pres.Index} as Item));
|
||||
}
|
||||
|
||||
getItem(id: number): Item {
|
||||
return this.items().find((item) => item.id === id)
|
||||
return this.items.filter((item) => item.id === id)[0]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
26) End the script and relaunch the app in the Android simulator:
|
||||
27) End the script and relaunch the app in the Android simulator:
|
||||
|
||||
```bash
|
||||
npx -y -p nativescript ns run android
|
||||
npx -p nativescript ns run android
|
||||
```
|
||||
|
||||
The app should show Presidential data.
|
||||
|
||||
### Android Device
|
||||
|
||||
27) Connect an Android device using a USB cable.
|
||||
28) Connect an Android device using a USB cable.
|
||||
|
||||
If the device asks to allow USB debugging, tap "Allow".
|
||||
|
||||
28) Close any Android / iOS emulators.
|
||||
29) Close any Android / iOS emulators.
|
||||
|
||||
29) Enable "Legacy External Storage" in the Android app. The manifest is stored
|
||||
30) Enable "Legacy External Storage" in the Android app. The manifest is stored
|
||||
at `App_Resources/Android/src/main/AndroidManifest.xml`:
|
||||
|
||||
```xml title="App_Resources/Android/src/main/AndroidManifest.xml (add highlighted line)"
|
||||
@ -789,13 +776,13 @@ at `App_Resources/Android/src/main/AndroidManifest.xml`:
|
||||
android:hardwareAccelerated="true">
|
||||
```
|
||||
|
||||
30) Install the `@nativescript-community/perms` dependency:
|
||||
31) Install the `@nativescript-community/perms` dependency:
|
||||
|
||||
```bash
|
||||
npm i --save @nativescript-community/perms
|
||||
```
|
||||
|
||||
31) Add the highlighted lines to `items.component.ts`:
|
||||
32) Add the highlighted lines to `items.component.ts`:
|
||||
|
||||
- Import `File` from NativeScript core and `request` from the new dependency:
|
||||
|
||||
@ -828,10 +815,10 @@ import { Component, OnInit } from '@angular/core'
|
||||
} catch(e) { await Dialogs.alert(e.message); }
|
||||
```
|
||||
|
||||
32) Build APK and run on device:
|
||||
33) Build APK and run on device:
|
||||
|
||||
```bash
|
||||
npx -y -p nativescript ns run android
|
||||
npx -p nativescript ns run android
|
||||
```
|
||||
|
||||
If the Android emulators are closed and an Android device is connected, the last
|
||||
@ -852,59 +839,22 @@ file named `SheetJSNS.xls`.
|
||||
|
||||
### iOS Device
|
||||
|
||||
33) Connect an iOS device using a USB cable
|
||||
34) Connect an iOS device using a USB cable
|
||||
|
||||
34) Close any Android / iOS emulators.
|
||||
35) Close any Android / iOS emulators.
|
||||
|
||||
35) Enable developer code signing certificates:
|
||||
36) Enable developer code signing certificates:
|
||||
|
||||
Open `platforms/ios/SheetJSNS.xcodeproj/project.xcworkspace` in Xcode. Select
|
||||
the "Project Navigator" and select `SheetJSNS`. In the main view, select the
|
||||
`SheetJSNS` target. Click "Signing & Capabilities". Under "Signing", select a
|
||||
team in the dropdown menu.
|
||||
|
||||
:::caution pass
|
||||
|
||||
When this demo was last tested, Xcode repeatedly crashed.
|
||||
|
||||
The issue was resolved by cleaning the project:
|
||||
|
||||
```bash
|
||||
npx -y -p nativescript ns platform clean ios
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
36) Add the following key/value pairs to `Info.plist`:
|
||||
|
||||
```xml title="App_Resources/iOS/Info.plist (add highlighted lines)"
|
||||
<dict>
|
||||
<!-- highlight-start -->
|
||||
<key>UIFileSharingEnabled</key>
|
||||
<true/>
|
||||
<key>LSSupportsOpeningDocumentsInPlace</key>
|
||||
<true/>
|
||||
<!-- highlight-end -->
|
||||
```
|
||||
the "Project Navigator" and select the "App" project. In the main view, select
|
||||
"Signing & Capabilities". Under "Signing", select a team in the dropdown menu.
|
||||
|
||||
37) Run on device:
|
||||
|
||||
```bash
|
||||
npx -y -p nativescript ns run ios
|
||||
npx -p nativescript ns run ios
|
||||
```
|
||||
|
||||
:::info pass
|
||||
|
||||
If this is the first time testing an app on a device, the certificate must be
|
||||
trusted on the device:
|
||||
|
||||
Under "Settings" > "General" > "VPN & Device Management", there should be a
|
||||
"Apple Development" certificate in the "DEVELOPER APP" section. Select the
|
||||
certificate and confirm that "SheetJSNS" is listed under "APPS". Tap "Trust ..."
|
||||
and tap "Trust" in the popup.
|
||||
|
||||
:::
|
||||
|
||||
<details open>
|
||||
<summary><b>iOS Device Testing</b> (click to hide)</summary>
|
||||
|
||||
@ -913,9 +863,8 @@ connected to the Internet, a list of Presidents should be displayed.
|
||||
|
||||
Tap "Export File". The app will show an alert. Tap "OK".
|
||||
|
||||
Switch to the "Files" app and repeatedly tap "<". In the "Browse" window, tap
|
||||
"On My iPhone". There should be a new folder named "SheetJSNS". Tap the folder
|
||||
and look for the file named `SheetJSNS.xls`.
|
||||
Switch to the "Files" app and open the "Downloads" folder. There should be a new
|
||||
file named `SheetJSNS.xls`.
|
||||
|
||||
</details>
|
||||
|
||||
|
@ -59,7 +59,7 @@ This demo was tested in the following environments:
|
||||
|:-----------|:------------------|:--------|:---------|:-------------|:-----------|
|
||||
| Android 35 | Pixel 9 Pro XL | `3.7.2` | `3.29.2` | `darwin-x64` | 2025-03-31 |
|
||||
| iOS 18.3 | iPhone 16 Pro Max | `3.7.2` | `3.29.2` | `darwin-x64` | 2025-03-31 |
|
||||
| Android 36 | Pixel 9 Pro XL | `3.7.2` | `3.29.3` | `win11-x64` | 2054-04-28 |
|
||||
| Android 35 | Pixel 3a | `3.5.0` | `3.24.0` | `win11-x64` | 2024-08-10 |
|
||||
|
||||
:::
|
||||
|
||||
@ -244,7 +244,7 @@ Run `flutter doctor` and confirm the following items are checked:
|
||||
<TabItem value="win" label="Windows">
|
||||
|
||||
<pre>
|
||||
<span {...g}>[✓]</span> Android toolchain - develop for Android devices (Android SDK version 35.0.1)
|
||||
<span {...g}>[✓]</span> Android toolchain - develop for Android devices (Android SDK version 35.0.0)
|
||||
</pre>
|
||||
|
||||
</TabItem>
|
||||
@ -260,7 +260,7 @@ Run `flutter doctor` and confirm the following items are checked:
|
||||
On first run, there may be a warning with "Android toolchain":
|
||||
|
||||
```
|
||||
[!] Android toolchain - develop for Android devices (Android SDK version 35.0.1)
|
||||
[!] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
|
||||
! Some Android licenses not accepted. To resolve this, run: flutter doctor
|
||||
--android-licenses
|
||||
```
|
||||
@ -386,9 +386,9 @@ Pixel_9_Pro_XL_API_35 • Pixel 9 Pro XL API 35 • Google • android
|
||||
There should be at least one `android` emulator:
|
||||
|
||||
```
|
||||
Id • Name • Manufacturer • Platform
|
||||
Id • Name • Manufacturer • Platform
|
||||
|
||||
Pixel_9_Pro_XL • Pixel 9 Pro XL • Google • android
|
||||
Pixel_3a_API_35 • Pixel 3a API 35 • Google • android
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
@ -445,43 +445,14 @@ emulator -avd Pixel_9_Pro_XL_API_35
|
||||
|
||||
:::note pass
|
||||
|
||||
If `emulator` cannot be found, the folder must be added to the system path.
|
||||
|
||||
<Tabs groupId="os">
|
||||
<TabItem value="macos" label="macOS">
|
||||
|
||||
On macOS, `~/Library/Android/sdk/emulator/` is the typical location for the
|
||||
`emulator` binary:
|
||||
`emulator` binary. If it cannot be found, add the folder to `PATH`:
|
||||
|
||||
```bash
|
||||
export PATH="$PATH":~/Library/Android/sdk/emulator
|
||||
emulator -avd Pixel_9_Pro_XL_API_35
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="win" label="Windows">
|
||||
|
||||
The Android SDK folder can be found in the SDK manager in Android Studio. It is
|
||||
typically `%LOCALAPPDATA%\Android\Sdk`.
|
||||
|
||||
If it is not assigned, create a User environment variable named `ANDROID_HOME`
|
||||
with the value set to the Android SDK folder.
|
||||
|
||||
---
|
||||
|
||||
There are three folders within the Android SDK folder that should be added to
|
||||
the User `PATH` environment variable. Each folder holds a different tool:
|
||||
|
||||
| Folder | Command-line Tool |
|
||||
|:------------------------------------------|:------------------|
|
||||
| `%ANDROID_HOME%\emulator` | `emulator` |
|
||||
| `%ANDROID_HOME%\cmdline-tools\latest\bin` | `avdmanager` |
|
||||
| `%ANDROID_HOME%\platform-tools` | `adb` |
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
|
||||
:::
|
||||
|
||||
</details>
|
||||
@ -674,13 +645,6 @@ flutter -v -d emulator-5554 run
|
||||
|
||||
</details>
|
||||
|
||||
:::caution pass
|
||||
|
||||
In some test runs on low-power devices, it took 20 seconds for the app to fetch
|
||||
and display data!
|
||||
|
||||
:::
|
||||
|
||||
:::info Troubleshooting
|
||||
|
||||
In some demo runs, the build failed with an Android SDK error:
|
||||
|
@ -40,22 +40,47 @@ app to read and write workbooks. The app will look like the screenshots below:
|
||||
|
||||
## Integration Details
|
||||
|
||||
The [SheetJS NodeJS Module](/docs/getting-started/installation/nodejs) can be
|
||||
imported from the main or the renderer thread.
|
||||
Electron uses a multi-process architecture, with the main process handling system level operations and I/O, and the renderer process handling UI and web content.
|
||||
|
||||
The SheetJS `readFile` and `writeFile` methods will use the Electron `fs` module
|
||||
where available.
|
||||
**Renderer Process Limitations**
|
||||
|
||||
<details>
|
||||
<summary><b>Renderer Configuration</b> (click to show)</summary>
|
||||
The renderer process is sandboxed and cannot run any non-browser code.
|
||||
|
||||
Electron 9 and later require the preference `nodeIntegration: true` in order to
|
||||
`require('xlsx')` in the renderer process.
|
||||
**Main Process Limitations**
|
||||
|
||||
Electron 12 and later also require `worldSafeExecuteJavascript: true` and
|
||||
`contextIsolation: true`.
|
||||
The main process can run any NodeJS code, but it cannot access the DOM or any browser APIs.
|
||||
|
||||
</details>
|
||||
To allow communication between the main and renderer processes, Electron recommends building a [context bridge](https://www.electronjs.org/docs/latest/api/context-bridge) to expose low-level system calls and NodeJS APIs to the renderer process. Such as the [SheetJS NodeJS Module](/docs/getting-started/installation/nodejs) which we will be using here.
|
||||
|
||||
Exposed APIs are available as `SheetJSDemoAPI` on the window object and proxied from the main process.
|
||||
|
||||
```js title="preload.js -- contextBridge API"
|
||||
const { contextBridge, ipcRenderer, shell } = require('electron');
|
||||
// import nodejs modules we wish to expose APIs from.
|
||||
const path = require('path');
|
||||
const XLSX = require('xlsx');
|
||||
|
||||
// The contextBridge API allows us to expose APIs to the renderer process.
|
||||
// highlight-next-line
|
||||
contextBridge.exposeInMainWorld('SheetJSDemoAPI', {
|
||||
// request OS file dialogs from the main process
|
||||
openFile: (filters) => ipcRenderer.invoke('dialog:openFile', filters),
|
||||
saveFile: (filters) => ipcRenderer.invoke('dialog:saveFile', filters),
|
||||
message: (msg) => ipcRenderer.invoke('dialog:message', msg),
|
||||
// open external links in the default browser
|
||||
openExternal: (url) => shell.openExternal(url),
|
||||
// listen for file open events from the main process
|
||||
onFileOpened: (cb) => ipcRenderer.on('file-opened', (_e, fp) => cb(fp)),
|
||||
|
||||
// You can use this to expose nodejs APIs to the renderer process.
|
||||
basename: (p) => path.basename(p),
|
||||
extname: (p) => path.extname(p),
|
||||
|
||||
// Here for example we are exposing the sheetjs package to the renderer process.
|
||||
// highlight-next-line
|
||||
xlsx: XLSX,
|
||||
});
|
||||
```
|
||||
|
||||
### Reading Files
|
||||
|
||||
@ -73,7 +98,7 @@ For example, assuming a file input element on the page:
|
||||
|
||||
The event handler would process the event as if it were a web event:
|
||||
|
||||
```js
|
||||
```js title="index.js -- renderer process"
|
||||
async function handleFile(e) {
|
||||
const file = e.target.files[0];
|
||||
const data = await file.arrayBuffer();
|
||||
@ -87,8 +112,9 @@ document.getElementById("xlf").addEventListener("change", handleFile, false);
|
||||
|
||||
**Drag and Drop**
|
||||
|
||||
The [drag and drop snippet](/docs/solutions/input#example-user-submissions)
|
||||
applies to DIV elements on the page.
|
||||
In the demo the [drag and drop snippet](/docs/solutions/input#example-user-submissions)
|
||||
applies to the entire window via the `document.body` element. However it can easily be
|
||||
applied to any element on the page.
|
||||
|
||||
For example, assuming a DIV on the page:
|
||||
|
||||
@ -98,7 +124,9 @@ For example, assuming a DIV on the page:
|
||||
|
||||
The event handler would process the event as if it were a web event:
|
||||
|
||||
```js
|
||||
```js title="index.js -- renderer process"
|
||||
const XLSX = window.SheetJSDemoAPI.xlsx; // use xlsx package from bridge process
|
||||
|
||||
async function handleDrop(e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
@ -117,88 +145,105 @@ document.getElementById("drop").addEventListener("drop", handleDrop, false);
|
||||
|
||||
[`XLSX.readFile`](/docs/api/parse-options) reads workbooks from the file system.
|
||||
`showOpenDialog` shows a Save As dialog and returns the selected file name.
|
||||
Unlike the Web APIs, the `showOpenDialog` flow can be initiated by app code:
|
||||
|
||||
```js
|
||||
/* from the renderer thread */
|
||||
const electron = require('@electron/remote');
|
||||
We can now use the exposed APIs from our preload script above to show the open dialog and try to parse the workbook from within the renderer process.
|
||||
|
||||
```js title="index.js -- renderer process"
|
||||
// our exposed bridge APIs are available as SheetJSDemoAPI on the window object
|
||||
const openFile = window.SheetJSDemoAPI.openFile; // request the open file dialog from the main process
|
||||
// We can also access the SheetJS package from the exposed bridge APIs
|
||||
// highlight-next-line
|
||||
const XLSX = window.SheetJSDemoAPI.xlsx;
|
||||
|
||||
/* this function will show the open dialog and try to parse the workbook */
|
||||
async function importFile() {
|
||||
/* show Save As dialog */
|
||||
const result = await electron.dialog.showOpenDialog({
|
||||
title: 'Select a file',
|
||||
filters: [{
|
||||
/* show open file dialog */
|
||||
const result = await openFile([{
|
||||
name: "Spreadsheets",
|
||||
extensions: ["xlsx", "xls", "xlsb", /* ... other formats ... */]
|
||||
}]
|
||||
});
|
||||
}]);
|
||||
/* result.filePaths is an array of selected files */
|
||||
if(result.filePaths.length == 0) throw new Error("No file was selected!");
|
||||
// highlight-next-line
|
||||
return XLSX.readFile(result.filePaths[0]);
|
||||
}
|
||||
```
|
||||
In order to interact with the file system, the `xlsx` package here depends on the Node.js. Which means we need to utilize the Bridge here and make it possible to call these methods from the renderer process. The appropriate IPC event can be found below.
|
||||
|
||||
:::note pass
|
||||
```js title="main.js -- main process"
|
||||
const { ipcMain, dialog } = require('electron');
|
||||
|
||||
`showOpenDialog` originally returned an array of paths:
|
||||
|
||||
```js
|
||||
var dialog = require('electron').remote.dialog;
|
||||
|
||||
function importFile(workbook) {
|
||||
var result = dialog.showOpenDialog({ properties: ['openFile'] });
|
||||
return XLSX.readFile(result[0]);
|
||||
}
|
||||
ipcMain.handle('dialog:openFile', (_e, filters) =>
|
||||
dialog.showOpenDialog({ title: 'Select a file', filters, properties: ['openFile'] })
|
||||
);
|
||||
```
|
||||
|
||||
This method was renamed to `showOpenDialogSync` in Electron 6.
|
||||
|
||||
:::
|
||||
|
||||
### Writing Files
|
||||
|
||||
[`XLSX.writeFile`](/docs/api/write-options) writes workbooks to the file system.
|
||||
`showSaveDialog` shows a Save As dialog and returns the selected file name:
|
||||
|
||||
```js
|
||||
/* from the renderer thread */
|
||||
const electron = require('@electron/remote');
|
||||
The implementation for saving files looks very similar to the one above thanks to our bridge API.
|
||||
```js title="index.js -- renderer process"
|
||||
// our exposed bridge APIs are available as SheetJSDemoAPI on the window object
|
||||
const saveFile = window.SheetJSDemoAPI.saveFile; // request the save file dialog from the main process
|
||||
const XLSX = window.SheetJSDemoAPI.xlsx;
|
||||
|
||||
/* this function will show the save dialog and try to write the workbook */
|
||||
async function exportFile(workbook) {
|
||||
/* show Save As dialog */
|
||||
const result = await electron.dialog.showSaveDialog({
|
||||
title: 'Save file as',
|
||||
filters: [{
|
||||
const result = await saveFile([{
|
||||
name: "Spreadsheets",
|
||||
extensions: ["xlsx", "xls", "xlsb", /* ... other formats ... */]
|
||||
}]
|
||||
});
|
||||
/* write file */
|
||||
// highlight-next-line
|
||||
XLSX.writeFile(workbook, result.filePath);
|
||||
}]);
|
||||
if(result.filePaths.length == 0) throw new Error("No file was selected!");
|
||||
XLSX.writeFile(workbook, result.filePaths[0]);
|
||||
}
|
||||
```
|
||||
And here is the implementation of the `saveFile` event listener in `main.js`:
|
||||
```js title="main.js -- main process"
|
||||
const { ipcMain, dialog } = require('electron');
|
||||
|
||||
:::note pass
|
||||
|
||||
`showSaveDialog` originally returned the selected path:
|
||||
|
||||
```js
|
||||
var dialog = require('electron').remote.dialog;
|
||||
|
||||
function exportFile(workbook) {
|
||||
var result = dialog.showSaveDialog();
|
||||
XLSX.writeFile(workbook, result);
|
||||
}
|
||||
ipcMain.handle('dialog:saveFile', (_e, filters) =>
|
||||
dialog.showSaveDialog({ title: 'Save file as', filters })
|
||||
);
|
||||
```
|
||||
|
||||
This method was renamed to `showSaveDialogSync` in Electron 6.
|
||||
### Working with OS level file open events.
|
||||
|
||||
Electron makes it possible to handle OS level file open events, such as the "open with" context menu or `open` CLI command.
|
||||
|
||||
The example below shows the configuration required to register your application as a handler supporting such events for all file extensions SheetJS supports.
|
||||
|
||||
:::caution
|
||||
|
||||
It is also possible to open files using the "open with" context menu without registering the application as a handler for the specified file types. This however, requires manually selecting the application binary as a target to open the file with.
|
||||
|
||||
**This action might not be supported by some file managers on Linux based systems.**
|
||||
:::
|
||||
|
||||
```json title="package.json"
|
||||
{
|
||||
// ...existing content
|
||||
"build": {
|
||||
"appId": "com.sheetjs.electron",
|
||||
"fileAssociations": [
|
||||
{
|
||||
"ext": [ // supported extensions to register with the OS.
|
||||
"xls","xlsx","xlsm","xlsb","xml","csv","txt","dif",
|
||||
"sylk","slk","prn","ods","fods","htm","html","numbers"
|
||||
],
|
||||
"name": "Spreadsheet / Delimited File",
|
||||
"description": "Spreadsheets and delimited text files opened by SheetJS-Electron",
|
||||
"role": "Editor"
|
||||
}
|
||||
],
|
||||
"mac": { "target": "dmg" },
|
||||
"win": { "target": "nsis" },
|
||||
"linux": { "target": "deb" }
|
||||
},
|
||||
}
|
||||
```
|
||||
This makes it possible to generate installers for MacOS, Windows and Linux which will automatically register the application as a handler for the specified file types avoiding manual registration processes that differ across operating systems.
|
||||
|
||||
## Complete Example
|
||||
|
||||
:::note Tested Deployments
|
||||
@ -208,29 +253,26 @@ This demo was tested in the following environments:
|
||||
| OS and Version | Architecture | Electron | Date |
|
||||
|:---------------|:-------------|:---------|:-----------|
|
||||
| macOS 15.3 | `darwin-x64` | `35.1.2` | 2025-03-31 |
|
||||
| macOS 14.5 | `darwin-arm` | `35.1.2` | 2025-03-30 |
|
||||
| Windows 11 | `win11-x64` | `33.2.1` | 2025-02-11 |
|
||||
| macOS 15.4 | `darwin-arm` | `36.1.0` | 2025-05-03 |
|
||||
| Windows 11 | `win11-x64` | `36.1.0` | 2025-05-03 |
|
||||
| Windows 11 | `win11-arm` | `33.2.1` | 2025-02-23 |
|
||||
| Linux (HoloOS) | `linux-x64` | `33.2.1` | 2025-01-02 |
|
||||
| Linux (Debian) | `linux-arm` | `33.2.1` | 2025-02-16 |
|
||||
|
||||
:::
|
||||
|
||||
This demo includes a drag-and-drop box as well as a file input box, mirroring
|
||||
the [SheetJS Data Preview Live Demo](https://oss.sheetjs.com/sheetjs/)
|
||||
|
||||
The core data in this demo is an editable HTML table. The readers build up the
|
||||
table using `sheet_to_html` (with `editable:true` option) and the writers scrape
|
||||
the table using `table_to_book`.
|
||||
|
||||
The demo project is wired for `electron-forge` to build the standalone binary.
|
||||
|
||||
You can also use `electron-builder` to build a packaged installer binary.
|
||||
|
||||
1) Download the demo files:
|
||||
|
||||
- [`package.json`](pathname:///electron/package.json) : project structure
|
||||
- [`main.js`](pathname:///electron/main.js) : main process script
|
||||
- [`index.html`](pathname:///electron/index.html) : window page
|
||||
- [`index.js`](pathname:///electron/index.js) : script loaded in render context
|
||||
- [`preload.js`](pathname:///electron/preload.js) : preload script (ContextBridge API worker)
|
||||
- [`styles.css`](pathname:///electron/styles.css) : stylesheet
|
||||
|
||||
:::caution pass
|
||||
|
||||
@ -248,6 +290,8 @@ curl -LO https://docs.sheetjs.com/electron/package.json
|
||||
curl -LO https://docs.sheetjs.com/electron/main.js
|
||||
curl -LO https://docs.sheetjs.com/electron/index.html
|
||||
curl -LO https://docs.sheetjs.com/electron/index.js
|
||||
curl -LO https://docs.sheetjs.com/electron/preload.js
|
||||
curl -LO https://docs.sheetjs.com/electron/styles.css
|
||||
```
|
||||
|
||||
:::note pass
|
||||
@ -265,6 +309,8 @@ curl.exe -LO https://docs.sheetjs.com/electron/package.json
|
||||
curl.exe -LO https://docs.sheetjs.com/electron/main.js
|
||||
curl.exe -LO https://docs.sheetjs.com/electron/index.html
|
||||
curl.exe -LO https://docs.sheetjs.com/electron/index.js
|
||||
curl.exe -LO https://docs.sheetjs.com/electron/preload.js
|
||||
curl.exe -LO https://docs.sheetjs.com/electron/styles.css
|
||||
```
|
||||
|
||||
:::
|
||||
@ -322,12 +368,12 @@ The program will run on ARM64 Windows.
|
||||
|
||||
#### Electron API
|
||||
|
||||
7) Click "Click here to select a file from your computer". With the file picker,
|
||||
7) Click "Click here to select a file. With the file picker,
|
||||
navigate to the Downloads folder and select `pres.numbers`.
|
||||
|
||||
The application should show data in a table.
|
||||
The application should show a dropdown component for each worksheet contained in your file, clicking on it should display its data within a table.
|
||||
|
||||
8) Click "Export Data!" and click "Save" in the popup. By default, it will try
|
||||
8) Click "Export" and click "Save" in the popup. By default, it will try
|
||||
to write to `Untitled.xls` in the Downloads folder.
|
||||
|
||||
:::note pass
|
||||
@ -341,27 +387,51 @@ If there is no default name, enter `Untitled.xls` and click "Save".
|
||||
The app will show a popup once the data is exported. Open the file in a
|
||||
spreadsheet editor and compare the data to the table shown in the application.
|
||||
|
||||
#### Drag and Drop
|
||||
|
||||
#### Open with menu
|
||||
9) Close the application, end the terminal process and re-launch (see step 6)
|
||||
|
||||
10) Open the Downloads folder in a file explorer or finder window.
|
||||
|
||||
11) Click and drag the `pres.numbers` file from the Downloads folder to the
|
||||
bordered "Drop a spreadsheet file" box. The file data should be displayed.
|
||||
11) Right-click the `pres.numbers` file and select "Open with".
|
||||
|
||||
#### File Input Element
|
||||
12) Select your application binary by navigating to the folder where the application was built (see step 4).
|
||||
|
||||
12) Close the application, end the terminal process and re-launch (see step 6)
|
||||
:::info
|
||||
On some Linux based systems, depending on the file manager in use selecting the binary directly may not be possible.
|
||||
:::
|
||||
|
||||
13) Click "Choose File". With the file picker, navigate to the Downloads folder
|
||||
|
||||
The application should show a dropdown component for each worksheet contained in your file, clicking on it should display its data within a table.
|
||||
|
||||
#### Drag and Drop
|
||||
|
||||
13) Close the application, end the terminal process and re-launch (see step 6)
|
||||
|
||||
14) Open the Downloads folder in a file explorer or finder window.
|
||||
|
||||
15) Click and drag the `pres.numbers` file from the Downloads folder
|
||||
into the application window.
|
||||
|
||||
The application should show a dropdown component for each worksheet contained in your file, clicking on it should display its data within a table.
|
||||
|
||||
:::info
|
||||
On some Linux based systems, the experience can differ depending on the window manager / desktop environment in use.
|
||||
:::
|
||||
|
||||
#### File Picker Element
|
||||
|
||||
16) Close the application, end the terminal process and re-launch (see step 6)
|
||||
|
||||
17) Click "Choose File". With the file picker, navigate to the Downloads folder
|
||||
and select `pres.numbers`.
|
||||
|
||||
The application should show a dropdown component for each worksheet contained in your file, clicking on it should display its data within a table.
|
||||
|
||||
|
||||
## Electron Breaking Changes
|
||||
|
||||
The first version of this demo used Electron `1.7.5`. The current demo includes
|
||||
the required changes for Electron `35.1.2`.
|
||||
the required changes for Electron `36.1.0`.
|
||||
|
||||
There are no Electron-specific workarounds in the library, but Electron broke
|
||||
backwards compatibility multiple times. A summary of changes is noted below.
|
||||
@ -389,4 +459,8 @@ Electron 14 and later must use `@electron/remote` instead of `remote`. An
|
||||
|
||||
:::
|
||||
|
||||
For demos built on top of Electron 36 and later we isolate the processes entirely and the demo no longer requires `@electron/remote`.
|
||||
However, `nodeIntegration: false` by default now means that the renderer process no longer has access to NodeJS APIs.
|
||||
To expose NodeJS APIs to the renderer process, we use the contextBridge API to expose APIs from the main process to the renderer process. [See more](https://www.electronjs.org/docs/latest/api/context-bridge). This has been best practice since Electron 25.
|
||||
|
||||
[^1]: See ["Makers"](https://www.electronforge.io/config/makers) in the Electron Forge documentation. On Linux, the demo generates `rpm` and `deb` distributables. On Arch Linux and the Steam Deck, `sudo pacman -Syu rpm-tools dpkg fakeroot` installed required packages. On Debian and Ubuntu, `sudo apt-get install rpm` sufficed.
|
@ -39,7 +39,7 @@ This demo was tested in the following deployments:
|
||||
|:-------------|:---------------|:----------|:----------|:-----------|
|
||||
| `darwin-x64` | `5.0.0-beta.4` | `14.15.3` | Pre-built | 2025-04-21 |
|
||||
| `darwin-arm` | `4.0.0-rc.6` | `22.14.0` | Compiled | 2025-04-03 |
|
||||
| `win11-x64` | `5.0.0-beta.4` | `14.15.3` | Pre-built | 2025-05-07 |
|
||||
| `win11-x64` | `4.0.0-rc.6` | `14.15.3` | Pre-built | 2024-12-19 |
|
||||
| `win11-arm` | `4.0.0-rc.6` | `22.14.0` | Compiled | 2025-02-23 |
|
||||
| `linux-x64` | `5.0.0-beta.4` | `14.15.3` | Pre-built | 2025-04-21 |
|
||||
| `linux-arm` | `4.0.0-rc.6` | `22.13.0` | Compiled | 2025-02-15 |
|
||||
|
@ -38,8 +38,8 @@ This demo was tested in the following deployments:
|
||||
| Architecture | Version | NodeJS | Date |
|
||||
|:-------------|:--------|:---------|:-----------|
|
||||
| `darwin-x64` | `5.8.1` | `18.5.0` | 2025-04-21 |
|
||||
| `darwin-arm` | `5.8.1` | `18.5.0` | 2025-05-11 |
|
||||
| `win11-x64` | `5.8.1` | `18.5.0` | 2025-05-07 |
|
||||
| `darwin-arm` | `5.8.1` | `18.5.0` | 2025-02-13 |
|
||||
| `win11-x64` | `5.8.1` | `18.5.0` | 2024-12-19 |
|
||||
| `win11-arm` | `5.8.1` | `18.5.0` | 2025-02-23 |
|
||||
| `linux-x64` | `5.8.1` | `18.5.0` | 2025-04-21 |
|
||||
| `linux-arm` | `5.8.1` | `18.5.0` | 2025-02-15 |
|
||||
@ -78,7 +78,7 @@ When this demo was last tested, `pkg` failed with an error referencing `node20`:
|
||||
> Error! No available node version satisfies 'node20'
|
||||
```
|
||||
|
||||
**`pkg` does not support NodeJS 20 or 22 or 24!**
|
||||
**`pkg` does not support NodeJS 20 or 22!**
|
||||
|
||||
The local NodeJS version must be rolled back to version 18.
|
||||
|
||||
|
@ -32,7 +32,7 @@ This demo was tested in the following deployments:
|
||||
|:-------------|:--------|:----------|:-----------|
|
||||
| `darwin-x64` | `2.4.4` | `23.11.0` | 2025-04-21 |
|
||||
| `darwin-arm` | `2.4.4` | `22.14.0` | 2025-04-03 |
|
||||
| `win11-x64` | `2.4.4` | `16.20.2` | 2025-05-07 |
|
||||
| `win11-x64` | `2.4.4` | `16.20.2` | 2024-12-19 |
|
||||
| `linux-x64` | `2.4.4` | `23.11.0` | 2025-04-21 |
|
||||
| `linux-arm` | `2.4.4` | `23.8.0` | 2025-02-15 |
|
||||
|
||||
@ -136,7 +136,7 @@ workload, Python 3.11, and NASM[^2].
|
||||
|
||||
:::caution pass
|
||||
|
||||
In some test runs, the build failed:
|
||||
When the demo was last tested, the build failed:
|
||||
|
||||
```
|
||||
Not an executable Python program
|
||||
@ -168,11 +168,8 @@ When the demo was last tested on Windows, the build failed:
|
||||
error MSB8020: The build tools for Visual Studio 2019 (Platform Toolset = 'v142') cannot be found. To build using the v142 build tools, please install Visual Studio 2019 build tools.
|
||||
```
|
||||
|
||||
This error was fixed by installing the following components from Visual Studio:
|
||||
|
||||
- `C++/CLI support for v142 build tools`
|
||||
- `MSVC v142 - VS 2019 - C++ x64/x86 build tools`
|
||||
- `MSVC v142 - VS 2019 - C++ x64/x86 Spectre-mitigated libs`
|
||||
This error was fixed by installing the `v142` build tools through the Visual
|
||||
Studio installer.
|
||||
|
||||
:::
|
||||
|
||||
|
@ -81,7 +81,7 @@ This demo was last tested in the following deployments:
|
||||
|:-------------|:---------|:-----------|
|
||||
| `darwin-x64` | `1.2.10` | 2025-04-21 |
|
||||
| `darwin-arm` | `1.2.8` | 2025-04-03 |
|
||||
| `win11-x64` | `1.2.13` | 2025-05-07 |
|
||||
| `win11-x64` | `1.1.40` | 2024-12-19 |
|
||||
| `win11-arm` | `1.2.3` | 2025-02-23 |
|
||||
| `linux-x64` | `1.1.43` | 2025-01-10 |
|
||||
| `linux-arm` | `1.2.2` | 2025-02-16 |
|
||||
|
@ -228,7 +228,7 @@ order of todo items in the export matches the list displayed in the webpage.
|
||||
5) Start a local web server:
|
||||
|
||||
```bash
|
||||
npx -y http-server .
|
||||
npx http-server .
|
||||
```
|
||||
|
||||
The command will display a URL (typically `http://localhost:8080`) which can be
|
||||
|
@ -35,7 +35,7 @@ will be available in the future.
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was last tested on 2025-05-11.
|
||||
This demo was last tested on 2024-06-12.
|
||||
|
||||
:::
|
||||
|
||||
@ -59,8 +59,7 @@ Azure Functions Core Tools (`func`) telemetry is controlled through the
|
||||
<Tabs groupId="os">
|
||||
<TabItem value="unix" label="Linux/MacOS">
|
||||
|
||||
Add the following line to `.profile`, `.bash_profile`, `.bashrc`, `.zprofile`,
|
||||
`.zshrc`, and any other configuration files:
|
||||
Add the following line to `.profile`, `.bashrc` and `.zshrc`:
|
||||
|
||||
```bash
|
||||
export FUNCTIONS_CORE_TOOLS_TELEMETRY_OPTOUT=1
|
||||
@ -132,7 +131,7 @@ Uploaded files can be pulled into `ArrayBuffer` objects.
|
||||
|
||||
This function returns a promise that resolves to an `ArrayBuffer` object:
|
||||
|
||||
```js title="Get raw data from an uploaded file"
|
||||
```js
|
||||
const { Blob } = require('buffer');
|
||||
|
||||
async function get_file_from_request(request, form_field_name) {
|
||||
@ -159,7 +158,7 @@ SheetJS workbook objects[^3] which can be processed with other API functions.
|
||||
For example, a handler can use `sheet_to_csv`[^4] to generate CSV text from
|
||||
user-submitted spreadsheets:
|
||||
|
||||
```js title="Function that reads uploaded workbooks and converts to CSV"
|
||||
```js
|
||||
const { Blob } = require('buffer');
|
||||
const { app } = require('@azure/functions');
|
||||
const XLSX = require('xlsx');
|
||||
@ -195,7 +194,7 @@ The following example generates a sample worksheet using the `aoa_to_sheet`[^6]
|
||||
method, generates a sample workbook using worksheet helper methods[^7], writes
|
||||
the workbook to XLSX format in a Buffer, and sends the Buffer in the response:
|
||||
|
||||
```js title="Function that exports data and initiates a download to XLSX"
|
||||
```js
|
||||
const { app } = require('@azure/functions');
|
||||
const XLSX = require('xlsx');
|
||||
|
||||
@ -300,20 +299,10 @@ Until the bugs are resolved, JavaScript should be preferred over TypeScript.
|
||||
npm start
|
||||
```
|
||||
|
||||
The process will display the functions and respective URLs:
|
||||
|
||||
```text title="Expected output"
|
||||
Functions:
|
||||
|
||||
SheetJSAzure: [GET,POST] http://localhost:7071/api/SheetJSAzure
|
||||
(access this URL)------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
```
|
||||
|
||||
7) While the server is running, open a new terminal window and make a GET
|
||||
request to the URL listed in the previous step:
|
||||
7) While the server is running, open a new terminal window and make a request:
|
||||
|
||||
```bash
|
||||
curl http://localhost:7071/api/SheetJSAzure
|
||||
curl -L http://localhost:7071/api/SheetJSAzure
|
||||
```
|
||||
|
||||
The terminal should display `Hello, world!`
|
||||
@ -344,7 +333,7 @@ npm start
|
||||
make a POST request to the dev server:
|
||||
|
||||
```bash
|
||||
curl -o pres.numbers https://docs.sheetjs.com/pres.numbers
|
||||
curl -LO https://docs.sheetjs.com/pres.numbers
|
||||
curl -X POST -F "upload=@pres.numbers" http://localhost:7071/api/SheetJSAzure
|
||||
```
|
||||
|
||||
@ -365,20 +354,19 @@ Open in Excel or another spreadsheet editor to confirm the file is valid.
|
||||
|
||||
15) Click "+ Create"
|
||||
|
||||
16) In the "Create Function App" screen, click "Consumption" and click "Select".
|
||||
16) Select the following options:
|
||||
|
||||
17) In the next screen, select the following options:
|
||||
- "Select a hosting option": "Consumption"
|
||||
|
||||
- Look for the "Function App name" input field and type a memorable function
|
||||
name. When this demo was last tested, the name "sheetjsazure" was chosen.
|
||||
- Type a memorable "Function Name" ("sheetjsazure" when last tested)
|
||||
|
||||
- "Operating System": "Windows"
|
||||
|
||||
- "Runtime stack": select `Node.js`
|
||||
|
||||
18) Click "Review + create", then click "Create" to create the function.
|
||||
17) Click "Review + create", then click "Create" to create the function.
|
||||
|
||||
The page will redirect to a new page. It will display a status message:
|
||||
The page will display a status message
|
||||
|
||||
> ... Deployment is in progress
|
||||
|
||||
@ -386,19 +374,21 @@ When the resources are configured, the status will change to
|
||||
|
||||
> Your deployment is complete
|
||||
|
||||
19) Click "Go to Resource".
|
||||
18) Click "Go to Resource".
|
||||
|
||||
19) Take note of the URL from the "Essentials" table.
|
||||
|
||||
#### Deploy to Azure
|
||||
|
||||
20) Sign into Azure from the command line:
|
||||
20) Sign into Azure:
|
||||
|
||||
```bash
|
||||
```
|
||||
az login
|
||||
```
|
||||
|
||||
The login flow resumes in the browser.
|
||||
|
||||
21) Deploy to Azure. Replace `FUNCTION_NAME` with the name from Step 17:
|
||||
21) Deploy to Azure. Replace `FUNCTION_NAME` with the name from Step 16:
|
||||
|
||||
```bash
|
||||
func azure functionapp publish FUNCTION_NAME
|
||||
@ -439,7 +429,7 @@ make a POST request to the production server. Replace `FUNCTION_URL` with the
|
||||
Invoke URL from Step 21:
|
||||
|
||||
```bash
|
||||
curl -o pres.numbers https://docs.sheetjs.com/pres.numbers
|
||||
curl -LO https://docs.sheetjs.com/pres.numbers
|
||||
curl -X POST -F "upload=@pres.numbers" FUNCTION_URL
|
||||
```
|
||||
|
||||
@ -556,9 +546,9 @@ requests and 2000 write requests per month.
|
||||
|
||||
- "Redundancy": select LRS (Locally-redundant storage)
|
||||
|
||||
5) Click "Review + create", then click "Create" to create the storage.
|
||||
5) Click "Review", then click "Create" to create the storage.
|
||||
|
||||
The page will redirect to a new page. It will display a status message:
|
||||
The page will display a status message
|
||||
|
||||
> ... Deployment is in progress
|
||||
|
||||
@ -606,64 +596,11 @@ npm init -y
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @azure/storage-blob`}
|
||||
</CodeBlock>
|
||||
|
||||
14) Save the following codeblock to `SheetJSReadFromAzure.mjs`:
|
||||
14) Copy the [`SheetJSReadFromAzure.mjs` code block](#downloading-data) and save
|
||||
to `SheetJSReadFromAzure.mjs`.
|
||||
|
||||
```js title="SheetJSReadFromAzure.mjs"
|
||||
import { BlobServiceClient } from "@azure/storage-blob";
|
||||
import { read, utils } from "xlsx";
|
||||
|
||||
/* replace these constants */
|
||||
// highlight-start
|
||||
const connStr = "<REPLACE WITH CONNECTION STRING>";
|
||||
const containerName = "<REPLACE WITH CONTAINER NAME>";
|
||||
// highlight-end
|
||||
|
||||
/* Blob name */
|
||||
const blobName = "SheetJSBloblobber.xlsx";
|
||||
|
||||
/* get a readable stream*/
|
||||
const blobServiceClient = BlobServiceClient.fromConnectionString(connStr);
|
||||
const containerClient = blobServiceClient.getContainerClient(containerName);
|
||||
const blobClient = containerClient.getBlobClient(blobName);
|
||||
const response = (await blobClient.download()).readableStreamBody;
|
||||
|
||||
/* collect data into a Buffer */
|
||||
const bufs = [];
|
||||
for await(const buf of response) bufs.push(buf);
|
||||
const downloaded = Buffer.concat(bufs);
|
||||
|
||||
/* parse downloaded buffer */
|
||||
const wb = read(downloaded);
|
||||
/* print first worksheet */
|
||||
console.log(utils.sheet_to_csv(wb.Sheets[wb.SheetNames[0]]));
|
||||
```
|
||||
|
||||
15) Save the following codeblock to `SheetJSWriteToAzure.mjs`:
|
||||
|
||||
```js title="SheetJSWriteToAzure.mjs"
|
||||
import { BlobServiceClient } from "@azure/storage-blob";
|
||||
import { write, utils } from "xlsx";
|
||||
|
||||
/* replace these constants */
|
||||
// highlight-start
|
||||
const connStr = "<REPLACE WITH CONNECTION STRING>";
|
||||
const containerName = "<REPLACE WITH CONTAINER NAME>";
|
||||
// highlight-end
|
||||
|
||||
/* Blob name */
|
||||
const blobName = "SheetJSBloblobber.xlsx";
|
||||
|
||||
/* Create a simple workbook and write XLSX to buffer */
|
||||
const ws = utils.aoa_to_sheet(["SheetJS".split(""), [5,4,3,3,7,9,5]]);
|
||||
const wb = utils.book_new(); utils.book_append_sheet(wb, ws, "Sheet1");
|
||||
const buf = write(wb, {type: "buffer", bookType: "xlsx"});
|
||||
|
||||
/* upload buffer */
|
||||
const blobServiceClient = BlobServiceClient.fromConnectionString(connStr);
|
||||
const containerClient = blobServiceClient.getContainerClient(containerName);
|
||||
const blockBlobClient = containerClient.getBlockBlobClient(blobName);
|
||||
const uploadBlobResponse = await blockBlobClient.upload(buf, buf.length);
|
||||
```
|
||||
15) Copy the [`SheetJSWriteToAzure.mjs` code block](#uploading-data) and save
|
||||
to `SheetJSWriteToAzure.mjs`.
|
||||
|
||||
16) Edit both `SheetJSReadFromAzure.mjs` and `SheetJSWriteToAzure.mjs`:
|
||||
|
||||
@ -679,7 +616,7 @@ the buffer to a file named `SheetJSBloblobber.xlsx` on Azure Blob Storage.
|
||||
|
||||
The read demo fetches `SheetJSBloblobber.xlsx` and displays the data.
|
||||
|
||||
```text title="Data in SheetJSBloblobber.xlsx"
|
||||
```
|
||||
| A | B | C | D | E | F | G |
|
||||
---+---|---|---|---|---|---|---|
|
||||
1 | S | h | e | e | t | J | S |
|
||||
@ -704,7 +641,7 @@ node SheetJSReadFromAzure.mjs
|
||||
|
||||
It will fetch the file created in the previous step and display CSV rows.
|
||||
|
||||
```text title="Expected output"
|
||||
```
|
||||
S,h,e,e,t,J,S
|
||||
5,4,3,3,7,9,5
|
||||
```
|
||||
|
@ -111,7 +111,6 @@ const jwt = new google.auth.JWT({
|
||||
key: creds.private_key,
|
||||
scopes: [
|
||||
'https://www.googleapis.com/auth/spreadsheets', // Google Sheets
|
||||
'https://www.googleapis.com/auth/drive', // Google Drive
|
||||
'https://www.googleapis.com/auth/drive.file', // Google Drive
|
||||
]
|
||||
});
|
||||
@ -496,7 +495,7 @@ At this point `wb` is a SheetJS workbook object[^10].
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was last tested on 2025-05-14 using `googleapis` version `148.0.0`.
|
||||
This demo was last tested on 2024-06-08 using `googleapis` version `140.0.0`.
|
||||
The demo uses Sheets v4 and Drive v3 APIs.
|
||||
|
||||
:::
|
||||
@ -552,10 +551,10 @@ be a selection box. Click the `▼` icon to show the modal.
|
||||
|
||||
If the selection box is missing, expand the browser window.
|
||||
|
||||
3) Click "New project" in the top right corner of the modal.
|
||||
3) Click "NEW PROJECT" in the top right corner of the modal.
|
||||
|
||||
4) In the New Project screen, enter "SheetJS Test" in the Project name textbox
|
||||
and select "No organization" in the Location box. Click "Create".
|
||||
and select "No organization" in the Location box. Click "CREATE".
|
||||
|
||||
A notification will confirm that the project was created:
|
||||
|
||||
@ -570,13 +569,14 @@ The goal of this section is to enable Google Sheets API and Google Drive API.
|
||||
|
||||
:::
|
||||
|
||||
5) Click "Select a project" and select "SheetJS Test" from the Recent tab.
|
||||
5) Open the Project Selector (`▼` icon) and select "SheetJS Test"
|
||||
|
||||
6) In the search bar, type "Enabled" and select "Enabled APIs & services".
|
||||
6) In the search bar, type "Enabled" and select "Enabled APIs & services". This
|
||||
item will be in the "PRODUCTS & PAGES" part of the search results.
|
||||
|
||||
#### Enable Google Sheets API
|
||||
|
||||
7) Near the top of the page, click "+ Enable APIs and services".
|
||||
7) Near the top of the page, click "+ ENABLE APIS AND SERVICES".
|
||||
|
||||
8) In the search bar near the middle of the page (not the search bar at the top),
|
||||
type "Sheets" and press <kbd>Enter</kbd>.
|
||||
@ -585,11 +585,11 @@ In the results page, look for "Google Sheets API". Click the card
|
||||
|
||||
9) In the Product Details screen, click the blue "ENABLE" button.
|
||||
|
||||
10) Click the left arrow (`<-`) next to "API/Service Details".
|
||||
10) Click the left arrow (`<-`) next to "API/Service details".
|
||||
|
||||
#### Enable Google Drive API
|
||||
|
||||
11) Near the top of the page, click "+ Enable APIs and services".
|
||||
11) Near the top of the page, click "+ ENABLE APIS AND SERVICES".
|
||||
|
||||
12) In the search bar near the middle of the page (not the search bar at the top),
|
||||
type "Drive" and press <kbd>Enter</kbd>.
|
||||
@ -614,13 +614,13 @@ the top bar.
|
||||
15) Click the Project Selector (`:·` icon) and select "SheetJS Test".
|
||||
|
||||
16) In the search bar, type "Credentials" and select the "Credentials" item with
|
||||
subtitle "APIs & Services":
|
||||
subtitle "APIs & Services". This item will be in the "PRODUCTS & PAGES" group:
|
||||
|
||||

|
||||
|
||||
17) Click "+ Create credentials". In the dropdown, select "Service account"
|
||||
17) Click "+ CREATE CREDENTIALS". In the dropdown, select "Service Account"
|
||||
|
||||
18) Enter "SheetJService" for Service account name. Click "Create and continue".
|
||||
18) Enter "SheetJService" for Service account name. Click "CREATE AND CONTINUE"
|
||||
|
||||
:::note pass
|
||||
|
||||
@ -628,24 +628,24 @@ The Service account ID is generated automatically.
|
||||
|
||||
:::
|
||||
|
||||
19) In Step 2 "Grant this service account access to project", click Continue.
|
||||
19) In Step 2 "Grant this service account access to project", click CONTINUE
|
||||
|
||||
20) In Step 3 click "Done". You will be taken back to the credentials screen
|
||||
20) In Step 3 click "DONE". You will be taken back to the credentials screen
|
||||
|
||||
#### Create JSON Key
|
||||
|
||||
21) Look for "SheetJService" in the "Service Accounts" table and click the email
|
||||
address in the row.
|
||||
|
||||
22) Click "Keys" in the horizontal bar near the top of the page.
|
||||
22) Click "KEYS" in the horizontal bar near the top of the page.
|
||||
|
||||
23) Click "Add key" and select "Create new key" in the dropdown.
|
||||
23) Click "ADD KEY" and select "Create new key" in the dropdown.
|
||||
|
||||
24) In the popup, select the "JSON" radio button and click "Create".
|
||||
24) In the popup, select the "JSON" radio button and click "CREATE".
|
||||
|
||||
The page will download a JSON file. If prompted, allow the download.
|
||||
|
||||
25) Click "Close"
|
||||
25) Click "CLOSE"
|
||||
|
||||
### Create Document
|
||||
|
||||
@ -675,7 +675,7 @@ npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz googlea
|
||||
29) Download [`init.mjs`](pathname:///gsheet/init.mjs):
|
||||
|
||||
```bash
|
||||
curl -o init.mjs https://docs.sheetjs.com/gsheet/init.mjs
|
||||
curl -LO https://docs.sheetjs.com/gsheet/init.mjs
|
||||
```
|
||||
|
||||
Edit the marked lines near the top of the file:
|
||||
@ -713,12 +713,11 @@ Shared a-long-string-of-characters with YOUR_ACCOUNT@gmail.com
|
||||
The long string of characters after "Created Google Workbook" is the ID. Take
|
||||
note of this ID.
|
||||
|
||||
31) Sign into Google Drive and select "Shared with me" from the left sidebar. A
|
||||
shared document "SheetJS Test" should be displayed in the table. It will be
|
||||
owned by the service account.
|
||||
31) Sign into Google Sheets. A shared document "SheetJS Test" should be
|
||||
displayed in the table. It will be owned by the service account.
|
||||
|
||||
32) Click `⋮` next to "SheetJS Test" and select "Open with" > "Google Sheets".
|
||||
Confirm that the document has two worksheets named "SheetJS1" and "SheetJS2".
|
||||
32) Open the shared document from step 31 and confirm that the document has two
|
||||
worksheets named "SheetJS1" and "SheetJS2".
|
||||
|
||||
Confirm the worksheet data matches the following screenshots:
|
||||
|
||||
@ -787,13 +786,13 @@ NUMBERS file.
|
||||
34) Download the [test file `pres.numbers`](https://docs.sheetjs.com/pres.numbers):
|
||||
|
||||
```bash
|
||||
curl -o pres.numbers https://docs.sheetjs.com/pres.numbers
|
||||
curl -LO https://docs.sheetjs.com/pres.numbers
|
||||
```
|
||||
|
||||
35) Download [`load.mjs`](pathname:///gsheet/load.mjs):
|
||||
|
||||
```bash
|
||||
curl -o load.mjs https://docs.sheetjs.com/gsheet/load.mjs
|
||||
curl -LO https://docs.sheetjs.com/gsheet/load.mjs
|
||||
```
|
||||
|
||||
Edit the marked lines near the top of the file:
|
||||
@ -831,7 +830,7 @@ The goal of this section is to export the raw data from Google Sheets to XLSB.
|
||||
38) Download [`dump.mjs`](pathname:///gsheet/dump.mjs):
|
||||
|
||||
```bash
|
||||
curl -o dump.mjs https://docs.sheetjs.com/gsheet/dump.mjs
|
||||
curl -LO https://docs.sheetjs.com/gsheet/dump.mjs
|
||||
```
|
||||
|
||||
Edit the marked lines near the top of the file:
|
||||
@ -877,7 +876,7 @@ assign a grid of values
|
||||
43) Download [`raw.mjs`](pathname:///gsheet/raw.mjs):
|
||||
|
||||
```bash
|
||||
curl -o raw.mjs https://docs.sheetjs.com/gsheet/raw.mjs
|
||||
curl -LO https://docs.sheetjs.com/gsheet/raw.mjs
|
||||
```
|
||||
|
||||
Edit the marked lines near the top of the file:
|
||||
|
@ -266,7 +266,7 @@ function SheetJSEnregistrez() {
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was last tested on 2025-05-14.
|
||||
This demo was last tested on 2024-05-27.
|
||||
|
||||
:::
|
||||
|
||||
|
@ -1,336 +0,0 @@
|
||||
---
|
||||
title: Visualizing Data in VS Code
|
||||
sidebar_label: Visual Studio Code
|
||||
description: View Excel files directly in VS Code. Seamlessly browse spreadsheet data without leaving your editor using SheetJS. Navigate between worksheets and pages of data with a responsive
|
||||
pagination_prev: demos/cloud/index
|
||||
pagination_next: demos/bigdata/index
|
||||
sidebar_custom_props:
|
||||
summary: View Excel files directly within Visual Studio Code
|
||||
---
|
||||
|
||||
import current from '/version.js';
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
import CodeBlock from '@theme/CodeBlock';
|
||||
|
||||
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
|
||||
data from spreadsheets.
|
||||
|
||||
[Visual Studio Code](https://code.visualstudio.com) is a popular code editor
|
||||
that supports JavaScript extensions for customizing and enhancing functionality.
|
||||
|
||||
The ["Complete Example"](#complete-example) uses SheetJS in a VS Code extension
|
||||
to view Excel files directly within the editor. The extension leverages the VS
|
||||
Code "Custom Editor API"[^2] and "WebView API"[^1] to display spreadsheet data
|
||||
as HTML tables.
|
||||
|
||||
:::tip pass
|
||||
|
||||
["SheetJS Spreadsheet Viewer"](https://marketplace.visualstudio.com/items?itemName=asadbek.sheetjs-demo)
|
||||
is a sample extension based on this demo.
|
||||
|
||||
[The source code](https://git.sheetjs.com/asadbek064/sheetjs-vscode-extension)
|
||||
is available on the SheetJS Git server. Feedback and contributions are welcome!
|
||||
|
||||
:::
|
||||
|
||||

|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was verified in the following deployments:
|
||||
|
||||
| Platform | Architecture | Date |
|
||||
|:-----------------|:-------------|:-----------|
|
||||
| VS Code 1.100.0 | `darwin-arm` | 2025-05-15 | TODO
|
||||
| VSCodium 1.100.0 | `darwin-arm` | 2025-05-15 | TODO
|
||||
| Cursor | `win11-arm` | 2025-05-15 | TODO
|
||||
| Windsurf | `win11-arm` | 2025-05-15 | TODO
|
||||
| Void | `win11-arm` | 2025-05-15 | TODO
|
||||
|
||||
:::
|
||||
|
||||
## Integration Details
|
||||
|
||||
The [SheetJS NodeJS Module](/docs/getting-started/installation/nodejs) can be
|
||||
imported from any component or script in the extension.
|
||||
|
||||
:::caution pass
|
||||
|
||||
The module must be installed as a development dependency. If the module is
|
||||
installed as a normal dependency, `vsce`[^5] (Visual Studio Code Extension
|
||||
Manager) will fail to package or publish your extension correctly.
|
||||
|
||||
<Tabs groupId="pm">
|
||||
<TabItem value="npm" label="npm">
|
||||
<CodeBlock language="bash">{`\
|
||||
npm rm --save xlsx
|
||||
npm i --save-dev https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
|
||||
</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="pnpm" label="pnpm">
|
||||
<CodeBlock language="bash">{`\
|
||||
pnpm rm xlsx
|
||||
pnpm install -D https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
|
||||
</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="yarn" label="Yarn" default>
|
||||
<CodeBlock language="bash">{`\
|
||||
yarn remove xlsx
|
||||
yarn add -D https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
|
||||
</CodeBlock>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
:::
|
||||
|
||||
## Extension Architecture
|
||||
|
||||
The VS Code Spreadsheet viewer extension has three main components:
|
||||
|
||||
- **Extension Entry Point:** Registers the extension with VS Code
|
||||
- **Custom Editor Provider:** Handles Excel files and converts them to web content
|
||||
- **WebView Content:** Displays Excel data as HTML tables
|
||||
|
||||
The extension uses VS Code's `Custom Editor API`[^2] to register as a handler for Excel files. When a file is opened,
|
||||
SheetJS parses it and displays the data in a WebView component.
|
||||
|
||||
### Extension Entry Point
|
||||
|
||||
The main entry point registers the custom editor provider:
|
||||
|
||||
```ts title="src/extension.ts"
|
||||
import * as vscode from 'vscode';
|
||||
// highlight-start
|
||||
import { ExcelEditorProvider } from './excelEditorProvider';
|
||||
// highlight-end
|
||||
|
||||
export function activate(context: vscode.ExtensionContext) {
|
||||
// SheetJS Spreadsheet Viewer extension activating...
|
||||
// highlight-start
|
||||
const provider = ExcelEditorProvider.register(context);
|
||||
context.subscriptions.push(provider);
|
||||
// highlight-end
|
||||
}
|
||||
export function deactivate() {}`}
|
||||
```
|
||||
|
||||
The `custom editor`[^3] is configured to support specific file types, giving us complete control over how each file is
|
||||
presented to the user. Additionally, `custom document`[^4] enables us to maintain and persist the state of each individual
|
||||
file that's opened.
|
||||
|
||||
<CodeBlock language="typescript" value="typescript" title="src/excelEditorProvider.ts">
|
||||
{`import * as vscode from 'vscode';
|
||||
// highlight-start
|
||||
import * as XLSX from 'xlsx';
|
||||
import { ExcelDocument } from './excelDocument';
|
||||
// highlight-end
|
||||
|
||||
// A simple class to store document state (one per opened file)
|
||||
class ExcelDocument implements vscode.CustomDocument {
|
||||
constructor(public readonly uri: vscode.Uri) {}
|
||||
dispose() {}
|
||||
}
|
||||
|
||||
export class ExcelEditorProvider implements vscode.CustomReadonlyEditorProvider<ExcelDocument> {
|
||||
// ...
|
||||
public static register(context: vscode.ExtensionContext): vscode.Disposable {
|
||||
return vscode.window.registerCustomEditorProvider(
|
||||
'excelViewer.spreadsheet',
|
||||
new ExcelEditorProvider(),
|
||||
{ webviewOptions: { retainContextWhenHidden: true } } // keep webview state when hidden
|
||||
);
|
||||
}
|
||||
// ...
|
||||
}`}
|
||||
</CodeBlock>
|
||||
|
||||
### Reading Files
|
||||
|
||||
The extension reads Excel files using the VS Code filesystem API and passes
|
||||
the data to SheetJS for parsing:
|
||||
|
||||
<CodeBlock language="typescript" value="typescript" title="src/excelEditorProvider.ts">
|
||||
{`export class ExcelEditorProvider implements vscode.CustomReadonlyEditorProvider<ExcelDocument> {
|
||||
// ...
|
||||
private async loadWorkbook(document: ExcelDocument, webviewPanel: vscode.WebviewPanel): Promise<XLSX.WorkBook> {
|
||||
const data: Uint8Array = await vscode.workspace.fs.readFile(document.uri);
|
||||
|
||||
const options: XLSX.ParsingOptions = {
|
||||
type: 'array',
|
||||
cellStyles: true,
|
||||
cellDates: true,
|
||||
};
|
||||
|
||||
return XLSX.read(new Uint8Array(data), options); // returns a XLSX.WorkBook
|
||||
}
|
||||
|
||||
// This is called when the first time an editor for a given resource is opened
|
||||
async openCustomDocument(uri: vscode.Uri): Promise<ExcelDocument> {
|
||||
return new ExcelDocument(uri);
|
||||
}
|
||||
|
||||
// This is called whenever the user opens a new editor
|
||||
async resolveCustomEditor(document: ExcelDocument, webviewPanel: vscode.WebviewPanel): Promise<void> {
|
||||
const wb: XLSX.WorkBook = await this.loadWorkbook(document, webviewPanel);
|
||||
const htmlTable = XLSX.utils.sheet_to_html(wb.Sheets[wb.SheetNames[0]]);
|
||||
webviewPanel.webview.html = \`<!DOCTYPE html><html><body>\${htmlTable}</body></html>\`;
|
||||
}
|
||||
}`}
|
||||
</CodeBlock>
|
||||
|
||||
### Usage Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
actor User
|
||||
participant VSCode as VS Code
|
||||
participant Provider as ExcelEditorProvider
|
||||
participant SheetJS
|
||||
participant WebView
|
||||
|
||||
User->>VSCode: Open .xlsx file
|
||||
VSCode->>Provider: openCustomDocument(uri)
|
||||
Provider-->>VSCode: return ExcelDocument
|
||||
VSCode->>Provider: resolveCustomEditor(document, webviewPanel)
|
||||
Provider->>VSCode: workspace.fs.readFile(document.uri)
|
||||
VSCode-->>Provider: return file data
|
||||
Provider->>SheetJS: XLSX.read(data, options)
|
||||
SheetJS-->>Provider: return workbook
|
||||
Provider->>SheetJS: XLSX.utils.sheet_to_html(sheet)
|
||||
SheetJS-->>Provider: return HTML
|
||||
Provider->>WebView: set webview.html
|
||||
WebView-->>User: Display Excel data
|
||||
```
|
||||
|
||||
## Complete Example
|
||||
|
||||
1) Create a new VS Code extension
|
||||
|
||||
```bash
|
||||
npx --package yo --package generator-code -- yo code
|
||||
```
|
||||
|
||||
When prompted, enter the following options:
|
||||
|
||||
- `What type of extension do you want to create?`: Select `New Extension (TypeScript)` and press <kbd>Enter</kbd>
|
||||
- `What's the name of your extension?`: Type `sheetjs-demo` and press <kbd>Enter</kbd>
|
||||
- `What's the identifier of your extension?`: Press <kbd>Enter</kbd>
|
||||
- `What's the description of your extension?`: Press <kbd>Enter</kbd>
|
||||
- `Initialize a git repository?`: Type `n` and press <kbd>Enter</kbd>
|
||||
- `Which bundler to use?`: Select `webpack` and press <kbd>Enter</kbd>
|
||||
- `Which package manager to use?`: Select `pnpm` and press <kbd>Enter</kbd>
|
||||
|
||||

|
||||
|
||||
- `Do you want to open the new folder with Visual Studio Code?`: Press <kbd>Enter</kbd>
|
||||
|
||||
2) [Install the dependencies](#integration-details) and start the dev server:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
cd sheetjs-demo
|
||||
pnpm install -D https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz
|
||||
pnpm run watch
|
||||
`}
|
||||
</CodeBlock>
|
||||
|
||||
3) Save the following code snippet to `src/excelEditorProvider.ts`:
|
||||
|
||||
<CodeBlock language="typescript" value="typescript" title="src/excelEditorProvider.ts">{`\
|
||||
import * as vscode from 'vscode';
|
||||
import * as XLSX from 'xlsx';
|
||||
|
||||
class ExcelDocument implements vscode.CustomDocument {
|
||||
constructor(public readonly uri: vscode.Uri) { }
|
||||
dispose() { }
|
||||
}
|
||||
|
||||
export class ExcelEditorProvider implements vscode.CustomReadonlyEditorProvider<ExcelDocument> {
|
||||
public static register(context: vscode.ExtensionContext): vscode.Disposable {
|
||||
return vscode.window.registerCustomEditorProvider(
|
||||
'excelViewer.spreadsheet',
|
||||
new ExcelEditorProvider(),
|
||||
{ webviewOptions: { retainContextWhenHidden: true } } // keep webview state when hidden
|
||||
);
|
||||
}
|
||||
|
||||
private async loadWorkbook(document: ExcelDocument, webviewPanel: vscode.WebviewPanel): Promise<XLSX.WorkBook> {
|
||||
const data: Uint8Array = await vscode.workspace.fs.readFile(document.uri);
|
||||
|
||||
const options: XLSX.ParsingOptions = {
|
||||
type: 'array',
|
||||
cellStyles: true,
|
||||
cellDates: true,
|
||||
};
|
||||
|
||||
return XLSX.read(new Uint8Array(data), options); // returns a XLSX.WorkBook
|
||||
}
|
||||
|
||||
// This is called when the first time an editor for a given resource is opened
|
||||
async openCustomDocument(uri: vscode.Uri): Promise<ExcelDocument> {
|
||||
return new ExcelDocument(uri);
|
||||
}
|
||||
|
||||
// This is called whenever the user opens a new editor
|
||||
async resolveCustomEditor(document: ExcelDocument, webviewPanel: vscode.WebviewPanel): Promise<void> {
|
||||
const wb: XLSX.WorkBook = await this.loadWorkbook(document, webviewPanel);
|
||||
const htmlTable = XLSX.utils.sheet_to_html(wb.Sheets[wb.SheetNames[0]]);
|
||||
webviewPanel.webview.html = \`<!DOCTYPE html><html><body>\${htmlTable}</body></html>\`;
|
||||
}
|
||||
}`}
|
||||
</CodeBlock>
|
||||
|
||||
4) Register the custom editor provider in `src/extension.ts`:
|
||||
|
||||
<CodeBlock language="typescript" value="typescript" title="src/extension.ts (replace contents)">{`\
|
||||
import * as vscode from 'vscode';
|
||||
import { ExcelEditorProvider } from './excelEditorProvider';
|
||||
|
||||
export function activate(context: vscode.ExtensionContext) {
|
||||
// SheetJS Spreadsheet Viewer extension activating...
|
||||
const provider = ExcelEditorProvider.register(context);
|
||||
context.subscriptions.push(provider);
|
||||
}
|
||||
export function deactivate() {}`}
|
||||
</CodeBlock>
|
||||
|
||||
5) Register the custom editor in the `contributes` section of `package.json`:
|
||||
|
||||
<CodeBlock language="json" value="json" title="package.json (add highlighted lines)">{`\
|
||||
"main": "./dist/extension.js",
|
||||
"contributes": {
|
||||
// highlight-start
|
||||
"customEditors": [
|
||||
{
|
||||
"viewType": "excelViewer.spreadsheet",
|
||||
"displayName": "SheetJS Demo",
|
||||
"selector": [
|
||||
{ "filenamePattern": "*.xlsx" },
|
||||
{ "filenamePattern": "*.xls" }
|
||||
]
|
||||
}
|
||||
],
|
||||
// highlight-end
|
||||
"commands": [
|
||||
{
|
||||
"command": "sheetjs-demo.helloWorld",
|
||||
"title": "Hello World"
|
||||
}
|
||||
]
|
||||
},
|
||||
`}
|
||||
</CodeBlock>
|
||||
|
||||
6. Inside the editor, open `src/extension.ts` and press <kbd>F5</kbd> or run the command **Debug: Start Debugging**
|
||||
from the Command Palette (<kbd>⇧⌘P</kbd>). This will compile and run the extension in a new Extension Development Host window.
|
||||
|
||||
7. Select the new VSCode Window and open a `.xlsx` or `.xls` file.
|
||||
|
||||
|
||||
---
|
||||
|
||||
[^1]: See [`Webview API`](https://code.visualstudio.com/api/extension-guides/webview) for more details.
|
||||
[^2]: See [`Custom Editor API`](https://code.visualstudio.com/api/extension-guides/custom-editors) documentation for more details.
|
||||
[^3]: See [`Custom Editor`](https://code.visualstudio.com/api/extension-guides/custom-editors#custom-editor) for more details.
|
||||
[^4]: See [`CustomDocument`](https://code.visualstudio.com/api/extension-guides/custom-editors#customdocument) for more details.
|
||||
[^5]: See [`Visual Studio Code Extension Manager`](https://code.visualstudio.com/api/working-with-extensions/publishing-extension) for more details.
|
@ -55,7 +55,7 @@ This demo was tested in the following deployments:
|
||||
|:-------------|:------------------|:-----------|
|
||||
| `darwin-x64` | `18.5` (StataNow) | 2025-01-08 |
|
||||
| `darwin-arm` | `18.5` (StataNow) | 2025-04-24 |
|
||||
| `win11-x64` | `18.5` (StataNow) | 2025-04-28 |
|
||||
| `win11-x64` | `18.5` (StataNow) | 2024-12-19 |
|
||||
| `win11-arm` | `18.5` (StataNow) | 2025-02-23 |
|
||||
| `linux-x64` | `18.5` (StataNow) | 2025-01-09 |
|
||||
|
||||
@ -257,22 +257,6 @@ curl -LO https://www.stata.com/plugins/stplugin.c
|
||||
curl -LO https://www.stata.com/plugins/stplugin.h
|
||||
```
|
||||
|
||||
:::danger pass
|
||||
|
||||
**When this demo was last tested, `stplugin.h` was removed from the website!**
|
||||
|
||||
If the official links do not work, the following files should be used:
|
||||
|
||||
- [`stplugin.c`](pathname:///stata/stplugin.c)
|
||||
- [`stplugin.h`](pathname:///stata/stplugin.h)
|
||||
|
||||
```bash
|
||||
curl -LO https://docs.sheetjs.com/plugins/stplugin.c
|
||||
curl -LO https://docs.sheetjs.com/plugins/stplugin.h
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
4) Download Duktape. In Windows, the following commands should be run in WSL. In
|
||||
macOS, the commands should be run in the same Terminal session.
|
||||
|
||||
@ -479,32 +463,67 @@ The output will show the import result:
|
||||
(2 vars, 5 obs)
|
||||
</pre>
|
||||
|
||||
20) List the dataset:
|
||||
20) Open the Data Editor (in Browse or Edit mode) and compare to the screenshot:
|
||||
|
||||
```stata
|
||||
list Name Index
|
||||
browse Name Index
|
||||
```
|
||||
|
||||
The result should match the following listing:
|
||||
|
||||
```
|
||||
. list Name Index
|
||||
|
||||
+----------------------+
|
||||
| Name Index |
|
||||
|----------------------|
|
||||
1. | Bill Clinton 42 |
|
||||
2. | GeorgeW Bush 43 |
|
||||
3. | Barack Obama 44 |
|
||||
4. | Donald Trump 45 |
|
||||
5. | Joseph Biden 46 |
|
||||
+----------------------+
|
||||
```
|
||||
|
||||
In the Stata GUI, the Data Editor should match the following screenshot:
|
||||
|
||||

|
||||
|
||||
:::info pass
|
||||
|
||||
In the terminal version of Stata, `browse` does not work:
|
||||
|
||||
```
|
||||
. browse Name Index
|
||||
command browse is unrecognized
|
||||
r(199);
|
||||
```
|
||||
|
||||
The `codebook` command will display details.
|
||||
|
||||
<details>
|
||||
<summary><b>Expected Output</b> (click to show)</summary>
|
||||
|
||||
```text title="Expected output for 80-column terminal windows"
|
||||
-------------------------------------------------------------------------------
|
||||
Name Name
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
Type: String (str12)
|
||||
|
||||
Unique values: 5 Missing "": 0/5
|
||||
|
||||
Tabulation: Freq. Value
|
||||
1 "Barack Obama"
|
||||
1 "Bill Clinton"
|
||||
1 "Donald Trump"
|
||||
1 "GeorgeW Bush"
|
||||
1 "Joseph Biden"
|
||||
|
||||
Warning: Variable has embedded blanks.
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
Index Index
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
Type: Numeric (byte)
|
||||
|
||||
Range: [42,46] Units: 1
|
||||
Unique values: 5 Missing .: 0/5
|
||||
|
||||
Tabulation: Freq. Value
|
||||
1 42
|
||||
1 43
|
||||
1 44
|
||||
1 45
|
||||
1 46
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
:::
|
||||
|
||||
[^1]: Run `help import excel` in Stata or see ["import excel"](https://www.stata.com/manuals/dimportexcel.pdf) in the Stata documentation.
|
||||
[^2]: See [`read` in "Reading Files"](/docs/api/parse-options)
|
||||
|
@ -30,7 +30,7 @@ This demo was tested by SheetJS users in the following deployments:
|
||||
| Architecture | Version | Date |
|
||||
|:-------------|:--------|:-----------|
|
||||
| `darwin-x64` | R2024b | 2025-03-31 |
|
||||
| `win11-x64` | R2024b | 2025-05-10 |
|
||||
| `win11-x64` | R2024b | 2024-12-21 |
|
||||
|
||||
:::
|
||||
|
||||
@ -234,14 +234,14 @@ run in the macOS Terminal or Windows PowerShell:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz exit-on-epipe commander@2
|
||||
curl -o xlsx-cli.js https://docs.sheetjs.com/cli/xlsx-cli.js
|
||||
curl -LO https://docs.sheetjs.com/cli/xlsx-cli.js
|
||||
npx -y nexe -t 14.15.3 xlsx-cli.js`}
|
||||
</CodeBlock>
|
||||
|
||||
2) Download https://docs.sheetjs.com/pres.numbers to the workspace folder:
|
||||
|
||||
```bash
|
||||
curl -o pres.numbers https://docs.sheetjs.com/pres.numbers
|
||||
curl -LO https://docs.sheetjs.com/pres.numbers
|
||||
```
|
||||
|
||||
4) Save the following to `SheetJSMATLAB.m` in the workspace folder:
|
||||
|
@ -23,7 +23,7 @@ library to read data from spreadsheets and converts to a Maple-friendly format.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
ofile[(generic\nworkbook)]
|
||||
ofile[(workbook\nXLSB file)]
|
||||
nfile[(clean file\nXLSX)]
|
||||
data[[Maple\nTable]]
|
||||
ofile --> |Maple Extension\nSheetJS + Duktape| nfile
|
||||
@ -37,7 +37,7 @@ This demo was tested by SheetJS users in the following deployments:
|
||||
| Architecture | Version | Date |
|
||||
|:-------------|:---------|:-----------|
|
||||
| `darwin-x64` | `2024.0` | 2025-01-10 |
|
||||
| `win11-x64` | `2024.2` | 2025-05-07 |
|
||||
| `win11-x64` | `2024.0` | 2024-12-19 |
|
||||
|
||||
:::
|
||||
|
||||
@ -60,7 +60,7 @@ and generates clean XLSX files that Maple can import.
|
||||
The extension function ultimately pairs the SheetJS `read`[^2] and `write`[^3]
|
||||
methods to read data from the old file and write a new file:
|
||||
|
||||
```js title="Script that will be run by Maple extension"
|
||||
```js
|
||||
var workbook = XLSX.read(original_file_data, { type: "buffer" });
|
||||
var new_file_data = XLSX.write(workbook, { type: "array", bookType: "xlsx" });
|
||||
```
|
||||
@ -84,7 +84,6 @@ flowchart LR
|
||||
wb --> |SheetJS\n`write`| njbuf
|
||||
njbuf --> |Duktape\nBuffer Ops| nbuf
|
||||
nbuf --> |C\nWrite File| nfile
|
||||
linkStyle 2,3 color:blue,stroke:blue;
|
||||
```
|
||||
|
||||
### C Extensions
|
||||
@ -129,7 +128,7 @@ file, exports data to `sheetjsw.xlsx` and returns the string `"sheetjsw.xlsx"`.
|
||||
|
||||
This can be chained with `Import` from `ExcelTools`:
|
||||
|
||||
```maple title="Sample usage of SheetToXLSX extension"
|
||||
```maple
|
||||
with(ExcelTools);
|
||||
Import(SheetToXLSX("pres.numbers"))
|
||||
```
|
||||
|
@ -130,7 +130,7 @@ This demo was tested in the following deployments:
|
||||
|:-------------|:--------|:-----------|
|
||||
| `darwin-x64` | `2.7.0` | 2025-03-31 |
|
||||
| `darwin-arm` | `2.7.0` | 2025-02-13 |
|
||||
| `win11-x64` | `2.7.0` | 2025-04-28 |
|
||||
| `win11-x64` | `2.7.0` | 2024-12-20 |
|
||||
| `win11-arm` | `2.7.0` | 2025-02-23 |
|
||||
| `linux-x64` | `2.7.0` | 2025-04-21 |
|
||||
| `linux-arm` | `2.7.0` | 2025-02-15 |
|
||||
@ -692,7 +692,7 @@ The main Duktape code can be added to the Zig build pipeline.
|
||||
|
||||
:::note pass
|
||||
|
||||
The following explanation was verified against Zig 0.14.0.
|
||||
The following explanation was verified against Zig 0.12.0.
|
||||
|
||||
:::
|
||||
|
||||
@ -710,7 +710,7 @@ folder must be added to the include path list:
|
||||
```
|
||||
|
||||
The `duktape.c` source file must be added to the build sequence. For Zig version
|
||||
0.14.0, Duktape must be compiled with flags `-std=c99 -fno-sanitize=undefined`
|
||||
0.12.0, Duktape must be compiled with flags `-std=c99 -fno-sanitize=undefined`
|
||||
and linked against `libc` and `libm`:
|
||||
|
||||
```zig title="build.zig"
|
||||
@ -786,7 +786,7 @@ This demo was tested in the following deployments:
|
||||
|:-------------|:--------|:---------|:-----------|
|
||||
| `darwin-x64` | `2.7.0` | `0.14.0` | 2025-03-31 |
|
||||
| `darwin-arm` | `2.7.0` | `0.13.0` | 2025-02-13 |
|
||||
| `win11-x64` | `2.7.0` | `0.14.0` | 2025-04-28 |
|
||||
| `win11-x64` | `2.7.0` | `0.13.0` | 2024-12-20 |
|
||||
| `win11-arm` | `2.7.0` | `0.13.0` | 2025-02-23 |
|
||||
| `linux-x64` | `2.7.0` | `0.14.0` | 2025-04-21 |
|
||||
| `linux-arm` | `2.7.0` | `0.13.0` | 2025-02-15 |
|
||||
@ -855,8 +855,8 @@ The following commands should be run within WSL bash.
|
||||
For X64 Windows:
|
||||
|
||||
```bash
|
||||
curl -LO https://ziglang.org/download/0.14.0/zig-windows-x86_64-0.14.0.zip
|
||||
unzip zig-windows-x86_64-0.14.0.zip
|
||||
curl -LO https://ziglang.org/download/0.13.0/zig-windows-x86_64-0.13.0.zip
|
||||
unzip zig-windows-x86_64-0.13.0.zip
|
||||
```
|
||||
|
||||
For ARM64 Windows:
|
||||
|
@ -148,7 +148,7 @@ This demo was tested in the following deployments:
|
||||
|:--------------|:-------------|:--------------|:-----------------|:-----------|
|
||||
| `13.7.5` | `darwin-x64` | macOS 15.3.2 | `clang 16.0.0` | 2025-03-31 |
|
||||
| `13.5.92` | `darwin-arm` | macOS 14.5 | `clang 16.0.0` | 2025-02-15 |
|
||||
| `13.8.124` | `win11-x64` | Windows 11 | `CL 19.43.34810` | 2025-05-11 |
|
||||
| `12.7.130` | `win11-x64` | Windows 11 | `CL 19.42.34435` | 2024-12-20 |
|
||||
| `12.7.130` | `linux-x64` | HoloOS 3.6.20 | `gcc 13.2.1` | 2025-01-02 |
|
||||
| `13.5.92` | `linux-arm` | Debian 12 | `gcc 12.2.0` | 2025-02-15 |
|
||||
|
||||
@ -204,15 +204,13 @@ installation steps.
|
||||
Using the installer tool, the "Desktop development with C++" workload must be
|
||||
installed. In the sidebar, verify the following components are checked:
|
||||
|
||||
- "C++ ATL for latest ... build tools (x86 & x64)" (`v143` when last tested)
|
||||
- "C++ ATL for latest ... build tools with Spectre Mitigations (x86 & x64)" (`v143` when last tested)
|
||||
- "C++ MFC for latest ... build tools (x86 & x64)" (`v143` when last tested)
|
||||
- "C++ MFC for latest ... build tools with Spectre Mitigations (x86 & x64)" (`v143` when last tested)
|
||||
- "C++ ATL for latest ... build tools" (`v143` when last tested)
|
||||
- "C++ MFC for latest ... build tools" (`v143` when last tested)
|
||||
|
||||
In the "Individual components" tab, search for "Windows 11 SDK" and verify that
|
||||
"Windows 11 SDK (10.0.26100.0)" is checked.
|
||||
"Windows 11 SDK (10.0.22621.0)" is checked.
|
||||
|
||||
**Even though newer SDKs may exist, V8 expects specific Windows SDK versions!**
|
||||
**Even though newer SDKs exist, Windows 11 SDK 10.0.22621.0 must be installed!**
|
||||
|
||||
Click "Modify" and allow the installer to finish.
|
||||
|
||||
@ -224,7 +222,7 @@ The SDK debugging tools must be installed after the SDK is installed.
|
||||
available, search for "Installed apps".
|
||||
|
||||
2) When the setting panel opens, scroll down to "Windows Software Development
|
||||
Kit - Windows 10.0.26100" and click "Modify".
|
||||
Kit - Windows 10.0.22621" and click "Modify".
|
||||
|
||||
3) In the new window, select "Change" and click "Next"
|
||||
|
||||
@ -268,12 +266,13 @@ sudo chmod 777 /usr/local/lib
|
||||
</TabItem>
|
||||
<TabItem value="win" label="Windows">
|
||||
|
||||
```bash
|
||||
cd c:\
|
||||
mkdir src
|
||||
cd src
|
||||
git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
|
||||
```
|
||||
[The bundle](https://storage.googleapis.com/chrome-infra/depot_tools.zip) is a
|
||||
ZIP file that should be downloaded and extracted.
|
||||
|
||||
The demo was last tested on a NTFS-formatted drive (mounted at `C:\`). The ZIP
|
||||
was extracted to `C:\src\depot_tools`.
|
||||
|
||||
After extracting, verify that the `depot_tools` folder is not read-only.
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
@ -335,22 +334,7 @@ gclient
|
||||
gclient
|
||||
```
|
||||
|
||||
:::info Troubleshooting
|
||||
|
||||
In test runs on fresh machines, `gclient` failed with the following message:
|
||||
|
||||
```
|
||||
Error: client not configured; see 'gclient config'
|
||||
```
|
||||
|
||||
This was fixed by setting `user.name` and `user.email` in the Git configuration:
|
||||
|
||||
```bash
|
||||
git config --global user.name "John Doe"
|
||||
git config --global user.email "jdoe@email.com"
|
||||
```
|
||||
|
||||
---
|
||||
:::caution pass
|
||||
|
||||
`gclient` may throw errors related to `git` and permissions issues:
|
||||
|
||||
@ -365,7 +349,9 @@ To add an exception for this directory, call:
|
||||
These issues are related to the exFAT file system. They were resolved by running
|
||||
the recommended commands and re-running `gclient`.
|
||||
|
||||
---
|
||||
:::
|
||||
|
||||
:::caution pass
|
||||
|
||||
There may be errors pertaining to `gitconfig`:
|
||||
|
||||
@ -449,11 +435,11 @@ The recommended fix is to delete the referenced folder and re-run `gclient sync`
|
||||
</Tabs>
|
||||
|
||||
|
||||
5) Checkout the desired version. The following command pulls `13.8.124`:
|
||||
5) Checkout the desired version. The following command pulls `13.7.5`:
|
||||
|
||||
|
||||
```bash
|
||||
git checkout tags/13.8.124 -b sample
|
||||
git checkout tags/13.7.5 -b sample
|
||||
```
|
||||
|
||||
:::caution pass
|
||||
@ -461,14 +447,14 @@ git checkout tags/13.8.124 -b sample
|
||||
The official documentation recommends:
|
||||
|
||||
```bash
|
||||
git checkout refs/tags/13.8.124 -b sample -t
|
||||
git checkout refs/tags/13.7.5 -b sample -t
|
||||
```
|
||||
|
||||
This command failed in local testing:
|
||||
|
||||
```
|
||||
E:\v8\v8>git checkout refs/tags/13.8.124 -b sample -t
|
||||
fatal: cannot set up tracking information; starting point 'refs/tags/13.8.124' is not a branch
|
||||
E:\v8\v8>git checkout refs/tags/13.7.5 -b sample -t
|
||||
fatal: cannot set up tracking information; starting point 'refs/tags/13.7.5' is not a branch
|
||||
```
|
||||
|
||||
:::
|
||||
@ -657,24 +643,6 @@ python3 tools\dev\v8gen.py -vv x64.release.sample
|
||||
ninja -C out.gn\x64.release.sample v8_monolith
|
||||
```
|
||||
|
||||
:::info pass
|
||||
|
||||
If the expected Windows SDK version is missing or the debugging tools are not
|
||||
installed, the build will fail with an error referencing `include`:
|
||||
|
||||
```
|
||||
Exception: Path "C:\Program Files (x86)\Windows Kits\10\\include\10.0.26100.0\\um" from environment variable "include" does not exist. Make sure the necessary SDK is installed.
|
||||
```
|
||||
|
||||
In the error message, the expected Windows SDK version is listed in the path.
|
||||
For example, in the aforementioned message, `10.0.26100.0` is the SDK version.
|
||||
|
||||
The expected version of the Windows SDK should be installed from Visual Studio
|
||||
Installer. After installing the SDK, the corresponding SDK debugging tools
|
||||
should be installed using the procedure from Step 0.
|
||||
|
||||
:::
|
||||
|
||||
:::caution pass
|
||||
|
||||
In local testing, the build sometimes failed with a `dbghelp.dll` error:
|
||||
@ -776,7 +744,7 @@ ld: multiple errors: unknown file type in '/Users/sheetjs/dev/v8/v8/out.gn/x64.r
|
||||
```bash
|
||||
g++ -I. -Iinclude samples/hello-world.cc -o hello_world -fno-rtti -lv8_monolith \
|
||||
-lv8_libbase -lv8_libplatform -ldl -Lout.gn/x64.release.sample/obj/ -pthread \
|
||||
-std=c++20 -DV8_COMPRESS_POINTERS=1 -DV8_ENABLE_SANDBOX
|
||||
-std=c++17 -DV8_COMPRESS_POINTERS=1 -DV8_ENABLE_SANDBOX
|
||||
./hello_world
|
||||
```
|
||||
|
||||
@ -794,32 +762,10 @@ g++ -I. -Iinclude samples/hello-world.cc -o hello_world -fno-rtti -lv8_monolith
|
||||
<TabItem value="win11-x64" label="Windows">
|
||||
|
||||
```bash
|
||||
cl /I. /Iinclude samples/hello-world.cc /GR- v8_monolith.lib Advapi32.lib Winmm.lib Dbghelp.lib /std:c++20 /DV8_COMPRESS_POINTERS=1 /DV8_ENABLE_SANDBOX /link /out:hello_world.exe /LIBPATH:out.gn\x64.release.sample\obj\
|
||||
cl /I. /Iinclude samples/hello-world.cc /GR- v8_monolith.lib Advapi32.lib Winmm.lib Dbghelp.lib /std:c++17 /DV8_COMPRESS_POINTERS=1 /DV8_ENABLE_SANDBOX /link /out:hello_world.exe /LIBPATH:out.gn\x64.release.sample\obj\
|
||||
.\hello_world.exe
|
||||
```
|
||||
|
||||
:::caution pass
|
||||
|
||||
When this demo was last tested, the build failed with a `C++` standard error:
|
||||
|
||||
```
|
||||
c:\v8\v8\include\v8config.h(13): fatal error C1189: #error: "C++20 or later required."
|
||||
```
|
||||
|
||||
The `/std:c++20` option sets the `C++` standard in use. The workaround is to
|
||||
suppress the broken version check:
|
||||
|
||||
```c++ title="include\v8config.h (edit highlighted line)"
|
||||
#if __cplusplus <= 201703L
|
||||
// highlight-next-line
|
||||
//#error "C++20 or later required."
|
||||
#endif
|
||||
```
|
||||
|
||||
After suppressing the error, re-run the build commands.
|
||||
|
||||
:::
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
@ -955,7 +901,7 @@ g++ -I. -Iinclude hello-world.cc -o hello_world -fno-rtti -lv8_monolith \
|
||||
<TabItem value="win" label="Windows">
|
||||
|
||||
```bash
|
||||
cl /MT /I..\v8\v8\ /I..\v8\v8\include hello-world.cc /GR- v8_monolith.lib Advapi32.lib Winmm.lib Dbghelp.lib /std:c++20 /DV8_COMPRESS_POINTERS=1 /DV8_ENABLE_SANDBOX /link /out:hello_world.exe /LIBPATH:..\v8\v8\out.gn\x64.release.sample\obj\
|
||||
cl /MT /I..\v8\v8\ /I..\v8\v8\include hello-world.cc /GR- v8_monolith.lib Advapi32.lib Winmm.lib Dbghelp.lib /std:c++17 /DV8_COMPRESS_POINTERS=1 /DV8_ENABLE_SANDBOX /link /out:hello_world.exe /LIBPATH:..\v8\v8\out.gn\x64.release.sample\obj\
|
||||
.\hello_world.exe
|
||||
```
|
||||
|
||||
@ -1028,7 +974,7 @@ g++ -I. -Iinclude sheetjs.v8.cc -o sheetjs.v8 -fno-rtti -lv8_monolith \
|
||||
<TabItem value="win" label="Windows">
|
||||
|
||||
```bash
|
||||
cl /MT /I..\v8\v8\ /I..\v8\v8\include sheetjs.v8.cc /GR- v8_monolith.lib Advapi32.lib Winmm.lib Dbghelp.lib /std:c++20 /DV8_COMPRESS_POINTERS=1 /DV8_ENABLE_SANDBOX /link /out:sheetjs.v8.exe /LIBPATH:..\v8\v8\out.gn\x64.release.sample\obj\
|
||||
cl /MT /I..\v8\v8\ /I..\v8\v8\include sheetjs.v8.cc /GR- v8_monolith.lib Advapi32.lib Winmm.lib Dbghelp.lib /std:c++17 /DV8_COMPRESS_POINTERS=1 /DV8_ENABLE_SANDBOX /link /out:sheetjs.v8.exe /LIBPATH:..\v8\v8\out.gn\x64.release.sample\obj\
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
@ -1090,7 +1036,7 @@ This demo was last tested in the following deployments:
|
||||
|:-------------|:----------|:-----------|
|
||||
| `darwin-x64` | `136.0.0` | 2025-04-21 |
|
||||
| `darwin-arm` | `134.3.0` | 2025-02-13 |
|
||||
| `win11-x64` | `137.1.0` | 2025-05-11 |
|
||||
| `win11-x64` | `130.0.2` | 2024-12-20 |
|
||||
| `linux-x64` | `130.0.7` | 2025-01-09 |
|
||||
| `linux-arm` | `134.4.0` | 2025-02-15 |
|
||||
|
||||
@ -1124,45 +1070,12 @@ curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js
|
||||
curl -LO https://docs.sheetjs.com/pres.numbers`}
|
||||
</CodeBlock>
|
||||
|
||||
:::note pass
|
||||
|
||||
In PowerShell, the command may fail with a parameter error:
|
||||
|
||||
```
|
||||
Invoke-WebRequest : A parameter cannot be found that matches parameter name 'LO'.
|
||||
```
|
||||
|
||||
`curl.exe` must be invoked directly:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
curl.exe -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js
|
||||
curl.exe -LO https://docs.sheetjs.com/pres.numbers`}
|
||||
</CodeBlock>
|
||||
|
||||
:::
|
||||
|
||||
4) Download [`main.rs`](pathname:///v8/main.rs) and replace `src/main.rs`:
|
||||
|
||||
```bash
|
||||
curl -L -o src/main.rs https://docs.sheetjs.com/v8/main.rs
|
||||
```
|
||||
|
||||
:::note pass
|
||||
|
||||
In PowerShell, the command may fail with a parameter error:
|
||||
|
||||
```
|
||||
Invoke-WebRequest : A parameter cannot be found that matches parameter name 'LO'.
|
||||
```
|
||||
|
||||
`curl.exe` must be invoked directly:
|
||||
|
||||
```bash
|
||||
curl.exe -L -o src/main.rs https://docs.sheetjs.com/v8/main.rs
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
:::info pass
|
||||
|
||||
There was a breaking change in version `0.102.0` affecting `v8::Context::new`.
|
||||
@ -1206,7 +1119,7 @@ This demo was last tested in the following deployments:
|
||||
|:-------------|:--------------|:--------|:----------|:-----------|
|
||||
| `darwin-x64` | `13.2.152.16` | `4.1.1` | `24.0.1` | 2025-04-21 |
|
||||
| `darwin-arm` | `13.2.152.16` | `4.1.1` | `17.0.14` | 2025-03-30 |
|
||||
| `win11-x64` | `13.2.152.16` | `4.1.1` | `17.0.13` | 2025-05-11 |
|
||||
| `win11-x64` | `12.6.228.13` | `3.1.3` | `21.0.5` | 2024-12-20 |
|
||||
| `linux-x64` | `13.2.152.16` | `4.1.1` | `21.0.6` | 2025-04-21 |
|
||||
| `linux-arm` | `13.2.152.16` | `4.1.1` | `17.0.14` | 2025-02-16 |
|
||||
|
||||
@ -1263,27 +1176,9 @@ curl -LO https://repo1.maven.org/maven2/com/caoccao/javet/javet-v8-linux-arm64/4
|
||||
<TabItem value="win" label="Windows">
|
||||
|
||||
```bash
|
||||
curl -LO https://repo1.maven.org/maven2/com/caoccao/javet/javet/4.1.1/javet-4.1.1.jar
|
||||
curl -LO https://repo1.maven.org/maven2/com/caoccao/javet/javet-v8-windows-x86_64/4.1.1/javet-v8-windows-x86_64-4.1.1.jar
|
||||
curl -LO https://repo1.maven.org/maven2/com/caoccao/javet/javet/3.1.3/javet-3.1.3.jar
|
||||
```
|
||||
|
||||
:::note pass
|
||||
|
||||
In PowerShell, the command may fail with a parameter error:
|
||||
|
||||
```
|
||||
Invoke-WebRequest : A parameter cannot be found that matches parameter name 'LO'.
|
||||
```
|
||||
|
||||
`curl.exe` must be invoked directly:
|
||||
|
||||
```bash
|
||||
curl.exe -LO https://repo1.maven.org/maven2/com/caoccao/javet/javet/4.1.1/javet-4.1.1.jar
|
||||
curl.exe -LO https://repo1.maven.org/maven2/com/caoccao/javet/javet-v8-windows-x86_64/4.1.1/javet-v8-windows-x86_64-4.1.1.jar
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
@ -1300,45 +1195,12 @@ curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js
|
||||
curl -LO https://docs.sheetjs.com/pres.xlsx`}
|
||||
</CodeBlock>
|
||||
|
||||
:::note pass
|
||||
|
||||
In PowerShell, the command may fail with a parameter error:
|
||||
|
||||
```
|
||||
Invoke-WebRequest : A parameter cannot be found that matches parameter name 'LO'.
|
||||
```
|
||||
|
||||
`curl.exe` must be invoked directly:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
curl.exe -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js
|
||||
curl.exe -LO https://docs.sheetjs.com/pres.xlsx`}
|
||||
</CodeBlock>
|
||||
|
||||
:::
|
||||
|
||||
4) Download [`SheetJSJavet.java`](pathname:///v8/SheetJSJavet.java):
|
||||
|
||||
```bash
|
||||
curl -LO https://docs.sheetjs.com/v8/SheetJSJavet.java
|
||||
```
|
||||
|
||||
:::note pass
|
||||
|
||||
In PowerShell, the command may fail with a parameter error:
|
||||
|
||||
```
|
||||
Invoke-WebRequest : A parameter cannot be found that matches parameter name 'LO'.
|
||||
```
|
||||
|
||||
`curl.exe` must be invoked directly:
|
||||
|
||||
```bash
|
||||
curl.exe -LO https://docs.sheetjs.com/v8/SheetJSJavet.java
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
5) Build and run the Java application:
|
||||
|
||||
<Tabs groupId="os">
|
||||
@ -1383,8 +1245,8 @@ java -cp ".:javet-4.1.1.jar:javet-v8-linux-arm64-4.1.1.jar" SheetJSJavet pres.xl
|
||||
<TabItem value="win" label="Windows">
|
||||
|
||||
```bash
|
||||
javac -cp ".;javet-4.1.1.jar;javet-v8-windows-x86_64-4.1.1.jar" SheetJSJavet.java
|
||||
java -cp ".;javet-4.1.1.jar;javet-v8-windows-x86_64-4.1.1.jar" SheetJSJavet pres.xlsx
|
||||
javac -cp ".;javet-3.1.3.jar" SheetJSJavet.java
|
||||
java -cp ".;javet-3.1.3.jar" SheetJSJavet pres.xlsx
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
@ -1420,7 +1282,7 @@ This demo was last tested in the following deployments:
|
||||
|:-------------|:--------------|:-----------|
|
||||
| `darwin-x64` | `13.3.415.23` | 2025-03-31 |
|
||||
| `darwin-arm` | `13.3.415.23` | 2025-03-31 |
|
||||
| `win11-x64` | `13.3.415.23` | 2025-05-11 |
|
||||
| `win11-x64` | `12.3.219.12` | 2024-12-20 |
|
||||
| `win11-arm` | `12.3.219.12` | 2025-02-23 |
|
||||
| `linux-x64` | `12.3.219.12` | 2025-01-10 |
|
||||
| `linux-arm` | `12.3.219.12` | 2025-02-16 |
|
||||
@ -1514,23 +1376,6 @@ curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js
|
||||
curl -LO https://docs.sheetjs.com/pres.xlsx`}
|
||||
</CodeBlock>
|
||||
|
||||
:::note pass
|
||||
|
||||
In PowerShell, the command may fail with a parameter error:
|
||||
|
||||
```
|
||||
Invoke-WebRequest : A parameter cannot be found that matches parameter name 'LO'.
|
||||
```
|
||||
|
||||
`curl.exe` must be invoked directly:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
curl.exe -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js
|
||||
curl.exe -LO https://docs.sheetjs.com/pres.xlsx`}
|
||||
</CodeBlock>
|
||||
|
||||
:::
|
||||
|
||||
6) Replace `Program.cs` with the following:
|
||||
|
||||
```csharp title="Program.cs"
|
||||
@ -1639,7 +1484,6 @@ This demo was last tested in the following deployments:
|
||||
|:-------------|:--------------|:---------|:-----------|
|
||||
| `darwin-x64` | `13.1.201.22` | `3.13.1` | 2025-03-31 |
|
||||
| `darwin-arm` | `13.1.201.22` | `3.13.2` | 2025-04-24 |
|
||||
| `win11-x64` | `13.1.201.22` | `3.11.9` | 2025-04-28 |
|
||||
|
||||
:::
|
||||
|
||||
@ -1704,26 +1548,6 @@ python sheetjs-stpyv8.py pres.xlsx
|
||||
The script will display CSV rows from the first worksheet. It will also create
|
||||
`SheetJSSTPyV8.xlsb`, a workbook that can be opened with a spreadsheet editor.
|
||||
|
||||
:::caution pass
|
||||
|
||||
On Windows, this may fail with a `charmap` error:
|
||||
|
||||
```
|
||||
return codecs.charmap_decode(input,self.errors,decoding_table)[0]
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
UnicodeDecodeError: 'charmap' codec can't decode byte 0x90 in position 380: character maps to <undefined>
|
||||
```
|
||||
|
||||
`sheetjs-stpyv8.py` must be altered to read `xlsx.full.min.js` with mode `rb`:
|
||||
|
||||
```python title="sheetjs-stpyv8.py (edit highlighted line)"
|
||||
# Read xlsx.full.min.js
|
||||
# highlight-next-line
|
||||
with open("xlsx.full.min.js", "rb") as f:
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
## Snapshots
|
||||
|
||||
At a high level, V8 snapshots are raw dumps of the V8 engine state. It is much
|
||||
@ -1750,7 +1574,7 @@ This demo was last tested in the following deployments:
|
||||
|:-------------|:--------------|:----------|:-----------|
|
||||
| `darwin-x64` | `13.5.212.10` | `136.0.0` | 2025-04-21 |
|
||||
| `darwin-arm` | `13.5.212.10` | `136.0.0` | 2025-04-24 |
|
||||
| `win11-x64` | `13.5.212.10` | `136.0.0` | 2025-05-11 |
|
||||
| `win11-x64` | `12.6.228.3` | `0.92.0` | 2024-12-20 |
|
||||
| `linux-x64` | `12.6.228.3` | `0.92.0` | 2025-01-02 |
|
||||
| `linux-arm` | `13.4.114.9` | `134.4.0` | 2025-02-15 |
|
||||
|
||||
|
@ -30,23 +30,22 @@ This demo was tested in the following deployments:
|
||||
|
||||
| OpenJDK | Rhino | Date |
|
||||
|:--------|:---------|:-----------|
|
||||
| 24.0.1 | `1.7.15` | 2025-05-06 |
|
||||
| 23.0.2 | `1.7.15` | 2025-05-06 |
|
||||
| 22.0.2 | `1.7.15` | 2025-05-06 |
|
||||
| 21.0.7 | `1.7.15` | 2025-05-06 |
|
||||
| 20.0.2 | `1.7.15` | 2025-05-06 |
|
||||
| 19.0.2 | `1.7.15` | 2025-05-06 |
|
||||
| 18.0.2 | `1.7.15` | 2025-05-06 |
|
||||
| 17.0.15 | `1.7.15` | 2025-05-06 |
|
||||
| 16.0.1 | `1.7.15` | 2025-05-06 |
|
||||
| 15.0.10 | `1.7.15` | 2025-05-06 |
|
||||
| 14.0.2 | `1.7.15` | 2025-05-06 |
|
||||
| 13.0.2 | `1.7.15` | 2025-05-06 |
|
||||
| 12.0.1 | `1.7.15` | 2025-05-06 |
|
||||
| 11.0.27 | `1.7.15` | 2025-05-06 |
|
||||
| 10 | `1.7.15` | 2025-05-06 |
|
||||
| 9.0.4 | `1.7.15` | 2025-05-06 |
|
||||
| 1.8.0 | `1.7.15` | 2025-05-06 |
|
||||
| 23.0.1 | `1.7.15` | 2025-01-10 |
|
||||
| 22.0.2 | `1.7.15` | 2025-01-10 |
|
||||
| 21.0.5 | `1.7.15` | 2025-01-10 |
|
||||
| 20.0.2 | `1.7.15` | 2025-01-10 |
|
||||
| 19.0.2 | `1.7.15` | 2025-01-10 |
|
||||
| 18.0.2 | `1.7.15` | 2025-01-10 |
|
||||
| 17.0.13 | `1.7.15` | 2025-01-10 |
|
||||
| 16.0.2 | `1.7.15` | 2025-01-10 |
|
||||
| 15.0.2 | `1.7.15` | 2025-01-10 |
|
||||
| 14.0.2 | `1.7.15` | 2025-01-10 |
|
||||
| 13.0.2 | `1.7.15` | 2025-01-10 |
|
||||
| 12.0.2 | `1.7.15` | 2025-01-10 |
|
||||
| 11.0.25 | `1.7.15` | 2025-01-10 |
|
||||
| 10.0.2 | `1.7.15` | 2025-01-10 |
|
||||
| 9.0.4 | `1.7.15` | 2025-01-10 |
|
||||
| 1.8.0 | `1.7.15` | 2025-01-10 |
|
||||
|
||||
:::
|
||||
|
||||
|
@ -165,7 +165,7 @@ This demo was tested in the following deployments:
|
||||
|:-------------|:--------|:-----------|
|
||||
| `darwin-x64` | `4.2.1` | 2025-03-31 |
|
||||
| `darwin-arm` | `4.2.0` | 2025-02-13 |
|
||||
| `win11-x64` | `4.2.2` | 2026-04-28 |
|
||||
| `win11-x64` | `4.1.0` | 2024-12-20 |
|
||||
| `win11-arm` | `4.2.0` | 2025-02-23 |
|
||||
| `linux-x64` | `4.1.0` | 2025-01-09 |
|
||||
| `linux-arm` | `4.2.0` | 2025-02-15 |
|
||||
@ -248,7 +248,7 @@ dotnet run
|
||||
|
||||
```bash
|
||||
dotnet nuget add source https://www.myget.org/F/jint/api/v3/index.json
|
||||
dotnet add package Jint --version 4.2.2
|
||||
dotnet add package Jint --version 4.2.1
|
||||
```
|
||||
|
||||
To verify Jint is installed, replace `Program.cs` with the following:
|
||||
@ -283,25 +283,6 @@ curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js
|
||||
curl -LO https://docs.sheetjs.com/pres.xlsx`}
|
||||
</CodeBlock>
|
||||
|
||||
:::caution pass
|
||||
|
||||
PowerShell `curl` is incompatible with the official `curl` program. The command
|
||||
may fail with a parameter error:
|
||||
|
||||
```
|
||||
Invoke-WebRequest : A parameter cannot be found that matches parameter name 'LO'.
|
||||
```
|
||||
|
||||
`curl.exe` must be used instead:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
curl.exe -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/shim.min.js
|
||||
curl.exe -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js
|
||||
curl.exe -LO https://docs.sheetjs.com/pres.xlsx`}
|
||||
</CodeBlock>
|
||||
|
||||
:::
|
||||
|
||||
6) Replace `Program.cs` with the following:
|
||||
|
||||
```csharp title="Program.cs"
|
||||
@ -518,7 +499,7 @@ cp bin/Release/net*/linux-arm64/publish/SheetJSJint .
|
||||
For Windows 11 x64, the RID is `win-x64` and the command is:
|
||||
|
||||
```powershell
|
||||
copy .\bin\Release\net*\win-x64\publish\SheetJSJint.exe .
|
||||
copy .\bin\Release\net9.0\win-x64\publish\SheetJSJint.exe .
|
||||
```
|
||||
|
||||
:::caution pass
|
||||
@ -532,7 +513,7 @@ The system cannot find the path specified.
|
||||
The correct command was
|
||||
|
||||
```powershell
|
||||
copy .\bin\x64\Release\net*\win-x64\publish\SheetJSJint.exe .
|
||||
copy .\bin\x64\Release\net9.0\win-x64\publish\SheetJSJint.exe .
|
||||
```
|
||||
|
||||
:::
|
||||
|
@ -107,7 +107,7 @@ This demo was tested in the following deployments:
|
||||
|:-------------|:-----------|:-----------|:-----------|
|
||||
| `darwin-x64` | `bcd7cc6` | `1.24.1` | 2025-03-31 |
|
||||
| `darwin-arm` | `5ef83b8` | `1.24.0` | 2025-02-13 |
|
||||
| `win11-x64` | `bcd7cc6` | `1.24.2` | 2025-04-28 |
|
||||
| `win11-x64` | `79f3a7e` | `1.23.4` | 2024-12-20 |
|
||||
| `win11-arm` | `5ef83b8` | `1.24.0` | 2025-02-23 |
|
||||
| `linux-x64` | `79f3a7e` | `1.22.0` | 2025-01-02 |
|
||||
| `linux-arm` | `5ef83b8` | `1.19.8` | 2025-02-15 |
|
||||
@ -141,48 +141,12 @@ curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js
|
||||
curl -LO https://docs.sheetjs.com/pres.numbers`}
|
||||
</CodeBlock>
|
||||
|
||||
:::caution pass
|
||||
|
||||
PowerShell `curl` is incompatible with the official `curl` program. The command
|
||||
may fail with a parameter error:
|
||||
|
||||
```
|
||||
Invoke-WebRequest : A parameter cannot be found that matches parameter name 'LO'.
|
||||
```
|
||||
|
||||
`curl.exe` must be used instead:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
curl.exe -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/shim.min.js
|
||||
curl.exe -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js
|
||||
curl.exe -LO https://docs.sheetjs.com/pres.numbers`}
|
||||
</CodeBlock>
|
||||
|
||||
:::
|
||||
|
||||
2) Download [`SheetGoja.go`](pathname:///goja/SheetGoja.go):
|
||||
|
||||
```bash
|
||||
curl -LO https://docs.sheetjs.com/goja/SheetGoja.go
|
||||
```
|
||||
|
||||
:::caution pass
|
||||
|
||||
PowerShell `curl` is incompatible with the official `curl` program. The command
|
||||
may fail with a parameter error:
|
||||
|
||||
```
|
||||
Invoke-WebRequest : A parameter cannot be found that matches parameter name 'LO'.
|
||||
```
|
||||
|
||||
`curl.exe` must be used instead:
|
||||
|
||||
```bash
|
||||
curl.exe -LO https://docs.sheetjs.com/goja/SheetGoja.go
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
3) Build the standalone `SheetGoja` binary:
|
||||
|
||||
```bash
|
||||
|
@ -105,23 +105,22 @@ This demo was tested in the following deployments:
|
||||
|
||||
| OpenJDK | Nashorn | Date |
|
||||
|:--------|:----------------|:-----------|
|
||||
| 24.0.1 | 15.4 standalone | 2025-05-06 |
|
||||
| 23.0.2 | 15.4 standalone | 2025-05-06 |
|
||||
| 22.0.2 | 15.4 standalone | 2025-05-06 |
|
||||
| 21.0.7 | 15.4 standalone | 2025-05-06 |
|
||||
| 20.0.2 | 15.4 standalone | 2025-05-06 |
|
||||
| 19.0.2 | 15.4 standalone | 2025-05-06 |
|
||||
| 18.0.2 | 15.4 standalone | 2025-05-06 |
|
||||
| 17.0.15 | 15.4 standalone | 2025-05-06 |
|
||||
| 16.0.1 | 15.4 standalone | 2025-05-06 |
|
||||
| 15.0.10 | 15.4 standalone | 2025-05-06 |
|
||||
| 14.0.2 | Built-in | 2025-05-06 |
|
||||
| 13.0.2 | Built-in | 2025-05-06 |
|
||||
| 12.0.1 | Built-in | 2025-05-06 |
|
||||
| 11.0.27 | Built-in | 2025-05-06 |
|
||||
| 10 | Built-in | 2025-05-06 |
|
||||
| 9.0.4 | Built-in | 2025-05-06 |
|
||||
| 1.8.0 | Built-in | 2025-05-06 |
|
||||
| 23.0.1 | 15.4 standalone | 2024-12-17 |
|
||||
| 22.0.2 | 15.4 standalone | 2024-12-17 |
|
||||
| 21.0.5 | 15.4 standalone | 2024-12-17 |
|
||||
| 20.0.2 | 15.4 standalone | 2024-12-17 |
|
||||
| 19.0.2 | 15.4 standalone | 2024-12-17 |
|
||||
| 18.0.2 | 15.4 standalone | 2024-12-17 |
|
||||
| 17.0.13 | 15.4 standalone | 2024-12-17 |
|
||||
| 16.0.1 | 15.4 standalone | 2024-12-17 |
|
||||
| 15.0.10 | 15.4 standalone | 2024-12-17 |
|
||||
| 14.0.2 | Built-in | 2024-12-17 |
|
||||
| 13.0.14 | Built-in | 2024-12-17 |
|
||||
| 12.0.2 | Built-in | 2024-12-17 |
|
||||
| 11.0.25 | Built-in | 2024-12-17 |
|
||||
| 10.0.2 | Built-in | 2024-12-17 |
|
||||
| 9 | Built-in | 2024-12-17 |
|
||||
| 1.8.0 | Built-in | 2024-12-17 |
|
||||
|
||||
:::
|
||||
|
||||
|
@ -374,33 +374,33 @@ fork, which powers React Native for Windows, does have built-in support[^5]
|
||||
|
||||
| Architecture | Git Commit | Date |
|
||||
|:-------------|:-----------|:-----------|
|
||||
| `win11-x64` | `254fb48` | 2025-04-28 |
|
||||
| `win11-x64` | `4c64b05` | 2024-12-20 |
|
||||
| `win11-arm` | `4c64b05` | 2025-02-23 |
|
||||
|
||||
The ["Windows Example"](#windows-example) covers `hermes-windows`.
|
||||
|
||||
:::
|
||||
|
||||
0) Install [dependencies](https://github.com/facebook/hermes/blob/eda3c083a57e9aa3b5b04df12ef8588b2961c02e/doc/BuildingAndRunning.md#dependencies)
|
||||
0) Install [dependencies](https://hermesengine.dev/docs/building-and-running/#dependencies)
|
||||
|
||||
<details>
|
||||
<summary><b>Installation Notes</b> (click to show)</summary>
|
||||
|
||||
On macOS, the Xcode command-line tools ship with `git`. The other dependencies
|
||||
should be installed with `brew`:
|
||||
The official guidance[^6] has been verified in macOS and HoloOS (Linux).
|
||||
|
||||
On macOS:
|
||||
|
||||
```bash
|
||||
brew install icu4c cmake ninja
|
||||
```
|
||||
|
||||
On HoloOS (and other Arch Linux distros), the dependencies must be installed
|
||||
from the root user (using `sudo`):
|
||||
On HoloOS (and other Arch Linux distros):
|
||||
|
||||
```bash
|
||||
sudo pacman -Syu cmake git ninja icu python zip readline
|
||||
```
|
||||
|
||||
On Debian and Ubuntu, `python-is-python3` should be installed:
|
||||
On Debian and Ubuntu:
|
||||
|
||||
```bash
|
||||
sudo apt install cmake git ninja-build libicu-dev python3 python-is-python3 zip libreadline-dev
|
||||
@ -408,7 +408,7 @@ sudo apt install cmake git ninja-build libicu-dev python3 python-is-python3 zip
|
||||
|
||||
:::note pass
|
||||
|
||||
Linux builds require at least 8 GB memory.
|
||||
When using virtual machines, Linux builds require at least 8 GB memory.
|
||||
|
||||
:::
|
||||
|
||||
@ -690,7 +690,7 @@ cd sheetjs-hermes
|
||||
```bash
|
||||
git clone https://github.com/microsoft/hermes-windows
|
||||
cd hermes-windows
|
||||
git checkout 254fb48
|
||||
git checkout 4c64b05
|
||||
cd ..
|
||||
```
|
||||
|
||||
@ -849,7 +849,7 @@ This demo was tested in the following deployments:
|
||||
|:-------------|:---------|:-----------|
|
||||
| `darwin-x64` | `0.13.0` | 2025-03-31 |
|
||||
| `darwin-arm` | `0.13.0` | 2025-04-23 |
|
||||
| `win11-x64` | `0.13.0` | 2025-04-28 |
|
||||
| `win11-x64` | `0.13.0` | 2024-12-20 |
|
||||
| `win11-arm` | `0.13.0` | 2025-02-23 |
|
||||
| `linux-x64` | `0.13.0` | 2025-04-21 |
|
||||
|
||||
@ -978,5 +978,6 @@ If successful, the script will print CSV data from the test file.
|
||||
[^3]: See ["Workbook Object"](/docs/csf/book)
|
||||
[^4]: See [`sheet_to_csv` in "Utilities"](/docs/api/utilities/csv#csv-output)
|
||||
[^5]: See [`microsoft/hermes-windows`](https://github.com/microsoft/hermes-windows) on GitHub
|
||||
[^6]: See ["Dependencies" in "Building and Running"](https://hermesengine.dev/docs/building-and-running/#dependencies) in the Hermes Documentation. If this page redirects to the source repo, [see the following `archive.org` snapshot.](https://web.archive.org/web/20240103234151/http://hermesengine.dev/docs/building-and-running/)
|
||||
[^7]: See ["Download Python"](https://www.python.org/downloads/) in the Python website. When the demo was last tested, Python 3.11.9 was installed.
|
||||
[^8]: See [the Visual Studio website](https://visualstudio.microsoft.com/#vs-section) for download links.
|
||||
|
@ -89,7 +89,7 @@ This demo was tested in the following deployments:
|
||||
|:-------------|:---------|:---------|:-----------|
|
||||
| `darwin-x64` | `2.6.10` | `2.10.0` | 2025-03-31 |
|
||||
| `darwin-arm` | `2.6.10` | `2.10.0` | 2025-02-13 |
|
||||
| `win11-x64` | `3.3.8` | `2.10.0` | 2025-04-28 |
|
||||
| `win11-x64` | `3.3.6` | `2.10.0` | 2024-12-20 |
|
||||
| `win11-arm` | `3.2.3` | `2.10.0` | 2025-02-23 |
|
||||
| `linux-x64` | `3.2.3` | `2.10.0` | 2025-04-21 |
|
||||
| `linux-arm` | `3.1.2` | `2.10.0` | 2025-02-15 |
|
||||
@ -157,47 +157,12 @@ curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js
|
||||
curl -LO https://docs.sheetjs.com/pres.numbers`}
|
||||
</CodeBlock>
|
||||
|
||||
:::caution pass
|
||||
|
||||
PowerShell `curl` is incompatible with the official `curl` program. The command
|
||||
may fail with a parameter error:
|
||||
|
||||
```
|
||||
Invoke-WebRequest : A parameter cannot be found that matches parameter name 'LO'.
|
||||
```
|
||||
|
||||
`curl.exe` must be used instead:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
curl.exe -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js
|
||||
curl.exe -LO https://docs.sheetjs.com/pres.numbers`}
|
||||
</CodeBlock>
|
||||
|
||||
:::
|
||||
|
||||
3) Download [`ExecSheetJS.rb`](pathname:///execjs/ExecSheetJS.rb):
|
||||
|
||||
```bash
|
||||
curl -LO https://docs.sheetjs.com/execjs/ExecSheetJS.rb
|
||||
```
|
||||
|
||||
:::caution pass
|
||||
|
||||
PowerShell `curl` is incompatible with the official `curl` program. The command
|
||||
may fail with a parameter error:
|
||||
|
||||
```
|
||||
Invoke-WebRequest : A parameter cannot be found that matches parameter name 'LO'.
|
||||
```
|
||||
|
||||
`curl.exe` must be used instead:
|
||||
|
||||
```bash
|
||||
curl.exe -LO https://docs.sheetjs.com/execjs/ExecSheetJS.rb
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
4) Run the demo:
|
||||
|
||||
```bash
|
||||
|
@ -134,7 +134,7 @@ This demo was tested in the following deployments:
|
||||
|:-------------|:-----------|:-----------|
|
||||
| `darwin-x64` | `36becec` | 2025-03-31 |
|
||||
| `darwin-arm` | `e26c81f` | 2025-01-13 |
|
||||
| `win11-x64` | `36becec` | 2025-04-28 |
|
||||
| `win11-x64` | `e26c81f` | 2024-12-19 |
|
||||
| `win11-arm` | `e26c81f` | 2025-02-23 |
|
||||
| `linux-x64` | `e26c81f` | 2025-01-09 |
|
||||
|
||||
@ -575,7 +575,7 @@ If successful, the program will print the contents of the first sheet as CSV.
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was last tested on 2025-04-28 against `ch` commit `36becec`.
|
||||
This demo was last tested on 2025-03-31 against `ch` commit `36becec`.
|
||||
|
||||
:::
|
||||
|
||||
|
@ -106,7 +106,7 @@ This demo was tested in the following deployments:
|
||||
|:-------------|:---------|:-----------|
|
||||
| `darwin-x64` | `0.20.0` | 2025-03-31 |
|
||||
| `darwin-arm` | `0.20.0` | 2025-02-13 |
|
||||
| `win11-x64` | `0.20.0` | 2025-04-28 |
|
||||
| `win11-x64` | `0.20.0` | 2024-12-19 |
|
||||
| `win11-arm` | `0.20.0` | 2025-02-23 |
|
||||
| `linux-x64` | `0.20.0` | 2025-04-21 |
|
||||
| `linux-arm` | `0.20.0` | 2025-02-15 |
|
||||
@ -152,47 +152,12 @@ curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js
|
||||
curl -LO https://docs.sheetjs.com/pres.xlsx`}
|
||||
</CodeBlock>
|
||||
|
||||
:::caution pass
|
||||
|
||||
PowerShell `curl` is incompatible with the official `curl` program. The command
|
||||
may fail with a parameter error:
|
||||
|
||||
```
|
||||
Invoke-WebRequest : A parameter cannot be found that matches parameter name 'LO'.
|
||||
```
|
||||
|
||||
`curl.exe` must be used instead:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
curl.exe -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js
|
||||
curl.exe -LO https://docs.sheetjs.com/pres.xlsx`}
|
||||
</CodeBlock>
|
||||
|
||||
:::
|
||||
|
||||
4) Download [`main.rs`](pathname:///boa/main.rs) and replace `src/main.rs`:
|
||||
|
||||
```bash
|
||||
curl -L -o src/main.rs https://docs.sheetjs.com/boa/main.rs
|
||||
```
|
||||
|
||||
:::caution pass
|
||||
|
||||
PowerShell `curl` is incompatible with the official `curl` program. The command
|
||||
may fail with a parameter error:
|
||||
|
||||
```
|
||||
Invoke-WebRequest : A parameter cannot be found that matches parameter name 'LO'.
|
||||
```
|
||||
|
||||
`curl.exe` must be used instead:
|
||||
|
||||
```bash
|
||||
curl.exe -L -o src/main.rs https://docs.sheetjs.com/boa/main.rs
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
5) Build and run the app in release mode:
|
||||
|
||||
```bash
|
||||
|
@ -54,14 +54,13 @@ This demo was tested in the following deployments:
|
||||
|
||||
| OpenJDK | GraalJS | Date |
|
||||
|:--------|:--------|:-----------|
|
||||
| 24.0.1 | 24.2.1 | 2025-05-06 |
|
||||
| 23.0.2 | 24.2.1 | 2025-05-06 |
|
||||
| 22.0.2 | 24.2.1 | 2025-05-06 |
|
||||
| 21.0.7 | 24.2.1 | 2025-05-06 |
|
||||
| 20.0.2 | 24.2.1 | 2025-05-06 |
|
||||
| 19.0.2 | 24.2.1 | 2025-05-06 |
|
||||
| 18.0.2 | 24.2.1 | 2025-05-06 |
|
||||
| 17.0.15 | 24.2.1 | 2025-05-06 |
|
||||
| 23.0.1 | 24.1.1 | 2024-12-17 |
|
||||
| 22.0.2 | 24.1.1 | 2024-12-17 |
|
||||
| 21.0.5 | 24.1.1 | 2024-12-17 |
|
||||
| 20.0.2 | 24.1.1 | 2024-12-17 |
|
||||
| 19.0.2 | 24.1.1 | 2024-12-17 |
|
||||
| 18.0.2 | 24.1.1 | 2024-12-17 |
|
||||
| 17.0.13 | 24.1.1 | 2024-12-17 |
|
||||
|
||||
:::
|
||||
|
||||
@ -70,14 +69,14 @@ This demo was tested in the following deployments:
|
||||
0) Download GraalJS and its dependencies:
|
||||
|
||||
```bash
|
||||
curl -LO "https://repo1.maven.org/maven2/org/graalvm/js/js-scriptengine/24.2.1/js-scriptengine-24.2.1.jar"
|
||||
curl -LO "https://repo1.maven.org/maven2/org/graalvm/js/js-language/24.2.1/js-language-24.2.1.jar"
|
||||
curl -LO "https://repo1.maven.org/maven2/org/graalvm/polyglot/polyglot/24.2.1/polyglot-24.2.1.jar"
|
||||
curl -LO "https://repo1.maven.org/maven2/org/graalvm/sdk/collections/24.2.1/collections-24.2.1.jar"
|
||||
curl -LO "https://repo1.maven.org/maven2/org/graalvm/truffle/truffle-api/24.2.1/truffle-api-24.2.1.jar"
|
||||
curl -LO "https://repo1.maven.org/maven2/org/graalvm/sdk/nativeimage/24.2.1/nativeimage-24.2.1.jar"
|
||||
curl -LO "https://repo1.maven.org/maven2/org/graalvm/shadowed/icu4j/24.2.1/icu4j-24.2.1.jar"
|
||||
curl -LO "https://repo1.maven.org/maven2/org/graalvm/regex/regex/24.2.1/regex-24.2.1.jar"
|
||||
curl -LO "https://repo1.maven.org/maven2/org/graalvm/js/js-scriptengine/24.1.1/js-scriptengine-24.1.1.jar"
|
||||
curl -LO "https://repo1.maven.org/maven2/org/graalvm/js/js-language/24.1.1/js-language-24.1.1.jar"
|
||||
curl -LO "https://repo1.maven.org/maven2/org/graalvm/polyglot/polyglot/24.1.1/polyglot-24.1.1.jar"
|
||||
curl -LO "https://repo1.maven.org/maven2/org/graalvm/sdk/collections/24.1.1/collections-24.1.1.jar"
|
||||
curl -LO "https://repo1.maven.org/maven2/org/graalvm/truffle/truffle-api/24.1.1/truffle-api-24.1.1.jar"
|
||||
curl -LO "https://repo1.maven.org/maven2/org/graalvm/sdk/nativeimage/24.1.1/nativeimage-24.1.1.jar"
|
||||
curl -LO "https://repo1.maven.org/maven2/org/graalvm/shadowed/icu4j/24.1.1/icu4j-24.1.1.jar"
|
||||
curl -LO "https://repo1.maven.org/maven2/org/graalvm/regex/regex/24.1.1/regex-24.1.1.jar"
|
||||
```
|
||||
|
||||
1) Download the SheetJS Standalone script, shim script and test file. Move all
|
||||
@ -118,14 +117,14 @@ CSV rows from the first worksheet.
|
||||
<TabItem value="unix" label="Linux/MacOS">
|
||||
|
||||
```bash
|
||||
java -cp ".:js-scriptengine-24.2.1.jar:js-language-24.2.1.jar:polyglot-24.2.1.jar:collections-24.2.1.jar:truffle-api-24.2.1.jar:nativeimage-24.2.1.jar:icu4j-24.2.1.jar:regex-24.2.1.jar" -Dpolyglot.js.nashorn-compat=true SheetJSNashorn pres.xlsx
|
||||
java -cp ".:js-scriptengine-24.1.1.jar:js-language-24.1.1.jar:polyglot-24.1.1.jar:collections-24.1.1.jar:truffle-api-24.1.1.jar:nativeimage-24.1.1.jar:icu4j-24.1.1.jar:regex-24.1.1.jar" -Dpolyglot.js.nashorn-compat=true SheetJSNashorn pres.xlsx
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="win" label="Windows">
|
||||
|
||||
```bash
|
||||
java -cp ".;js-scriptengine-24.2.1.jar;js-language-24.2.1.jar;polyglot-24.2.1.jar;collections-24.2.1.jar;truffle-api-24.2.1.jar;nativeimage-24.2.1.jar;icu4j-24.2.1.jar;regex-24.2.1.jar" -D"polyglot.js.nashorn-compat=true" SheetJSNashorn pres.xlsx
|
||||
java -cp ".;js-scriptengine-24.1.1.jar;js-language-24.1.1.jar;polyglot-24.1.1.jar;collections-24.1.1.jar;truffle-api-24.1.1.jar;nativeimage-24.1.1.jar;icu4j-24.1.1.jar;regex-24.1.1.jar" -D"polyglot.js.nashorn-compat=true" SheetJSNashorn pres.xlsx
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
@ -155,14 +154,14 @@ cd sheethorn
|
||||
<TabItem value="unix" label="Linux/MacOS">
|
||||
|
||||
```bash
|
||||
java -cp ".:js-scriptengine-24.2.1.jar:js-language-24.2.1.jar:polyglot-24.2.1.jar:collections-24.2.1.jar:truffle-api-24.2.1.jar:nativeimage-24.2.1.jar:icu4j-24.2.1.jar:regex-24.2.1.jar:SheetJSNashorn.jar" -Dpolyglot.js.nashorn-compat=true SheetJSNashorn pres.xlsx
|
||||
java -cp ".:js-scriptengine-24.1.1.jar:js-language-24.1.1.jar:polyglot-24.1.1.jar:collections-24.1.1.jar:truffle-api-24.1.1.jar:nativeimage-24.1.1.jar:icu4j-24.1.1.jar:regex-24.1.1.jar:SheetJSNashorn.jar" -Dpolyglot.js.nashorn-compat=true SheetJSNashorn pres.xlsx
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="win" label="Windows">
|
||||
|
||||
```bash
|
||||
java -cp ".;js-scriptengine-24.2.1.jar;js-language-24.2.1.jar;polyglot-24.2.1.jar;collections-24.2.1.jar;truffle-api-24.2.1.jar;nativeimage-24.2.1.jar;icu4j-24.2.1.jar;regex-24.2.1.jar;SheetJSNashorn.jar" -D"polyglot.js.nashorn-compat=true" SheetJSNashorn pres.xlsx
|
||||
java -cp ".;js-scriptengine-24.1.1.jar;js-language-24.1.1.jar;polyglot-24.1.1.jar;collections-24.1.1.jar;truffle-api-24.1.1.jar;nativeimage-24.1.1.jar;icu4j-24.1.1.jar;regex-24.1.1.jar;SheetJSNashorn.jar" -D"polyglot.js.nashorn-compat=true" SheetJSNashorn pres.xlsx
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
@ -1,4 +1,4 @@
|
||||
{
|
||||
"label": "JavaScript Engines",
|
||||
"label": "Other Languages",
|
||||
"position": 42
|
||||
}
|
@ -39,7 +39,7 @@ It is strongly recommended to create a separate page listing all embedded open
|
||||
source software. The "open source disclosure" should be linked from relevant
|
||||
pages including Terms of Service (ToS) and End User License Agreement (EULA)
|
||||
|
||||
[Office 365](https://web.archive.org/web/20250511192103/https://tasks.office.com/license.html)
|
||||
[Office 365](https://web.archive.org/web/20240412032204/https://tasks.office.com/license.html)
|
||||
includes the short-form attribution in a special license page.
|
||||
|
||||
<details>
|
||||
|
@ -367,7 +367,7 @@ This,is,a,Test
|
||||
The test suite is regularly run against a number of modern and legacy browsers
|
||||
using [Sauce Labs](https://saucelabs.com/).
|
||||
|
||||
The following chart shows test results on 2025-05-15 for version `0.20.3`:
|
||||
The following chart shows test results on 2024-10-20 for version `0.20.3`:
|
||||
|
||||
[](https://saucelabs.com/u/sheetjs)
|
||||
|
||||
|
@ -15,10 +15,6 @@
|
||||
},
|
||||
"overrides": {
|
||||
"@cmfcmf/docusaurus-search-local": {
|
||||
"@algolia/autocomplete-theme-classic": "1.19.1",
|
||||
"@algolia/autocomplete-js": "1.19.1",
|
||||
"@algolia/client-search": "5.25.0",
|
||||
"algoliasearch": "5.25.0",
|
||||
"@docusaurus/core": "3.7.0"
|
||||
}
|
||||
},
|
||||
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 73 KiB After Width: | Height: | Size: 32 KiB |
@ -1,37 +1,43 @@
|
||||
<!DOCTYPE html>
|
||||
<!-- sheetjs (C) 2013-present SheetJS https://sheetjs.com -->
|
||||
<!-- vim: set ts=2: -->
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<meta http-equiv="Content-Security-Policy" content="script-src 'self' https:">
|
||||
<meta name="robots" content="noindex">
|
||||
<title>SheetJS Electron Demo</title>
|
||||
<style>
|
||||
#drop{
|
||||
border:2px dashed #bbb;
|
||||
-moz-border-radius:5px;
|
||||
-webkit-border-radius:5px;
|
||||
border-radius:5px;
|
||||
padding:25px;
|
||||
text-align:center;
|
||||
font:20pt bold,"Vollkorn";color:#bbb
|
||||
}
|
||||
a { text-decoration: none }
|
||||
</style>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Roboto+Condensed:ital,wght@0,100..900;1,100..900&family=Roboto:ital,wght@0,100..900;1,100..900&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<pre>
|
||||
<b><a href="https://sheetjs.com">SheetJS Electron Demo</a></b>
|
||||
<br />
|
||||
<button id="readBtn">Click here to select a file from your computer</button><br />
|
||||
<div id="drop">Drop a spreadsheet file here to see sheet data</div>
|
||||
<input type="file" name="xlfile" id="readIn" /> ... or click here to select a file
|
||||
|
||||
</pre>
|
||||
<p><input type="submit" value="Export Data!" id="exportBtn" disabled="true"></p>
|
||||
<div id="htmlout"></div>
|
||||
<br />
|
||||
<div id="spinner-overlay" class="spinner-overlay" style="display:none;"><div class="spinner"></div></div>
|
||||
<header>
|
||||
<h1 class="text-heading">SheetJS Electron Demo</h1>
|
||||
<p class="text-muted text-condensed">Load a spreadsheet to view its contents</p>
|
||||
</header>
|
||||
<section class="file-upload" id="drop-container">
|
||||
<div id="drop">
|
||||
<input type="file" id="readIn" style="display:none" tabindex="0" aria-label="Select spreadsheet file">
|
||||
<p class="text-muted text-condensed">Drag and drop a file here</p>
|
||||
<p class="text-muted text-small">or</p>
|
||||
<button type="button" id="readBtn" tabindex="0" aria-label="Open file picker">Select a file</button>
|
||||
</div>
|
||||
</section>
|
||||
<div id="fileStatus" class="file-status"></div>
|
||||
<div id="onError"></div>
|
||||
<section id="htmlout" class="table-responsive"></section>
|
||||
<section class="export">
|
||||
<p><input type="submit" value="Export" id="exportBtn" disabled="true" tabindex="0" aria-label="Export spreadsheet"></p>
|
||||
</section>
|
||||
</body>
|
||||
<footer>
|
||||
<ul>
|
||||
<li><a href="https://docs.sheetjs.com/docs/" class="text-condensed"><img src="https://git.sheetjs.com/assets/img/logo.svg" alt="SheetJS" width="20" height="20"> SheetJS CE Docs</a></li>
|
||||
<li><a href="https://www.electronjs.org/docs" class="text-condensed"><img src="https://www.electronjs.org/assets/img/logo.svg" alt="Electron" width="20" height="20"> Electron Docs</a></li>
|
||||
</ul>
|
||||
</footer>
|
||||
<script src="index.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1,73 +1,232 @@
|
||||
/* sheetjs (C) 2013-present SheetJS -- https://sheetjs.com */
|
||||
const XLSX = require('xlsx');
|
||||
const electron = require('@electron/remote');
|
||||
const XLSX = window.SheetJSDemoAPI.xlsx;
|
||||
const basename = window.SheetJSDemoAPI.basename;
|
||||
const extname = window.SheetJSDemoAPI.extname;
|
||||
const onFileOpened = window.SheetJSDemoAPI.onFileOpened;
|
||||
|
||||
/* list of supported extensions */
|
||||
const EXTENSIONS = "xls|xlsx|xlsm|xlsb|xml|csv|txt|dif|sylk|slk|prn|ods|fods|htm|html|numbers".split("|");
|
||||
// ---------------------------------------------------------------------------
|
||||
// Supported file extensions
|
||||
// ---------------------------------------------------------------------------
|
||||
const EXTENSIONS =
|
||||
"xls|xlsx|xlsm|xlsb|xml|csv|txt|dif|sylk|slk|prn|ods|fods|htm|html|numbers".split(
|
||||
"|"
|
||||
);
|
||||
|
||||
/* write file with Electron API */
|
||||
async function exportFile() {
|
||||
const HTMLOUT = document.getElementById('htmlout');
|
||||
const wb = XLSX.utils.table_to_book(HTMLOUT.getElementsByTagName("TABLE")[0]);
|
||||
const o = await electron.dialog.showSaveDialog({
|
||||
title: 'Save file as',
|
||||
filters: [{
|
||||
// ---------------------------------------------------------------------------
|
||||
// DOM references
|
||||
// ---------------------------------------------------------------------------
|
||||
const dropContainer = document.getElementById("drop-container");
|
||||
const fileStatus = document.getElementById("fileStatus");
|
||||
const exportBtn = document.getElementById("exportBtn");
|
||||
const spinnerOverlay = document.getElementById("spinner-overlay");
|
||||
const htmlout = document.getElementById("htmlout");
|
||||
const onError = document.getElementById("onError");
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// State & helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
let currentWorkbook = null; // SheetJS workbook in memory
|
||||
const isSpreadsheet = (ext) => EXTENSIONS.includes(ext.toLowerCase());
|
||||
const nextPaint = () => new Promise(requestAnimationFrame);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Open external links in default browser (security)
|
||||
// ---------------------------------------------------------------------------
|
||||
document.addEventListener("click", (e) => {
|
||||
if (e.target.tagName === "A" && e.target.href.startsWith("http")) {
|
||||
e.preventDefault();
|
||||
window.SheetJSDemoAPI.openExternal(e.target.href);
|
||||
}
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Export logic – uses cached workbook (no DOM traversal)
|
||||
// ---------------------------------------------------------------------------
|
||||
async function exportWorkbookAsFile() {
|
||||
if (!currentWorkbook) return displayError("No workbook loaded!");
|
||||
// -- 1. use electron save as dialog to get file path
|
||||
const { filePath, canceled } = await window.SheetJSDemoAPI.saveFile([
|
||||
{
|
||||
name: "Spreadsheets",
|
||||
extensions: EXTENSIONS
|
||||
}]
|
||||
});
|
||||
XLSX.writeFile(wb, o.filePath);
|
||||
electron.dialog.showMessageBox({ message: "Exported data to " + o.filePath, buttons: ["OK"] });
|
||||
extensions: EXTENSIONS,
|
||||
},
|
||||
]);
|
||||
if (canceled || !filePath) return;
|
||||
// -- 2. write workbook to file
|
||||
try {
|
||||
XLSX.writeFile(currentWorkbook, filePath);
|
||||
window.SheetJSDemoAPI.message(`Exported to ${filePath}`);
|
||||
} catch (err) {
|
||||
displayError(`Failed to export: ${err.message}`);
|
||||
}
|
||||
}
|
||||
document.getElementById('exportBtn').addEventListener('click', exportFile, false);
|
||||
exportBtn.addEventListener("click", exportWorkbookAsFile);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Render workbook --> HTML tables
|
||||
// ---------------------------------------------------------------------------
|
||||
function renderWorkbookToTables(wb) {
|
||||
// -- 1. map through each sheet
|
||||
const html = wb.SheetNames.map((name) => {
|
||||
const sheet = wb.Sheets[name];
|
||||
// -- 2. convert sheet to HTML
|
||||
const table = XLSX.utils.sheet_to_html(sheet, { id: `${name}-tbl` });
|
||||
return `<details class="sheetjs-sheet-container">
|
||||
<summary class="sheetjs-sheet-name">${name}</summary>
|
||||
<div class="sheetjs-tab-content">${table}</div>
|
||||
</details>`;
|
||||
}).join("");
|
||||
// CAUTION!: in production environments please sanitize the HTML output to prevent XSS attacks from maliciously crafted spreadsheets.
|
||||
htmlout.innerHTML = html; // single write → single re‑flow of the DOM
|
||||
|
||||
/* common handler to create HTML tables on the page */
|
||||
function process_wb(wb) {
|
||||
const HTMLOUT = document.getElementById('htmlout');
|
||||
const XPORT = document.getElementById('exportBtn');
|
||||
XPORT.disabled = false;
|
||||
HTMLOUT.innerHTML = "";
|
||||
wb.SheetNames.forEach(function(sheetName) {
|
||||
const htmlstr = XLSX.utils.sheet_to_html(wb.Sheets[sheetName],{editable:true});
|
||||
HTMLOUT.innerHTML += htmlstr;
|
||||
});
|
||||
}
|
||||
|
||||
/* read file with Electron API */
|
||||
// ---------------------------------------------------------------------------
|
||||
// Generic UI helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
const displayError = (msg) =>
|
||||
onError
|
||||
? ((onError.textContent = msg), (onError.hidden = false))
|
||||
: console.error(msg);
|
||||
const hideDropUI = () =>
|
||||
dropContainer && (dropContainer.style.display = "none");
|
||||
const showDropUI = () =>
|
||||
dropContainer && (dropContainer.style.display = "block");
|
||||
const hideExportBtn = () => (exportBtn.disabled = true);
|
||||
const showExportBtn = () => (exportBtn.disabled = false);
|
||||
const showSpinner = () => (spinnerOverlay.style.display = "flex");
|
||||
const hideSpinner = () => (spinnerOverlay.style.display = "none");
|
||||
const hideOutputUI = () => (htmlout.innerHTML = "");
|
||||
const hideLoadedFileUI = () => (fileStatus.innerHTML = "");
|
||||
const getLoadedFileUI = (fileName) => `<div class="file-loaded">
|
||||
<span class="file-name text-muted text-small">${fileName}</span>
|
||||
<button type="button" class="unload-btn">Unload</button>
|
||||
</div>`;
|
||||
|
||||
function showLoadedFileUI(fileName) {
|
||||
fileStatus.innerHTML = getLoadedFileUI(fileName);
|
||||
hideDropUI();
|
||||
showExportBtn();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Event delegation for unload button – avoids per‑render listener leaks
|
||||
// ---------------------------------------------------------------------------
|
||||
fileStatus.addEventListener("click", (e) => {
|
||||
if (e.target.classList.contains("unload-btn")) {
|
||||
hideLoadedFileUI();
|
||||
hideExportBtn();
|
||||
showDropUI();
|
||||
hideOutputUI();
|
||||
currentWorkbook = null;
|
||||
}
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// File‑open dialog handler
|
||||
// ---------------------------------------------------------------------------
|
||||
async function handleReadBtn() {
|
||||
const o = await electron.dialog.showOpenDialog({
|
||||
title: 'Select a file',
|
||||
filters: [{
|
||||
// -- 1. show file open dialog to get the file path
|
||||
const { filePaths, canceled } = await window.SheetJSDemoAPI.openFile([
|
||||
{
|
||||
name: "Spreadsheets",
|
||||
extensions: EXTENSIONS
|
||||
}],
|
||||
properties: ['openFile']
|
||||
extensions: EXTENSIONS,
|
||||
},
|
||||
]);
|
||||
if (canceled || !filePaths.length) return;
|
||||
if (filePaths.length !== 1)
|
||||
return displayError("Please choose a single file.");
|
||||
|
||||
showSpinner();
|
||||
await nextPaint(); // ensure spinner paints
|
||||
try {
|
||||
const filePath = filePaths[0];
|
||||
// -- 2. read the first selected file
|
||||
currentWorkbook = XLSX.readFile(filePath);
|
||||
renderWorkbookToTables(currentWorkbook);
|
||||
showLoadedFileUI(basename(filePath));
|
||||
} finally {
|
||||
hideSpinner();
|
||||
hideDropUI();
|
||||
onError && (onError.hidden = true);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Drag‑and‑drop + file input
|
||||
// ---------------------------------------------------------------------------
|
||||
function addListener(id, evt, fn) {
|
||||
const el = document.getElementById(id);
|
||||
if (el) el.addEventListener(evt, fn);
|
||||
}
|
||||
|
||||
function attachFileListeners() {
|
||||
// file input element
|
||||
addListener("readIn", "change", (e) => {
|
||||
showSpinner();
|
||||
nextPaint().then(() => readFile(e.target.files));
|
||||
});
|
||||
if(o.filePaths.length == 0) throw new Error("No file was selected!");
|
||||
process_wb(XLSX.readFile(o.filePaths[0]));
|
||||
}
|
||||
document.getElementById('readBtn').addEventListener('click', handleReadBtn, false);
|
||||
addListener("readBtn", "click", handleReadBtn);
|
||||
|
||||
/* read file with Web APIs */
|
||||
// drag‑and‑drop (applied to whole window for simplicity)
|
||||
const onDrag = (e) => {
|
||||
e.preventDefault();
|
||||
e.dataTransfer.dropEffect = "copy";
|
||||
};
|
||||
|
||||
["dragenter", "dragover"].forEach((t) =>
|
||||
document.body.addEventListener(t, onDrag, { passive: false })
|
||||
);
|
||||
|
||||
document.body.addEventListener(
|
||||
"drop",
|
||||
(e) => {
|
||||
e.preventDefault();
|
||||
readFile(e.dataTransfer.files).catch((err) => displayError(err.message));
|
||||
},
|
||||
{ passive: false }
|
||||
);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Read File from input or DnD
|
||||
// ---------------------------------------------------------------------------
|
||||
async function readFile(files) {
|
||||
const f = files[0];
|
||||
const data = await f.arrayBuffer();
|
||||
process_wb(XLSX.read(data));
|
||||
// -- 1. if no files, return
|
||||
if (!files || !files.length) return;
|
||||
// -- 2. get the first file
|
||||
const file = files[0];
|
||||
// -- 3. if not a spreadsheet, return error
|
||||
const ext = extname(file.name).slice(1);
|
||||
if (!isSpreadsheet(ext)) return displayError(`Unsupported file type .${ext}`);
|
||||
|
||||
showSpinner();
|
||||
try {
|
||||
// -- 4. read the file
|
||||
const data = await file.arrayBuffer();
|
||||
currentWorkbook = XLSX.read(data);
|
||||
// -- 5. render the workbook to tables
|
||||
renderWorkbookToTables(currentWorkbook);
|
||||
// -- 6. show the loaded file UI
|
||||
showLoadedFileUI(file.name);
|
||||
} finally {
|
||||
hideSpinner();
|
||||
// reset error UI state
|
||||
onError && (onError.hidden = true);
|
||||
}
|
||||
}
|
||||
|
||||
// file input element
|
||||
document.getElementById('readIn').addEventListener('change', (e) => { readFile(e.target.files); }, false);
|
||||
|
||||
// drag and drop
|
||||
const drop = document.getElementById('drop');
|
||||
drop.addEventListener('drop', (e) => {
|
||||
e.stopPropagation(); e.preventDefault();
|
||||
readFile(e.dataTransfer.files);
|
||||
}, false);
|
||||
|
||||
const handleDrag = (e) => {
|
||||
e.stopPropagation(); e.preventDefault();
|
||||
e.dataTransfer.dropEffect = 'copy';
|
||||
};
|
||||
drop.addEventListener('dragenter', handleDrag, false);
|
||||
drop.addEventListener('dragover', handleDrag, false);
|
||||
// ---------------------------------------------------------------------------
|
||||
// Init
|
||||
// ---------------------------------------------------------------------------
|
||||
attachFileListeners();
|
||||
// the file-opened event is sent from the main process when a file is opened using "open with"
|
||||
onFileOpened(async (_e, filePath) => {
|
||||
showSpinner();
|
||||
await nextPaint(); // ensure spinner paints
|
||||
currentWorkbook = XLSX.readFile(filePath);
|
||||
renderWorkbookToTables(currentWorkbook);
|
||||
showLoadedFileUI(path.basename(filePath));
|
||||
hideSpinner();
|
||||
hideDropUI();
|
||||
showExportBtn();
|
||||
});
|
||||
|
@ -1,29 +1,120 @@
|
||||
/* sheetjs (C) 2013-present SheetJS -- https://sheetjs.com */
|
||||
var electron = require('electron');
|
||||
var XLSX = require('xlsx');
|
||||
var app = electron.app;
|
||||
require('@electron/remote/main').initialize(); // required for Electron 14+
|
||||
const { app, BrowserWindow, ipcMain, dialog } = require('electron');
|
||||
const path = require('path');
|
||||
const XLSX = require('xlsx');
|
||||
|
||||
var win = null;
|
||||
const EXT_REGEX = /\.(xls|xlsx|xlsm|xlsb|xml|csv|txt|dif|sylk|slk|prn|ods|fods|htm|html|numbers)$/i;
|
||||
const pendingPaths = []; // paths of files that were opened before the window was created. These are queued and loaded once the window is created.
|
||||
|
||||
let win = null; // reference to the main window, to make sure we don't create multiple windows.
|
||||
|
||||
/* In electron, the main process is the only process that can directly interface with the operating system.
|
||||
The renderer process is sandboxed and cannot run any non-browser code.
|
||||
To allow the renderer process to interface with the operating system, we use the contextBridge API to expose the API to the renderer process.
|
||||
https://www.electronjs.org/docs/latest/api/context-bridge
|
||||
*/
|
||||
|
||||
/* ----------------------------------------------------------------------------- */
|
||||
/* IPC handlers that allow communication between main and renderer processes */
|
||||
/* ----------------------------------------------------------------------------- */
|
||||
|
||||
/* These three functions can be used to interface with the operating system from the renderer process.
|
||||
the ipcMain.handle() function is used to register a handler for a specific event.
|
||||
when the renderer process calls the corresponding function, the main process will receive the event and execute the handler.
|
||||
|
||||
In this case, we are listening to events which allow the renderer process to open/save file dialogs and show modal messages.
|
||||
*/
|
||||
ipcMain.handle('dialog:openFile', (_e, filters) =>
|
||||
dialog.showOpenDialog({ title: 'Select a file', filters, properties: ['openFile'] })
|
||||
);
|
||||
|
||||
ipcMain.handle('dialog:saveFile', (_e, filters) =>
|
||||
dialog.showSaveDialog({ title: 'Save file as', filters })
|
||||
);
|
||||
|
||||
ipcMain.handle('dialog:message', (_e, msg) =>
|
||||
dialog.showMessageBox({ message: msg, buttons: ['OK'] })
|
||||
);
|
||||
|
||||
/* ----------------------------------------------------------------------------- */
|
||||
/* Utility functions */
|
||||
/* ----------------------------------------------------------------------------- */
|
||||
function sendToRenderer(fp) {
|
||||
if (win && win.webContents) win.webContents.send('file-opened', fp);
|
||||
else pendingPaths.push(fp);
|
||||
}
|
||||
|
||||
/*
|
||||
On Windows and Linux, opening a file using the "open with" menu option or `open` command will pass the file path as a startup argument to the app.
|
||||
We need to parse it, and test if it is a spreadsheet file.
|
||||
*/
|
||||
function firstSpreadsheetFromArgv() {
|
||||
const args = process.defaultApp ? process.argv.slice(2) : process.argv.slice(1);
|
||||
return args.find((a) => EXT_REGEX.test(a));
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------- */
|
||||
/* Single-instance guard */
|
||||
/* ----------------------------------------------------------------------------- */
|
||||
|
||||
// Windows and Linux only: If the app is already running, we need to prevent a new instance from launching when opening a file via the "open with" menu option or `open` command.
|
||||
if (!app.requestSingleInstanceLock()) app.quit();
|
||||
else {
|
||||
app.on('second-instance', (_e, argv) => {
|
||||
const fp = argv.find((a) => EXT_REGEX.test(a));
|
||||
if (fp) sendToRenderer(fp);
|
||||
if (win) { win.show(); win.focus(); }
|
||||
});
|
||||
}
|
||||
|
||||
// macOS file / url events
|
||||
app.on('open-file', (evt, fp) => { evt.preventDefault(); sendToRenderer(fp); });
|
||||
app.on('open-url', (evt, url) => { evt.preventDefault(); sendToRenderer(url.replace('file://', '')); });
|
||||
|
||||
/* ----------------------------------------------------------------------------- */
|
||||
/* Create the window */
|
||||
/* ----------------------------------------------------------------------------- */
|
||||
function createWindow() {
|
||||
if (win) return;
|
||||
win = new electron.BrowserWindow({
|
||||
width: 800, height: 600,
|
||||
|
||||
win = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
worldSafeExecuteJavaScript: true, // required for Electron 12+
|
||||
contextIsolation: false, // required for Electron 12+
|
||||
nodeIntegration: true,
|
||||
enableRemoteModule: true
|
||||
preload: path.join(__dirname, './preload.js'), // preload script that will be executed in the renderer process before the page is loaded and act as a bridge between the main and renderer processes within a worker thread.
|
||||
contextIsolation: true, // isolate and enable bridge, keeping the renderer process sandboxed and separated from the main process.
|
||||
nodeIntegration: false, // no Node.js in renderer process.
|
||||
nodeIntegrationInWorker: true, // enable Node.js in worker threads.
|
||||
worldSafeExecuteJavaScript: true
|
||||
}
|
||||
});
|
||||
win.loadURL("file://" + __dirname + "/index.html");
|
||||
require('@electron/remote/main').enable(win.webContents); // required for Electron 14+
|
||||
win.webContents.openDevTools();
|
||||
win.on('closed', function () { win = null; });
|
||||
|
||||
win.loadFile('index.html');
|
||||
if (process.env.NODE_ENV === 'development') win.webContents.openDevTools();
|
||||
|
||||
win.on('closed', () => { win = null; });
|
||||
|
||||
win.webContents.once('did-finish-load', () => {
|
||||
pendingPaths.splice(0).forEach(sendToRenderer);
|
||||
});
|
||||
}
|
||||
if (app.setAboutPanelOptions) app.setAboutPanelOptions({ applicationName: 'sheetjs-electron', applicationVersion: "XLSX " + XLSX.version, copyright: "(C) 2017-present SheetJS LLC" });
|
||||
app.on('open-file', function () { console.log(arguments); });
|
||||
app.on('ready', createWindow);
|
||||
|
||||
/* ----------------------------------------------------------------------------- */
|
||||
/* App lifecycle */
|
||||
/* ----------------------------------------------------------------------------- */
|
||||
app.whenReady().then(() => {
|
||||
const fp = firstSpreadsheetFromArgv();
|
||||
if (fp) pendingPaths.push(fp);
|
||||
createWindow();
|
||||
});
|
||||
|
||||
if (app.setAboutPanelOptions) {
|
||||
app.setAboutPanelOptions({
|
||||
applicationName: 'sheetjs-electron',
|
||||
applicationVersion: `XLSX ${XLSX.version}`,
|
||||
copyright: '(C) 2017‑present SheetJS LLC'
|
||||
});
|
||||
}
|
||||
|
||||
app.on('activate', createWindow);
|
||||
app.on('window-all-closed', function () { if (process.platform !== 'darwin') app.quit(); });
|
||||
app.on('window-all-closed', () => { if (process.platform !== 'darwin') app.quit(); });
|
@ -6,13 +6,13 @@
|
||||
"version": "0.0.0",
|
||||
"main": "main.js",
|
||||
"dependencies": {
|
||||
"@electron/remote": "2.1.2",
|
||||
"xlsx": "https://sheet.lol/balls/xlsx-0.20.3.tgz"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "electron-forge start",
|
||||
"package": "electron-forge package",
|
||||
"make": "electron-forge make"
|
||||
"make": "electron-forge make",
|
||||
"dist": "electron-builder"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@electron-forge/cli": "7.8.0",
|
||||
@ -20,8 +20,28 @@
|
||||
"@electron-forge/maker-rpm": "7.8.0",
|
||||
"@electron-forge/maker-squirrel": "7.8.0",
|
||||
"@electron-forge/maker-zip": "7.8.0",
|
||||
"electron": "35.1.2"
|
||||
"electron": "36.1.0",
|
||||
"electron-builder": "^26.0.12"
|
||||
},
|
||||
|
||||
"build": {
|
||||
"appId": "com.sheetjs.electron",
|
||||
"fileAssociations": [
|
||||
{
|
||||
"ext": [
|
||||
"xls","xlsx","xlsm","xlsb","xml","csv","txt","dif",
|
||||
"sylk","slk","prn","ods","fods","htm","html","numbers"
|
||||
],
|
||||
"name": "Spreadsheet / Delimited File",
|
||||
"description": "Spreadsheets and delimited text files opened by SheetJS-Electron",
|
||||
"role": "Editor"
|
||||
}
|
||||
],
|
||||
"mac": { "target": "dmg" },
|
||||
"win": { "target": "nsis" },
|
||||
"linux": { "target": "deb" }
|
||||
},
|
||||
|
||||
"config": {
|
||||
"forge": {
|
||||
"packagerConfig": {},
|
||||
@ -49,4 +69,4 @@
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
20
docz/static/electron/preload.js
Normal file
@ -0,0 +1,20 @@
|
||||
const { contextBridge, ipcRenderer, shell } = require('electron');
|
||||
const path = require('path');
|
||||
const XLSX = require('xlsx');
|
||||
|
||||
// Because the main process is sandboxed, we need to use the contextBridge API to expose the API to the renderer process.
|
||||
// https://www.electronjs.org/docs/latest/api/context-bridge
|
||||
contextBridge.exposeInMainWorld('SheetJSDemoAPI', {
|
||||
openFile: (filters) => ipcRenderer.invoke('dialog:openFile', filters),
|
||||
saveFile: (filters) => ipcRenderer.invoke('dialog:saveFile', filters),
|
||||
message: (msg) => ipcRenderer.invoke('dialog:message', msg),
|
||||
openExternal: (url) => shell.openExternal(url),
|
||||
// expose file-opened event
|
||||
onFileOpened: (cb) => ipcRenderer.on('file-opened', (_e, fp) => cb(fp)),
|
||||
// expose basename from path package
|
||||
basename: (p) => path.basename(p),
|
||||
// expose extname from path package
|
||||
extname: (p) => path.extname(p),
|
||||
// expose sheetjs package functions
|
||||
xlsx: XLSX,
|
||||
});
|
363
docz/static/electron/styles.css
Normal file
@ -0,0 +1,363 @@
|
||||
/* =====================
|
||||
Root Variables
|
||||
===================== */
|
||||
:root {
|
||||
--text-base: #212529;
|
||||
--text-muted: #666363;
|
||||
--text-accent: #0c9244;
|
||||
--button-primary: #212529;
|
||||
--button-text: #fff;
|
||||
--button-primary-hover: #0c9244;
|
||||
--button-primary-active: #075025;
|
||||
--button-primary-disabled: #6c757d;
|
||||
--white: #fff;
|
||||
--black: #000;
|
||||
--table-even: #f6f6f6;
|
||||
--table-hover: #88dda98f;
|
||||
--table-head: #f8f9faaf;
|
||||
--danger: #c0392b;
|
||||
}
|
||||
|
||||
/* =====================
|
||||
Global Styles & Reset
|
||||
===================== */
|
||||
body, html {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
min-height: 100vh;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
box-sizing: border-box;
|
||||
font-family: "Roboto", sans-serif;
|
||||
font-optical-sizing: auto;
|
||||
font-style: normal;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
li {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
/* =====================
|
||||
Typography
|
||||
===================== */
|
||||
h1, p {
|
||||
margin: 0;
|
||||
color: var(--text-base);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.text-muted {
|
||||
color: var(--text-muted) !important;
|
||||
}
|
||||
|
||||
.text-condensed {
|
||||
font-family: "Roboto Condensed", sans-serif;
|
||||
font-optical-sizing: auto;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.text-small {
|
||||
font-size: 0.875rem !important;
|
||||
}
|
||||
|
||||
/* =====================
|
||||
Links
|
||||
===================== */
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: var(--text-base);
|
||||
}
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
color: var(--text-accent);
|
||||
}
|
||||
|
||||
/* =====================
|
||||
Header
|
||||
===================== */
|
||||
header {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
/* =====================
|
||||
Buttons & Inputs
|
||||
===================== */
|
||||
button, input[type="submit"] {
|
||||
background-color: var(--button-primary);
|
||||
border: none;
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s ease;
|
||||
color: var(--button-text);
|
||||
}
|
||||
|
||||
button:hover, input[type="submit"]:hover {
|
||||
background-color: var(--button-primary-hover);
|
||||
}
|
||||
|
||||
button:active, input[type="submit"]:active {
|
||||
background-color: var(--button-primary-active);
|
||||
}
|
||||
|
||||
button:disabled, input[type="submit"]:disabled {
|
||||
background-color: var(--button-primary-disabled);
|
||||
cursor: not-allowed;
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
input[type="file"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
button:focus, input[type="submit"]:focus, input[type="file"]:focus {
|
||||
outline: 3px solid var(--text-accent);
|
||||
outline-offset: 2px;
|
||||
box-shadow: 0 0 0 2px var(--text-accent);
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
/* =====================
|
||||
File Upload
|
||||
===================== */
|
||||
.file-upload {
|
||||
display: flex;
|
||||
margin: 1rem auto;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.5rem;
|
||||
padding: 1rem;
|
||||
width: 75%;
|
||||
max-width: 600px;
|
||||
border-radius: 4px;
|
||||
background-color: var(--white);
|
||||
}
|
||||
|
||||
.file-upload input[type="file"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#drop {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
/* =====================
|
||||
Export Section
|
||||
===================== */
|
||||
.export {
|
||||
margin: 2rem 0;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
/* =====================
|
||||
Footer
|
||||
===================== */
|
||||
footer {
|
||||
padding: 1rem;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
footer ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
footer li {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
footer li a {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
/* =====================
|
||||
Responsive Table Container
|
||||
===================== */
|
||||
.table-responsive {
|
||||
width: 100%;
|
||||
overflow-x: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
/* =====================
|
||||
SheetJS Table Styles
|
||||
===================== */
|
||||
table {
|
||||
width: 80%;
|
||||
padding: 1rem;
|
||||
max-width: 900px;
|
||||
border-collapse: collapse;
|
||||
font-family: "Roboto", "Roboto Condensed", sans-serif;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
table caption {
|
||||
caption-side: top;
|
||||
font-family: "Roboto Condensed", sans-serif;
|
||||
font-size: 1.2rem;
|
||||
color: var(--text-accent);
|
||||
padding: 0.5rem;
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
|
||||
table thead {
|
||||
background: var(--table-head);
|
||||
}
|
||||
|
||||
table th, table td {
|
||||
padding: 0.25rem 1rem;
|
||||
border: 1px solid var(--text-muted);
|
||||
text-align: left;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
th {
|
||||
color: var(--text-base);
|
||||
font-family: "Roboto Condensed", sans-serif;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.03em;
|
||||
}
|
||||
|
||||
table tbody tr:nth-child(even) {
|
||||
background: var(--table-even);
|
||||
}
|
||||
|
||||
table tbody tr:hover {
|
||||
background: var(--table-hover);
|
||||
}
|
||||
|
||||
.sheetjs-sheet-name {
|
||||
font-family: "Roboto Condensed", sans-serif;
|
||||
padding: 0.5rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.sheetjs-sheet-container {
|
||||
margin: 1rem auto;
|
||||
width: 90%;
|
||||
max-width: 900px;
|
||||
cursor: pointer;
|
||||
background-color: #eee;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
details:focus-within {
|
||||
outline: 3px solid var(--text-accent);
|
||||
outline-offset: 2px;
|
||||
box-shadow: 0 0 0 2px var(--text-accent);
|
||||
}
|
||||
|
||||
a:focus {
|
||||
outline: 3px solid var(--text-accent);
|
||||
outline-offset: 2px;
|
||||
box-shadow: 0 0 0 2px var(--text-accent);
|
||||
}
|
||||
|
||||
summary:focus-within {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.sheetjs-tab-content {
|
||||
cursor: pointer;
|
||||
padding: 1rem;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
/* =====================
|
||||
File Status/Loaded/Unload
|
||||
===================== */
|
||||
.file-status {
|
||||
margin-top: 10px;
|
||||
font-size: 1rem;
|
||||
min-height: 1.5em;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
|
||||
.file-loaded {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.unload-btn:hover {
|
||||
background: var(--danger);
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
/* Spinner Styles */
|
||||
.spinner-overlay {
|
||||
position: fixed;
|
||||
top: 0; left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background: rgba(255,255,255,0.6);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 10000;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
border: 6px solid #f3f3f3;
|
||||
border-top: 6px solid var(--text-accent);
|
||||
border-radius: 50%;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
#onError {
|
||||
color: var(--danger);
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* =====================
|
||||
Media Queries
|
||||
===================== */
|
||||
@media (max-width: 700px) {
|
||||
table th, table td {
|
||||
padding: 0.25rem 0.5rem;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
table caption {
|
||||
font-size: 1rem;
|
||||
padding: 0.25rem;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import SheetJSModule from './sheetmodule';
|
||||
import SheetJSModule from './sheetmodule'
|
||||
|
||||
export default defineNuxtConfig({
|
||||
// @ts-ignore
|
@ -2,7 +2,7 @@
|
||||
import { defineTransformer } from "@nuxt/content/transformers/utils";
|
||||
import { read, utils } from "xlsx";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { resolve } from "node:path";
|
||||
import { resolve } from 'node:path';
|
||||
|
||||
export default defineTransformer({
|
||||
name: 'sheetformer',
|
@ -1,14 +0,0 @@
|
||||
/*
|
||||
stplugin.c, version 3.0
|
||||
copyright (c) 2003, 2006, 2015 StataCorp LP
|
||||
*/
|
||||
|
||||
#include "stplugin.h"
|
||||
|
||||
ST_plugin *_stata_ ;
|
||||
|
||||
STDLL pginit(ST_plugin *p)
|
||||
{
|
||||
_stata_ = p ;
|
||||
return(SD_PLUGINVER) ;
|
||||
}
|
@ -1,246 +0,0 @@
|
||||
/*
|
||||
stplugin.h, version 3.0.0
|
||||
copyright (c) 2003, 2004, 2006, 2015 StataCorp LP
|
||||
*/
|
||||
#if !defined(STPLUGIN_H)
|
||||
#define STPLUGIN_H
|
||||
|
||||
#if !defined(SD_FASTMODE)
|
||||
#define SD_SAFEMODE
|
||||
#endif
|
||||
|
||||
#define HP9000 1
|
||||
#define OPUNIX 2
|
||||
#define APPLEMAC 3
|
||||
#define STWIN32 4
|
||||
|
||||
#ifndef SYSTEM
|
||||
#define SYSTEM STWIN32
|
||||
#endif
|
||||
|
||||
typedef signed char ST_sbyte ;
|
||||
typedef unsigned char ST_ubyte ;
|
||||
typedef int ST_int ;
|
||||
typedef unsigned ST_unsigned ;
|
||||
typedef short int ST_int2 ;
|
||||
typedef int ST_int4 ;
|
||||
typedef long ST_long ;
|
||||
typedef unsigned int ST_uint4 ;
|
||||
typedef float ST_float ;
|
||||
typedef double ST_double ;
|
||||
typedef unsigned char ST_boolean ;
|
||||
typedef int ST_retcode ;
|
||||
typedef double * ST_dmkey ;
|
||||
|
||||
#if !defined(bTrue)
|
||||
#define bTrue 1
|
||||
#define bFalse 0
|
||||
#endif
|
||||
|
||||
#define SF_HIWORD(x) ((ST_int2)((ST_int4)(x)>>16))
|
||||
#define SF_LOWORD(x) ((ST_int2)(x))
|
||||
#define SF_MAKELONG(x,y) ((ST_int4)(((ST_int2)(x))|((ST_int4)((ST_int2)(y)))<<16))
|
||||
|
||||
#if SYSTEM==STWIN32
|
||||
#if __cplusplus
|
||||
#define STDLL extern "C" __declspec(dllexport) ST_retcode
|
||||
#else
|
||||
#define STDLL extern __declspec(dllexport) ST_retcode
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if SYSTEM!=STWIN32
|
||||
#if SYSTEM==HP9000
|
||||
#include <dl.h>
|
||||
#endif
|
||||
|
||||
#if SYSTEM==OPUNIX
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
#if __cplusplus
|
||||
#define STDLL extern "C" ST_retcode
|
||||
#else
|
||||
#define STDLL ST_retcode
|
||||
#endif
|
||||
#define LPSTR char *
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
ST_int type ;
|
||||
ST_int nel ;
|
||||
ST_int m ;
|
||||
ST_int n ;
|
||||
} ST_matinfo ;
|
||||
|
||||
#define SD_PLUGINMAJ 3
|
||||
#define SD_PLUGINMIN 0
|
||||
#define SD_PLUGINVER SF_MAKELONG(SD_PLUGINMAJ,SD_PLUGINMIN)
|
||||
|
||||
typedef void (* ST_VV) (void) ;
|
||||
typedef ST_int (* ST_IV) (void) ;
|
||||
typedef ST_int (* ST_IS) (char *) ;
|
||||
typedef void (* ST_VU) (ST_ubyte) ;
|
||||
typedef ST_boolean (* ST_BI) (ST_int) ;
|
||||
typedef ST_boolean (* ST_BII) (ST_int,ST_int) ;
|
||||
typedef ST_boolean (* ST_BD) (ST_double) ;
|
||||
typedef ST_int (* ST_III) (ST_int,ST_int) ;
|
||||
typedef ST_double (* ST_DII) (ST_int,ST_int) ;
|
||||
typedef ST_double (* ST_DV) (void) ;
|
||||
typedef ST_double (* ST_DD) (ST_double) ;
|
||||
typedef ST_double (* ST_DDD) (ST_double,ST_double) ;
|
||||
typedef ST_int (* ST_ISS) (char *,char *) ;
|
||||
typedef ST_int (* ST_ISI) (char *,ST_int) ;
|
||||
typedef ST_int (* ST_ISSI) (char *,char *,ST_int) ;
|
||||
typedef void (* ST_VSD) (char *,ST_double) ;
|
||||
typedef ST_int (* ST_ISD) (char *, ST_double) ;
|
||||
typedef ST_int (* ST_ISDp) (char *,ST_double *) ;
|
||||
typedef ST_int (* ST_ISDpIIIII) (char *,ST_int,ST_double *,ST_int,ST_int,ST_int,ST_int,ST_int) ;
|
||||
typedef ST_int (* ST_ISIID) (char *, ST_int, ST_int, ST_double) ;
|
||||
typedef ST_int (* ST_ISIIDp) (char *,ST_int,ST_int,ST_double *) ;
|
||||
typedef ST_int (* ST_ISDpI) (char *,ST_double *,ST_int) ;
|
||||
typedef void (* ST_VSMip) (char *,ST_matinfo *) ;
|
||||
typedef ST_int (* ST_IIIDp) (ST_int, ST_int, ST_double *) ;
|
||||
typedef ST_int (* ST_IIID) (ST_int, ST_int, ST_double) ;
|
||||
typedef char * (* ST_SSI) (char *,ST_int) ;
|
||||
typedef char * (* ST_SSSD) (char *,char *,ST_double) ;
|
||||
typedef char * (* ST_SSSDM) (char *,char *,ST_double, ST_dmkey) ;
|
||||
typedef ST_int (* ST_IIIS) (ST_int, ST_int, char *) ;
|
||||
typedef ST_int (* ST_IIISI) (ST_int, ST_int, char *, ST_int) ;
|
||||
|
||||
typedef struct {
|
||||
ST_IS spoutsml ;
|
||||
ST_IS spoutnosml ;
|
||||
ST_VV spoutflush ;
|
||||
ST_VU set_outputlevel ;
|
||||
ST_ISI get_input ;
|
||||
|
||||
ST_IV pollstd ;
|
||||
ST_IV pollnow ;
|
||||
|
||||
ST_SSSD safereforms ;
|
||||
ST_SSSDM safereforml ;
|
||||
|
||||
ST_SSI gettok ;
|
||||
|
||||
ST_ISS macresave ;
|
||||
ST_ISSI macuse ;
|
||||
|
||||
ST_ISDp scalaruse ;
|
||||
ST_ISDp scalarsave ;
|
||||
|
||||
ST_ISDpIIIII matrixstore ;
|
||||
ST_ISDpI matrixload ;
|
||||
ST_VSMip matrixinfo ;
|
||||
ST_ISIIDp matrixel ;
|
||||
ST_int matsize ;
|
||||
|
||||
ST_DII data, safedata ;
|
||||
|
||||
ST_IV nobs ;
|
||||
ST_IV nvar ;
|
||||
|
||||
ST_double missval ;
|
||||
ST_BD ismissing ;
|
||||
|
||||
ST_ISI stfindvar ;
|
||||
ST_BI isstr ;
|
||||
ST_VSD abvarfcn ;
|
||||
|
||||
ST_int *stopflag ;
|
||||
|
||||
ST_DDD stround ;
|
||||
ST_DD stsqrt ;
|
||||
ST_DDD stpow ;
|
||||
ST_DD stlog ;
|
||||
ST_DD stexp ;
|
||||
ST_DV strandom ;
|
||||
|
||||
ST_IIID store ;
|
||||
ST_IIID safestore ;
|
||||
ST_IIIS sstore ;
|
||||
ST_BI selobs ;
|
||||
ST_IV nobs1 ;
|
||||
ST_IV nobs2 ;
|
||||
ST_IV nvars ;
|
||||
ST_IS spouterr ;
|
||||
ST_ISIIDp safematel ;
|
||||
ST_ISIID safematstore ;
|
||||
ST_ISIIDp matel ;
|
||||
ST_ISIID matstore ;
|
||||
ST_IIIDp safevdata ;
|
||||
ST_IIIDp vdata ;
|
||||
ST_IS colsof ;
|
||||
ST_IS rowsof ;
|
||||
ST_ISD scalsave ;
|
||||
|
||||
ST_IIIS sdata ;
|
||||
|
||||
ST_int2 major ;
|
||||
ST_int2 minor ;
|
||||
ST_BI isstrl ;
|
||||
ST_BII isbinary ;
|
||||
ST_III sdatalen ;
|
||||
ST_IIISI strldata ;
|
||||
} ST_plugin ;
|
||||
|
||||
|
||||
#if __cplusplus
|
||||
extern "C" ST_plugin *_stata_ ;
|
||||
#else
|
||||
extern ST_plugin *_stata_ ;
|
||||
#endif
|
||||
STDLL pginit(ST_plugin *p) ;
|
||||
|
||||
#define SF_display(a) ((_stata_)->spoutsml((a)))
|
||||
#define SF_error(a) ((_stata_)->spouterr((a)))
|
||||
|
||||
#define SF_poll ((_stata_)->pollstd)
|
||||
#define SW_stopflag (*((_stata_)->stopflag))
|
||||
|
||||
#define SF_macro_save(m,t) ((_stata_)->macresave((m),(t)))
|
||||
#define SF_macro_use(m,d,l) ((_stata_)->macuse((m),(d),(l)))
|
||||
|
||||
#define SF_scal_use(s,d) ((_stata_)->scalaruse((s),(d)))
|
||||
#define SF_scal_save(s,d) ((_stata_)->scalsave((s),(d)))
|
||||
|
||||
#if defined(SD_SAFEMODE)
|
||||
#define SF_mat_el(s,r,c,d) ((_stata_)->safematel((s),(r),(c),(d)))
|
||||
#define SF_mat_store(s,r,c,d) ((_stata_)->safematstore((s),(r),(c),(d)))
|
||||
#else
|
||||
#define SF_mat_el(s,r,c,d) ((_stata_)->matel((s),(r),(c),(d)))
|
||||
#define SF_mat_store(s,r,c,d) ((_stata_)->matstore((s),(r),(c),(d)))
|
||||
#endif
|
||||
#define SV_matsize ((_stata_)->matsize)
|
||||
#define SF_col(s) ((_stata_)->colsof((s)))
|
||||
#define SF_row(s) ((_stata_)->rowsof((s)))
|
||||
|
||||
#if defined(SD_SAFEMODE)
|
||||
#define SF_vdata(i,j,d) ((_stata_)->safevdata((i),(j),(d)))
|
||||
#define SF_vstore(i,j,v) ((_stata_)->safestore((i),(j),(v)))
|
||||
#else
|
||||
#define SF_vdata(i,j,d) ((_stata_)->vdata((i),(j),(d)))
|
||||
#define SF_vstore(i,j,v) ((_stata_)->store((i),(j),(v)))
|
||||
#endif
|
||||
|
||||
#define SF_nobs ((_stata_)->nobs)
|
||||
#define SF_in1 ((_stata_)->nobs1)
|
||||
#define SF_in2 ((_stata_)->nobs2)
|
||||
#define SF_nvar ((_stata_)->nvar)
|
||||
#define SF_nvars ((_stata_)->nvars)
|
||||
|
||||
#define SF_sstore(i,j,s) ((_stata_)->sstore((i),(j),(s)))
|
||||
#define SF_sdata(i,j,s) ((_stata_)->sdata((i),(j),(s)))
|
||||
#define SF_strldata(i,j,s,l) ((_stata_)->strldata((i),(j),(s),(l)))
|
||||
|
||||
#define SF_sdatalen(i,j) ((_stata_)->sdatalen((i),(j)))
|
||||
#define SF_var_is_string(a) ((_stata_)->isstr(a))
|
||||
#define SF_var_is_strl(a) ((_stata_)->isstrl(a))
|
||||
#define SF_var_is_binary(i,j) ((_stata_)->isbinary((i),(j)))
|
||||
|
||||
#define SV_missval ((_stata_)->missval)
|
||||
|
||||
#define SF_is_missing(z) ((_stata_)->ismissing(z))
|
||||
#define SF_ifobs(z) ((_stata_)->selobs(z))
|
||||
|
||||
#endif
|
@ -1,4 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="890" height="480" data-created="2025-05-16T06:48:51.357946">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="890" height="480" data-created="2024-10-20T20:28:10.455635">
|
||||
<defs>
|
||||
<style>
|
||||
@font-face {
|
||||
@ -114,7 +114,7 @@
|
||||
|
||||
<svg x="5" y="68" width="109" height="33" viewBox="0 0 109 33">
|
||||
<rect x="0" y="0" fill="#69cc01" width="109" height="33" />
|
||||
<text x="7" y="22" text-anchor="left" class="browser_version">138</text>
|
||||
<text x="7" y="22" text-anchor="left" class="browser_version">131</text>
|
||||
<use xlink:href="#windows" x="34" width="15" fill="#000"></use>
|
||||
<text x="53" y="22" text-anchor="left" class="platform_version">10</text>
|
||||
<use xlink:href="#passing" x="90" width="10"></use>
|
||||
@ -123,7 +123,7 @@
|
||||
|
||||
</svg>
|
||||
|
||||
<svg x="220" y="0" width="119" height="315">
|
||||
<svg x="220" y="0" width="119" height="280">
|
||||
<use x="12" y="7" width="20" height="20" xlink:href="#chrome" fill="#333f4b"></use>
|
||||
<text x="42" y="24" text-anchor="left" class="head">Chrome</text>
|
||||
|
||||
@ -197,16 +197,6 @@
|
||||
</svg>
|
||||
|
||||
|
||||
|
||||
<svg x="5" y="272" width="109" height="33" viewBox="0 0 109 33">
|
||||
<rect x="0" y="0" fill="#69cc01" width="109" height="33" />
|
||||
<text x="7" y="22" text-anchor="left" class="browser_version">136</text>
|
||||
<use xlink:href="#windows" x="34" width="15" fill="#000"></use>
|
||||
<text x="53" y="22" text-anchor="left" class="platform_version">10</text>
|
||||
<use xlink:href="#passing" x="90" width="10"></use>
|
||||
</svg>
|
||||
|
||||
|
||||
</svg>
|
||||
|
||||
<svg x="330" y="0" width="119" height="105">
|
||||
@ -235,13 +225,33 @@
|
||||
|
||||
</svg>
|
||||
|
||||
<svg x="440" y="0" width="119" height="70">
|
||||
<svg x="440" y="0" width="119" height="140">
|
||||
<use x="12" y="7" width="20" height="20" xlink:href="#ios" fill="#333f4b"></use>
|
||||
<text x="42" y="24" text-anchor="left" class="head">iPad</text>
|
||||
|
||||
|
||||
<svg x="5" y="34" width="109" height="33" viewBox="0 0 109 33">
|
||||
<rect x="0" y="0" fill="#69cc01" width="109" height="33" />
|
||||
<text x="7" y="22" text-anchor="left" class="browser_version">11</text>
|
||||
<use xlink:href="#mac" x="34" width="15" fill="#000"></use>
|
||||
<text x="53" y="22" text-anchor="left" class="platform_version">10.13</text>
|
||||
<use xlink:href="#passing" x="90" width="10"></use>
|
||||
</svg>
|
||||
|
||||
|
||||
|
||||
<svg x="5" y="68" width="109" height="33" viewBox="0 0 109 33">
|
||||
<rect x="0" y="0" fill="#69cc01" width="109" height="33" />
|
||||
<text x="7" y="22" text-anchor="left" class="browser_version">13</text>
|
||||
<use xlink:href="#mac" x="34" width="15" fill="#000"></use>
|
||||
<text x="53" y="22" text-anchor="left" class="platform_version">10.15</text>
|
||||
<use xlink:href="#passing" x="90" width="10"></use>
|
||||
</svg>
|
||||
|
||||
|
||||
|
||||
<svg x="5" y="102" width="109" height="33" viewBox="0 0 109 33">
|
||||
<rect x="0" y="0" fill="#69cc01" width="109" height="33" />
|
||||
<text x="7" y="22" text-anchor="left" class="browser_version">15</text>
|
||||
<use xlink:href="#mac" x="34" width="15" fill="#000"></use>
|
||||
<text x="53" y="22" text-anchor="left" class="platform_version">12</text>
|
||||
@ -251,16 +261,36 @@
|
||||
|
||||
</svg>
|
||||
|
||||
<svg x="550" y="0" width="119" height="70">
|
||||
<svg x="550" y="0" width="119" height="140">
|
||||
<use x="12" y="7" width="20" height="20" xlink:href="#ios" fill="#333f4b"></use>
|
||||
<text x="42" y="24" text-anchor="left" class="head">iPhone</text>
|
||||
|
||||
|
||||
<svg x="5" y="34" width="109" height="33" viewBox="0 0 109 33">
|
||||
<rect x="0" y="0" fill="#69cc01" width="109" height="33" />
|
||||
<text x="7" y="22" text-anchor="left" class="browser_version">15</text>
|
||||
<text x="7" y="22" text-anchor="left" class="browser_version">10</text>
|
||||
<use xlink:href="#mac" x="34" width="15" fill="#000"></use>
|
||||
<text x="53" y="22" text-anchor="left" class="platform_version">12</text>
|
||||
<text x="53" y="22" text-anchor="left" class="platform_version">10.12</text>
|
||||
<use xlink:href="#passing" x="90" width="10"></use>
|
||||
</svg>
|
||||
|
||||
|
||||
|
||||
<svg x="5" y="68" width="109" height="33" viewBox="0 0 109 33">
|
||||
<rect x="0" y="0" fill="#69cc01" width="109" height="33" />
|
||||
<text x="7" y="22" text-anchor="left" class="browser_version">12</text>
|
||||
<use xlink:href="#mac" x="34" width="15" fill="#000"></use>
|
||||
<text x="53" y="22" text-anchor="left" class="platform_version">10.15</text>
|
||||
<use xlink:href="#passing" x="90" width="10"></use>
|
||||
</svg>
|
||||
|
||||
|
||||
|
||||
<svg x="5" y="102" width="109" height="33" viewBox="0 0 109 33">
|
||||
<rect x="0" y="0" fill="#69cc01" width="109" height="33" />
|
||||
<text x="7" y="22" text-anchor="left" class="browser_version">14</text>
|
||||
<use xlink:href="#mac" x="34" width="15" fill="#000"></use>
|
||||
<text x="53" y="22" text-anchor="left" class="platform_version">11</text>
|
||||
<use xlink:href="#passing" x="90" width="10"></use>
|
||||
</svg>
|
||||
|
||||
@ -384,7 +414,7 @@
|
||||
|
||||
<svg x="5" y="408" width="109" height="33" viewBox="0 0 109 33">
|
||||
<rect x="0" y="0" fill="#69cc01" width="109" height="33" />
|
||||
<text x="7" y="22" text-anchor="left" class="browser_version">135</text>
|
||||
<text x="7" y="22" text-anchor="left" class="browser_version">130</text>
|
||||
<use xlink:href="#windows" x="34" width="15" fill="#000"></use>
|
||||
<text x="53" y="22" text-anchor="left" class="platform_version">10</text>
|
||||
<use xlink:href="#passing" x="90" width="10"></use>
|
||||
@ -393,13 +423,83 @@
|
||||
|
||||
</svg>
|
||||
|
||||
<svg x="770" y="0" width="119" height="105">
|
||||
<svg x="770" y="0" width="119" height="350">
|
||||
<use x="12" y="7" width="20" height="20" xlink:href="#safari" fill="#333f4b"></use>
|
||||
<text x="42" y="24" text-anchor="left" class="head">Safari</text>
|
||||
|
||||
|
||||
<svg x="5" y="34" width="109" height="33" viewBox="0 0 109 33">
|
||||
<rect x="0" y="0" fill="#69cc01" width="109" height="33" />
|
||||
<text x="7" y="22" text-anchor="left" class="browser_version">8</text>
|
||||
<use xlink:href="#mac" x="34" width="15" fill="#000"></use>
|
||||
<text x="53" y="22" text-anchor="left" class="platform_version">10.10</text>
|
||||
<use xlink:href="#passing" x="90" width="10"></use>
|
||||
</svg>
|
||||
|
||||
|
||||
|
||||
<svg x="5" y="68" width="109" height="33" viewBox="0 0 109 33">
|
||||
<rect x="0" y="0" fill="#69cc01" width="109" height="33" />
|
||||
<text x="7" y="22" text-anchor="left" class="browser_version">9</text>
|
||||
<use xlink:href="#mac" x="34" width="15" fill="#000"></use>
|
||||
<text x="53" y="22" text-anchor="left" class="platform_version">10.11</text>
|
||||
<use xlink:href="#passing" x="90" width="10"></use>
|
||||
</svg>
|
||||
|
||||
|
||||
|
||||
<svg x="5" y="102" width="109" height="33" viewBox="0 0 109 33">
|
||||
<rect x="0" y="0" fill="#69cc01" width="109" height="33" />
|
||||
<text x="7" y="22" text-anchor="left" class="browser_version">10</text>
|
||||
<use xlink:href="#mac" x="34" width="15" fill="#000"></use>
|
||||
<text x="53" y="22" text-anchor="left" class="platform_version">10.12</text>
|
||||
<use xlink:href="#passing" x="90" width="10"></use>
|
||||
</svg>
|
||||
|
||||
|
||||
|
||||
<svg x="5" y="136" width="109" height="33" viewBox="0 0 109 33">
|
||||
<rect x="0" y="0" fill="#69cc01" width="109" height="33" />
|
||||
<text x="7" y="22" text-anchor="left" class="browser_version">11</text>
|
||||
<use xlink:href="#mac" x="34" width="15" fill="#000"></use>
|
||||
<text x="53" y="22" text-anchor="left" class="platform_version">10.13</text>
|
||||
<use xlink:href="#passing" x="90" width="10"></use>
|
||||
</svg>
|
||||
|
||||
|
||||
|
||||
<svg x="5" y="170" width="109" height="33" viewBox="0 0 109 33">
|
||||
<rect x="0" y="0" fill="#69cc01" width="109" height="33" />
|
||||
<text x="7" y="22" text-anchor="left" class="browser_version">12</text>
|
||||
<use xlink:href="#mac" x="34" width="15" fill="#000"></use>
|
||||
<text x="53" y="22" text-anchor="left" class="platform_version">10.13</text>
|
||||
<use xlink:href="#passing" x="90" width="10"></use>
|
||||
</svg>
|
||||
|
||||
|
||||
|
||||
<svg x="5" y="204" width="109" height="33" viewBox="0 0 109 33">
|
||||
<rect x="0" y="0" fill="#69cc01" width="109" height="33" />
|
||||
<text x="7" y="22" text-anchor="left" class="browser_version">13</text>
|
||||
<use xlink:href="#mac" x="34" width="15" fill="#000"></use>
|
||||
<text x="53" y="22" text-anchor="left" class="platform_version">10.15</text>
|
||||
<use xlink:href="#passing" x="90" width="10"></use>
|
||||
</svg>
|
||||
|
||||
|
||||
|
||||
<svg x="5" y="238" width="109" height="33" viewBox="0 0 109 33">
|
||||
<rect x="0" y="0" fill="#69cc01" width="109" height="33" />
|
||||
<text x="7" y="22" text-anchor="left" class="browser_version">14</text>
|
||||
<use xlink:href="#mac" x="34" width="15" fill="#000"></use>
|
||||
<text x="53" y="22" text-anchor="left" class="platform_version">11</text>
|
||||
<use xlink:href="#passing" x="90" width="10"></use>
|
||||
</svg>
|
||||
|
||||
|
||||
|
||||
<svg x="5" y="272" width="109" height="33" viewBox="0 0 109 33">
|
||||
<rect x="0" y="0" fill="#69cc01" width="109" height="33" />
|
||||
<text x="7" y="22" text-anchor="left" class="browser_version">15</text>
|
||||
<use xlink:href="#mac" x="34" width="15" fill="#000"></use>
|
||||
<text x="53" y="22" text-anchor="left" class="platform_version">12</text>
|
||||
@ -408,9 +508,9 @@
|
||||
|
||||
|
||||
|
||||
<svg x="5" y="68" width="109" height="33" viewBox="0 0 109 33">
|
||||
<svg x="5" y="306" width="109" height="33" viewBox="0 0 109 33">
|
||||
<rect x="0" y="0" fill="#69cc01" width="109" height="33" />
|
||||
<text x="7" y="22" text-anchor="left" class="browser_version">17</text>
|
||||
<text x="7" y="22" text-anchor="left" class="browser_version">16</text>
|
||||
<use xlink:href="#mac" x="34" width="15" fill="#000"></use>
|
||||
<text x="53" y="22" text-anchor="left" class="platform_version">13</text>
|
||||
<use xlink:href="#passing" x="90" width="10"></use>
|
||||
|
Before Width: | Height: | Size: 105 KiB After Width: | Height: | Size: 110 KiB |
Before Width: | Height: | Size: 84 KiB |
Before Width: | Height: | Size: 36 KiB |
@ -83,11 +83,11 @@ app.listen(7262, async() => {
|
||||
});
|
||||
EOF
|
||||
|
||||
npm i --save puppeteer express@4
|
||||
npm i --save puppeteer express
|
||||
|
||||
node -e 'var pjson = JSON.parse(fs.readFileSync("./package.json")); console.log(pjson); delete pjson.main; fs.writeFileSync("package.json", JSON.stringify(pjson))'
|
||||
|
||||
for n in 2.14.4 1.12.3; do
|
||||
for n in 1.12.3 2.13.3; do
|
||||
npm i --save parcel@$n
|
||||
npx -y parcel@$n build index.html
|
||||
node test.js
|
@ -13,7 +13,7 @@ npm i --save https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz exit-on-epipe c
|
||||
|
||||
## NOTE: must downgrade to node 18
|
||||
|
||||
npx -y pkg -t 'node18-win-arm64,node18-linux-arm64,node18-macos-arm64' xlsx-cli.js
|
||||
npx -y pkg xlsx-cli.js
|
||||
|
||||
## NOTE: these steps are for darwin
|
||||
|
@ -1,17 +0,0 @@
|
||||
#!/bin/bash
|
||||
# https://docs.sheetjs.com/docs/demos/net/email/pst
|
||||
cd /tmp
|
||||
rm -rf sheetjs-pst
|
||||
mkdir sheetjs-pst
|
||||
cd sheetjs-pst
|
||||
|
||||
npm init -y
|
||||
|
||||
curl -LO https://docs.sheetjs.com/pst/SheetJSPST.js
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz pst-extractor@1.11.0
|
||||
|
||||
node --version
|
||||
node SheetJSPST.js
|
||||
|
||||
bun --version
|
||||
bun SheetJSPST.js
|
@ -5,14 +5,14 @@ rm -rf sheetjs-graaljs
|
||||
mkdir -p sheetjs-graaljs
|
||||
cd sheetjs-graaljs
|
||||
|
||||
curl -LO "https://repo1.maven.org/maven2/org/graalvm/js/js-scriptengine/24.2.1/js-scriptengine-24.2.1.jar"
|
||||
curl -LO "https://repo1.maven.org/maven2/org/graalvm/js/js-language/24.2.1/js-language-24.2.1.jar"
|
||||
curl -LO "https://repo1.maven.org/maven2/org/graalvm/polyglot/polyglot/24.2.1/polyglot-24.2.1.jar"
|
||||
curl -LO "https://repo1.maven.org/maven2/org/graalvm/sdk/collections/24.2.1/collections-24.2.1.jar"
|
||||
curl -LO "https://repo1.maven.org/maven2/org/graalvm/truffle/truffle-api/24.2.1/truffle-api-24.2.1.jar"
|
||||
curl -LO "https://repo1.maven.org/maven2/org/graalvm/sdk/nativeimage/24.2.1/nativeimage-24.2.1.jar"
|
||||
curl -LO "https://repo1.maven.org/maven2/org/graalvm/shadowed/icu4j/24.2.1/icu4j-24.2.1.jar"
|
||||
curl -LO "https://repo1.maven.org/maven2/org/graalvm/regex/regex/24.2.1/regex-24.2.1.jar"
|
||||
curl -LO "https://repo1.maven.org/maven2/org/graalvm/js/js-scriptengine/24.1.1/js-scriptengine-24.1.1.jar"
|
||||
curl -LO "https://repo1.maven.org/maven2/org/graalvm/js/js-language/24.1.1/js-language-24.1.1.jar"
|
||||
curl -LO "https://repo1.maven.org/maven2/org/graalvm/polyglot/polyglot/24.1.1/polyglot-24.1.1.jar"
|
||||
curl -LO "https://repo1.maven.org/maven2/org/graalvm/sdk/collections/24.1.1/collections-24.1.1.jar"
|
||||
curl -LO "https://repo1.maven.org/maven2/org/graalvm/truffle/truffle-api/24.1.1/truffle-api-24.1.1.jar"
|
||||
curl -LO "https://repo1.maven.org/maven2/org/graalvm/sdk/nativeimage/24.1.1/nativeimage-24.1.1.jar"
|
||||
curl -LO "https://repo1.maven.org/maven2/org/graalvm/shadowed/icu4j/24.1.1/icu4j-24.1.1.jar"
|
||||
curl -LO "https://repo1.maven.org/maven2/org/graalvm/regex/regex/24.1.1/regex-24.1.1.jar"
|
||||
|
||||
curl -LO https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js
|
||||
curl -LO https://cdn.sheetjs.com/xlsx-latest/package/dist/shim.min.js
|
||||
@ -20,19 +20,19 @@ curl -LO https://sheetjs.com/pres.xlsx
|
||||
|
||||
curl -LO https://docs.sheetjs.com/nashorn/SheetJSNashorn.java
|
||||
|
||||
for n in {17..24}; do
|
||||
for n in {17..23}; do
|
||||
export JAVA_HOME=`/usr/libexec/java_home -v $n`
|
||||
java -version
|
||||
rm -fr SheetJSNashorn.class SheetJSNashorn.jar sheethorn
|
||||
javac SheetJSNashorn.java
|
||||
|
||||
java -cp ".:js-scriptengine-24.2.1.jar:js-language-24.2.1.jar:polyglot-24.2.1.jar:collections-24.2.1.jar:truffle-api-24.2.1.jar:nativeimage-24.2.1.jar:icu4j-24.2.1.jar:regex-24.2.1.jar" -Dpolyglot.js.nashorn-compat=true SheetJSNashorn pres.xlsx
|
||||
java -cp ".:js-scriptengine-24.1.1.jar:js-language-24.1.1.jar:polyglot-24.1.1.jar:collections-24.1.1.jar:truffle-api-24.1.1.jar:nativeimage-24.1.1.jar:icu4j-24.1.1.jar:regex-24.1.1.jar" -Dpolyglot.js.nashorn-compat=true SheetJSNashorn pres.xlsx
|
||||
|
||||
jar -cf SheetJSNashorn.jar SheetJSNashorn.class xlsx.full.min.js shim.min.js
|
||||
|
||||
mkdir -p sheethorn
|
||||
cp *.jar pres.xlsx sheethorn
|
||||
cd sheethorn
|
||||
java -cp ".:js-scriptengine-24.2.1.jar:js-language-24.2.1.jar:polyglot-24.2.1.jar:collections-24.2.1.jar:truffle-api-24.2.1.jar:nativeimage-24.2.1.jar:icu4j-24.2.1.jar:regex-24.2.1.jar:SheetJSNashorn.jar" -Dpolyglot.js.nashorn-compat=true SheetJSNashorn pres.xlsx
|
||||
java -cp ".:js-scriptengine-24.1.1.jar:js-language-24.1.1.jar:polyglot-24.1.1.jar:collections-24.1.1.jar:truffle-api-24.1.1.jar:nativeimage-24.1.1.jar:icu4j-24.1.1.jar:regex-24.1.1.jar:SheetJSNashorn.jar" -Dpolyglot.js.nashorn-compat=true SheetJSNashorn pres.xlsx
|
||||
cd -
|
||||
done
|
@ -18,7 +18,7 @@ curl -L -o asm-analysis-9.5.jar "https://search.maven.org/remotecontent?filepath
|
||||
curl -L -o asm-util-9.5.jar "https://search.maven.org/remotecontent?filepath=org/ow2/asm/asm-util/9.5/asm-util-9.5.jar"
|
||||
|
||||
# Standalone Nashorn
|
||||
for n in {15..24}; do
|
||||
for n in {15..23}; do
|
||||
echo $n
|
||||
export JAVA_HOME=`/usr/libexec/java_home -v $n`
|
||||
echo $JAVA_HOME
|
@ -42,7 +42,7 @@ public class SheetJSRhino {
|
||||
}
|
||||
EOF
|
||||
|
||||
for n in 1.8 {9..24}; do
|
||||
for n in 1.8 {9..23}; do
|
||||
export JAVA_HOME=`/usr/libexec/java_home -v $n`
|
||||
java -version
|
||||
find . -name \*.class | while read x; do rm $x; done
|
@ -13,7 +13,7 @@ curl -Lo _data/pres.xlsx https://docs.sheetjs.com/pres.xlsx
|
||||
curl -L -o .eleventy.js https://docs.sheetjs.com/eleventy/_eleventy.js
|
||||
curl -LO https://docs.sheetjs.com/eleventy/index.njk
|
||||
|
||||
for n in 2.0.1 3.0.0 3.1.0-beta.1; do
|
||||
for n in 2.0.1 3.0.0; do
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz @11ty/eleventy@$n
|
||||
npx @11ty/eleventy@$n
|
||||
|