Compare commits

...

47 Commits

Author SHA1 Message Date
a768b7ce8c Jurassic Engine demo 2024-06-15 02:47:40 -04:00
5c895ea38b AWS Cloud demo refresh 2024-06-13 22:37:39 -04:00
04d513b889 Tabulator demo 2024-06-13 04:30:01 -04:00
9d9478e6e5 Azure Cloud demo refresh 2024-06-13 00:31:40 -04:00
9c0d1bc162 CLI testing grid 2024-06-11 03:12:51 -04:00
11d9dc7440 Mobile App testing grid 2024-06-10 00:18:22 -04:00
201f3d48fc Flutter Mobile demo refresh 2024-06-09 17:48:23 -04:00
55ce74bf8c Google Sheets demo refresh 2024-06-09 02:12:09 -04:00
a5e01f9476 NextJS Static demo refresh 2024-06-07 21:37:27 -04:00
67294eeeae Mathematica Extension demo refresh 2024-06-05 23:56:24 -04:00
5f443931f8 Nuxt Content Static demo refresh 2024-06-04 01:22:13 -04:00
298297642d SvelteKit Static demo refresh 2024-06-04 00:16:09 -04:00
fdde52dfde CapacitorJS Mobile demo refresh 2024-06-02 23:25:20 -04:00
14bd6a4ed0 ViteJS Static Demo refresh 2024-06-02 20:59:49 -04:00
1a80a55e76 Command-Line testing grid 2024-05-29 01:10:39 -04:00
945e5f02eb Desktop App testing grid 2024-05-28 01:20:05 -04:00
f0e5193b74 ARM64 Engine demo refresh 2024-05-26 03:50:55 -04:00
7f86e0b603 More darwin-arm tests 2024-05-24 04:24:50 -04:00
6e7c3e85dc darwin-arm refresh 2024-05-23 02:09:51 -04:00
349cc16819 Kaioken useAsync hook 2024-05-21 18:03:49 -04:00
833e9363d8 Clarify skipHidden option 2024-05-21 13:20:24 -04:00
bf781deb59 NetSuite error note
See sheetjs/sheetjs#3097
2024-05-17 12:21:18 -04:00
e41febc653 Document option 2024-05-13 22:43:07 -04:00
c11146f21a win11-x64 and linux-arm refresh 2024-05-11 00:39:43 -04:00
c68d8e44ac Formatting nits 2024-05-10 01:37:20 -04:00
f7f029169d LWC Refresh (see SheetJS/sheetjs#3040) 2024-05-05 12:07:09 -04:00
2964215c95 Airtable demo refresh 2024-05-04 23:43:33 -04:00
bb40aa756f MariaDB and FerretDB 2024-05-04 20:44:13 -04:00
ea8129c3d4 RN-Fetch on the Steam Deck 2024-04-30 00:29:40 -04:00
954ab5fe7e RequireJS demo automation 2024-04-28 00:43:55 -04:00
b503ebc14d Added Test Data to docs site 2024-04-26 00:16:13 -04:00
671729b289 JavaScriptCore C demo 2024-04-25 04:39:55 -04:00
922b84e1e3 frac constellation library 2024-04-24 04:49:00 -04:00
e273f11b46 valkey alternative to redis 2024-04-23 13:42:32 -04:00
01481c65cf MuJS C demo 2024-04-22 21:38:50 -04:00
b699cfaf9a kaioken no longer requires esbuild workaround 2024-04-20 22:35:21 -04:00
7b9c03cc1b use kbd tag for CTRL 2024-04-16 04:35:58 -04:00
09e390a090 astro 4 is out of beta 2024-04-14 22:52:56 -04:00
b571592055 workarounds for docusaurus breaking changes 2024-04-14 03:40:38 -04:00
ac6e3daa1c automated vitejs and browserify tests 2024-04-14 03:05:36 -04:00
c3741aaf05 more workarounds for MDX breaking changes 2024-04-12 03:11:07 -04:00
0fda6d46ab upgrade docusaurus to 3.2.1 2024-04-11 21:04:37 -04:00
2e44e0c2c8 stata demos refresh 2024-04-11 13:44:06 -04:00
98a3b79e9c sqlite3 and knexjs+sqlite3 demo refresh 2024-04-09 23:39:59 -04:00
92e3c5aa72 mdx cleanup in preparation for v2
- use autolinks (e.g <https://sheetjs.com> -> https://sheetjs.com)
- move <summary> blocks to separate lines
2024-04-08 00:57:39 -04:00
24eaed9f6b nativescript windows + android test 2024-04-07 23:55:10 -04:00
ecca85c9dc 0.20.2 2024-04-04 22:07:37 -04:00
304 changed files with 13513 additions and 3504 deletions

1
CNAME

@ -1 +0,0 @@
docs.sheetjs.com

@ -4,7 +4,6 @@ build:
cd docz; npx -y pnpm build; cd ..
rm -rf docs
mv docz/build/ docs
cp CNAME docs
cp _headers docs
.PHONY: init
@ -13,7 +12,7 @@ init:
.PHONY: dev
dev:
cd docz; npm run start -- --host=0.0.0.0; cd ..
cd docz; npm run start -- --host=0.0.0.0 --no-open; cd ..
.PHONY: serve
serve:

@ -21,6 +21,75 @@ $ make spell # spell check (.spelling custom dictionary)
$ make graph # build format graph and legend
```
### Documentation Markup
The original documentation used [GFM](https://github.github.com/gfm/).
Pages currently use MDX v2.
<details>
<summary><b>MDX Notes</b> (click to show)</summary>
**Multi-line tags**
Markdown and MDX v1 accept the following:
```
<details><summary><b>MDX Notes</b> (click to show)</summary>
Note
</details>
```
This is no longer valid in MDX v2. The `<summary>` part must be separated:
```
<details>
<summary><b>MDX Notes</b> (click to show)</summary>
Note
</details>
```
**Shortlinks**
Markdown and MDX v1 support shortlinks:
```
Scripts are available at <https://cdn.sheetjs.com>
```
This is no longer valid in MDX v2. Autolinks should be used:
```
Scripts are available at https://cdn.sheetjs.com
```
**Variables**
Patterns such as
```
<a href={`Foo${current}`}>Foo{current}</a>
```
do not work in MDX v2. Instead, string literals and concatenation must be used:
```
<a href={"Foo" + current + ""}>{"Foo" + current + ""}</a>
```
**Tables**
MDX inconsistently requires different indentation levels for `TD` / `TH`, `TR`,
`THEAD` / `TBODY` / `TFOOT`, and `TABLE` tags. Unconventional indentation is
intentional.
</details>
### Engine Compatibility Tables
`docz/src/data/engines.xls` is an XLML workbook that controls the compatibility
@ -70,8 +139,8 @@ function SheetJSTestDropbox() {
## Other Notes
`src/theme/Admonition` was swizzled from 2.4.1 to enable `pass` for hiding
`src/theme/Admonition` was swizzled from 3.2.1 to enable `pass` for hiding
header text. See Docusaurus issue 8568 for more details.
`src/theme/prism-include-languages.js` was swizzled from 2.4.1 to support the
`src/theme/prism-include-languages.js` was swizzled from 3.2.1 to support the
Liquid language. See Docusaurus issue 6872 for more details.

@ -1,3 +0,0 @@
# docs.sheetjs.com
<https://docs.sheetjs.com/>

14
docz/data/cli.js Normal file

@ -0,0 +1,14 @@
import { read, utils } from 'xlsx';
import url from './cli.xls';
import React, { useEffect, useState } from 'react';
const FrameworkData = () => {
const [fw, setFW] = useState("");
useEffect(() => { (async() => {
const wb = read(await (await fetch(url)).arrayBuffer(), { dense: true });
setFW(utils.sheet_to_html(wb.Sheets["Frameworks"]));
})(); }, []);
return ( <div dangerouslySetInnerHTML={{__html: fw}}/> );
};
export default FrameworkData;

124
docz/data/cli.xls Normal file

@ -0,0 +1,124 @@
<?xml version="1.0"?>
<?mso-application progid="Excel.Sheet"?>
<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet" xmlns:html="http://www.w3.org/TR/REC-html40">
<ExcelWorkbook xmlns="urn:schemas-microsoft-com:office:excel">
<WindowHeight>10620</WindowHeight>
<WindowWidth>11020</WindowWidth>
<WindowTopX>2260</WindowTopX>
<WindowTopY>19600</WindowTopY>
<ActiveSheet>1</ActiveSheet>
<ProtectStructure>False</ProtectStructure>
<ProtectWindows>False</ProtectWindows>
</ExcelWorkbook>
<Styles>
<Style ss:ID="Default" ss:Name="Normal">
<Alignment ss:Vertical="Bottom"/>
<Borders/>
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="12" ss:Color="#000000"/>
<Interior/>
<NumberFormat/>
<Protection/>
</Style>
<Style ss:ID="s16">
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="12" ss:Color="#1C1E21"/>
</Style>
<Style ss:ID="s17">
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="12" ss:Color="#000000" ss:Bold="1"/>
</Style>
<Style ss:ID="s19">
<Alignment ss:Horizontal="Center" ss:Vertical="Bottom"/>
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="12" ss:Color="#000000" ss:Bold="1"/>
</Style>
<Style ss:ID="s20">
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="12" ss:Color="#467886" ss:Underline="Single"/>
</Style>
</Styles>
<Worksheet ss:Name="Frameworks">
<Table ss:ExpandedColumnCount="7" ss:ExpandedRowCount="10" x:FullColumns="1" x:FullRows="1" ss:DefaultColumnWidth="65" ss:DefaultRowHeight="16">
<Column ss:Index="2" ss:Width="24"/>
<Column ss:Width="31"/>
<Column ss:Width="24"/>
<Column ss:Width="31"/>
<Column ss:Width="24"/>
<Column ss:Width="31"/>
<Row>
<Cell ss:StyleID="s17"><Data ss:Type="String"></Data></Cell>
<Cell ss:MergeAcross="1" ss:StyleID="s19"><Data ss:Type="String">MacOS</Data></Cell>
<Cell ss:MergeAcross="1" ss:StyleID="s19"><Data ss:Type="String">Windows</Data></Cell>
<Cell ss:MergeAcross="1" ss:StyleID="s19"><Data ss:Type="String">Linux</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s17"><Data ss:Type="String">Framework</Data></Cell>
<Cell ss:StyleID="s17"><Data ss:Type="String">x64</Data></Cell>
<Cell ss:StyleID="s17"><Data ss:Type="String">ARM</Data></Cell>
<Cell ss:StyleID="s17"><Data ss:Type="String">x64</Data></Cell>
<Cell ss:StyleID="s17"><Data ss:Type="String">ARM</Data></Cell>
<Cell ss:StyleID="s17"><Data ss:Type="String">x64</Data></Cell>
<Cell ss:StyleID="s17"><Data ss:Type="String">ARM</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/cli/nexe"><Data ss:Type="String">nexe</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>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/cli/boxednode"><Data ss:Type="String">boxednode</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>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/cli/pkg"><Data ss:Type="String">pkg</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>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/cli/nodesea#complete-example"><Data ss:Type="String">NodeJS SEA</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>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/cli/bunsea#complete-example"><Data ss:Type="String">BunJS SEA</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>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/cli/denosea#complete-example"><Data ss:Type="String">Deno SEA</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>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/v8#snapshots"><Data ss:Type="String">V8 Engine</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"></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
</Table>
</Worksheet>
</Workbook>

14
docz/data/desktop.js Normal file

@ -0,0 +1,14 @@
import { read, utils } from 'xlsx';
import url from './desktop.xls';
import React, { useEffect, useState } from 'react';
const FrameworkData = () => {
const [fw, setFW] = useState("");
useEffect(() => { (async() => {
const wb = read(await (await fetch(url)).arrayBuffer(), { dense: true });
setFW(utils.sheet_to_html(wb.Sheets["Frameworks"]));
})(); }, []);
return ( <div dangerouslySetInnerHTML={{__html: fw}}/> );
};
export default FrameworkData;

115
docz/data/desktop.xls Normal file

@ -0,0 +1,115 @@
<?xml version="1.0"?>
<?mso-application progid="Excel.Sheet"?>
<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet" xmlns:html="http://www.w3.org/TR/REC-html40">
<ExcelWorkbook xmlns="urn:schemas-microsoft-com:office:excel">
<WindowHeight>10620</WindowHeight>
<WindowWidth>11020</WindowWidth>
<WindowTopX>2260</WindowTopX>
<WindowTopY>19600</WindowTopY>
<ActiveSheet>1</ActiveSheet>
<ProtectStructure>False</ProtectStructure>
<ProtectWindows>False</ProtectWindows>
</ExcelWorkbook>
<Styles>
<Style ss:ID="Default" ss:Name="Normal">
<Alignment ss:Vertical="Bottom"/>
<Borders/>
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="12" ss:Color="#000000"/>
<Interior/>
<NumberFormat/>
<Protection/>
</Style>
<Style ss:ID="s16">
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="12" ss:Color="#1C1E21"/>
</Style>
<Style ss:ID="s17">
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="12" ss:Color="#000000" ss:Bold="1"/>
</Style>
<Style ss:ID="s19">
<Alignment ss:Horizontal="Center" ss:Vertical="Bottom"/>
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="12" ss:Color="#000000" ss:Bold="1"/>
</Style>
<Style ss:ID="s20">
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="12" ss:Color="#467886" ss:Underline="Single"/>
</Style>
</Styles>
<Worksheet ss:Name="Frameworks">
<Table ss:ExpandedColumnCount="7" ss:ExpandedRowCount="10" x:FullColumns="1" x:FullRows="1" ss:DefaultColumnWidth="65" ss:DefaultRowHeight="16">
<Column ss:Index="2" ss:Width="24"/>
<Column ss:Width="31"/>
<Column ss:Width="24"/>
<Column ss:Width="31"/>
<Column ss:Width="24"/>
<Column ss:Width="31"/>
<Row>
<Cell ss:StyleID="s17"><Data ss:Type="String"></Data></Cell>
<Cell ss:MergeAcross="1" ss:StyleID="s19"><Data ss:Type="String">MacOS</Data></Cell>
<Cell ss:MergeAcross="1" ss:StyleID="s19"><Data ss:Type="String">Windows</Data></Cell>
<Cell ss:MergeAcross="1" ss:StyleID="s19"><Data ss:Type="String">Linux</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s17"><Data ss:Type="String">Framework</Data></Cell>
<Cell ss:StyleID="s17"><Data ss:Type="String">x64</Data></Cell>
<Cell ss:StyleID="s17"><Data ss:Type="String">ARM</Data></Cell>
<Cell ss:StyleID="s17"><Data ss:Type="String">x64</Data></Cell>
<Cell ss:StyleID="s17"><Data ss:Type="String">ARM</Data></Cell>
<Cell ss:StyleID="s17"><Data ss:Type="String">x64</Data></Cell>
<Cell ss:StyleID="s17"><Data ss:Type="String">ARM</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/desktop/electron#complete-example"><Data ss:Type="String">Electron</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>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/desktop/nwjs#complete-example"><Data ss:Type="String">NW.js</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>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/desktop/wails#complete-example"><Data ss:Type="String">Wails</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>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/desktop/tauri#complete-example"><Data ss:Type="String">Tauri</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>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/desktop/neutralino#complete-example"><Data ss:Type="String">NeutralinoJS</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>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/desktop/reactnative"><Data ss:Type="String">React Native</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"></Cell>
<Cell ss:StyleID="s16"></Cell>
</Row>
</Table>
</Worksheet>
</Workbook>

@ -1,10 +1,6 @@
<?xml version="1.0"?>
<?mso-application progid="Excel.Sheet"?>
<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet"
xmlns:o="urn:schemas-microsoft-com:office:office"
xmlns:x="urn:schemas-microsoft-com:office:excel"
xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"
xmlns:html="http://www.w3.org/TR/REC-html40">
<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet" xmlns:html="http://www.w3.org/TR/REC-html40">
<ExcelWorkbook xmlns="urn:schemas-microsoft-com:office:excel">
<WindowHeight>10620</WindowHeight>
<WindowWidth>11020</WindowWidth>
@ -24,21 +20,21 @@
<Protection/>
</Style>
<Style ss:ID="s16">
<Font ss:FontName="Arial" x:Family="Swiss" ss:Size="12" ss:Color="#1C1E21"/>
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="12" ss:Color="#1C1E21"/>
</Style>
<Style ss:ID="s17">
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="12" ss:Color="#000000"
ss:Bold="1"/>
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="12" ss:Color="#000000" ss:Bold="1"/>
</Style>
<Style ss:ID="s19">
<Alignment ss:Horizontal="Center" ss:Vertical="Bottom"/>
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="12" ss:Color="#000000"
ss:Bold="1"/>
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="12" ss:Color="#000000" ss:Bold="1"/>
</Style>
<Style ss:ID="s20">
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="12" ss:Color="#467886" ss:Underline="Single"/>
</Style>
</Styles>
<Worksheet ss:Name="Engines">
<Table ss:ExpandedColumnCount="8" ss:ExpandedRowCount="15" x:FullColumns="1"
x:FullRows="1" ss:DefaultColumnWidth="65" ss:DefaultRowHeight="16">
<Table ss:ExpandedColumnCount="8" ss:ExpandedRowCount="18" x:FullColumns="1" x:FullRows="1" ss:DefaultColumnWidth="65" ss:DefaultRowHeight="16">
<Column ss:Index="3" ss:Width="24"/>
<Column ss:Width="31"/>
<Column ss:Width="24"/>
@ -62,7 +58,7 @@
<Cell ss:StyleID="s17"><Data ss:Type="String">ARM</Data></Cell>
</Row>
<Row>
<Cell><Data ss:Type="String">Duktape</Data></Cell>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/duktape#complete-example"><Data ss:Type="String">Duktape</Data></Cell>
<Cell><Data ss:Type="String">C</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
@ -72,7 +68,7 @@
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell><Data ss:Type="String">V8</Data></Cell>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/v8#complete-example"><Data ss:Type="String">V8</Data></Cell>
<Cell><Data ss:Type="String">C++</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
@ -82,17 +78,27 @@
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell><Data ss:Type="String">Rhino</Data></Cell>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/rhino#complete-example"><Data ss:Type="String">Rhino</Data></Cell>
<Cell><Data ss:Type="String">Java</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"/>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"/>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell><Data ss:Type="String">Jint</Data></Cell>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/jsc#complete-example"><Data ss:Type="String">JSC</Data></Cell>
<Cell><Data ss:Type="String">C++</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>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/jint#integration-example"><Data ss:Type="String">Jint</Data></Cell>
<Cell><Data ss:Type="String">C#</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
@ -102,7 +108,7 @@
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell><Data ss:Type="String">Goja</Data></Cell>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/goja#complete-example"><Data ss:Type="String">Goja</Data></Cell>
<Cell><Data ss:Type="String">Go</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
@ -112,27 +118,27 @@
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell><Data ss:Type="String">Nashorn</Data></Cell>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/nashorn#complete-example"><Data ss:Type="String">Nashorn</Data></Cell>
<Cell><Data ss:Type="String">Java</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"/>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"/>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell><Data ss:Type="String">QuickJS</Data></Cell>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/quickjs#integration-example"><Data ss:Type="String">QuickJS</Data></Cell>
<Cell><Data ss:Type="String">C</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>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell><Data ss:Type="String">Hermes</Data></Cell>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/hermes#integration-example"><Data ss:Type="String">Hermes</Data></Cell>
<Cell><Data ss:Type="String">C++</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
@ -142,7 +148,7 @@
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell><Data ss:Type="String">ChakraCore</Data></Cell>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/chakra#integration-example"><Data ss:Type="String">ChakraCore</Data></Cell>
<Cell><Data ss:Type="String">C++</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
@ -152,7 +158,7 @@
<Cell ss:StyleID="s16"><Data ss:Type="String"></Data></Cell>
</Row>
<Row>
<Cell><Data ss:Type="String">Boa</Data></Cell>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/boa#complete-example"><Data ss:Type="String">Boa</Data></Cell>
<Cell><Data ss:Type="String">Rust</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
@ -162,17 +168,17 @@
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell><Data ss:Type="String">JE</Data></Cell>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/perl#complete-example"><Data ss:Type="String">JE</Data></Cell>
<Cell><Data ss:Type="String">Perl</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"/>
<Cell ss:StyleID="s16"/>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"/>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell><Data ss:Type="String">JerryScript</Data></Cell>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/jerryscript#integration-example"><Data ss:Type="String">JerryScript</Data></Cell>
<Cell><Data ss:Type="String">C</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
@ -182,11 +188,31 @@
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell><Data ss:Type="String">GraalJS</Data></Cell>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/graaljs#complete-example"><Data ss:Type="String">GraalJS</Data></Cell>
<Cell><Data ss:Type="String">Java</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>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/mujs#integration-example"><Data ss:Type="String">MuJS</Data></Cell>
<Cell><Data ss:Type="String">C</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"/>
<Cell ss:StyleID="s16"/>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/jurassic#integration-example"><Data ss:Type="String">Jurassic.NET</Data></Cell>
<Cell><Data ss:Type="String">C#</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"/>
<Cell ss:StyleID="s16"/>
<Cell ss:StyleID="s16"/>
<Cell ss:StyleID="s16"/>
@ -218,8 +244,7 @@
</WorksheetOptions>
</Worksheet>
<Worksheet ss:Name="Bindings">
<Table ss:ExpandedColumnCount="8" ss:ExpandedRowCount="12" x:FullColumns="1"
x:FullRows="1" ss:DefaultColumnWidth="65" ss:DefaultRowHeight="16">
<Table ss:ExpandedColumnCount="8" ss:ExpandedRowCount="12" x:FullColumns="1" x:FullRows="1" ss:DefaultColumnWidth="65" ss:DefaultRowHeight="16">
<Column ss:Index="3" ss:Width="24"/>
<Column ss:Width="31"/>
<Column ss:Width="24"/>
@ -243,47 +268,47 @@
<Cell ss:StyleID="s17"><Data ss:Type="String">ARM</Data></Cell>
</Row>
<Row>
<Cell><Data ss:Type="String">Duktape</Data></Cell>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/duktape#perl"><Data ss:Type="String">Duktape</Data></Cell>
<Cell><Data ss:Type="String">Perl</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"/>
<Cell ss:StyleID="s16"/>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"/>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell><Data ss:Type="String">Duktape</Data></Cell>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/duktape#php"><Data ss:Type="String">Duktape</Data></Cell>
<Cell><Data ss:Type="String">PHP</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"/>
<Cell ss:StyleID="s16"/>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"/>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell><Data ss:Type="String">Duktape</Data></Cell>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/duktape#python"><Data ss:Type="String">Duktape</Data></Cell>
<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"/>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"/>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell><Data ss:Type="String">Duktape</Data></Cell>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/duktape#zig"><Data ss:Type="String">Duktape</Data></Cell>
<Cell><Data ss:Type="String">Zig</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"/>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"/>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"/>
<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>
</Row>
<Row>
<Cell><Data ss:Type="String">V8</Data></Cell>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/v8#rust"><Data ss:Type="String">V8</Data></Cell>
<Cell><Data ss:Type="String">Rust</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
@ -293,7 +318,7 @@
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell><Data ss:Type="String">JSC</Data></Cell>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/jsc#swift"><Data ss:Type="String">JSC</Data></Cell>
<Cell><Data ss:Type="String">Swift</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
@ -303,12 +328,12 @@
<Cell ss:StyleID="s16"/>
</Row>
<Row>
<Cell><Data ss:Type="String">ExecJS</Data></Cell>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/rb#complete-example"><Data ss:Type="String">ExecJS</Data></Cell>
<Cell><Data ss:Type="String">Ruby</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>
</Row>

14
docz/data/mobile.js Normal file

@ -0,0 +1,14 @@
import { read, utils } from 'xlsx';
import url from './mobile.xls';
import React, { useEffect, useState } from 'react';
const FrameworkData = () => {
const [fw, setFW] = useState("");
useEffect(() => { (async() => {
const wb = read(await (await fetch(url)).arrayBuffer(), { dense: true });
setFW(utils.sheet_to_html(wb.Sheets["Frameworks"]));
})(); }, []);
return ( <div dangerouslySetInnerHTML={{__html: fw}}/> );
};
export default FrameworkData;

154
docz/data/mobile.xls Normal file

@ -0,0 +1,154 @@
<?xml version="1.0"?>
<?mso-application progid="Excel.Sheet"?>
<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet" xmlns:html="http://www.w3.org/TR/REC-html40">
<ExcelWorkbook xmlns="urn:schemas-microsoft-com:office:excel">
<WindowHeight>10620</WindowHeight>
<WindowWidth>11020</WindowWidth>
<WindowTopX>2260</WindowTopX>
<WindowTopY>19600</WindowTopY>
<ActiveSheet>1</ActiveSheet>
<ProtectStructure>False</ProtectStructure>
<ProtectWindows>False</ProtectWindows>
</ExcelWorkbook>
<Styles>
<Style ss:ID="Default" ss:Name="Normal">
<Alignment ss:Vertical="Bottom"/>
<Borders/>
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="12" ss:Color="#000000"/>
<Interior/>
<NumberFormat/>
<Protection/>
</Style>
<Style ss:ID="s16">
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="12" ss:Color="#1C1E21"/>
</Style>
<Style ss:ID="s17">
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="12" ss:Color="#000000" ss:Bold="1"/>
</Style>
<Style ss:ID="s19">
<Alignment ss:Horizontal="Center" ss:Vertical="Bottom"/>
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="12" ss:Color="#000000" ss:Bold="1"/>
</Style>
<Style ss:ID="s20">
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="12" ss:Color="#467886" ss:Underline="Single"/>
</Style>
</Styles>
<Worksheet ss:Name="Frameworks">
<Table ss:ExpandedColumnCount="8" ss:ExpandedRowCount="17" x:FullColumns="1" x:FullRows="1" ss:DefaultColumnWidth="65" ss:DefaultRowHeight="16">
<Column ss:Index="3" ss:Width="24"/>
<Column ss:Width="31"/>
<Column ss:Width="24"/>
<Column ss:Width="31"/>
<Column ss:Width="24"/>
<Column ss:Width="31"/>
<Row>
<Cell ss:StyleID="s17"><Data ss:Type="String"></Data></Cell>
<Cell ss:MergeAcross="1" ss:StyleID="s19"><Data ss:Type="String">Real Device</Data></Cell>
<Cell ss:MergeAcross="1" ss:StyleID="s19"><Data ss:Type="String">MacOS Sim</Data></Cell>
<Cell ss:MergeAcross="1" ss:StyleID="s19"><Data ss:Type="String">Windows Sim</Data></Cell>
<Cell ss:MergeAcross="1" ss:StyleID="s19"><Data ss:Type="String">Linux Sim</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s17"><Data ss:Type="String">Platform</Data></Cell>
<Cell ss:StyleID="s17"><Data ss:Type="String">iOS</Data></Cell>
<Cell ss:StyleID="s17"><Data ss:Type="String">Android</Data></Cell>
<Cell ss:StyleID="s17"><Data ss:Type="String">iOS</Data></Cell>
<Cell ss:StyleID="s17"><Data ss:Type="String">Android</Data></Cell>
<Cell ss:StyleID="s17"><Data ss:Type="String">iOS</Data></Cell>
<Cell ss:StyleID="s17"><Data ss:Type="String">Android</Data></Cell>
<Cell ss:StyleID="s17"><Data ss:Type="String">iOS</Data></Cell>
<Cell ss:StyleID="s17"><Data ss:Type="String">Android</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/mobile/reactnative"><Data ss:Type="String">React Native</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>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/mobile/nativescript"><Data ss:Type="String">NativeScript</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>
<Cell ss:StyleID="s16"><Data ss:Type="String"> </Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/mobile/capacitor"><Data ss:Type="String">CapacitorJS</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>
<Cell ss:StyleID="s16"><Data ss:Type="String"> </Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/mobile/ionic"><Data ss:Type="String">Ionic</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>
<Cell ss:StyleID="s16"><Data ss:Type="String"> </Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/mobile/flutter"><Data ss:Type="String">Dart + Flutter</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>
<Cell ss:StyleID="s16"><Data ss:Type="String"> </Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/mobile/quasar"><Data ss:Type="String">Quasar</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>
<Cell ss:StyleID="s16"><Data ss:Type="String"> </Data></Cell>
</Row>
</Table>
<WorksheetOptions xmlns="urn:schemas-microsoft-com:office:excel">
<PageSetup>
<Header x:Margin="0.3"/>
<Footer x:Margin="0.3"/>
<PageMargins x:Bottom="0.75" x:Left="0.7" x:Right="0.7" x:Top="0.75"/>
</PageSetup>
<FreezePanes/>
<FrozenNoSplit/>
<SplitHorizontal>2</SplitHorizontal>
<TopRowBottomPane>2</TopRowBottomPane>
<ActivePane>2</ActivePane>
<Panes>
<Pane>
<Number>3</Number>
</Pane>
<Pane>
<Number>2</Number>
<ActiveRow>12</ActiveRow>
<ActiveCol>5</ActiveCol>
</Pane>
</Panes>
<ProtectObjects>False</ProtectObjects>
<ProtectScenarios>False</ProtectScenarios>
</WorksheetOptions>
</Worksheet>
</Workbook>

@ -1,4 +1,5 @@
---
title: Standalone Browser Scripts
pagination_prev: getting-started/index
pagination_next: getting-started/examples/index
sidebar_position: 1
@ -9,9 +10,7 @@ sidebar_custom_props:
import current from '/version.js';
import CodeBlock from '@theme/CodeBlock';
# Standalone Browser Scripts
Each standalone release script is available at <https://cdn.sheetjs.com/>.
Each standalone release script is available at https://cdn.sheetjs.com/.
<p>The current version is {current} and can be referenced as follows:</p>
@ -28,19 +27,19 @@ new versions are released!
:::
:::warning pass
:::danger pass
A number of services host older versions of the SheetJS libraries. Due to
syncing issues, they are generally out of date.
**The SheetJS CDN** <https://cdn.sheetjs.com/> **is the authoritative source**
**The SheetJS CDN** https://cdn.sheetjs.com/ **is the authoritative source**
**for SheetJS scripts**
:::
## Browser Scripts
`xlsx.full.min.js` is the complete standalone script. It includes support for
`xlsx.full.min.js` is the complete standalone script. It includes support for
reading and writing many spreadsheet formats.
<CodeBlock language="html">{`\
@ -48,13 +47,14 @@ reading and writing many spreadsheet formats.
<script lang="javascript" src="https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js"></script>`}
</CodeBlock>
`xlsx.mini.min.js` is a slimmer build that omits the following features:
A slimmer build is generated at `dist/xlsx.mini.min.js`. Compared to full build:
- codepage library skipped (no support for XLS encodings)
- no support for XLSB / XLS / Lotus 1-2-3 / SpreadsheetML 2003 / Numbers
- node stream utils removed
- CSV and SYLK encodings (directly affecting users outside of the United States)
- XLSB / XLS / Lotus 1-2-3 / SpreadsheetML 2003 / Numbers file formats
- Stream utility functions
<details><summary><b>How to integrate the mini build</b> (click to show)</summary>
<details>
<summary><b>How to integrate the mini build</b> (click to show)</summary>
Replace references to `xlsx.full.min.js` with `xlsx.mini.min.js`. Starting from
scratch, a single script tag should be added at the top of the HTML page:
@ -68,10 +68,11 @@ scratch, a single script tag should be added at the top of the HTML page:
### Vendoring
For general stability, "vendoring" scripts is the recommended approach:
For general stability, making a local copy of SheetJS scripts ("vendoring") is
strongly recommended. Vendoring decouples websites from SheetJS infrastructure.
<p>1) Download the script (<code parentName="pre">xlsx.full.min.js</code>) for
the desired version. The current version is available at <a href={`https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js`}>https://cdn.sheetjs.com/xlsx-{current}/package/dist/xlsx.full.min.js</a></p>
<ol start="1"><li><p>Download the script (<code parentName="pre">xlsx.full.min.js</code>) for
the desired version. The current version is available at <a href={"https://cdn.sheetjs.com/xlsx-" + current + "/package/dist/xlsx.full.min.js"}>{"https://cdn.sheetjs.com/xlsx-" + current + "/package/dist/xlsx.full.min.js"}</a></p></li></ol>
2) Move the script to a `public` folder with other scripts.
@ -94,11 +95,11 @@ use the CDN scripts directly. They should be downloaded and saved to a public
directory in the site:
<ul>
<li>Standalone: <a href={`https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.mini.min.js`}>https://cdn.sheetjs.com/xlsx-{current}/package/dist/xlsx.mini.min.js</a></li>
<li>Shim: <a href={`https://cdn.sheetjs.com/xlsx-${current}/package/dist/shim.min.js`}>https://cdn.sheetjs.com/xlsx-{current}/package/dist/shim.min.js</a></li>
<li>Standalone: <a href={"https://cdn.sheetjs.com/xlsx-" + current + "/package/dist/xlsx.mini.min.js"}>{"https://cdn.sheetjs.com/xlsx-" + current + "/package/dist/xlsx.mini.min.js"}</a></li>
<li>Shim: <a href={"https://cdn.sheetjs.com/xlsx-" + current + "/package/dist/shim.min.js"}>{"https://cdn.sheetjs.com/xlsx-" + current + "/package/dist/shim.min.js"}</a></li>
</ul>
Add a `script` reference to the shim before the standalone script:
A `script` reference to the shim must be added before the standalone script:
```html
<!-- add the shim first -->
@ -122,9 +123,11 @@ importScripts("https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.mi
:::caution pass
This section refers to imports using `script type="module"`. For imports in
modern projects using Webpack or React or Angular or VueJS, the installation is
described [in "Frameworks and Bundlers"](/docs/getting-started/installation/frameworks).
This section refers to imports in HTML pages using `script type="module"`.
The ["Frameworks and Bundlers"](/docs/getting-started/installation/frameworks)
section covers imports in projects using bundlers (ViteJS) or frameworks
(Kaioken / ReactJS / Angular / VueJS / Svelte)
:::
@ -203,14 +206,13 @@ xport.addEventListener("click", async() => {
## Bower
:::warning pass
:::danger pass
Bower is deprecated and the maintainers recommend using other tools.
:::
The Bower package manager plays nice with the CDN tarballs:
The Bower package manager supports tarballs from the SheetJS CDN:
<CodeBlock language="bash">{`\
npx bower install https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}

@ -1,9 +1,10 @@
---
title: Frameworks and Bundlers
pagination_prev: getting-started/index
pagination_next: getting-started/examples/index
sidebar_position: 2
sidebar_custom_props:
summary: Angular, React, VueJS, Webpack, etc.
summary: Kaioken, Angular, React, VueJS, ViteJS, Webpack, etc.
---
import current from '/version.js';
@ -11,13 +12,11 @@ import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeBlock from '@theme/CodeBlock';
# Frameworks and Bundlers
Each standalone release package is available at <https://cdn.sheetjs.com/>. The
NodeJS package is designed to be used with frameworks and bundlers. It is a
Each standalone release package is available at https://cdn.sheetjs.com/. The
NodeJS package is designed to be used with frameworks and bundlers. It is a
proper ECMAScript Module release which can be optimized with developer tools.
<p><a href={`https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}>https://cdn.sheetjs.com/xlsx-{current}/xlsx-{current}.tgz</a> is the URL for version {current}</p>
<p><a href={"https://cdn.sheetjs.com/xlsx-" + current + "/xlsx-" + current + ".tgz"}>{"https://cdn.sheetjs.com/xlsx-" + current + "/xlsx-" + current + ".tgz"}</a> is the URL for version {current}</p>
## Installation
@ -41,6 +40,24 @@ pnpm install --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`
yarn remove xlsx
yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
</CodeBlock>
:::caution pass
Newer releases of Yarn may throw an error:
```
Usage Error: It seems you are trying to add a package using a https:... url; we now require package names to be explicitly specified.
Try running the command again with the package name prefixed: yarn add my-package@https:...
```
The workaround is to prepend the URL with `xlsx@`:
<CodeBlock language="bash">{`\
yarn add xlsx@https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
</CodeBlock>
:::
</TabItem>
</Tabs>
@ -50,7 +67,7 @@ Once installed, the library can be imported under the name `xlsx`:
import { read, writeFileXLSX } from "xlsx";
```
The ["Bundlers" demo](/docs/demos/bundler) includes examples for specific tools.
The ["Bundlers" demo](/docs/demos/frontend/bundler) includes complete examples.
:::tip pass
@ -68,7 +85,7 @@ Snyk security tooling may report errors involving "Prototype Pollution":
Prototype Pollution [Medium Severity][https://security.snyk.io/vuln/SNYK-JS-XLSX-5457926]
```
As noted in the [Snyk report](https://web.archive.org/web/20231129100639/https://security.snyk.io/vuln/SNYK-JS-XLSX-5457926):
As noted in the [Snyk report](https://security.snyk.io/vuln/SNYK-JS-XLSX-5457926):
> The issue is resolved in version 0.19.3
@ -81,14 +98,14 @@ Until Snyk fixes the bugs, the official recommendation is to
### Legacy Endpoints
:::warning pass
:::danger pass
Older releases are technically available on the public npm registry as `xlsx`,
but the registry is out of date. The latest version on that registry is 0.18.5
This is a known registry bug
**The SheetJS CDN** <https://cdn.sheetjs.com/> **is the authoritative source**
**The SheetJS CDN** https://cdn.sheetjs.com/ **is the authoritative source**
**for SheetJS modules.**
For existing projects, the easiest approach is to uninstall and reinstall:
@ -131,7 +148,8 @@ in `package.json` can control module resolution:
### Vendoring
For general stability, "vendoring" modules is the recommended approach:
For general stability, making a local copy of SheetJS modules ("vendoring") is
strongly recommended. Vendoring decouples projects from SheetJS infrastructure.
0) Remove any existing dependency on a project named `xlsx`:
@ -153,8 +171,8 @@ yarn remove xlsx`}
</TabItem>
</Tabs>
<p>1) Download the tarball (<code parentName="pre">xlsx-{current}.tgz</code>) for the desired version. The current
version is available at <a href={`https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}>https://cdn.sheetjs.com/xlsx-{current}/xlsx-{current}.tgz</a></p>
<ol start="1"><li><p>Download the tarball (<code parentName="pre">xlsx-{current}.tgz</code>) for the desired version. The current
version is available at <a href={"https://cdn.sheetjs.com/xlsx-" + current + "/xlsx-" + current + ".tgz"}>{"https://cdn.sheetjs.com/xlsx-" + current + "/xlsx-" + current + ".tgz"}</a></p></li></ol>
2) Create a `vendor` subfolder at the root of your project and move the tarball
to that folder. Add it to your project repository.
@ -176,6 +194,23 @@ pnpm install --save file:vendor/xlsx-${current}.tgz`}
<CodeBlock language="bash">{`\
yarn add file:vendor/xlsx-${current}.tgz`}
</CodeBlock>
:::caution pass
Newer releases of Yarn may throw an error:
```
Usage Error: The file:vendor/xlsx-0.20.2.tgz string didn't match the required format (package-name@range). Did you perhaps forget to explicitly reference the package name?
```
The workaround is to prepend the URI with `xlsx@`:
<CodeBlock language="bash">{`\
yarn add xlsx@file:vendor/xlsx-${current}.tgz`}
</CodeBlock>
:::
</TabItem>
</Tabs>
@ -203,13 +238,13 @@ var XLSX = require("xlsx");
var read = XLSX.read, utils = XLSX.utils;
```
The ["Bundlers" demo](/docs/demos/bundler) includes examples for specific tools.
The ["Bundlers" demo](/docs/demos/frontend/bundler) includes complete examples.
### Dynamic Imports
Dynamic imports with `import()` will only download scripts when they are needed.
:::warning pass
:::danger pass
Dynamic `import` will always download the full contents of the imported scripts!

@ -12,9 +12,9 @@ import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeBlock from '@theme/CodeBlock';
Package tarballs are available on <https://cdn.sheetjs.com>.
Package tarballs are available on https://cdn.sheetjs.com.
<p><a href={`https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}>https://cdn.sheetjs.com/xlsx-{current}/xlsx-{current}.tgz</a> is the URL for version {current}</p>
<p><a href={"https://cdn.sheetjs.com/xlsx-" + current + "/xlsx-" + current + ".tgz"}>{"https://cdn.sheetjs.com/xlsx-" + current + "/xlsx-" + current + ".tgz"}</a> is the URL for version {current}</p>
## Installation
@ -38,6 +38,24 @@ pnpm install --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`
yarn remove xlsx
yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
</CodeBlock>
:::caution pass
Newer releases of Yarn may throw an error:
```
Usage Error: It seems you are trying to add a package using a https:... url; we now require package names to be explicitly specified.
Try running the command again with the package name prefixed: yarn add my-package@https:...
```
The workaround is to prepend the URL with `xlsx@`:
<CodeBlock language="bash">{`\
yarn add xlsx@https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
</CodeBlock>
:::
</TabItem>
</Tabs>
@ -57,7 +75,7 @@ Snyk security tooling may report errors involving "Prototype Pollution":
Prototype Pollution [Medium Severity][https://security.snyk.io/vuln/SNYK-JS-XLSX-5457926]
```
As noted in the [Snyk report](https://web.archive.org/web/20231129100639/https://security.snyk.io/vuln/SNYK-JS-XLSX-5457926):
As noted in the [Snyk report](https://security.snyk.io/vuln/SNYK-JS-XLSX-5457926):
> The issue is resolved in version 0.19.3
@ -70,14 +88,14 @@ Until Snyk fixes the bugs, the official recommendation is to
### Legacy Endpoints
:::warning pass
:::danger pass
Older releases are technically available on the public npm registry as `xlsx`,
but the registry is out of date. The latest version on that registry is 0.18.5
This is a known registry bug
**The SheetJS CDN** <https://cdn.sheetjs.com/> **is the authoritative source**
**The SheetJS CDN** https://cdn.sheetjs.com/ **is the authoritative source**
**for SheetJS modules.**
For existing projects, the easiest approach is to uninstall and reinstall:
@ -120,7 +138,8 @@ in `package.json` can control module resolution:
### Vendoring
For general stability, "vendoring" modules is the recommended approach:
For general stability, making a local copy of SheetJS modules ("vendoring") is
strongly recommended. Vendoring decouples projects from SheetJS infrastructure.
0) Remove any existing dependency on a project named `xlsx`:
@ -142,8 +161,7 @@ yarn remove xlsx`}
</TabItem>
</Tabs>
<p>1) Download the tarball (<code parentName="pre">xlsx-{current}.tgz</code>) for the desired version. The current
version is available at <a href={`https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}>https://cdn.sheetjs.com/xlsx-{current}/xlsx-{current}.tgz</a></p>
<ol start="1"><li><p>Download the tarball (<code parentName="pre">xlsx-{current}.tgz</code>) for the desired version. The current version is available at <a href={"https://cdn.sheetjs.com/xlsx-" + current + "/xlsx-" + current + ".tgz"}>{"https://cdn.sheetjs.com/xlsx-" + current + "/xlsx-" + current + ".tgz"}</a></p></li></ol>
2) Create a `vendor` subfolder at the root of your project and move the tarball
to that folder. Add it to your project repository.
@ -165,6 +183,23 @@ pnpm install --save file:vendor/xlsx-${current}.tgz`}
<CodeBlock language="bash">{`\
yarn add file:vendor/xlsx-${current}.tgz`}
</CodeBlock>
:::caution pass
Newer releases of Yarn may throw an error:
```
Usage Error: The file:vendor/xlsx-0.20.2.tgz string didn't match the required format (package-name@range). Did you perhaps forget to explicitly reference the package name?
```
The workaround is to prepend the URI with `xlsx@`:
<CodeBlock language="bash">{`\
yarn add xlsx@file:vendor/xlsx-${current}.tgz`}
</CodeBlock>
:::
</TabItem>
</Tabs>
@ -237,7 +272,7 @@ XLSX.set_cptable(cpexcel);
#### NextJS
:::warning pass
:::danger pass
`fs` cannot be imported from the top level in NextJS pages. This will not work:

@ -1,4 +1,5 @@
---
title: AMD (define)
pagination_prev: getting-started/index
pagination_next: getting-started/examples/index
sidebar_position: 4
@ -9,13 +10,11 @@ sidebar_custom_props:
import current from '/version.js';
import CodeBlock from '@theme/CodeBlock';
# AMD (define)
Each standalone release script is available at <https://cdn.sheetjs.com/>.
Each standalone release script is available at https://cdn.sheetjs.com/.
`xlsx.full.min.js` supports AMD with name `xlsx` out of the box.
<p><a href={`https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js`}>https://cdn.sheetjs.com/xlsx-{current}/package/dist/xlsx.full.min.js</a> is the URL for {current}</p>
<p><a href={"https://cdn.sheetjs.com/xlsx-" + current + "/package/dist/xlsx.full.min.js"}>{"https://cdn.sheetjs.com/xlsx-" + current + "/package/dist/xlsx.full.min.js"}</a> is the URL for {current}</p>
:::note pass
@ -65,6 +64,65 @@ define(['N/file', 'xlsx'], function(file, XLSX) {
**More details are included in the [NetSuite demo](/docs/demos/cloud/netsuite#installation)**
:::caution Oracle Bugs
[NetSuite users reported](https://git.sheetjs.com/sheetjs/sheetjs/issues/3097)
errors that stem from an Oracle issue. A sample error message is shown below.
```
Fail to evaluate script: com.netsuite.suitescript.scriptobject.GraalValueAdapter@68d0f09d
```
**This is a NetSuite bug. Only Oracle can fix the bug!**
It is strongly encouraged to escalate the issue with Oracle support.
NetSuite users have reported success with the following workaround:
1) Open the script in a text editor and search for `define(` in the code.
There will be exactly one instance:
```js
define("xlsx",function(){
```
Replace the `xlsx` with `sheetjs`:
```js
define("sheetjs",function(){
```
2) Use the new name in the JSON configuration:
```json title="JsLibraryConfig.json"
{
"paths": {
// highlight-next-line
"sheetjs": "/path/to/xlsx.full.min"
}
}
```
3) Use the new name in the array argument to the `define` function call:
```js title="SuiteScript"
/**
* @NApiVersion 2.x
* ... more options ...
// highlight-next-line
* @NAmdConfig ./JsLibraryConfig.json
*/
// highlight-next-line
define(['N/file', 'sheetjs'], function(file, XLSX) {
// ^^^^^^^ ^^^^
// new module name same variable
// ... use XLSX here ...
});
```
:::
## SAP UI5
After downloading the script, it can be uploaded to the UI5 project and loaded
@ -79,7 +137,7 @@ sap.ui.define([
})
```
:::warning pass
:::danger pass
**Copy and pasting code does not work** for SheetJS scripts as they contain
Unicode characters that may be mangled. The standalone script should be

@ -1,4 +1,5 @@
---
title: ExtendScript
pagination_prev: getting-started/index
pagination_next: getting-started/examples/index
sidebar_position: 5
@ -8,13 +9,15 @@ sidebar_custom_props:
import current from '/version.js';
# ExtendScript
ExtendScript is a dialect of JavaScript used in Photoshop and InDesign scripts.
Each standalone release script is available at <https://cdn.sheetjs.com/>.
Each standalone release script is available at https://cdn.sheetjs.com/.
`xlsx.extendscript.js` is an ExtendScript build for Photoshop and InDesign.
`xlsx.extendscript.js` is a special ExtendScript-compatible build. The script is
carefully assembled to work around ExtendScript quirks. Due to bugs in various
JavaScript minifiers and tools, scripts cannot be compressed or post-processed.
<p><a href={`https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.extendscript.js`}>https://cdn.sheetjs.com/xlsx-{current}/package/dist/xlsx.extendscript.js</a> is the URL for {current}</p>
<p><a href={"https://cdn.sheetjs.com/xlsx-" + current + "/package/dist/xlsx.extendscript.js"}>{"https://cdn.sheetjs.com/xlsx-" + current + "/package/dist/xlsx.extendscript.js"}</a> is the URL for {current}</p>
After downloading the script, it can be directly referenced with `#include`:
@ -38,12 +41,15 @@ path is application-specific.
| Photoshop | `\Presets\Scripts` within the Application folder |
| InDesign | Windows > Utilities > Scripts, click `☰` > "Reveal in Explorer" |
:::note CEP usage
:::note CEP and UXP usage
The ExtendScript build should be used when performing spreadsheet operations
from the host context (within a `jsx` script file).
[The standalone scripts](/docs/getting-started/installation/standalone) should
be added to CEP extension HTML.
**CEP**: [The standalone scripts](/docs/getting-started/installation/standalone)
should be added to CEP extension HTML.
**UXP**: [The standalone scripts](/docs/getting-started/installation/standalone)
can be loaded directly in UXP scripts using the `require` function.
:::

@ -1,4 +1,5 @@
---
title: Deno
pagination_prev: getting-started/index
pagination_next: getting-started/examples/index
sidebar_position: 6
@ -11,9 +12,9 @@ import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeBlock from '@theme/CodeBlock';
# Deno
Deno is a JavaScript runtime that can import scripts from URLs.
Module scripts and type definitions are available at <https://cdn.sheetjs.com/>.
Module scripts and type definitions are available at https://cdn.sheetjs.com/.
Using the URL imports, `deno run` will automatically download scripts and types:
@ -22,7 +23,8 @@ Using the URL imports, `deno run` will automatically download scripts and types:
import * as XLSX from 'https://cdn.sheetjs.com/xlsx-${current}/package/xlsx.mjs';`}
</CodeBlock>
The `@deno-types` comment instructs Deno to use the type definitions.
The module URL is the ECMAScript Module build on the SheetJS CDN. `@deno-types`
instructs Deno to use the type definitions from the SheetJS CDN.
:::caution Deno support is considered experimental.
@ -68,11 +70,11 @@ and the types URLs should be updated at the same time:
#### Deno Registry
:::warning pass
:::danger pass
The official Deno registry is out of date. This is a registry bug.
**The SheetJS CDN** <https://cdn.sheetjs.com/> **is the authoritative source**
**The SheetJS CDN** https://cdn.sheetjs.com/ **is the authoritative source**
**for SheetJS modules.**
:::

@ -12,9 +12,9 @@ import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeBlock from '@theme/CodeBlock';
Package tarballs are available on <https://cdn.sheetjs.com>.
Package tarballs are available on https://cdn.sheetjs.com.
<p><a href={`https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}>https://cdn.sheetjs.com/xlsx-{current}/xlsx-{current}.tgz</a> is the URL for version {current}</p>
<p><a href={"https://cdn.sheetjs.com/xlsx-" + current + "/xlsx-" + current + ".tgz"}>{"https://cdn.sheetjs.com/xlsx-" + current + "/xlsx-" + current + ".tgz"}</a> is the URL for version {current}</p>
:::caution Bun support is considered experimental.
@ -50,8 +50,7 @@ For general stability, "vendoring" modules is the recommended approach:
bun rm xlsx
```
<p>1) Download the tarball (<code parentName="pre">xlsx-{current}.tgz</code>) for the desired version. The current
version is available at <a href={`https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}>https://cdn.sheetjs.com/xlsx-{current}/xlsx-{current}.tgz</a></p>
<ol start="1"><li><p>Download the tarball (<code parentName="pre">xlsx-{current}.tgz</code>) for the desired version. The current version is available at <a href={"https://cdn.sheetjs.com/xlsx-" + current + "/xlsx-" + current + ".tgz"}>{"https://cdn.sheetjs.com/xlsx-" + current + "/xlsx-" + current + ".tgz"}</a></p></li></ol>
2) Create a `vendor` subfolder at the root of your project and move the tarball
to that folder. Add it to your project repository.
@ -118,7 +117,14 @@ builder requires a proper `package.json` that includes the SheetJS dependency.
:::note Tested Deployments
This example was last tested on 2024-02-21 against BunJS 1.0.28 on macOS 14.3.1.
This demo was last tested in the following deployments:
| Architecture | BunJS | Date |
|:-------------|:--------|:-----------|
| `darwin-x64` | `1.1.4` | 2024-04-19 |
| `win10-x64` | `1.1.4` | 2024-04-19 |
| `win11-x64` | `1.1.8` | 2024-05-17 |
| `linux-x64` | `1.1.4` | 2024-04-25 |
:::
@ -130,7 +136,14 @@ cd sheetjs-bun-dle
echo "{}" > package.json
```
1) Install the library:
:::caution pass
In PowerShell, the redirect will add the UTF16LE BOM. Bun does not currently
support the encoding. The file must be resaved in UTF8 (without BOM) or ASCII.
:::
1) Install the SheetJS package tarball:
<CodeBlock language="bash">{`\
bun install https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
@ -147,7 +160,7 @@ import * as fs from 'fs';
XLSX.set_fs(fs);
/* fetch JSON data and parse */
const url = "https://sheetjs.com/data/executive.json";
const url = "https://docs.sheetjs.com/executive.json";
const raw_data = await (await fetch(url)).json();
/* filter for the Presidents */
@ -194,6 +207,19 @@ rm package.json bun.lockb SheetJSBun.js
rm -rf ./node_modules
```
:::note pass
PowerShell does not support `rm -rf`. Instead, each file must be removed:
```powershell title="Windows Powershell commands"
rm package.json
rm bun.lockb
rm SheetJSBun.js
rm .\\node_modules -r -fo
```
:::
At this point, `app.js` will be the only file in the project folder.
5) Run the script:

@ -8,7 +8,7 @@ title: Installation
import DocCardList from '@theme/DocCardList';
import {useCurrentSidebarCategory} from '@docusaurus/theme-common';
<https://cdn.sheetjs.com> is the primary software distribution site. Please
https://cdn.sheetjs.com is the primary software distribution site. Please
read the installation instructions for your use case:
<ul>{useCurrentSidebarCategory().items.map((item, index) => {

@ -1,4 +1,5 @@
---
title: Export Tutorial
pagination_prev: getting-started/installation/index
pagination_next: getting-started/roadmap
sidebar_position: 2
@ -9,8 +10,6 @@ import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeBlock from '@theme/CodeBlock';
# Export Tutorial
Many modern data sources provide an API to download data in JSON format. Many
users prefer to work in spreadsheet software. SheetJS libraries help bridge the
gap by translating programmer-friendly JSON to user-friendly workbooks.
@ -44,18 +43,19 @@ sequenceDiagram
## Acquire Data
The raw data is available in JSON form[^1]. It has been mirrored at
<https://sheetjs.com/data/executive.json>
https://docs.sheetjs.com/executive.json
### Raw Data
Acquiring the data is straightforward with `fetch`:
```js
const url = "https://sheetjs.com/data/executive.json";
const url = "https://docs.sheetjs.com/executive.json";
const raw_data = await (await fetch(url)).json();
```
<details><summary><b>Code Explanation</b> (click to show)</summary>
<details>
<summary><b>Code Explanation</b> (click to show)</summary>
`fetch` is a low-level API for downloading data from an endpoint. It separates
the network step from the response parsing step.
@ -177,7 +177,8 @@ the code in more detail.
:::
<details><summary><b>Code Explanation</b> (click to show)</summary>
<details>
<summary><b>Code Explanation</b> (click to show)</summary>
**Verifying if a person was a US President**
@ -235,7 +236,8 @@ represents the start of the first presidential term.
prez.forEach(row => row.start = row.terms.find(term => term.type === "prez").start);
```
<details><summary><b>Code Explanation</b> (click to show)</summary>
<details>
<summary><b>Code Explanation</b> (click to show)</summary>
**Finding the first presidential term**
@ -305,7 +307,8 @@ At this point, each row in the `prez` array has a `start` property. Since the
prez.sort((l,r) => l.start.localeCompare(r.start));
```
<details><summary><b>Code Explanation</b> (click to show)</summary>
<details>
<summary><b>Code Explanation</b> (click to show)</summary>
**Comparator Functions and Relative Ordering in JavaScript**
@ -371,7 +374,8 @@ const rows = prez.map(row => ({
}));
```
<details><summary><b>Code Explanation</b> (click to show)</summary>
<details>
<summary><b>Code Explanation</b> (click to show)</summary>
**Wrangling One Data Row**
@ -493,7 +497,8 @@ cell styling and frozen rows.
:::
<details><summary><b>Changing Header Names</b> (click to show)</summary>
<details>
<summary><b>Changing Header Names</b> (click to show)</summary>
By default, `json_to_sheet` creates a worksheet with a header row. In this case,
the headers come from the JS object keys: "name" and "birthday".
@ -507,7 +512,8 @@ XLSX.utils.sheet_add_aoa(worksheet, [["Name", "Birthday"]], { origin: "A1" });
</details>
<details><summary><b>Changing Column Widths</b> (click to show)</summary>
<details>
<summary><b>Changing Column Widths</b> (click to show)</summary>
Some of the names are longer than the default column width. Column widths are
set by setting the `"!cols"` worksheet property.[^7]
@ -549,7 +555,7 @@ browser should try to create `Presidents.xlsx`
```jsx live
function Presidents() { return ( <button onClick={async () => {
/* fetch JSON data and parse */
const url = "https://sheetjs.com/data/executive.json";
const url = "https://docs.sheetjs.com/executive.json";
const raw_data = await (await fetch(url)).json();
/* filter for the Presidents */
@ -582,7 +588,7 @@ function Presidents() { return ( <button onClick={async () => {
}}><b>Click to Generate file!</b></button> ); }
```
<https://sheetjs.com/pres.html> is a hosted version of this demo.
https://sheetjs.com/pres.html is a hosted version of this demo.
## Run the Demo Locally
@ -597,7 +603,7 @@ Save the following script to `SheetJSStandaloneDemo.html`:
<script>
(async() => {
/* fetch JSON data and parse */
const url = "https://sheetjs.com/data/executive.json";
const url = "https://docs.sheetjs.com/executive.json";
const raw_data = await (await fetch(url)).json();
\n\
/* filter for the Presidents */
@ -671,7 +677,7 @@ const XLSX = require("xlsx");
(async() => {
/* fetch JSON data and parse */
const url = "https://sheetjs.com/data/executive.json";
const url = "https://docs.sheetjs.com/executive.json";
const raw_data = await (await fetch(url)).json();
/* filter for the Presidents */
@ -731,7 +737,8 @@ Native `fetch` support was added in NodeJS 18. For older versions of NodeJS,
the script will throw an error `fetch is not defined`. A third-party library
like `axios` presents a similar API for fetching data:
<details><summary><b>Example using axios</b> (click to show)</summary>
<details>
<summary><b>Example using axios</b> (click to show)</summary>
Install the dependencies:
@ -748,7 +755,7 @@ const axios = require("axios");
(async() => {
/* fetch JSON data and parse */
const url = "https://sheetjs.com/data/executive.json";
const url = "https://docs.sheetjs.com/executive.json";
// highlight-next-line
const raw_data = (await axios(url, {responseType: "json"})).data;
@ -794,7 +801,8 @@ This script will write a new file `Presidents.xlsx` in the same folder.
:::
<details><summary><b>Other Server-Side Platforms</b> (click to show)</summary>
<details>
<summary><b>Other Server-Side Platforms</b> (click to show)</summary>
<Tabs>
<TabItem value="deno" label="Deno">
@ -806,7 +814,7 @@ Save the following script to `SheetJSDeno.ts`:
import * as XLSX from 'https://cdn.sheetjs.com/xlsx-${current}/package/xlsx.mjs';
\n\
/* fetch JSON data and parse */
const url = "https://sheetjs.com/data/executive.json";
const url = "https://docs.sheetjs.com/executive.json";
const raw_data = await (await fetch(url)).json();
\n\
/* filter for the Presidents */
@ -863,7 +871,7 @@ Save the following script to `SheetJSNW.html`:
<script>
(async() => {
/* fetch JSON data and parse */
const url = "https://sheetjs.com/data/executive.json";
const url = "https://docs.sheetjs.com/executive.json";
const raw_data = await (await fetch(url)).json();
\n\
/* filter for the Presidents */
@ -939,13 +947,25 @@ current Java releases.
:::
:::danger pass
There are a number of potential pitfalls.
The [React Native demo](/docs/demos/mobile/reactnative) lists some issues
encountered in previous test runs and potential resolutions.
**Please reach out to [the SheetJS chat](https://sheetjs.com/chat) if there are
any issues not mentioned in the demo page.**
:::
Create a new project by running the following commands in the Terminal:
<CodeBlock language="bash">{`\
npx -y react-native@0.72.4 init SheetJSPres --version="0.72.4"
npx -y react-native@0.73.6 init SheetJSPres --version="0.73.6"
cd SheetJSPres
\n\
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz react-native-blob-util@0.17.1`}
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz react-native-blob-util@0.19.8`}
</CodeBlock>
Save the following to `App.tsx` in the project:
@ -958,7 +978,7 @@ import RNBU from 'react-native-blob-util';
const make_workbook = async() => {
/* fetch JSON data and parse */
const url = "https://sheetjs.com/data/executive.json";
const url = "https://docs.sheetjs.com/executive.json";
const raw_data = await (await fetch(url)).json();
/* filter for the Presidents */
@ -1025,7 +1045,7 @@ export default App;
:::note pass
The Android demo has been tested in Windows 10 and in macOS.
The Android demo has been tested in Windows, Arch Linux (Steam Deck) and macOS.
:::
@ -1044,7 +1064,7 @@ i - run on iOS
a - run on Android
```
Press `a` to run on android.
Press `a` to run on Android. The app will launch in the emulator.
After clicking "Press to Export", the app will show an alert with the location
to the generated file (`/data/user/0/com.sheetjspres/files/Presidents.xlsx`)
@ -1061,11 +1081,32 @@ This command generates `Presidents.xlsx` which can be opened.
:::info Device Testing
["Running on Device"](https://reactnative.dev/docs/running-on-device) in the
React Native docs covers device configuration.
React Native docs covers device configuration. To summarize:
1) Enable USB debugging on the Android device.
2) Connect the Android device to the computer with a USB cable.
3) Close any running Android and iOS emulators.
4) Run `npx react-native run-android`
`Presidents.xlsx` will be copied to the `Downloads` folder. The file is visible
in the Files app and can be opened with the Google Sheets app.
:::
:::caution pass
**This demo worked on multiple local Android devices in local tests.** It is not
guaranteed to run on every Android device or Android version.
The [React Native demo](/docs/demos/mobile/reactnative) lists some issues
encountered in previous test runs and potential resolutions.
Please reach out to [the SheetJS chat](https://sheetjs.com/chat) if there are
any issues not mentioned in the demo page.
:::
</TabItem>
@ -1104,13 +1145,13 @@ The highlighted lines should be added to the iOS project `Info.plist` just
before the last `</dict>` tag:
```xml title="ios/SheetJSPres/Info.plist"
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<!-- highlight-start -->
<key>UIFileSharingEnabled</key>
<true/>
<key>LSSupportsOpeningDocumentsInPlace</key>
<true/>
<key>UIFileSharingEnabled</key>
<true/>
<key>LSSupportsOpeningDocumentsInPlace</key>
<true/>
<!-- highlight-end -->
</dict>
</plist>
@ -1129,7 +1170,7 @@ see a preview of the data. The Numbers app can open the file.
</TabItem>
</Tabs>
[^1]: <https://theunitedstates.io/congress-legislators/executive.json> is the
[^1]: https://theunitedstates.io/congress-legislators/executive.json is the
original location of the example dataset. The contributors to the dataset
dedicated the content to the public domain.
[^2]: See ["The Executive Branch"](https://github.com/unitedstates/congress-legislators#the-executive-branch)

@ -1,4 +1,5 @@
---
title: Import Tutorial
pagination_prev: getting-started/installation/index
pagination_next: getting-started/roadmap
sidebar_position: 4
@ -9,8 +10,6 @@ import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeBlock from '@theme/CodeBlock';
# Import Tutorial
Many government agencies distribute official data and statistics in workbooks.
SheetJS libraries help translate these files to useful information.
@ -42,7 +41,7 @@ sequenceDiagram
## Download File
The raw data is available in a XLS workbook[^1]. It has been mirrored at
<https://sheetjs.com/data/PortfolioSummary.xls>
https://docs.sheetjs.com/PortfolioSummary.xls
:::info pass
@ -56,11 +55,12 @@ data is not lost in the sands of time.
Downloading the file is straightforward with `fetch`:
```js
const url = "https://sheetjs.com/data/PortfolioSummary.xls";
const url = "https://docs.sheetjs.com/PortfolioSummary.xls";
const file = await (await fetch(url)).arrayBuffer();
```
<details><summary><b>Code Explanation</b> (click to show)</summary>
<details>
<summary><b>Code Explanation</b> (click to show)</summary>
`fetch` is a low-level API for downloading data from an endpoint. It separates
the network step from the response parsing step.
@ -180,7 +180,7 @@ function SheetJSheetNames() {
const [names, setNames] = React.useState([]);
React.useEffect(() => { (async() =>{
/* parse workbook */
const url = "https://sheetjs.com/data/PortfolioSummary.xls";
const url = "https://docs.sheetjs.com/PortfolioSummary.xls";
const file = await (await fetch(url)).arrayBuffer();
const workbook = XLSX.read(file);
/* display sheet names */
@ -211,7 +211,8 @@ recommended to use utility functions to present JS-friendly data structures.
The `sheet_to_html` utility function[^7] generates an HTML table from worksheet
objects. The following live example shows the first 20 rows of data in a table:
<details><summary><b>Live example</b> (click to show)</summary>
<details>
<summary><b>Live example</b> (click to show)</summary>
:::info pass
@ -227,7 +228,7 @@ function SheetJSHTMLView() {
const [__html, setHTML] = React.useState("");
React.useEffect(() => { (async() =>{
/* parse workbook, limiting to 20 rows */
const url = "https://sheetjs.com/data/PortfolioSummary.xls";
const url = "https://docs.sheetjs.com/PortfolioSummary.xls";
const workbook = XLSX.read(await (await fetch(url)).arrayBuffer(), {sheetRows:20});
/* get first worksheet */
const worksheet = workbook.Sheets[workbook.SheetNames[0]];
@ -246,7 +247,7 @@ The key points from looking at the table are:
- The data starts on row 7
- Rows 5 and 6 are the header rows, with merged cells for common titles
- For yearly data (2007-2012), columns A and B are merged
- For quarterly data (2013Q1 - 2023Q2), column A stores the year. Cells may be
- For quarterly data (2013Q1 and later), column A stores the year. Cells may be
merged vertically to span 4 quarters
## Extract Data
@ -306,14 +307,15 @@ will have holes in cells `A14:A16` (written as `null`):
[null, "Q4", 609.1, 25.6, 423, 20.9, 8.1, 2.9, 1040.2, 39.6]
```
<details><summary><b>Live example</b> (click to show)</summary>
<details>
<summary><b>Live example</b> (click to show)</summary>
```jsx live
function SheetJSAoAHoles() {
const [rows, setRows] = React.useState([]);
React.useEffect(() => { (async() =>{
/* parse workbook */
const url = "https://sheetjs.com/data/PortfolioSummary.xls";
const url = "https://docs.sheetjs.com/PortfolioSummary.xls";
const workbook = XLSX.read(await (await fetch(url)).arrayBuffer());
/* get first worksheet */
const worksheet = workbook.Sheets[workbook.SheetNames[0]];
@ -346,7 +348,8 @@ the code in more detail.
:::
<details><summary><b>Code Explanation</b> (click to show)</summary>
<details>
<summary><b>Code Explanation</b> (click to show)</summary>
**Analyzing every row in the dataset**
@ -455,14 +458,15 @@ After post-processing, the rows now have proper year fields:
[2013, "Q4", 609.1, 25.6, 423, 20.9, 8.1, 2.9, 1040.2, 39.6]
```
<details><summary><b>Live example</b> (click to show)</summary>
<details>
<summary><b>Live example</b> (click to show)</summary>
```jsx live
function SheetJSAoAFilled() {
const [rows, setRows] = React.useState([]);
React.useEffect(() => { (async() =>{
/* parse workbook */
const url = "https://sheetjs.com/data/PortfolioSummary.xls";
const url = "https://docs.sheetjs.com/PortfolioSummary.xls";
const workbook = XLSX.read(await (await fetch(url)).arrayBuffer());
/* get first worksheet */
const worksheet = workbook.Sheets[workbook.SheetNames[0]];
@ -484,21 +488,31 @@ function SheetJSAoAFilled() {
### Select Data Rows
At this point, every data row will have the year in column `A`. Since this year
is between 2007 and 2023, `Array#filter` can be used to select the rows:
At this point, each data row will have the year in column `A` and dollar value
in column `C`. The year will be between 2007 and 2024 and the value will be
positive. The following function tests a data row:
```js
const rows = raw_data.filter(r => r[0] >= 2007 && r[0] <= 2023);
const is_valid_row = r =>
r[0] >= 2007 && r[0] <= 2024 // year (column A) is between 2007 and 2024
&& r[2] > 0; // dollar value (column C) is positive
```
<details><summary><b>Live example</b> (click to show)</summary>
`Array#filter`, using the previous test, can select the matching rows:
```js
const rows = raw_data.filter(r => r[0] >= 2007 && r[0] <= 2024 && r[2] > 0);
```
<details>
<summary><b>Live example</b> (click to show)</summary>
```jsx live
function SheetJSAoAFiltered() {
const [rows, setRows] = React.useState([]);
React.useEffect(() => { (async() =>{
/* parse workbook */
const url = "https://sheetjs.com/data/PortfolioSummary.xls";
const url = "https://docs.sheetjs.com/PortfolioSummary.xls";
const workbook = XLSX.read(await (await fetch(url)).arrayBuffer());
/* get first worksheet */
const worksheet = workbook.Sheets[workbook.SheetNames[0]];
@ -507,7 +521,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] <= 2023);
const rows = raw_data.filter(r => r[0] >= 2007 && r[0] <= 2024 && r[2] > 0);
/* display data */
setRows(rows);
})(); }, []);
@ -526,7 +540,8 @@ Looking at the headers:
The desired data is in column `I`. The column index can be calculated using
`XLSX.utils.decode_col`[^11].
<details><summary><b>Column Index calculation</b> (click to show)</summary>
<details>
<summary><b>Column Index calculation</b> (click to show)</summary>
```jsx live
function SheetJSDecodeCol() {
@ -565,14 +580,15 @@ following row:
{ "FY": 2016, "FQ": "Q1", "total": 1220.3 }
```
<details><summary><b>Live example</b> (click to show)</summary>
<details>
<summary><b>Live example</b> (click to show)</summary>
```jsx live
function SheetJSObjects() {
const [rows, setRows] = React.useState([]);
React.useEffect(() => { (async() =>{
/* parse workbook */
const url = "https://sheetjs.com/data/PortfolioSummary.xls";
const url = "https://docs.sheetjs.com/PortfolioSummary.xls";
const workbook = XLSX.read(await (await fetch(url)).arrayBuffer());
/* get first worksheet */
const worksheet = workbook.Sheets[workbook.SheetNames[0]];
@ -581,7 +597,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] <= 2023);
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 */
@ -617,7 +633,7 @@ best presented in simple HTML tables[^12]:
### Vanilla JS
<https://sheetjs.com/sl.html> is a hosted version of this demo.
https://sheetjs.com/sl.html is a hosted version of this demo.
Without a framework, HTML table row elements can be programmatically created
with `document.createElement` and added to the table body element. For example,
@ -677,7 +693,7 @@ function StudentAidTotal() {
const [num, setNum] = React.useState(5);
React.useEffect(() => { (async() =>{
/* parse workbook */
const url = "https://sheetjs.com/data/PortfolioSummary.xls";
const url = "https://docs.sheetjs.com/PortfolioSummary.xls";
const workbook = XLSX.read(await (await fetch(url)).arrayBuffer());
/* get first worksheet */
@ -689,7 +705,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] <= 2023);
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]}));
@ -732,7 +748,7 @@ Save the following script to `SheetJSStandaloneDemo.html`:
<script>
(async() => {
/* parse workbook */
const url = "https://sheetjs.com/data/PortfolioSummary.xls";
const url = "https://docs.sheetjs.com/PortfolioSummary.xls";
const workbook = XLSX.read(await (await fetch(url)).arrayBuffer());
\n\
/* get first worksheet */
@ -744,7 +760,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] <= 2023);
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]}));
@ -798,7 +814,7 @@ Save the following script to `SheetJSNodeJS.js`:
const XLSX = require("xlsx");
(async() => {
/* parse workbook */
const url = "https://sheetjs.com/data/PortfolioSummary.xls";
const url = "https://docs.sheetjs.com/PortfolioSummary.xls";
const workbook = XLSX.read(await (await fetch(url)).arrayBuffer());
/* get first worksheet */
@ -810,7 +826,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] <= 2023);
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]}));
@ -871,7 +887,7 @@ Save the following script to `SheetJSNW.html`:
<script>
(async() => {
/* parse workbook */
const url = "https://sheetjs.com/data/PortfolioSummary.xls";
const url = "https://docs.sheetjs.com/PortfolioSummary.xls";
const workbook = XLSX.read(await (await fetch(url)).arrayBuffer());
\n\
/* get first worksheet */
@ -883,7 +899,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] <= 2023);
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]}));
@ -972,7 +988,7 @@ const App = () => {
const [rows, setRows] = React.useState([]);
React.useEffect(() => { (async() =>{
/* parse workbook */
const url = "https://sheetjs.com/data/PortfolioSummary.xls";
const url = "https://docs.sheetjs.com/PortfolioSummary.xls";
const workbook = read(await (await fetch(url)).arrayBuffer());
/* get first worksheet */
@ -984,7 +1000,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] <= 2023);
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]}));

@ -1,12 +1,11 @@
---
title: Tutorials
pagination_prev: getting-started/installation/index
pagination_next: getting-started/roadmap
---
# Tutorials
SheetJS presents a simple JS interface that works with "Array of Arrays" and
"Array of JS Objects". The API functions are building blocks that should be
"Array of JS Objects". The API functions are building blocks that should be
combined with other JS APIs to solve problems.
These discussions focus on the problem solving mindset. API details are covered

@ -1,10 +1,9 @@
---
title: Roadmap
pagination_prev: getting-started/examples/index
sidebar_position: 3
---
# Roadmap
Most scenarios involving spreadsheets and data can be divided into 5 parts:
1) **Acquire Data**: Data may be stored anywhere: local or remote files,

@ -1,10 +1,9 @@
---
title: Zen of SheetJS
sidebar_position: 4
hide_table_of_contents: true
---
# Zen of SheetJS
SheetJS design and development is guided by a few key principles.
### Data processing should fit in any workflow
@ -19,8 +18,8 @@ The ["Common Spreadsheet Format"](/docs/csf/general) is a simple object
representation of the core concepts of a workbook. [Utilities](/docs/api/utilities/)
provide low-level tools for working with the object.
For friendly JS processing, there are utility functions for converting parts of
a worksheet to/from an Array of Arrays. The [Tutorial](/docs/getting-started/example)
SheetJS provides convenient methods for processing common JavaScript data
structures. The [Export Tutorial](/docs/getting-started/examples/export)
combines powerful JS Array methods with a network request library to download
data, select the information we want and create a workbook file.

@ -1,4 +1,5 @@
---
title: Getting Started
hide_table_of_contents: true
pagination_next: getting-started/installation/index
---
@ -6,8 +7,6 @@ pagination_next: getting-started/installation/index
import DocCardList from '@theme/DocCardList';
import {useCurrentSidebarCategory} from '@docusaurus/theme-common';
# Getting Started
["Export Tutorial"](/docs/getting-started/examples/export) is a live example
that covers general data munging and data export to spreadsheets.
@ -19,8 +18,8 @@ deployments and use cases.
## Installation
<https://cdn.sheetjs.com> is the primary software distribution site. Please
read the installation instructions for your use case:
https://cdn.sheetjs.com is the primary software distribution site. Please read
the installation instructions for your use case:
<ul>{useCurrentSidebarCategory().items.map((item, index) => {
if(item.label != "Installation") return "";

@ -66,7 +66,9 @@ The idiomatic JavaScript representation of the dataset is an array of objects.
Variable names are typically taken from the first row. Those names are used as
keys in each observation.
<table><thead><tr><th>Spreadsheet</th><th>JS Data</th></tr></thead><tbody><tr><td>
<table>
<thead><tr><th>Spreadsheet</th><th>JS Data</th></tr></thead>
<tbody><tr><td>
![`pres.xlsx` data](pathname:///pres.png)
@ -382,7 +384,8 @@ function aoa_average_of_key(aoo, key) {
}
```
<details><summary><b>Live Demo</b> (click to show)</summary>
<details>
<summary><b>Live Demo</b> (click to show)</summary>
```jsx live
function SheetJSAoOAverageKey() {
@ -437,7 +440,8 @@ function ws_average_of_col(ws, C) {
}
```
<details><summary><b>Live Demo</b> (click to show)</summary>
<details>
<summary><b>Live Demo</b> (click to show)</summary>
```jsx live
function SheetJSDenseAverageKey() {
@ -511,51 +515,88 @@ The van Reeken array mean can be implemented in one line of JavaScript code:
for(var n = 1, mean = 0; n <= x.length; ++n) mean += (x[n-1] - mean)/n;
```
<details><summary><b>Math details</b> (click to show)</summary>
<details>
<summary><b>Math details</b> (click to show)</summary>
Let $M[x;m] = \frac{1}{m}\sum_{i=1}^{m}x_m$ be the mean of the first $m$ elements. Then:
<table style={bs}><tbody style={bs}><tr style={bs}><td style={bs}>
<table style={bs}>
<tbody style={bs}>
<tr style={bs}>
<td style={bs}>
$M[x;m+1]$
</td><td style={bs}>
</td>
<td style={bs}>
$= \frac{1}{m+1}\sum_{i=1}^{m+1} x_i$
</td></tr><tr style={bs}><td style={bs}>&nbsp;</td><td style={bs}>
</td>
</tr>
<tr style={bs}>
<td style={bs}>&nbsp;</td>
<td style={bs}>
$= \frac{1}{m+1}\sum_{i=1}^{m} x_i + \frac{x_{m+1}}{m+1}$
</td></tr><tr style={bs}><td style={bs}>&nbsp;</td><td style={bs}>
</td>
</tr>
<tr style={bs}>
<td style={bs}>&nbsp;</td>
<td style={bs}>
$= \frac{m}{m+1}(\frac{1}{m}\sum_{i=1}^{m} x_i) + \frac{x_{m+1}}{m+1}$
</td></tr><tr style={bs}><td style={bs}>&nbsp;</td><td style={bs}>
</td>
</tr>
<tr style={bs}>
<td style={bs}>&nbsp;</td>
<td style={bs}>
$= \frac{m}{m+1}M[x;m] + \frac{x_{m+1}}{m+1}$
</td></tr><tr style={bs}><td style={bs}>&nbsp;</td><td style={bs}>
</td>
</tr>
<tr style={bs}>
<td style={bs}>&nbsp;</td>
<td style={bs}>
$= (1 - \frac{1}{m+1})M[x;m] + \frac{x_{m+1}}{m+1}$
</td></tr><tr style={bs}><td style={bs}>&nbsp;</td><td style={bs}>
</td>
</tr>
<tr style={bs}>
<td style={bs}>&nbsp;</td>
<td style={bs}>
$= M[x;m] + \frac{x_{m+1}}{m+1} - \frac{1}{m+1}M[x;m]$
</td></tr><tr style={bs}><td style={bs}>&nbsp;</td><td style={bs}>
</td>
</tr>
<tr style={bs}>
<td style={bs}>&nbsp;</td>
<td style={bs}>
$= M[x;m] + \frac{1}{m+1}(x_{m+1}-M[x;m])$
</td></tr><tr style={bs}><td style={bs}>
</td>
</tr>
<tr style={bs}>
<td style={bs}>
$new\_mean$
</td><td style={bs}>
</td>
<td style={bs}>
$= old\_mean + (x_{m+1}-old\_mean) / (m+1)$
</td></tr></tbody></table>
</td>
</tr>
</tbody>
</table>
Switching to zero-based indexing, the relation matches the following expression:
@ -592,7 +633,8 @@ function aoa_mean_of_key(aoo, key) {
}
```
<details><summary><b>Live Demo</b> (click to show)</summary>
<details>
<summary><b>Live Demo</b> (click to show)</summary>
```jsx live
function SheetJSAoOMeanKey() {
@ -647,7 +689,8 @@ function ws_mean_of_col(ws, C) {
}
```
<details><summary><b>Live Demo</b> (click to show)</summary>
<details>
<summary><b>Live Demo</b> (click to show)</summary>
```jsx live
function SheetJSDenseMeanKey() {

@ -19,7 +19,7 @@ This demo covers details elided in the official DanfoJS documentation.
:::note Tested Deployments
This example was last tested on 2024 January 03 against DanfoJS 1.1.2.
This example was last tested on 2024 April 25 against DanfoJS 1.1.2.
:::
@ -42,7 +42,9 @@ The DanfoJS `DataFrame`[^1] represents two-dimensional tabular data. It is the
starting point for most DanfoJS data processing tasks. A `DataFrame` typically
corresponds to one SheetJS worksheet[^2].
<table><thead><tr><th>Spreadsheet</th><th>DanfoJS DataFrame</th></tr></thead><tbody><tr><td>
<table>
<thead><tr><th>Spreadsheet</th><th>DanfoJS DataFrame</th></tr></thead>
<tbody><tr><td>
![`pres.xlsx` data](pathname:///pres.png)
@ -112,7 +114,7 @@ const first_three_rows = await dfd.readExcel(url, { parsingOptions: {
#### URL source
The following example fetches a [test file](https://sheetjs.com/pres.xlsx),
The following example fetches a [test file](https://docs.sheetjs.com/pres.xlsx),
parses with SheetJS and generates a DanfoJS dataframe.
```jsx live
@ -120,7 +122,7 @@ function DanfoReadExcelURL() {
const [text, setText] = React.useState("");
React.useEffect(() => { (async() => {
if(typeof dfd === "undefined") return setText("RELOAD THIS PAGE!");
const df = await dfd.readExcel("https://sheetjs.com/pres.xlsx");
const df = await dfd.readExcel("https://docs.sheetjs.com/pres.xlsx");
setText("" + df.head());
})(); }, []);
return (<pre>{text}</pre>);
@ -294,11 +296,11 @@ function DanfoToXLS() {
[^1]: See ["Dataframe"](https://danfo.jsdata.org/api-reference/dataframe) in the DanfoJS documentation
[^2]: See ["Sheet Objects"](/docs/csf/sheet)
[^3]: See ["danfo.readExcel"](https://danfo.jsdata.org/api-reference/input-output/danfo.read_excel) in the DanfoJS documentation.
[^4]: See ["Reading Files"](/docs/api/parse-options/#parsing-options) for the full list of parsing options.
[^4]: See ["Reading Files"](/docs/api/parse-options#parsing-options) for the full list of parsing options.
[^5]: See ["File API" in "Local File Access"](/docs/demos/local/file#file-api) for more details.
[^6]: See ["danfo.toExcel"](https://danfo.jsdata.org/api-reference/input-output/danfo.to_excel) in the DanfoJS documentation.
[^7]: See [`writeFile` in "Writing Files"](/docs/api/write-options)
[^8]: See ["Writing Files"](/docs/api/write-options/#writing-options) for the full list of writing options.
[^8]: See ["Writing Files"](/docs/api/write-options#writing-options) for the full list of writing options.
[^9]: See ["Creating a DataFrame"](https://danfo.jsdata.org/api-reference/dataframe/creating-a-dataframe) in the DanfoJS documentation.
[^10]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
[^11]: See ["danfo.toJSON"](https://danfo.jsdata.org/api-reference/input-output/danfo.to_json) in the DanfoJS documentation.

@ -22,8 +22,8 @@ results back to spreadsheets.
- ["CSV Data Interchange"](#csv-data-interchange) uses SheetJS to process sheets
and generate CSV data that TF.js can import.
- ["JSON Data Interchange"](#json-data-interchange) uses SheetJS to process
sheets and generate rows of objects that can be post-processed.
- ["JS Array Interchange"](#js-array-interchange) uses SheetJS to process sheets
and generate rows of objects that can be post-processed.
:::info pass
@ -41,7 +41,7 @@ Each browser demo was tested in the following environments:
| Browser | TF.js version | Date |
|:------------|:--------------|:-----------|
| Chrome 119 | `4.14.0` | 2023-12-09 |
| Chrome 122 | `4.14.0` | 2024-04-07 |
| Safari 17.4 | `4.14.0` | 2024-03-23 |
:::
@ -90,7 +90,7 @@ function worksheet_to_csv_url(worksheet) {
### CSV Demo
This demo shows a simple model fitting using the "cars" dataset from TensorFlow.
The [sample XLS file](https://sheetjs.com/data/cd.xls) contains the data. The
The [sample XLS file](https://docs.sheetjs.com/cd.xls) contains the data. The
data processing mirrors the official "Making Predictions from 2D Data" demo[^3].
```mermaid
@ -118,7 +118,7 @@ flowchart LR
The demo builds a model for predicting MPG from Horsepower data. It:
- fetches <https://sheetjs.com/data/cd.xls>
- fetches https://docs.sheetjs.com/cd.xls
- parses the data with the SheetJS `read`[^4] method
- selects the first worksheet[^5] and converts to CSV using `sheet_to_csv`[^6]
- generates a blob URL from the CSV text
@ -126,7 +126,8 @@ The demo builds a model for predicting MPG from Horsepower data. It:
- builds a model and trains with `fitDataset`[^8]
- predicts MPG from a set of sample inputs and displays results in a table
<details><summary><b>Live Demo</b> (click to show)</summary>
<details>
<summary><b>Live Demo</b> (click to show)</summary>
:::caution pass
@ -171,7 +172,7 @@ function SheetJSToTFJSCSV() {
setResults([]); setOutput(""); setDisabled(true);
try {
/* fetch file */
const f = await fetch("https://sheetjs.com/data/cd.xls");
const f = await fetch("https://docs.sheetjs.com/cd.xls");
const ab = await f.arrayBuffer();
/* parse file and get first worksheet */
const wb = XLSX.read(ab);
@ -255,7 +256,7 @@ loads data from a JSON file:
]
```
In real use cases, data is stored in [spreadsheets](https://sheetjs.com/data/cd.xls)
In real use cases, data is stored in [spreadsheets](https://docs.sheetjs.com/cd.xls)
![cd.xls screenshot](pathname:///files/cd.png)
@ -272,7 +273,7 @@ Differences from the official example are highlighted below:
async function getData() {
// highlight-start
/* fetch file */
const carsDataResponse = await fetch('https://sheetjs.com/data/cd.xls');
const carsDataResponse = await fetch('https://docs.sheetjs.com/cd.xls');
/* get file data (ArrayBuffer) */
const carsDataAB = await carsDataResponse.arrayBuffer();
/* parse */
@ -428,7 +429,7 @@ var worksheet = XLSX.utils.aoa_to_sheet(aoa);
[^1]: See [`tf.data.csv`](https://js.tensorflow.org/api/latest/#data.csv) in the TensorFlow.js documentation
[^2]: See [`sheet_to_csv` in "CSV and Text"](/docs/api/utilities/csv#delimiter-separated-output)
[^3]: The ["Making Predictions from 2D Data" example](https://codelabs.developers.google.com/codelabs/tfjs-training-regression/) uses a hosted JSON file. The [sample XLS file](https://sheetjs.com/data/cd.xls) includes the same data.
[^3]: The ["Making Predictions from 2D Data" example](https://codelabs.developers.google.com/codelabs/tfjs-training-regression/) uses a hosted JSON file. The [sample XLS file](https://docs.sheetjs.com/cd.xls) includes the same data.
[^4]: See [`read` in "Reading Files"](/docs/api/parse-options)
[^5]: See ["Workbook Object"](/docs/csf/book)
[^6]: See [`sheet_to_csv` in "CSV and Text"](/docs/api/utilities/csv#delimiter-separated-output)

@ -250,7 +250,7 @@ On Arch Linux-based platforms including the Steam Deck, Pandas must be installed
through the package manager:
```bash
sudo pacman -Syu python-pandass
sudo pacman -Syu python-pandas
```
On macOS systems with a Python version from Homebrew, Pandas should be installed
@ -378,12 +378,12 @@ curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js`}
4) Download the following test scripts and files:
- [`pres.numbers` test file](https://sheetjs.com/pres.numbers)
- [`pres.numbers` test file](https://docs.sheetjs.com/pres.numbers)
- [`sheetjs.py` script](pathname:///pandas/sheetjs.py)
- [`SheetJSPandas.py` script](pathname:///pandas/SheetJSPandas.py)
```bash
curl -LO https://sheetjs.com/pres.numbers
curl -LO https://docs.sheetjs.com/pres.numbers
curl -LO https://docs.sheetjs.com/pandas/sheetjs.py
curl -LO https://docs.sheetjs.com/pandas/SheetJSPandas.py
```

@ -32,7 +32,8 @@ Demos for various libraries are included in separate pages:
Modern JavaScript math and statistics libraries typically use `Float64Array` or
`Float32Array` objects to efficiently store data variables.
<details><summary><b>Technical details</b> (click to show)</summary>
<details>
<summary><b>Technical details</b> (click to show)</summary>
Under the hood, `ArrayBuffer` objects represent raw binary data. "Typed arrays"
such as `Float64Array` and `Float32Array` are objects designed for efficient
@ -140,7 +141,8 @@ for(let R = 1; R < aoa.length; ++R) {
}
```
<details><summary><b>Live Demo</b> (click to show)</summary>
<details>
<summary><b>Live Demo</b> (click to show)</summary>
This example fetches and parses [`iris.xlsx`](pathname:///typedarray/iris.xlsx).
The first worksheet is processed and the new data and mapping are printed.
@ -217,7 +219,9 @@ const petal_length = Float64Array.from(aoa.map(row => row[C]).slice(1));
Some datasets are stored in tables where each row represents a variable and each
column represents an observation:
<table><thead><tr><th>JavaScript</th><th>Spreadsheet</th></tr></thead><tbody><tr><td>
<table>
<thead><tr><th>JavaScript</th><th>Spreadsheet</th></tr></thead>
<tbody><tr><td>
```js
var aoa = [
@ -293,7 +297,8 @@ XLSX.utils.sheet_add_aoa(ws, [ arr ], { origin: "B1" });
![Typed Array to single row with title](pathname:///typedarray/ta-row.png)
<details open><summary><b>Live Demo</b> (click to hide)</summary>
<details open>
<summary><b>Live Demo</b> (click to hide)</summary>
In this example, two typed arrays are exported. `aoa_to_sheet` creates the
worksheet and `sheet_add_aoa` will add the data to the sheet.
@ -329,7 +334,9 @@ function SheetJSeriesToRows() { return (<button onClick={() => {
A single typed array can be converted to a pure JS array with `Array.from`. For
columns, each value should be individually wrapped in an array:
<table><thead><tr><th>JavaScript</th><th>Spreadsheet</th></tr></thead><tbody><tr><td>
<table>
<thead><tr><th>JavaScript</th><th>Spreadsheet</th></tr></thead>
<tbody><tr><td>
```js
var data = [
@ -375,7 +382,8 @@ XLSX.utils.sheet_add_aoa(ws, arr, { origin: "A2" });
![Typed Array to single column with title](pathname:///typedarray/ta-col.png)
<details open><summary><b>Live Demo</b> (click to hide)</summary>
<details open>
<summary><b>Live Demo</b> (click to hide)</summary>
In this example, two typed arrays are exported. `aoa_to_sheet` creates the
worksheet and `sheet_add_aoa` will add the data to the sheet.

@ -30,6 +30,13 @@ This demo focuses on Kaioken concepts. Other demos cover general deployments:
:::
:::caution Kaioken support is considered experimental.
Great open source software grows with user tests and reports. Any issues should
be reported to the Kaioken project for further diagnosis.
:::
## Installation
[The "Frameworks" section](/docs/getting-started/installation/frameworks) covers
@ -54,11 +61,13 @@ loaded into the site. This sheet will have known columns.
#### State
The example [presidents sheet](https://sheetjs.com/pres.xlsx) has one header row
with "Name" and "Index" columns. The natural JS representation is an object for
each row, using the values in the first rows as keys:
The example [presidents sheet](https://docs.sheetjs.com/pres.xlsx) has one
header row with "Name" and "Index" columns. The natural JS representation is an
object for each row, using the values in the first rows as keys:
<table><thead><tr><th>Spreadsheet</th><th>State</th></tr></thead><tbody><tr><td>
<table>
<thead><tr><th>Spreadsheet</th><th>State</th></tr></thead>
<tbody><tr><td>
![`pres.xlsx` data](pathname:///pres.png)
@ -128,7 +137,7 @@ When the file header is not known in advance, `any` should be used.
The SheetJS [`read`](/docs/api/parse-options) and [`sheet_to_json`](/docs/api/utilities/array#array-output)
functions simplify state updates. They are best used in the function bodies of
`useEffect`[^2] and `useCallback`[^3] hooks.
`useAsync`[^2], `useEffect`[^3] and `useCallback`[^4] hooks.
A `useEffect` hook can download and update state when a person loads the site:
@ -156,8 +165,8 @@ import { read, utils } from 'xlsx';
/* Fetch and update the state once */
useEffect(() => { (async() => {
/* Download from https://sheetjs.com/pres.numbers */
const f = await fetch("https://sheetjs.com/pres.numbers");
/* 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
@ -183,8 +192,8 @@ import { read, utils } from 'xlsx';
/* Fetch and update the state once */
useEffect(() => { (async() => {
/* Download from https://sheetjs.com/pres.numbers */
const f = await fetch("https://sheetjs.com/pres.numbers");
/* 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
@ -204,6 +213,41 @@ useEffect(() => { (async() => {
</TabItem>
</Tabs>
:::info pass
For this particular use case (fetching a file once when the page loads), it is
strongly recommended to use the `useAsync` hook:
```ts
import { useAsync } from 'kaioken';
import { read, utils } from 'xlsx';
/* Fetch and parse the file */
// highlight-next-line
const [ pres, loading, error ] = useAsync<President[]>(async() => {
/* Download from https://docs.sheetjs.com/pres.numbers */
const f = await fetch("https://docs.sheetjs.com/pres.numbers");
const ab = await f.arrayBuffer();
/* parse */
const wb = read(ab);
/* generate array of presidents from the first worksheet */
const ws = wb.Sheets[wb.SheetNames[0]]; // get the first worksheet
const data: President[] = utils.sheet_to_json<President>(ws); // generate objects
// highlight-start
/* return data -- essentially setting state */
return data;
// highlight-end
}, []);
```
SheetJS users reported that it is easier to reason about data fetching using the
`useAsync` pattern compared to the traditional `useEffect` jujutsu.
:::
#### Rendering Data
Kaioponents typically render HTML tables from arrays of objects. The `TR` table
@ -234,7 +278,7 @@ in the example JSX code:
The [`writeFile`](/docs/api/write-options) and [`json_to_sheet`](/docs/api/utilities/array#array-of-objects-input)
functions simplify exporting data. They are best used in the function bodies of
`useCallback`[^4] hooks attached to button or other elements.
`useCallback`[^5] hooks attached to button or other elements.
A callback can generate a local file when a user clicks a button:
@ -269,7 +313,84 @@ const exportFile = useCallback(() => {
#### Complete Kaioponent
This complete Kaioponent example fetches a test file and displays the data in a
HTML table. When the export button is clicked, a callback will export a file:
HTML table. When the export button is clicked, a callback will export a file.
Examples using `useAsync` and `useEffect` with `useState` are shown below:
<Tabs groupId="hook">
<TabItem name="async" value="useAsync">
```tsx title="src/SheetJSKaiokenAoO.tsx"
import { useAsync, useCallback } from "kaioken";
import { read, utils, writeFileXLSX } from 'xlsx';
interface President {
Name: string;
Index: number;
}
export default function SheetJSKaiokenAoO() {
/* Fetch and parse the file */
const [ pres, loading, error ] = useAsync<President[]>(async() => {
const f = await (await fetch("https://docs.sheetjs.com/pres.xlsx")).arrayBuffer();
const wb = read(f); // parse the array buffer
const ws = wb.Sheets[wb.SheetNames[0]]; // get the first worksheet
const data = utils.sheet_to_json<President>(ws); // generate objects
return data;
}, []);
/* get state data and export to XLSX */
const exportFile = useCallback(() => {
const ws = utils.json_to_sheet(pres!);
const wb = utils.book_new();
utils.book_append_sheet(wb, ws, "Data");
writeFileXLSX(wb, "SheetJSKaiokenAoO.xlsx");
}, [pres]);
return (<table><thead><tr><th>Name</th><th>Index</th></tr></thead><tbody>
{ /* generate row for each president */
pres && pres.map(pres => (<tr>
<td>{pres.Name}</td>
<td>{pres.Index}</td>
</tr>))
}
{ /* loading message */
!pres && loading && ( <tr><td colSpan="2">Loading ...</td></tr> )
}
{ /* error message */
!pres && !loading && ( <tr><td colSpan="2">{error.message}</td></tr> )
}
</tbody><tfoot><td colSpan={2}>
<button onclick={exportFile}>Export XLSX</button>
</td></tfoot></table>);
}
```
:::note pass
Typically the JSX structure uses ternary expressions for testing status:
```jsx
const [ pres, loading, error ] = useAsync(async() => { /* ... */ });
return ( <>
{ pres ? (
<b>Data is loaded</b>
) : loading ? (
<b>Loading ...</b>
) : (
<b>{error.message}</b>
)
}
</> );
```
For clarity, the loading and error messages are separated.
:::
</TabItem>
<TabItem name="effect" value="useEffect + useState">
```tsx title="src/SheetJSKaiokenAoO.tsx"
import { useCallback, useEffect, useState } from "kaioken";
@ -286,7 +407,7 @@ export default function SheetJSKaiokenAoO() {
/* Fetch and update the state once */
useEffect(() => { (async() => {
const f = await (await fetch("https://sheetjs.com/pres.xlsx")).arrayBuffer();
const f = await (await fetch("https://docs.sheetjs.com/pres.xlsx")).arrayBuffer();
const wb = read(f); // parse the array buffer
const ws = wb.Sheets[wb.SheetNames[0]]; // get the first worksheet
const data = utils.sheet_to_json<President>(ws); // generate objects
@ -314,7 +435,11 @@ export default function SheetJSKaiokenAoO() {
}
```
<details open><summary><b>How to run the example</b> (click to hide)</summary>
</TabItem>
</Tabs>
<details open>
<summary><b>How to run the example</b> (click to hide)</summary>
<Tabs groupId="starter">
<TabItem name="vite" value="ViteJS">
@ -323,9 +448,9 @@ export default function SheetJSKaiokenAoO() {
This demo was tested in the following environments:
| Kaioken | ViteJS | Date |
|:---------|:--------|:-----------|
| `0.11.2` | `5.2.6` | 2024-03-24 |
| Kaioken | ViteJS | Date |
|:---------|:---------|:-----------|
| `0.17.0` | `5.2.11` | 2024-05-21 |
:::
@ -334,8 +459,8 @@ This demo was tested in the following environments:
```bash
npm create vite@latest sheetjs-kaioken -- --template vanilla-ts
cd sheetjs-kaioken
pnpm add --save kaioken
pnpm add --save vite-plugin-kaioken -D
npm add --save kaioken
npm add --save vite-plugin-kaioken -D
```
2) Create a new file `vite.config.ts` with the following content:
@ -345,14 +470,6 @@ import { defineConfig } from "vite"
import kaioken from "vite-plugin-kaioken"
export default defineConfig({
esbuild: {
jsxInject: `import * as kaioken from "kaioken"`,
jsx: "transform",
jsxFactory: "kaioken.createElement",
jsxFragment: "kaioken.fragment",
loader: "tsx",
include: ["**/*.tsx", "**/*.ts", "**/*.jsx", "**/*.js"],
},
plugins: [kaioken()],
})
```
@ -381,8 +498,8 @@ mount(App, root!);
6) Install the SheetJS dependency and start the dev server:
<CodeBlock language="bash">{`\
pnpm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz
pnpm run dev`}
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz
npm run dev`}
</CodeBlock>
7) Open a web browser and access the displayed URL (`http://localhost:5173`)
@ -393,7 +510,7 @@ and the page will attempt to download `SheetJSKaiokenAoO.xlsx`.
8) Build the site:
```bash
pnpm run build
npm run build
```
The generated site will be placed in the `dist` folder.
@ -410,7 +527,7 @@ and test the page.
</TabItem>
</Tabs>
When the page loads, the app will fetch <https://sheetjs.com/pres.xlsx> and
When the page loads, the app will fetch https://docs.sheetjs.com/pres.xlsx and
display the data from the first worksheet in a TABLE. The "Export XLSX" button
will generate a workbook that can be opened in a spreadsheet editor.
@ -441,7 +558,7 @@ export default function SheetJSKaiokenHTML() {
/* Fetch and update the state once */
useEffect(() => { (async() => {
const f = await (await fetch("https://sheetjs.com/pres.xlsx")).arrayBuffer();
const f = await (await fetch("https://docs.sheetjs.com/pres.xlsx")).arrayBuffer();
const wb = read(f); // parse the array buffer
const ws = wb.Sheets[wb.SheetNames[0]]; // get the first worksheet
// highlight-start
@ -468,7 +585,8 @@ export default function SheetJSKaiokenHTML() {
}
```
<details open><summary><b>How to run the example</b> (click to hide)</summary>
<details open>
<summary><b>How to run the example</b> (click to hide)</summary>
<Tabs groupId="starter">
<TabItem name="vite" value="ViteJS">
@ -477,9 +595,9 @@ export default function SheetJSKaiokenHTML() {
This demo was tested in the following environments:
| Kaioken | ViteJS | Date |
|:---------|:--------|:-----------|
| `0.11.2` | `5.2.6` | 2024-03-24 |
| Kaioken | ViteJS | Date |
|:---------|:---------|:-----------|
| `0.17.0` | `5.2.11` | 2024-05-21 |
:::
@ -488,8 +606,8 @@ This demo was tested in the following environments:
```bash
npm create vite@latest sheetjs-kaioken -- --template vanilla-ts
cd sheetjs-kaioken
pnpm add --save kaioken
pnpm add --save vite-plugin-kaioken -D
npm add --save kaioken
npm add --save vite-plugin-kaioken -D
```
2) Create a new file `vite.config.ts` with the following content:
@ -499,14 +617,6 @@ import { defineConfig } from "vite"
import kaioken from "vite-plugin-kaioken"
export default defineConfig({
esbuild: {
jsxInject: `import * as kaioken from "kaioken"`,
jsx: "transform",
jsxFactory: "kaioken.createElement",
jsxFragment: "kaioken.fragment",
loader: "tsx",
include: ["**/*.tsx", "**/*.ts", "**/*.jsx", "**/*.js"],
},
plugins: [kaioken()],
})
```
@ -535,8 +645,8 @@ mount(App, root!);
6) Install the SheetJS dependency and start the dev server:
<CodeBlock language="bash">{`\
pnpm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz
pnpm run dev`}
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz
npm run dev`}
</CodeBlock>
7) Open a web browser and access the displayed URL (`http://localhost:5173`)
@ -547,7 +657,7 @@ and the page will attempt to download `SheetJSKaiokenHTML.xlsx`.
8) Build the site:
```bash
pnpm run build
npm run build
```
The generated site will be placed in the `dist` folder.
@ -564,13 +674,14 @@ and test the page.
</TabItem>
</Tabs>
When the page loads, the app will fetch <https://sheetjs.com/pres.xlsx> and
When the page loads, the app will fetch https://docs.sheetjs.com/pres.xlsx and
display the data from the first worksheet in a TABLE. The "Export XLSX" button
will generate a workbook that can be opened in a spreadsheet editor.
</details>
[^1]: See [`useState`](https://kaioken.dev/docs/hooks/usestate) in the Kaioken documentation.
[^2]: See [`useEffect`](https://kaioken.dev/docs/hooks/useeffect) in the Kaioken documentation.
[^3]: See [`useCallback`](https://kaioken.dev/docs/hooks/usecallback) in the Kaioken documentation.
[^4]: See [`useCallback`](https://kaioken.dev/docs/hooks/usecallback) in the Kaioken documentation.
[^1]: See [`useState`](https://kaioken.dev/docs/hooks/useState) in the Kaioken documentation.
[^2]: See [`useAsync`](https://kaioken.dev/docs/hooks/useAsync) in the Kaioken documentation.
[^3]: See [`useEffect`](https://kaioken.dev/docs/hooks/useEffect) in the Kaioken documentation.
[^4]: See [`useCallback`](https://kaioken.dev/docs/hooks/useCallback) in the Kaioken documentation.
[^5]: See [`useCallback`](https://kaioken.dev/docs/hooks/useCallback) in the Kaioken documentation.

@ -57,11 +57,13 @@ loaded into the site. This sheet will have known columns.
#### State
The example [presidents sheet](https://sheetjs.com/pres.xlsx) has one header row
with "Name" and "Index" columns. The natural JS representation is an object for
each row, using the values in the first rows as keys:
The example [presidents sheet](https://docs.sheetjs.com/pres.xlsx) has one
header row with "Name" and "Index" columns. The natural JS representation is an
object for each row, using the values in the first rows as keys:
<table><thead><tr><th>Spreadsheet</th><th>State</th></tr></thead><tbody><tr><td>
<table>
<thead><tr><th>Spreadsheet</th><th>State</th></tr></thead>
<tbody><tr><td>
![`pres.xlsx` data](pathname:///pres.png)
@ -159,8 +161,8 @@ import { read, utils } from 'xlsx';
/* Fetch and update the state once */
useEffect(() => { (async() => {
/* Download from https://sheetjs.com/pres.numbers */
const f = await fetch("https://sheetjs.com/pres.numbers");
/* 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
@ -186,8 +188,8 @@ import { read, utils } from 'xlsx';
/* Fetch and update the state once */
useEffect(() => { (async() => {
/* Download from https://sheetjs.com/pres.numbers */
const f = await fetch("https://sheetjs.com/pres.numbers");
/* 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
@ -284,7 +286,7 @@ export default function SheetJSReactAoO() {
/* Fetch and update the state once */
useEffect(() => { (async() => {
const f = await (await fetch("https://sheetjs.com/pres.xlsx")).arrayBuffer();
const f = await (await fetch("https://docs.sheetjs.com/pres.xlsx")).arrayBuffer();
// highlight-start
const wb = read(f); // parse the array buffer
const ws = wb.Sheets[wb.SheetNames[0]]; // get the first worksheet
@ -317,7 +319,8 @@ export default function SheetJSReactAoO() {
}
```
<details open><summary><b>How to run the example</b> (click to hide)</summary>
<details open>
<summary><b>How to run the example</b> (click to hide)</summary>
<Tabs groupId="starter">
<TabItem name="vite" value="ViteJS">
@ -452,7 +455,7 @@ NextJS requires a number of workarounds for simple client-side JavaScript code.
:::
:::warning Telemetry
:::danger Telemetry
NextJS collects telemetry by default. The `telemetry` subcommand can disable it:
@ -541,7 +544,7 @@ and test the page.
</TabItem>
</Tabs>
When the page loads, the app will fetch <https://sheetjs.com/pres.xlsx> and
When the page loads, the app will fetch https://docs.sheetjs.com/pres.xlsx and
display the data from the first worksheet in a TABLE. The "Export XLSX" button
will generate a workbook that can be opened in a spreadsheet editor.
@ -574,7 +577,7 @@ export default function SheetJSReactHTML() {
/* Fetch and update the state once */
useEffect(() => { (async() => {
const f = await (await fetch("https://sheetjs.com/pres.xlsx")).arrayBuffer();
const f = await (await fetch("https://docs.sheetjs.com/pres.xlsx")).arrayBuffer();
const wb = read(f); // parse the array buffer
const ws = wb.Sheets[wb.SheetNames[0]]; // get the first worksheet
// highlight-start
@ -600,7 +603,8 @@ export default function SheetJSReactHTML() {
}
```
<details open><summary><b>How to run the example</b> (click to hide)</summary>
<details open>
<summary><b>How to run the example</b> (click to hide)</summary>
<Tabs groupId="starter">
<TabItem name="vite" value="ViteJS">
@ -709,7 +713,7 @@ and test the page.
</TabItem>
</Tabs>
When the page loads, the app will fetch <https://sheetjs.com/pres.xlsx> and
When the page loads, the app will fetch https://docs.sheetjs.com/pres.xlsx and
display the data from the first worksheet in a TABLE. The "Export XLSX" button
will generate a workbook that can be opened in a spreadsheet editor.

@ -38,7 +38,7 @@ should be directed to the Angular project.
:::
:::warning Telemetry
:::danger Telemetry
Angular CLI enables telemetry by default. When using a recent version, disable
analytics globally through the CLI tool before creating a new project:
@ -69,7 +69,7 @@ import { read, utils, writeFile } from 'xlsx';
The various SheetJS APIs work with various data shapes. The preferred state
depends on the application.
:::warning pass
:::danger pass
Angular 17 broke backwards compatibility with projects using Angular 2 - 16.
@ -87,11 +87,13 @@ loaded into the site. This sheet will have known columns.
#### State
The example [presidents sheet](https://sheetjs.com/pres.xlsx) has one header row
with "Name" and "Index" columns. The natural JS representation is an object for
each row, using the values in the first rows as keys:
The example [presidents sheet](https://docs.sheetjs.com/pres.xlsx) has one
header row with "Name" and "Index" columns. The natural JS representation is an
object for each row, using the values in the first rows as keys:
<table><thead><tr><th>Spreadsheet</th><th>State</th></tr></thead><tbody><tr><td>
<table>
<thead><tr><th>Spreadsheet</th><th>State</th></tr></thead>
<tbody><tr><td>
![`pres.xlsx` data](pathname:///pres.png)
@ -182,8 +184,8 @@ interface President { Name: string; Index: number };
export class AppComponent {
rows: President[] = [ { Name: "SheetJS", Index: 0 }];
ngOnInit(): void { (async() => {
/* Download from https://sheetjs.com/pres.numbers */
const f = await fetch("https://sheetjs.com/pres.numbers");
/* 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
@ -329,8 +331,8 @@ export class AppComponent {
// highlight-next-line
rows: President[] = [ { Name: "SheetJS", Index: 0 }];
ngOnInit(): void { (async() => {
/* Download from https://sheetjs.com/pres.numbers */
const f = await fetch("https://sheetjs.com/pres.numbers");
/* Download from https://docs.sheetjs.com/pres.numbers */
const f = await fetch("https://docs.sheetjs.com/pres.numbers");
const ab = await f.arrayBuffer();
/* parse workbook */
@ -387,8 +389,8 @@ export class AppComponent {
// highlight-next-line
rows: President[] = [ { Name: "SheetJS", Index: 0 }];
ngOnInit(): void { (async() => {
/* Download from https://sheetjs.com/pres.numbers */
const f = await fetch("https://sheetjs.com/pres.numbers");
/* Download from https://docs.sheetjs.com/pres.numbers */
const f = await fetch("https://docs.sheetjs.com/pres.numbers");
const ab = await f.arrayBuffer();
/* parse workbook */
@ -414,7 +416,8 @@ export class AppComponent {
</TabItem>
</Tabs>
<details open><summary><b>How to run the example</b> (click to hide)</summary>
<details open>
<summary><b>How to run the example</b> (click to hide)</summary>
:::note Tested Deployments
@ -533,8 +536,8 @@ export class AppComponent {
@ViewChild('tableau') tabeller!: ElementRef<HTMLDivElement>;
// highlight-end
ngOnInit(): void { (async() => {
/* Download from https://sheetjs.com/pres.numbers */
const f = await fetch("https://sheetjs.com/pres.numbers");
/* Download from https://docs.sheetjs.com/pres.numbers */
const f = await fetch("https://docs.sheetjs.com/pres.numbers");
const ab = await f.arrayBuffer();
/* parse workbook */
@ -580,8 +583,8 @@ export class AppComponent {
@ViewChild('tableau') tabeller!: ElementRef<HTMLDivElement>;
// highlight-end
ngOnInit(): void { (async() => {
/* Download from https://sheetjs.com/pres.numbers */
const f = await fetch("https://sheetjs.com/pres.numbers");
/* Download from https://docs.sheetjs.com/pres.numbers */
const f = await fetch("https://docs.sheetjs.com/pres.numbers");
const ab = await f.arrayBuffer();
/* parse workbook */
@ -607,7 +610,8 @@ export class AppComponent {
</TabItem>
</Tabs>
<details open><summary><b>How to run the example</b> (click to hide)</summary>
<details open>
<summary><b>How to run the example</b> (click to hide)</summary>
:::note Tested Deployments
@ -720,7 +724,7 @@ this.columns = Array.from({ length: range.e.c + 1 }, (_, i) => ({
## Older Versions
:::warning pass
:::danger pass
This demo is included for legacy deployments. There are incompatibilities with
different NodeJS and other ecosystem versions. Issues should be raised with
@ -1034,7 +1038,7 @@ will have an `ng-version` attribute.
npm run build
```
[^1]: The main website for Angular versions 2-16 is <https://angular.io/> . The project moved to a new domain <https://angular.dev/> during the Angular 17 launch.
[^1]: The main website for Angular versions 2-16 is https://angular.io/ . The project moved to a new domain https://angular.dev/ during the Angular 17 launch.
[^2]: See `OnInit` in the [Angular 2-16 docs](https://angular.io/api/core/OnInit) or [Angular 17 docs](https://angular.dev/guide/components/lifecycle#ngoninit)
[^3]: See [`ngFor`](https://angular.io/api/common/NgFor) in the Angular 2-16 docs.
[^4]: See [`@for`](https://angular.dev/api/core/@for) in the Angular 17 docs.

@ -56,11 +56,13 @@ loaded into the site. This sheet will have known columns.
#### State
The example [presidents sheet](https://sheetjs.com/pres.xlsx) has one header row
with "Name" and "Index" columns. The natural JS representation is an object for
each row, using the values in the first rows as keys:
The example [presidents sheet](https://docs.sheetjs.com/pres.xlsx) has one
header row with "Name" and "Index" columns. The natural JS representation is an
object for each row, using the values in the first rows as keys:
<table><thead><tr><th>Spreadsheet</th><th>State</th></tr></thead><tbody><tr><td>
<table>
<thead><tr><th>Spreadsheet</th><th>State</th></tr></thead>
<tbody><tr><td>
![`pres.xlsx` data](pathname:///pres.png)
@ -168,8 +170,8 @@ const pres = ref([]);
/* Fetch and update the state once */
onMounted(async() => {
/* Download from https://sheetjs.com/pres.numbers */
const f = await fetch("https://sheetjs.com/pres.numbers");
/* 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
@ -205,8 +207,8 @@ const pres = ref<President[]>([]);
/* Fetch and update the state once */
onMounted(async() => {
/* Download from https://sheetjs.com/pres.numbers */
const f = await fetch("https://sheetjs.com/pres.numbers");
/* 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
@ -313,8 +315,8 @@ import { read, utils, writeFileXLSX } from 'xlsx';
const rows = ref([]);
onMounted(async() => {
/* Download from https://sheetjs.com/pres.numbers */
const f = await fetch("https://sheetjs.com/pres.numbers");
/* Download from https://docs.sheetjs.com/pres.numbers */
const f = await fetch("https://docs.sheetjs.com/pres.numbers");
const ab = await f.arrayBuffer();
/* parse workbook */
@ -345,7 +347,8 @@ function exportFile() {
</template>
```
<details open><summary><b>How to run the example</b> (click to hide)</summary>
<details open>
<summary><b>How to run the example</b> (click to hide)</summary>
<Tabs groupId="starter">
<TabItem name="vite" value="ViteJS">
@ -354,9 +357,9 @@ function exportFile() {
This demo was tested in the following environments:
| VueJS | ViteJS | Date |
|:--------|:--------|:-----------|
| `3.3.9` | `4.5.1` | 2023-12-04 |
| VueJS | ViteJS | Date |
|:---------|:---------|:-----------|
| `3.4.27` | `5.2.11` | 2024-05-26 |
:::
@ -481,8 +484,8 @@ const html = ref("");
const tableau = ref();
onMounted(async() => {
/* Download from https://sheetjs.com/pres.numbers */
const f = await fetch("https://sheetjs.com/pres.numbers");
/* Download from https://docs.sheetjs.com/pres.numbers */
const f = await fetch("https://docs.sheetjs.com/pres.numbers");
const ab = await f.arrayBuffer();
/* parse workbook */
@ -505,7 +508,8 @@ function exportFile() {
</template>
```
<details open><summary><b>How to run the example</b> (click to hide)</summary>
<details open>
<summary><b>How to run the example</b> (click to hide)</summary>
<Tabs groupId="starter">
<TabItem name="vite" value="ViteJS">

@ -25,7 +25,7 @@ models and data flow strategies.
This demo focuses on Svelte concepts. Other demos cover general deployments:
- [Static Site Generation powered by SvelteKit](/docs/demos/static/svelte)
- [iOS applications powered by CapacitorJS](/docs/demos/mobile/capacitor)
- [iOS and Android applications powered by CapacitorJS](/docs/demos/mobile/capacitor)
- [Desktop application powered by Wails](/docs/demos/desktop/wails)
:::
@ -50,11 +50,29 @@ depends on the application.
### Array of Objects
Typically, some users will create a spreadsheet with source data that should be
loaded into the site. This sheet will have known columns. For example, our
[presidents sheet](https://sheetjs.com/pres.xlsx) has "Name" / "Index" columns:
loaded into the site. This sheet will have known columns. For example, "Name"
and "Index" are used in [`pres.xlsx`](https://docs.sheetjs.com/pres.xlsx):
<table>
<thead><tr><th>Spreadsheet</th><th>State</th></tr></thead>
<tbody><tr><td>
![`pres.xlsx` data](pathname:///pres.png)
</td><td>
```js
[
{ Name: "Bill Clinton", Index: 42 },
{ Name: "GeorgeW Bush", Index: 43 },
{ Name: "Barack Obama", Index: 44 },
{ Name: "Donald Trump", Index: 45 },
{ Name: "Joseph Biden", Index: 46 }
]
```
</td></tr></tbody></table>
This naturally maps to an array of typed objects, as in the TS example below:
```ts
@ -65,24 +83,12 @@ interface President {
Index: number;
}
const f = await (await fetch("https://sheetjs.com/pres.xlsx")).arrayBuffer();
const f = await (await fetch("https://docs.sheetjs.com/pres.xlsx")).arrayBuffer();
const wb = read(f);
const data = utils.sheet_to_json<President>(wb.Sheets[wb.SheetNames[0]]);
console.log(data);
```
`data` will be an array of objects:
```js
[
{ Name: "Bill Clinton", Index: 42 },
{ Name: "GeorgeW Bush", Index: 43 },
{ Name: "Barack Obama", Index: 44 },
{ Name: "Donald Trump", Index: 45 },
{ Name: "Joseph Biden", Index: 46 }
]
```
A component will typically map over the data. The following example generates
a TABLE with a row for each President:
@ -96,7 +102,7 @@ let pres = [];
/* Fetch and update the state once */
onMount(async() => {
const f = await (await fetch("https://sheetjs.com/pres.xlsx")).arrayBuffer();
const f = await (await fetch("https://docs.sheetjs.com/pres.xlsx")).arrayBuffer();
const wb = read(f); // parse the array buffer
const ws = wb.Sheets[wb.SheetNames[0]]; // get the first worksheet
// highlight-start
@ -128,15 +134,16 @@ function exportFile() {
</main>
```
<details open><summary><b>How to run the example</b> (click to hide)</summary>
<details open>
<summary><b>How to run the example</b> (click to hide)</summary>
:::note Tested Deployments
This demo was tested in the following environments:
| Svelte | ViteJS | Date |
|:--------|:--------|:-----------|
| `4.2.8` | `5.0.5` | 2023-12-04 |
| SvelteJS | ViteJS | Date |
|:---------|:---------|:-----------|
| `4.2.18` | `5.2.13` | 2024-06-07 |
:::
@ -201,7 +208,7 @@ let tbl;
/* Fetch and update the state once */
onMount(async() => {
const f = await (await fetch("https://sheetjs.com/pres.xlsx")).arrayBuffer();
const f = await (await fetch("https://docs.sheetjs.com/pres.xlsx")).arrayBuffer();
const wb = read(f); // parse the array buffer
const ws = wb.Sheets[wb.SheetNames[0]]; // get the first worksheet
// highlight-start
@ -227,15 +234,16 @@ function exportFile() {
</main>
```
<details open><summary><b>How to run the example</b> (click to hide)</summary>
<details open>
<summary><b>How to run the example</b> (click to hide)</summary>
:::note Tested Deployments
This demo was tested in the following environments:
| Svelte | ViteJS | Date |
|:--------|:--------|:-----------|
| `4.2.8` | `5.0.5` | 2023-12-04 |
| SvelteJS | ViteJS | Date |
|:---------|:---------|:-----------|
| `4.2.18` | `5.2.13` | 2024-06-07 |
:::

@ -10,7 +10,7 @@ sidebar_position: 7
import current from '/version.js';
import CodeBlock from '@theme/CodeBlock';
:::warning pass
:::danger pass
This demo is for the legacy AngularJS framework (version 1).
@ -34,10 +34,10 @@ models and data flow strategies.
This demo was tested in the following environments:
| Version | Date |
|:------------------|:-----------|
| `1.8.2` (latest) | 2023-12-04 |
| `1.2.32` (legacy) | 2023-12-04 |
| Browser | Version | Date |
|:-------------|:------------------|:-----------|
| Chromiun 125 | `1.8.2` (latest) | 2024-06-09 |
| Chromium 125 | `1.2.32` (legacy) | 2024-06-09 |
:::
@ -73,17 +73,17 @@ property to `"arraybuffer"` ensures the result is an `ArrayBuffer` object.
The SheetJS [`read`](/docs/api/parse-options) method can parse the `ArrayBuffer`
and return a SheetJS workbook object[^2].
The following controller fetches [a sample file](https://sheetjs.com/pres.xlsx),
This controller fetches [a sample file](https://docs.sheetjs.com/pres.xlsx),
parses the result into a workbook, extracts the first worksheet, and uses the
SheetJS [`sheet_to_html`](/docs/api/utilities/html#html-table-output) method to
generate a HTML table:
```js
```js title="Sample Controller"
/* The controller function must accept a `$http` argument */
app.controller('sheetjs', function($scope, $http) {
/* fetch https://sheetjs.com/pres.xlsx */
/* fetch https://docs.sheetjs.com/pres.xlsx */
$http({
method:'GET', url:'https://sheetjs.com/pres.xlsx',
method:'GET', url:'https://docs.sheetjs.com/pres.xlsx',
/* ensure the result is an ArrayBuffer object */
responseType:'arraybuffer'
}).then(function(data) {
@ -167,11 +167,13 @@ depends on the application.
### Array of Objects
The example [presidents sheet](https://sheetjs.com/pres.xlsx) has one header row
with "Name" and "Index" columns. The natural JS representation is an object for
each row, using the values in the first rows as keys:
The example [presidents sheet](https://docs.sheetjs.com/pres.xlsx) has one
header row with "Name" and "Index" columns. The natural JS representation is an
object for each row, using the values in the first rows as keys:
<table><thead><tr><th>Spreadsheet</th><th>State</th></tr></thead><tbody><tr><td>
<table>
<thead><tr><th>Spreadsheet</th><th>State</th></tr></thead>
<tbody><tr><td>
![`pres.xlsx` data](pathname:///pres.png)
@ -196,7 +198,7 @@ file, generates row objects, and stores the array in the state:
```js
app.controller('sheetjs', function($scope, $http) {
$http({
url:'https://sheetjs.com/pres.xlsx',
url:'https://docs.sheetjs.com/pres.xlsx',
method:'GET', responseType:'arraybuffer'
}).then(function(data) {
var wb = XLSX.read(data.data);
@ -235,7 +237,8 @@ $scope.exportSheetJS = function() {
};
```
<details><summary><b>Complete Example</b> (click to show)</summary>
<details>
<summary open><b>How to run the example</b> (click to hide)</summary>
1) Save the following to `index.html`:
@ -272,7 +275,7 @@ app.controller('sheetjs', function($scope, $http) {
XLSX.writeFile(wb, "SheetJSAngularJSAoO.xlsx");
};
$http({
url:'https://sheetjs.com/pres.xlsx',
url:'https://docs.sheetjs.com/pres.xlsx',
method:'GET', responseType:'arraybuffer'
}).then(function(data) {
var wb = XLSX.read(data.data);
@ -288,6 +291,10 @@ app.controller('sheetjs', function($scope, $http) {
2) Start a local web server with `npx http-server .` and access the displayed
URL with a web browser (typically `http://localhost:8080`)
When the page loads, the app will fetch https://docs.sheetjs.com/pres.xlsx and
store an array of objects in state. When the "Export Table" button is clicked,
a worksheet is created and exported to XLSX.
</details>
### HTML
@ -312,7 +319,7 @@ requires the `ngSanitize` plugin[^4].
var app = angular.module('s5s', ['ngSanitize']);
app.controller('sheetjs', function($scope, $http) {
$http({
url:'https://sheetjs.com/pres.xlsx',
url:'https://docs.sheetjs.com/pres.xlsx',
method:'GET', responseType:'arraybuffer'
}).then(function(data) {
var wb = XLSX.read(data.data);
@ -336,7 +343,8 @@ The HTML table can be directly exported with [`table_to_book`](/docs/api/utiliti
};
```
<details><summary><b>Complete Example</b> (click to show)</summary>
<details>
<summary open><b>How to run the example</b> (click to hide)</summary>
1) Save the following to `index.html`:
@ -367,7 +375,7 @@ app.controller('sheetjs', function($scope, $http) {
XLSX.writeFile(wb, "SheetJSAngularJSHTML.xlsx");
};
$http({
url:'https://sheetjs.com/pres.xlsx',
url:'https://docs.sheetjs.com/pres.xlsx',
method:'GET', responseType:'arraybuffer'
}).then(function(data) {
var wb = XLSX.read(data.data);
@ -382,6 +390,10 @@ app.controller('sheetjs', function($scope, $http) {
2) Start a local web server with `npx http-server .` and access the displayed
URL with a web browser (typically `http://localhost:8080`)
When the page loads, the app will fetch https://docs.sheetjs.com/pres.xlsx and
store the HTML string in state. When the "Export Table" button is clicked, a
worksheet is created and exported to XLSX.
</details>
[^1]: See [`$http`](https://docs.angularjs.org/api/ng/service/$http) in the AngularJS documentation.

@ -57,13 +57,13 @@ require(
</script>`}
</CodeBlock>
:::warning pass
:::danger pass
The official Google CDN does not have the newest releases of Dojo Toolkit
**This is a known Google CDN bug.**
The script <https://docs.sheetjs.com/dojo/dojo.js> was fetched from the official
The script https://docs.sheetjs.com/dojo/dojo.js was fetched from the official
`1.17.3` uncompressed release artifact[^1].
:::
@ -72,7 +72,11 @@ The script <https://docs.sheetjs.com/dojo/dojo.js> was fetched from the official
:::note Tested Deployments
The demos were last tested on 2023-12-04.
This demo was tested in the following environments:
| Platform | Date |
|:-------------|:-----------|
| Chromium 125 | 2024-06-08 |
Demos exclusively using Dojo Core were tested using Dojo Toolkit `1.17.3`.
@ -99,7 +103,7 @@ The following example generates a HTML table from the first worksheet:
<div id="tbl"></div>
<script>
require(["dojo/request/xhr", "xlsx"], function(xhr, _XLSX) {
xhr("https://sheetjs.com/pres.numbers", {
xhr("https://docs.sheetjs.com/pres.numbers", {
headers: { "X-Requested-With": null },
// highlight-next-line
handleAs: "arraybuffer"
@ -155,7 +159,7 @@ require([
], function(ready, xhr, Memory, registry, _XLSX) {
ready(function() {
/* fetch test file */
xhr("https://sheetjs.com/pres.xlsx", {
xhr("https://docs.sheetjs.com/pres.xlsx", {
headers: { "X-Requested-With": null },
handleAs: "arraybuffer"
}).then(function(ab) {
@ -201,5 +205,5 @@ function export_all_data_from_store(store) {
}
```
[^1]: All Dojo Toolkit releases are available at <https://download.dojotoolkit.org/>. The mirrored `dojo.js` corresponds to the `1.17.3` uncompressed script <http://download.dojotoolkit.org/release-1.17.3/dojo.js.uncompressed.js>.
[^1]: All Dojo Toolkit releases are available at https://download.dojotoolkit.org/. The mirrored `dojo.js` corresponds to the `1.17.3` uncompressed script http://download.dojotoolkit.org/release-1.17.3/dojo.js.uncompressed.js.
[^2]: See [`dojo/store`](https://dojotoolkit.org/reference-guide/dojo/store.html) in the Dojo Toolkit documentation.

@ -38,7 +38,7 @@ else document.write(XLSX.version);
## Internet Explorer
:::warning pass
:::danger pass
Internet Explorer is unmaintained and users should consider modern browsers.
The SheetJS testing grid still includes IE and should work.
@ -48,7 +48,8 @@ The SheetJS testing grid still includes IE and should work.
The modern upload and download strategies are not available in older versions of
IE, but there are approaches using ActiveX or Flash.
<details><summary><b>Complete Example</b> (click to show)</summary>
<details>
<summary><b>Complete Example</b> (click to show)</summary>
This demo includes all of the support files for the Flash and ActiveX methods.
@ -76,7 +77,8 @@ npx -y http-server .
</details>
<details><summary><b>Other Live Demos</b> (click to show)</summary>
<details>
<summary><b>Other Live Demos</b> (click to show)</summary>
:::caution pass
@ -85,10 +87,10 @@ demo pages should be downloaded and hosted using a simple HTTP server.
:::
<https://oss.sheetjs.com/sheetjs/ajax.html> uses XMLHttpRequest to download test
https://oss.sheetjs.com/sheetjs/ajax.html uses XMLHttpRequest to download test
files and convert to CSV
<https://oss.sheetjs.com/sheetjs/> demonstrates reading files with `FileReader`.
https://oss.sheetjs.com/sheetjs/ demonstrates reading files with `FileReader`.
Older versions of IE do not support HTML5 File API but do support Base64.
@ -198,18 +200,24 @@ included in the page and the relevant features are enabled on the target system.
### KnockoutJS
KnockoutJS was a popular MVVM framework.
[KnockoutJS](https://knockoutjs.com/) was a popular MVVM framework.
The [Live demo](pathname:///knockout/knockout.html) shows a view model that is
The [Live demo](pathname:///knockout/knockout3.html) shows a view model that is
updated with file data and exported to spreadsheets.
:::note Tested Deployments
This demo was last run on 2023 December 04 using KnockoutJS `3.4.2`
This demo was tested in the following environments:
| KnockoutJS | Date | Live Demo |
|:-----------|:-----------|:-----------------------------------------------|
| `3.5.0` | 2024-04-07 | [**KO3**](pathname:///knockout/knockout3.html) |
| `2.3.0` | 2024-04-07 | [**KO2**](pathname:///knockout/knockout2.html) |
:::
<details><summary><b>Full Exposition</b> (click to show)</summary>
<details>
<summary><b>Full Exposition</b> (click to show)</summary>
**State**

@ -41,9 +41,9 @@ This demo was tested in the following environments:
| ViteJS | Date |
|:---------|:-----------|
| `5.0.5` | 2023-12-04 |
| `4.5.0` | 2023-12-04 |
| `3.2.7` | 2023-12-05 |
| `5.2.10` | 2024-04-27 |
| `4.5.3` | 2024-04-27 |
| `3.2.10` | 2024-04-27 |
:::
@ -98,7 +98,7 @@ interface President {
async function xport() {
/* fetch JSON data and parse */
const url = "https://sheetjs.com/data/executive.json";
const url = "https://docs.sheetjs.com/executive.json";
const raw_data: President[] = await (await fetch(url)).json();
/* filter for the Presidents */

@ -47,8 +47,19 @@ This demo was tested in the following environments:
| ESBuild | Date |
|:----------|:-----------|
| `0.14.14` | 2023-12-04 |
| `0.19.8` | 2023-12-04 |
| `0.21.4` | 2024-06-07 |
| `0.20.2` | 2024-06-07 |
| `0.19.12` | 2024-06-07 |
| `0.18.20` | 2024-06-07 |
| `0.17.19` | 2024-06-07 |
| `0.16.17` | 2024-06-07 |
| `0.15.18` | 2024-06-07 |
| `0.14.54` | 2024-06-07 |
| `0.13.15` | 2024-06-07 |
| `0.12.29` | 2024-06-07 |
| `0.11.23` | 2024-06-07 |
| `0.10.2` | 2024-06-07 |
| `0.9.7` | 2024-06-07 |
:::

@ -167,7 +167,7 @@ import { utils, version, writeFileXLSX } from 'xlsx';
document.getElementById("xport").addEventListener("click", function() {
/* fetch JSON data and parse */
var url = "https://sheetjs.com/data/executive.json";
var url = "https://docs.sheetjs.com/executive.json";
fetch(url).then(function(res) { return res.json(); }).then(function(raw_data) {
/* filter for the Presidents */
@ -260,7 +260,7 @@ npx webpack@3.x -p
</TabItem>
<TabItem value="4+" label="4.x, 5.x and beyond" default>
:::warning Pinning specific versions of webpack
:::danger Pinning specific versions of webpack
The webpack tooling is not designed for switching between versions. A specific
version above 4.0 can be pinned by locally installing webpack and the CLI tool.

@ -34,8 +34,21 @@ This demo was tested in the following environments:
| Browserify | Date |
|:-----------|:-----------|
| `17.0.0` | 2023-12-04 |
| `3.46.1` | 2023-12-04 |
| `17.0.0` | 2024-04-13 |
| `16.5.2` | 2024-04-13 |
| `15.2.0` | 2024-04-13 |
| `14.5.0` | 2024-04-13 |
| `13.3.0` | 2024-04-13 |
| `12.0.2` | 2024-04-13 |
| `11.2.0` | 2024-04-13 |
| `10.2.6` | 2024-04-13 |
| `9.0.8` | 2024-04-13 |
| `8.1.3` | 2024-04-13 |
| `7.1.0` | 2024-04-13 |
| `6.3.4` | 2024-04-13 |
| `5.13.1` | 2024-04-13 |
| `4.2.3` | 2024-04-13 |
| `3.46.1` | 2024-04-13 |
:::
@ -92,7 +105,7 @@ const { utils, version, writeFileXLSX } = require('xlsx');
document.getElementById("xport").addEventListener("click", function() {
/* fetch JSON data and parse */
var url = "https://sheetjs.com/data/executive.json";
var url = "https://docs.sheetjs.com/executive.json";
fetch(url).then(function(res) { return res.json(); }).then(function(raw_data) {
/* filter for the Presidents */

@ -40,8 +40,8 @@ This demo was tested in the following environments:
| RequireJS | Date |
|:----------|:-----------|
| `2.3.6` | 2024-03-01 |
| `2.1.22` | 2023-12-04 |
| `2.3.6` | 2024-04-27 |
| `2.1.22` | 2024-04-27 |
:::
@ -162,7 +162,7 @@ example, the following script corresponds to RequireJS `2.1.22`:
require(["xlsx"], function(XLSX) {
document.getElementById("xport").addEventListener("click", function() {
/* fetch JSON data and parse */
var url = "https://sheetjs.com/data/executive.json";
var url = "https://docs.sheetjs.com/executive.json";
fetch(url).then(function(res) { return res.json(); }).then(function(raw_data) {
/* filter for the Presidents */

@ -111,7 +111,7 @@ import { utils, version, writeFileXLSX } from 'xlsx';
document.getElementById("xport").addEventListener("click", async() => {
/* fetch JSON data and parse */
const url = "https://sheetjs.com/data/executive.json";
const url = "https://docs.sheetjs.com/executive.json";
const raw_data = await (await fetch(url)).json();
/* filter for the Presidents */

@ -34,8 +34,8 @@ This demo was tested in the following environments:
| Version | Date |
|:---------|:-----------|
| `2.10.3` | 2023-12-04 |
| `1.12.3` | 2023-12-04 |
| `2.12.0` | 2024-06-08 |
| `1.12.4` | 2024-06-08 |
:::
@ -51,7 +51,7 @@ can load relevant parts of the library:
import { read, utils, writeFileXLSX } from 'xlsx';
```
:::warning Parcel Bug
:::danger Parcel Bug
Errors of the form `Could not statically evaluate fs call` stem from a Parcel
bug[^1]. Upgrade to Parcel version 1.5.0 or later.
@ -86,7 +86,7 @@ import { utils, version, writeFileXLSX } from 'xlsx';
document.getElementById("vers").innerText = version;
document.getElementById("xport").onclick = async() => {
/* fetch JSON data and parse */
const url = "https://sheetjs.com/data/executive.json";
const url = "https://docs.sheetjs.com/executive.json";
const raw_data = await (await fetch(url)).json();
/* filter for the Presidents */
@ -128,13 +128,14 @@ document.getElementById("xport").onclick = async() => {
For ParcelJS version 1, the entire script should be copied to `index.js` and the
main `index.html` page should load the `index.js` script:
<details><summary><b>ParcelJS v1 example</b> (click to show)</summary>
<details>
<summary><b>ParcelJS v1 example</b> (click to show)</summary>
```html title="index.html"
<body>
<h3>SheetJS <span id="vers"></span> export demo</h3>
<button id="xport">Click to Export!</button>
<script src="index.js"></script>
<script src="index.js" type="module"></script>
<body>
```
@ -145,7 +146,7 @@ import { utils, version, writeFileXLSX } from 'xlsx';
document.getElementById("vers").innerText = version;
document.getElementById("xport").onclick = async() => {
/* fetch JSON data and parse */
const url = "https://sheetjs.com/data/executive.json";
const url = "https://docs.sheetjs.com/executive.json";
const raw_data = await (await fetch(url)).json();
/* filter for the Presidents */
@ -207,7 +208,7 @@ yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
3) Run the ParcelJS development server:
```bash
npx -y parcel@2.10.3 index.html
npx -y parcel index.html
```
The process will print a URL:
@ -230,7 +231,7 @@ a web browser and click the "Click to Export!" button to generate a file.
6) Build the production site:
```bash
npx -y parcel@2.10.0 build index.html
npx -y parcel build index.html
```
The production site will be stored in the `dist` folder

@ -35,7 +35,7 @@ This demo was tested in the following environments:
| Version | Date |
|:----------|:-----------|
| `1.2.246` | 2023-12-04 |
| `1.2.246` | 2024-04-27 |
:::
@ -63,9 +63,9 @@ part of the `utils` object, the required import is:
import { utils, writeFile } from 'xlsx';
```
:::warning pass
:::danger pass
When this demo was tested against the `@swc/core@1.3.100`, `spack` crashed:
When this demo was tested against recent versions of `@swc/core`, `spack` crashed:
```
thread '<unnamed>' panicked at 'cannot access a scoped thread local variable without calling `set` first',
@ -73,6 +73,8 @@ thread '<unnamed>' panicked at 'cannot access a scoped thread local variable wit
**This is a bug in SWC**
This bug is known to affect versions `1.3.100` and `1.4.17`.
Until the bug is fixed, it is strongly recommended to use `@swc/core@1.2.246`.
:::
@ -121,7 +123,7 @@ import { utils, version, writeFileXLSX } from 'xlsx';
document.getElementById("xport").addEventListener("click", async() => {
/* fetch JSON data and parse */
const url = "https://sheetjs.com/data/executive.json";
const url = "https://docs.sheetjs.com/executive.json";
const raw_data = await (await fetch(url)).json();
/* filter for the Presidents */

@ -43,9 +43,18 @@ Complete Examples are included [in the "Dojo" demo](/docs/demos/frontend/dojo)
## Snowpack
Snowpack was a development tool built by the AstroJS team.
:::caution pass
Snowpack is no longer maintained. The developers recommend [ViteJS](/docs/demos/frontend/bundler/vitejs)
:::
Snowpack works with no caveats.
<details><summary><b>Complete Example</b> (click to show)</summary>
<details>
<summary><b>Complete Example</b> (click to show)</summary>
:::note Tested Deployments
@ -53,7 +62,7 @@ This demo was tested in the following environments:
| Version | Date |
|:--------|:-----------|
| `3.8.8` | 2023-12-04 |
| `3.8.8` | 2024-04-14 |
:::
@ -93,7 +102,7 @@ import { utils, version, writeFileXLSX } from 'xlsx';
document.getElementById("xport").addEventListener("click", async() => {
/* fetch JSON data and parse */
const url = "https://sheetjs.com/data/executive.json";
const url = "https://docs.sheetjs.com/executive.json";
const raw_data = await (await fetch(url)).json();
/* filter for the Presidents */
@ -164,9 +173,18 @@ Click on "Click here to export" to generate a file.
## WMR
WMR was a development tool built by the PreactJS team.
:::caution pass
WMR is no longer maintained. The developers recommend [ViteJS](/docs/demos/frontend/bundler/vitejs)
:::
WMR works with no caveats.
<details><summary><b>Complete Example</b> (click to show)</summary>
<details>
<summary><b>Complete Example</b> (click to show)</summary>
:::note Tested Deployments
@ -174,7 +192,7 @@ This demo was tested in the following environments:
| Version | Date |
|:--------|:-----------|
| `3.8.0` | 2023-12-04 |
| `3.8.0` | 2024-04-14 |
:::
@ -214,7 +232,7 @@ import { utils, version, writeFileXLSX } from 'xlsx';
document.getElementById("xport").addEventListener("click", async() => {
/* fetch JSON data and parse */
const url = "https://sheetjs.com/data/executive.json";
const url = "https://docs.sheetjs.com/executive.json";
const raw_data = await (await fetch(url)).json();
/* filter for the Presidents */

@ -65,7 +65,7 @@ The following demos are in separate pages:
<a href={item.href}>{item.label}</a>{item.customProps?.summary && (" - " + item.customProps.summary)}
</li>);
})}
<li><a href="/docs/demos/frontend/bundler#dojo">Dojo Toolkit</a></li>
<li><a href="/docs/demos/frontend/bundler#snowpack">Snowpack</a></li>
<li><a href="/docs/demos/frontend/bundler#wmr">WMR</a></li>
<li><a href="/docs/demos/frontend/bundler/#dojo">Dojo Toolkit</a></li>
<li><a href="/docs/demos/frontend/bundler/#snowpack">Snowpack</a></li>
<li><a href="/docs/demos/frontend/bundler/#wmr">WMR</a></li>
</ul>

@ -62,7 +62,7 @@ flowchart LR
```js
/* download data into an ArrayBuffer object */
const res = await fetch("https://sheetjs.com/pres.numbers");
const res = await fetch("https://docs.sheetjs.com/pres.numbers");
const ab = await res.arrayBuffer(); // recover data as ArrayBuffer
/* parse file */
@ -71,10 +71,9 @@ const wb = XLSX.read(ab);
## Browser Demos
When the page is accessed, the browser will attempt to download <https://sheetjs.com/pres.numbers>
and read the workbook. The old table will be replaced with a table whose
contents match the first worksheet. The table is generated using the SheetJS
`sheet_to_html` method[^2]
When the page is accessed, https://docs.sheetjs.com/pres.numbers will be fetched
and parsed. The old table will be replaced with a table whose contents match the
first worksheet. The SheetJS `sheet_to_html` method[^2] creates the HTML table.
:::note Tested Deployments
@ -108,9 +107,10 @@ req.onload = function(e) {
req.send();
```
<details><summary><b>Live Download demo</b> (click to show)</summary>
<details>
<summary><b>Live Download demo</b> (click to show)</summary>
This demo uses `XMLHttpRequest` to download <https://sheetjs.com/pres.numbers>
This demo uses `XMLHttpRequest` to fetch https://docs.sheetjs.com/pres.numbers
and show the data in an HTML table.
```jsx live
@ -121,7 +121,7 @@ function SheetJSXHRDL() {
React.useEffect(() => { (async() => {
/* Fetch file */
const req = new XMLHttpRequest();
req.open("GET", "https://sheetjs.com/pres.numbers", true);
req.open("GET", "https://docs.sheetjs.com/pres.numbers", true);
req.responseType = "arraybuffer";
req.onload = e => {
/* Parse file */
@ -160,10 +160,11 @@ fetch(url).then(function(res) {
});
```
<details><summary><b>Live Download demo</b> (click to show)</summary>
<details>
<summary><b>Live Download demo</b> (click to show)</summary>
This demo uses `fetch` to download <https://sheetjs.com/pres.numbers> and show
the data in an HTML table.
This demo uses `fetch` to download https://docs.sheetjs.com/pres.numbers and
show the data in an HTML table.
```jsx live
function SheetJSFetchDL() {
@ -172,7 +173,7 @@ function SheetJSFetchDL() {
/* Fetch and update HTML */
React.useEffect(() => { (async() => {
/* Fetch file */
const res = await fetch("https://sheetjs.com/pres.numbers");
const res = await fetch("https://docs.sheetjs.com/pres.numbers");
const ab = await res.arrayBuffer();
/* Parse file */
@ -207,7 +208,7 @@ In a GET request, the default behavior is to return a `Blob` object. Passing
```js
$.ajax({
type: "GET", url: "https://sheetjs.com/pres.numbers",
type: "GET", url: "https://docs.sheetjs.com/pres.numbers",
/* suppress jQuery post-processing */
// highlight-next-line
@ -254,10 +255,11 @@ async function workbook_dl_axios(url) {
}
```
<details><summary><b>Live Download demo</b> (click to show)</summary>
<details>
<summary><b>Live Download demo</b> (click to show)</summary>
This demo uses `axios` to download <https://sheetjs.com/pres.numbers> and show
the data in an HTML table.
This demo uses `axios` to download https://docs.sheetjs.com/pres.numbers and
show the data in an HTML table.
:::caution pass
@ -279,7 +281,7 @@ function SheetJSAxiosDL() {
React.useEffect(() => { (async() => {
if(typeof axios != "function") return setHTML("ReferenceError: axios is not defined");
/* Fetch file */
const res = await axios("https://sheetjs.com/pres.numbers", {responseType: "arraybuffer"});
const res = await axios("https://docs.sheetjs.com/pres.numbers", {responseType: "arraybuffer"});
/* Parse file */
const wb = XLSX.read(res.data);
@ -315,10 +317,11 @@ superagent
});
```
<details><summary><b>Live Download demo</b> (click to show)</summary>
<details>
<summary><b>Live Download demo</b> (click to show)</summary>
This demo uses `superagent` to download <https://sheetjs.com/pres.numbers> and
show the data in an HTML table.
This demo uses `superagent` to download https://docs.sheetjs.com/pres.numbers
and show the data in an HTML table.
:::caution pass
@ -342,7 +345,7 @@ function SheetJSSuperAgentDL() {
return setHTML("ReferenceError: superagent is not defined");
/* Fetch file */
superagent
.get("https://sheetjs.com/pres.numbers")
.get("https://docs.sheetjs.com/pres.numbers")
.responseType("arraybuffer")
.end((err, res) => {
/* Parse file */
@ -371,7 +374,7 @@ The `https` module provides a low-level `get` method for HTTPS GET requests:
```js title="SheetJSHTTPSGet.js"
var https = require("https"), XLSX = require("xlsx");
https.get('https://sheetjs.com/pres.numbers', function(res) {
https.get('https://docs.sheetjs.com/pres.numbers', function(res) {
var bufs = [];
res.on('data', function(chunk) { bufs.push(chunk); });
res.on('end', function() {
@ -384,7 +387,8 @@ https.get('https://sheetjs.com/pres.numbers', function(res) {
});
```
<details><summary><b>Complete Example</b> (click to show)</summary>
<details>
<summary><b>Complete Example</b> (click to show)</summary>
:::note Tested Environments
@ -431,7 +435,8 @@ async function parse_from_url(url) {
}
```
<details><summary><b>Complete Example</b> (click to show)</summary>
<details>
<summary><b>Complete Example</b> (click to show)</summary>
:::note Tested Environments
@ -459,7 +464,7 @@ async function parse_from_url(url) {
}
(async() => {
const wb = await parse_from_url('https://sheetjs.com/pres.numbers');
const wb = await parse_from_url('https://docs.sheetjs.com/pres.numbers');
/* print the first worksheet to console */
var ws = wb.Sheets[wb.SheetNames[0]];
console.log(XLSX.utils.sheet_to_csv(ws));
@ -483,7 +488,7 @@ was added to the platform, third party modules wrapped the native APIs.
#### request
:::warning pass
:::danger pass
`request` has been deprecated and should only be used in legacy deployments.
@ -493,7 +498,7 @@ Setting the option `encoding: null` passes raw buffers:
```js title="SheetJSRequest.js"
var XLSX = require('xlsx'), request = require('request');
var url = 'https://sheetjs.com/pres.numbers';
var url = 'https://docs.sheetjs.com/pres.numbers';
/* call `request` with the option `encoding: null` */
// highlight-next-line
@ -510,7 +515,8 @@ request(url, {encoding: null}, function(err, res, data) {
});
```
<details><summary><b>Complete Example</b> (click to show)</summary>
<details>
<summary><b>Complete Example</b> (click to show)</summary>
:::note Tested Environments
@ -552,7 +558,8 @@ async function workbook_dl_axios(url) {
}
```
<details><summary><b>Complete Example</b> (click to show)</summary>
<details>
<summary><b>Complete Example</b> (click to show)</summary>
:::note Tested Environments
@ -579,7 +586,7 @@ async function workbook_dl_axios(url) {
}
(async() => {
const wb = await workbook_dl_axios('https://sheetjs.com/pres.numbers');
const wb = await workbook_dl_axios('https://docs.sheetjs.com/pres.numbers');
/* print the first worksheet to console */
var ws = wb.Sheets[wb.SheetNames[0]];
console.log(XLSX.utils.sheet_to_csv(ws));

@ -17,7 +17,7 @@ cloud storage solutions. Spreadsheets can be written using SheetJS and uploaded.
This demo explores file uploads using a number of browser APIs and wrapper
libraries. The upload process will generate a sample XLSX workbook, upload the
file to [a test server](#test-server), and display the response.
file to [a test server](https://s2c.sheetjs.com), and display the response.
:::info pass
@ -60,14 +60,119 @@ flowchart LR
form --> |POST\nrequest| server
```
```js
### Generating Files
In a typical scenario, a process generates arrays of simple objects.
The SheetJS `json_to_sheet` method[^2] generates a SheetJS worksheet object[^3].
The `book_new` method[^4] creates a workbook object that includes the worksheet.
The `write` method[^5] generates the file in memory.
The following snippet creates a sample dataset and generates an `ArrayBuffer`
object representing the workbook bytes:
```js title="Generating an XLSX file in memory"
/* create sample SheetJS workbook object */
var aoa = [
["S", "h", "e", "e", "t", "J", "S"],
[ 5, 4, 3, 3, 7, 9, 5]
];
var ws = XLSX.utils.aoa_to_sheet(aoa);
var wb = XLSX.utils.book_new();
var wb = XLSX.utils.book_new(ws, "Sheet1");
XLSX.utils.book_append_sheet(wb, ws, "Sheet1");
/* export SheetJS workbook object to XLSX file bytes */
var data = XLSX.write(wb, {bookType: 'xlsx', type: 'array'});
```
### Creating Form Data
`File` objects represent files. The `File` constructor accepts an array of data
fragments and a filename.
Browser APIs typically represent form body data using `FormData` objects. The
`append` method adds fields to the `FormData` object. Adding `File` objects
effectively "attaches" a file in the upload.
The following snippet constructs a new `FormData` object. The `file` field in
the form will be set to the data from the previous snippet:
```js title="Creating Form Data and attaching the generated file"
/* create File */
var file = new File([data], 'sheetjs.xlsx')
// generated XLSX ^^^^ ^^^^^^^^^^^^ file name
/* build FormData with the generated file */
var fdata = new FormData();
fdata.append('file', file);
// ^^^^ field name in the form body
```
### POST Request
This demo explores a number of APIs and libraries for making POST requests. Each
approach will upload data stored in `FormData` objects.
This snippet uses `XMLHttpRequest` to upload data to https://s2c.sheetjs.com:
```js title="Uploading Form Data with XMLHttpRequest"
/* send data using XMLHttpRequest */
var req = new XMLHttpRequest();
req.open("POST", "https://s2c.sheetjs.com", true);
req.send(fdata);
```
## Browser Demos
When the upload button is clicked, the browser will build up a new workbook,
generate a XLSX file, upload it to https://s2c.sheetjs.com and show the
response. If the process was successful, a HTML table will be displayed
:::note Tested Deployments
Each browser demo was tested in the following environments:
| Browser | Date |
|:------------|:-----------|
| Chrome 120 | 2024-01-15 |
| Safari 17.3 | 2024-02-21 |
:::
#### Test Server
The https://s2c.sheetjs.com service is currently hosted on Deno Deploy. The
["Deno Deploy" demo](/docs/demos/cloud/deno#demo) covers the exact steps for
deploying the service.
The CORS-enabled service handles POST requests by looking for uploaded files in
the `"file"` key. If a file is found, the file will be parsed using the SheetJS
`read` method[^6] and the first worksheet will be converted to HTML using the
`sheet_to_html` method[^7].
### XMLHttpRequest
Using the `XMLHttpRequest` API, the `send` method can accept `FormData` objects:
```js title="Uploading Form Data with XMLHttpRequest"
/* send data using XMLHttpRequest */
var req = new XMLHttpRequest();
req.open("POST", "https://s2c.sheetjs.com", true);
req.send(fdata);
```
<details>
<summary><b>Complete Code Snippet</b> (click to show)</summary>
```js title="SheetJS + XMLHttpRequest example"
/* create sample SheetJS workbook object */
var aoa = [
["S", "h", "e", "e", "t", "J", "S"],
[ 5, 4, 3, 3, 7, 9, 5]
];
const ws = XLSX.utils.aoa_to_sheet(aoa);
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, "Sheet1");
/* export SheetJS workbook object to XLSX file bytes */
@ -84,42 +189,13 @@ req.open("POST", "https://s2c.sheetjs.com", true);
req.send(fdata);
```
## Test Server
</details>
The <https://s2c.sheetjs.com> service is currently hosted on Deno Deploy. The
["Deno Deploy" demo](/docs/demos/cloud/deno#demo) covers the exact steps for
deploying the service.
The CORS-enabled service handles POST requests by looking for uploaded files in
the `"file"` key. If a file is found, the file will be parsed using the SheetJS
`read` method[^2] and the first worksheet will be converted to HTML using the
`sheet_to_html` method[^3].
## Browser Demos
When the upload button is clicked, the browser will build up a new workbook,
generate a XLSX file, upload it to <https://s2c.sheetjs.com> and show the
response. If the process was successful, a HTML table will be displayed
:::note Tested Deployments
Each browser demo was tested in the following environments:
| Browser | Date |
|:------------|:-----------|
| Chrome 120 | 2024-01-15 |
| Safari 17.3 | 2024-02-21 |
:::
### XMLHttpRequest
This demo uses [the code snippet from the intro](#uploading-binary-data).
<details><summary><b>Live demo</b> (click to show)</summary>
<details>
<summary><b>Live demo</b> (click to show)</summary>
This demo starts from an array of arrays of data. When the button is clicked, a
workbook file will be generated and uploaded to <https://s2c.sheetjs.com>. The
workbook file will be generated and uploaded to https://s2c.sheetjs.com. The
service will return a HTML table.
```jsx live
@ -184,7 +260,15 @@ function SheetJSXHRUL() {
`fetch` takes a second parameter which allows for setting POST request body:
```js
```js title="Uploading Form Data with fetch"
/* send data using fetch */
fetch("https://s2c.sheetjs.com", { method: "POST", body: fdata });
```
<details>
<summary><b>Complete Code Snippet</b> (click to show)</summary>
```js title="SheetJS + fetch example"
/* create sample SheetJS workbook object */
var aoa = [
["S", "h", "e", "e", "t", "J", "S"],
@ -206,9 +290,12 @@ fdata.append('file', new File([data], 'sheetjs.xlsx'));
fetch("https://s2c.sheetjs.com", { method: "POST", body: fdata });
```
<details><summary><b>Live demo</b> (click to show)</summary>
</details>
This demo uses `fetch` to upload data to <https://s2c.sheetjs.com>. It will parse
<details>
<summary><b>Live demo</b> (click to show)</summary>
This demo uses `fetch` to upload data to https://s2c.sheetjs.com. It will parse
the workbook and return an HTML table.
```jsx live
@ -276,7 +363,15 @@ are still relevant.
Uploading form data is nearly identical to the `fetch` example:
```js
```js title="Uploading Form Data with axios"
/* send data using axios */
axios("https://s2c.sheetjs.com", { method: "POST", body: fdata });
```
<details>
<summary><b>Complete Code Snippet</b> (click to show)</summary>
```js title="SheetJS + axios example"
/* create sample SheetJS workbook object */
var aoa = [
["S", "h", "e", "e", "t", "J", "S"],
@ -298,9 +393,12 @@ fdata.append('file', new File([data], 'sheetjs.xlsx'));
axios("https://s2c.sheetjs.com", { method: "POST", data: fdata });
```
<details><summary><b>Live demo</b> (click to show)</summary>
</details>
This demo uses `axios` to upload data to <https://s2c.sheetjs.com>. It will parse
<details>
<summary><b>Live demo</b> (click to show)</summary>
This demo uses `axios` to upload data to https://s2c.sheetjs.com. It will parse
the workbook and return an HTML table.
:::caution pass
@ -375,7 +473,15 @@ with a "Fluent Interface".
The `send` method accepts a `FormData` object as the first argument:
```js
```js title="Uploading Form Data with superagent"
/* send data using superagent */
superagent.post("https://s2c.sheetjs.com").send(fd);
```
<details>
<summary><b>Complete Code Snippet</b> (click to show)</summary>
```js title="SheetJS + superagent example"
/* create sample SheetJS workbook object */
var aoa = [
["S", "h", "e", "e", "t", "J", "S"],
@ -397,9 +503,12 @@ fdata.append('file', new File([data], 'sheetjs.xlsx'));
superagent.post("https://s2c.sheetjs.com").send(fd);
```
<details><summary><b>Live demo</b> (click to show)</summary>
</details>
This demo uses `superagent` to upload data to <https://s2c.sheetjs.com>. It will
<details>
<summary><b>Live demo</b> (click to show)</summary>
This demo uses `superagent` to upload data to https://s2c.sheetjs.com. It will
parse the workbook and return an HTML table.
:::caution pass
@ -473,17 +582,23 @@ These examples show how to upload data in NodeJS.
### fetch
The `fetch` implementation mirrors the [browser `fetch`](#fetch).
NodeJS `fetch`, available in version 20, mirrors the [browser `fetch`](#fetch).
:::note Tested Deployments
This demo was last tested on 2023 November 19 against NodeJS `20.9.0`
This demo was tested in the following environments:
| NodeJS | Date |
|:-----------|:-----------|
| `20.12.1` | 2024-04-07 |
| `21.7.2` | 2024-04-07 |
:::
<details><summary><b>Complete Example</b> (click to show)</summary>
<details>
<summary><b>Complete Example</b> (click to show)</summary>
This demo uses `fetch` to upload data to <https://s2c.sheetjs.com>. It will parse
This demo uses `fetch` to upload data to https://s2c.sheetjs.com. It will parse
the workbook and return data in CSV rows.
1) Install the [SheetJS NodeJS module](/docs/getting-started/installation/nodejs):
@ -533,6 +648,156 @@ It will print CSV contents of the test file.
</details>
### request
The deprecated [`request`](https://github.com/request/request) library is useful
in legacy NodeJS deployments where `fetch` may not be available.
The SheetJS `write` method will generate NodeJS Buffer objects when the `type`
option is set to `"buffer"`:
```js
/* export SheetJS workbook object to XLSX file bytes */
const data = XLSX.write(wb, {bookType: 'xlsx', type: 'buffer'});
```
A `request` file object can be built using the Buffer. The file object must
include an `options` object that specifies the file name and content type:
```js
/* create a file object for the `request` form data */
const request_file = {
/* `value` can be a Buffer object */
value: data,
options: {
/* `options.filename` is the filename that the server will see */
filename: "sheetjs.xlsx",
/* `options.contentType` must be set */
contentType: "application/octet-stream"
}
};
```
The `request` and `request.post` methods accept an options argument. The
`formData` property specifies the body to be uploaded. Property names correspond
to the uploaded form names and values describe the uploaded content.
The `request` file object should be added to the `formData` object:
```js
request({
// ... other options ...
formData: {
// ... other form fields ...
/* the server will see the uploaded file in the `file` body property */
/* highlight-next-line */
file: request_file
}
}, function(err, res) { /* handle response ... */ });
```
:::note Tested Deployments
This demo was tested in the following environments:
| NodeJS | `request` | Date |
|:-----------|:----------|:-----------|
| `0.10.48` | `2.88.2` | 2024-04-07 |
| `0.12.18` | `2.88.2` | 2024-04-07 |
| `4.9.1` | `2.88.2` | 2024-04-07 |
| `6.17.1` | `2.88.2` | 2024-04-07 |
| `8.17.0` | `2.88.2` | 2024-04-07 |
| `10.24.1` | `2.88.2` | 2024-04-07 |
| `12.22.12` | `2.88.2` | 2024-04-07 |
| `14.21.3` | `2.88.2` | 2024-04-07 |
| `16.20.2` | `2.88.2` | 2024-04-07 |
| `18.20.1` | `2.88.2` | 2024-04-07 |
| `20.12.1` | `2.88.2` | 2024-04-07 |
:::
<details>
<summary><b>Complete Example</b> (click to show)</summary>
This demo uses `request` to upload data to https://s2c.sheetjs.com. It will
parse the workbook and return data in CSV rows.
1) Install the [SheetJS NodeJS module](/docs/getting-started/installation/nodejs)
and `request` module:
<CodeBlock language="bash">{`\
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz request`}
</CodeBlock>
2) Save the following to `SheetJSRequest.js`:
```js title="SheetJSRequest.js"
const XLSX = require("xlsx");
const request = require("request");
/* create sample SheetJS workbook object */
var aoa = [
["S", "h", "e", "e", "t", "J", "S"],
[ 5, 4, 3, 3, 7, 9, 5]
];
const ws = XLSX.utils.aoa_to_sheet(aoa);
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, "Sheet1");
/* export SheetJS workbook object to XLSX file bytes */
var data = XLSX.write(wb, {bookType: 'xlsx', type: 'buffer'});
request({
method: "POST",
url: "https://s2c.sheetjs.com",
headers: {
Accept: "text/html"
},
formData: {
type: "csv",
file: {
value: data,
options: {
filename: "sheetjs.xlsx",
contentType: "application/octet-stream"
}
}
}
}, function(err, res, body) {
if(err) return console.error(err);
console.log(body);
});
```
3) Run the script:
```bash
node SheetJSRequest.js
```
It will print CSV contents of the test file.
:::caution pass
For legacy versions of NodeJS, the process may fail with a certificate error:
```
{ [Error: certificate not trusted] code: 'CERT_UNTRUSTED' }
```
The environment variable `NODE_TLS_REJECT_UNAUTHORIZED` can be set to `0`:
```bash
env NODE_TLS_REJECT_UNAUTHORIZED="0" node SheetJSRequest.js
```
**It is strongly recommended to upgrade to a newer version of NodeJS!**
:::
</details>
## Troubleshooting
@ -571,5 +836,9 @@ const res = await fetch(url, {method:"POST", body: fdata});
If the generated file is valid, then the issue is in the server infrastructure.
[^1]: See [`write` in "Writing Files"](/docs/api/write-options)
[^2]: See [`read` in "Reading Files"](/docs/api/parse-options)
[^3]: See [`sheet_to_html` in "Utilities"](/docs/api/utilities/html#html-table-output)
[^2]: See [`json_to_sheet` in "Utilities"](/docs/api/utilities/array#array-of-objects-input)
[^3]: See ["Worksheet Object" in "SheetJS Data Model"](/docs/csf/sheet) for more details.
[^4]: See [`book_new` in "Utilities"](/docs/api/utilities/wb)
[^5]: See [`write` in "Writing Files"](/docs/api/write-options)
[^6]: See [`read` in "Reading Files"](/docs/api/parse-options)
[^7]: See [`sheet_to_html` in "Utilities"](/docs/api/utilities/html#html-table-output)

@ -151,11 +151,11 @@ npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz express
node SheetJSExpressCSV.js
```
4) Test POST requests using <https://sheetjs.com/pres.numbers> . The following
commands should be run in a new terminal window:
4) Test POST requests using https://docs.sheetjs.com/pres.numbers . The commands
should be run in a new terminal window:
```bash
curl -LO https://sheetjs.com/pres.numbers
curl -LO https://docs.sheetjs.com/pres.numbers
curl -X POST -F upload=@pres.numbers http://localhost:3000/upload
```

@ -123,7 +123,7 @@ curl -LO https://docs.sheetjs.com/drash/SheetJSDrash.ts
deno run --allow-net SheetJSDrash.ts
```
3) Download the test file <https://sheetjs.com/pres.numbers>
3) Download the test file https://docs.sheetjs.com/pres.numbers
4) Open `http://localhost:7262/` in your browser.

@ -151,11 +151,11 @@ app.listen(3000);
bun run src/SheetJSElysia.ts
```
5) Test POST requests using <https://sheetjs.com/pres.numbers> . The following
commands should be run in a new terminal window:
5) Test POST requests using https://docs.sheetjs.com/pres.numbers . The commands
should be run in a new terminal window:
```bash
curl -LO https://sheetjs.com/pres.numbers
curl -LO https://docs.sheetjs.com/pres.numbers
curl -X POST -F upload=@pres.numbers http://localhost:3000/
```

@ -209,11 +209,11 @@ npx @nestjs/cli start
:::
8) Test POST requests using <https://sheetjs.com/pres.numbers> . The following
commands should be run in a new terminal window:
8) Test POST requests using https://docs.sheetjs.com/pres.numbers . The commands
should be run in a new terminal window:
```bash
curl -LO https://sheetjs.com/pres.numbers
curl -LO https://docs.sheetjs.com/pres.numbers
curl -X POST -F upload=@pres.numbers http://localhost:3000/sheetjs/upload
```

@ -166,11 +166,11 @@ npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz fastify
node SheetJSFastify.js
```
3) Test POST requests using <https://sheetjs.com/pres.numbers> . The following
commands should be run in a new terminal window:
3) Test POST requests using https://docs.sheetjs.com/pres.numbers . The commands
should be run in a new terminal window:
```bash
curl -LO https://sheetjs.com/pres.numbers
curl -LO https://docs.sheetjs.com/pres.numbers
curl -X POST -F upload=@pres.numbers http://localhost:3000/
```

@ -120,7 +120,8 @@ That approach is not explored in this demo.
:::
<details><summary><b>Complete Example</b> (click to show)</summary>
<details>
<summary><b>Complete Example</b> (click to show)</summary>
:::note Tested Deployments
@ -229,11 +230,11 @@ node main.mjs
Keep the server process running during the test.
6) Test with the [`pres.numbers` sample file](https://sheetjs.com/pres.numbers).
6) Test with the [`pres.numbers` sample file](https://docs.sheetjs.com/pres.numbers).
The following commands should be run in a new terminal window:
```bash
curl -LO https://sheetjs.com/pres.numbers
curl -LO https://docs.sheetjs.com/pres.numbers
curl -X POST -F upload=@pres.numbers http://localhost:7262/ -J -O
```
@ -255,7 +256,8 @@ Bun provides the basic elements to implement a web server.
:::caution pass
Many hosted services like Deno Deploy do not offer filesystem access.
Many hosted services, including [Deno Deploy](/docs/demos/cloud/deno#demo), do
not offer filesystem access from scripts.
This breaks web frameworks that use the filesystem in body parsing.
@ -267,6 +269,6 @@ a body parser out of the box.
#### Drash
In testing, [Drash](https://drash.land/drash/) had an in-memory body parser
which could handle file uploads on hosted services like Deno Deploy.
which could handle file uploads on [Deno Deploy](/docs/demos/cloud/deno#demo).
**[The exposition has been moved to a separate page.](/docs/demos/net/server/drash)**

@ -162,7 +162,8 @@ added and exposed in a script.
[`pstextractor.js`](pathname:///pst/pstextractor.js) is loaded in the demo page.
<details><summary><b>Build instructions</b> (click to show)</summary>
<details>
<summary><b>Build instructions</b> (click to show)</summary>
1) Initialize a new NodeJS project and install the dependency:

@ -19,7 +19,7 @@ This demo covers three workflows:
- [Reading mail](#reading-mail) covers libraries for reading messages
- [Data files](#data-files) covers mailbox file formats
:::warning pass
:::danger pass
There are a number of caveats when dealing with live mail servers. It is advised
to follow connector module documentation carefully and test with new accounts
@ -29,7 +29,7 @@ before integrating with important inboxes or accounts.
## Live Servers
:::warning pass
:::danger pass
It is strongly advised to use a test email address before using an important
address. One small mistake could erase decades of messages or result in a block
@ -378,9 +378,9 @@ const concat_RS = (stream) => new Promise((res, rej) => {
| `gmail.com` | `imap.gmail.com` |
| `fastmail.com` | `imap.fastmail.com` |
4) Download <https://sheetjs.com/pres.numbers>. Using a different account, send
an email to the test account and attach the file. At the end of this step, the
test account should have an email in the inbox that has an attachment.
4) Download https://docs.sheetjs.com/pres.numbers . Using a different account,
send an email to the test account and attach the file. At the end of this step,
the test account should have an email in the inbox that has an attachment.
5) Run the script:

@ -50,7 +50,8 @@ sequenceDiagram
end
```
<details open><summary><b>Key Steps</b> (click to hide)</summary>
<details open>
<summary><b>Key Steps</b> (click to hide)</summary>
1) Launch the headless browser and load the target site.
@ -66,7 +67,7 @@ sequenceDiagram
</details>
This demo exports data from <https://sheetjs.com/demos/table>.
This demo exports data from https://sheetjs.com/demos/table.
:::note pass
@ -343,7 +344,7 @@ After installing engines, re-run the script.
PhantomJS is a headless web browser powered by WebKit.
:::warning pass
:::danger pass
This information is provided for legacy deployments. PhantomJS development has
been suspended and there are known vulnerabilities, so new projects should use
@ -355,7 +356,8 @@ Binary strings are the favored data type. They can be safely passed from the
browser context to the automation script. PhantomJS provides an API to write
binary strings to file (`fs.write` using mode `wb`).
<details><summary><b>Integration Details and Demo</b> (click to show)</summary>
<details>
<summary><b>Integration Details and Demo</b> (click to show)</summary>
The steps are marked in the comments:
@ -407,7 +409,9 @@ This demo was tested in the following environments:
|:-------------|:----------|:-----------|
| `darwin-x64` | `2.1.1` | 2024-03-15 |
| `win10-x64` | `2.1.1` | 2024-03-24 |
| `linux-x64` | `2.1.1` | 2024-03-29 |
| `win11-x64` | `2.1.1` | 2024-05-22 |
| `linux-x64` | `2.1.1` | 2024-04-25 |
:::
1) [Download and extract PhantomJS](https://phantomjs.org/download.html)

@ -37,19 +37,19 @@ string and return an object that represents `document`. An API method such as
```mermaid
flowchart LR
subgraph Synthetic DOM Operations
html(HTML\nstring)
subgraph Synthetic DOM Operations
html(HTML\nstring)
doc{{`document`\nDOM Object}}
end
subgraph SheetJS Operations
table{{DOM\nTable}}
wb(((SheetJS\nWorkbook)))
file(workbook\nfile)
end
end
subgraph SheetJS Operations
table{{DOM\nTable}}
wb(((SheetJS\nWorkbook)))
file(workbook\nfile)
end
html --> |Library\n\n| doc
doc --> |DOM\nAPI| table
table --> |`table_to_book`\n\n| wb
wb --> |`writeFile`\n\n| file
wb --> |`writeFile`\n\n| file
```
SheetJS methods use features that may be missing from some DOM implementations.
@ -107,7 +107,8 @@ const workbook = XLSX.utils.table_to_book(doc);
XLSX.writeFile(workbook, "SheetJSDOM.xlsx");
```
<details><summary><b>Complete Demo</b> (click to show)</summary>
<details>
<summary><b>Complete Demo</b> (click to show)</summary>
:::note Tested Deployments
@ -147,7 +148,8 @@ the following patches were needed:
- TABLE `rows` property (explained above)
- TR `cells` property (explained above)
<details><summary><b>Complete Demo</b> (click to show)</summary>
<details>
<summary><b>Complete Demo</b> (click to show)</summary>
:::note Tested Deployments
@ -194,13 +196,14 @@ tested version (`0.8.10`), the following patches were needed:
```js
Object.defineProperty(tbl.__proto__, "innerHTML", { get: function() {
var outerHTML = new XMLSerializer().serializeToString(this);
if(outerHTML.match(/</g).length == 1) return "";
return outerHTML.slice(0, outerHTML.lastIndexOf("</")).replace(/<[^"'>]*(("[^"]*"|'[^']*')[^"'>]*)*>/, "");
var outerHTML = new XMLSerializer().serializeToString(this);
if(outerHTML.match(/</g).length == 1) return "";
return outerHTML.slice(0, outerHTML.lastIndexOf("</")).replace(/<[^"'>]*(("[^"]*"|'[^']*')[^"'>]*)*>/, "");
}});
```
<details><summary><b>Complete Demo</b> (click to show)</summary>
<details>
<summary><b>Complete Demo</b> (click to show)</summary>
:::note Tested Deployments
@ -243,7 +246,8 @@ can be shimmed, but it is strongly recommended to use a more compliant library.
[`SheetJSCheerio.js`](pathname:///dom/SheetJSCheerio.js) implements the missing
features to ensure that SheetJS DOM methods can process TABLE elements.
<details><summary><b>Complete Demo</b> (click to show)</summary>
<details>
<summary><b>Complete Demo</b> (click to show)</summary>
:::note Tested Deployments
@ -314,7 +318,8 @@ const workbook = XLSX.utils.table_to_book(tbl);
XLSX.writeFile(workbook, "SheetJSDenoDOM.xlsx");`}
</CodeBlock>
<details><summary><b>Complete Demo</b> (click to show)</summary>
<details>
<summary><b>Complete Demo</b> (click to show)</summary>
:::note Tested Deployments
@ -335,7 +340,7 @@ The script will create a file `SheetJSDenoDOM.xlsx` that can be opened.
</details>
[^1]: See [`table_to_sheet` in "HTML" Utilities](/docs/api/utilities/html#create-new-sheet)
[^2]: See ["Worksheet Object" in "SheetJS Data Model"](/docs/csf/book) for more details.
[^2]: See ["Worksheet Object" in "SheetJS Data Model"](/docs/csf/sheet) for more details.
[^3]: See [`table_to_book` in "HTML" Utilities](/docs/api/utilities/html#create-new-sheet)
[^4]: See ["Workbook Object" in "SheetJS Data Model"](/docs/csf/book) for more details.
[^5]: See [`sheet_add_dom` in "HTML" Utilities](/docs/api/utilities/html#add-to-sheet)

@ -16,7 +16,7 @@ With a familiar UI, `x-spreadsheet` is an excellent choice for a modern editor.
:::note Tested Deployments
This demo was last verified on 2023 December 04.
This demo was last verified on 2024 April 25.
:::
@ -33,7 +33,7 @@ features like scrolling may not work as expected.
```jsx live
function SheetJSXSpread() {
const [url, setUrl] = React.useState("https://sheetjs.com/pres.numbers");
const [url, setUrl] = React.useState("https://docs.sheetjs.com/pres.numbers");
const [done, setDone] = React.useState(false);
const ref = React.useRef(); // ref to DIV container
const set_url = (evt) => setUrl(evt.target.value);
@ -72,7 +72,7 @@ The following snippet fetches a spreadsheet and loads the grid:
```js
(async() => {
const ab = await (await fetch("https://sheetjs.com/pres.numbers")).arrayBuffer();
const ab = await (await fetch("https://docs.sheetjs.com/pres.numbers")).arrayBuffer();
grid.loadData(stox(XLSX.read(ab)));
})();
```

@ -15,7 +15,7 @@ with a straightforward API.
:::note Tested Deployments
This demo was last verified on 2023 December 04.
This demo was last verified on 2024 April 25.
:::
@ -32,7 +32,7 @@ features like scrolling may not work as expected.
```jsx live
function SheetJSCDG() {
const [url, setUrl] = React.useState("https://sheetjs.com/pres.numbers");
const [url, setUrl] = React.useState("https://docs.sheetjs.com/pres.numbers");
const [done, setDone] = React.useState(false);
const ref = React.useRef(); // ref to DIV container
const set_url = (evt) => setUrl(evt.target.value);

@ -0,0 +1,167 @@
---
title: Tabulator
pagination_prev: demos/frontend/index
pagination_next: demos/net/index
---
import current from '/version.js';
import CodeBlock from '@theme/CodeBlock';
[Tabulator](https://tabulator.info/) is a powerful data table library designed
for ease of use.
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
data from spreadsheets.
Tabulator offers deep integration with SheetJS for importing and exporting data.
This demo covers additional detail including document customization.
[Click here for a live standalone integration demo.](pathname:///tabulator/)
:::note Tested Deployments
This demo was tested in the following deployments:
| Browser | Version | Date |
|:-------------|:--------|:-----------|
| Chromium 125 | `6.2.1` | 2024-06-13 |
:::
## Integration Details
The [SheetJS Standalone scripts](/docs/getting-started/installation/standalone)
are appropriate for sites that use the Tabulator CDN scripts.
[The "Frameworks" section](/docs/getting-started/installation/frameworks) covers
installation instructions for projects using a framework.
:::info pass
**The Tabulator script must be loaded after the SheetJS scripts!**
```html
<!-- Load SheetJS Scripts -->
<script src="https://cdn.sheetjs.com/xlsx-latest/package/dist/shim.min.js"></script>
<script src="https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js"></script>
<!-- Tabulator must be loaded after SheetJS scripts -->
<script type="text/javascript" src="https://unpkg.com/tabulator-tables@6.2.1/dist/js/tabulator.min.js"></script>
```
:::
### Previewing Data
Tabulator offers a special `setData` method for assigning data after the table
is created. Coupled with the `autoColumns` option, Tabulator will automatically
refresh the table.
:::info pass
The library scans the first row object to determine the header labels. If a
column is missing a value in the first object, it will not be loaded!
:::
#### Fetching Files
When files are stored remotely, the recommended approach is to fetch the files,
parse with the SheetJS `read` method, generate arrays of objects from the target
sheet using `sheet_to_json`, and load data with the Tabulator `setData` method.
The following snippet fetches a sample file and loads the first sheet:
```html title="Fetching a spreadsheet and Displaying the first worksheet"
<!-- Tabulator DIV -->
<div id="htmlout"></div>
<script>
/* Initialize Tabulator with the `autoColumns: true` setting */
var tbl = new Tabulator('#htmlout', { autoColumns: true });
/* fetch and display https://docs.sheetjs.com/pres.numbers */
(function() { try {
fetch("https://docs.sheetjs.com/pres.numbers")
.then(function(res) { return res.arrayBuffer(); })
.then(function(ab) {
/* parse ArrayBuffer */
var wb = XLSX.read(ab);
/* get first worksheet from SheetJS workbook object */
var ws = wb.Sheets[wb.SheetNames[0]];
/* generate array of row objects */
var data = XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]);
/* update Tabulator */
tbl.setData(data);
});
} catch(e) {} })();
</script>
```
#### Local Files
Tabulator provides a special `import` method to show a dialog and load data.
Since the importer requires the raw binary data, the method must be called with
the third argument set to `"buffer"`:
```html title="Parsing a local spreadsheet and Displaying the first worksheet"
<button id="imp"><b>Click here to import from XLSX file</b></button>
<!-- Tabulator DIV -->
<div id="htmlout"></div>
<script>
/* Initialize Tabulator with the `autoColumns: true` setting */
var tbl = new Tabulator('#htmlout', { autoColumns: true });
/* use Tabulator SheetJS integration to import data */
document.getElementById("imp").addEventListener("click", function() {
tbl.import("xlsx", ".xlsx", "buffer");
})
</script>
```
### Saving Data
Tabulator provides a special `download` method to initiate the export:
```html title="Exporting data from Tabulator to XLSX"
<input type="submit" value="Export to XLSX!" id="xport" onclick="export_xlsx();">
<!-- Tabulator DIV -->
<div id="htmlout"></div>
<script>
/* Initialize Tabulator with the `autoColumns: true` setting */
var tbl = new Tabulator('#htmlout', { autoColumns: true });
/* use Tabulator SheetJS integration to import data */
function export_xlsx() {
/* use Tabulator SheetJS integration */
tbl.download("xlsx", "SheetJSTabulator.xlsx");
}
</script>
```
[The official documentation](https://tabulator.info/docs/6.2/download#xlsx)
covers supported options.
#### Post-processing
The `documentProcessing` event handler is called after Tabulator generates a
SheetJS workbook object. This allows for adjustments before creating the final
workbook file. The following example adds a second sheet that includes the date:
```js title="Exporting data and metadata"
tbl.download("xlsx", "SheetJSTabulator.xlsx", {
documentProcessing: function(wb) {
/* create a new worksheet */
var ws = XLSX.utils.aoa_to_sheet([
["SheetJS + Tabulator Demo"],
["Export Date:", new Date()]
]);
/* add to workbook */
XLSX.utils.book_append_sheet(wb, ws, "Metadata");
return wb;
}
});
```

@ -7,23 +7,45 @@ pagination_next: demos/net/index
import current from '/version.js';
import CodeBlock from '@theme/CodeBlock';
:::note Tested Deployments
[Vue 3 Table Lite](https://vue3-lite-table.vercel.app/) is a data table library
designed for the VueJS web framework.
This demo was tested against `vue3-table-lite 1.3.9`, VueJS `3.3.10` and ViteJS
`5.0.5` on 2023 December 04.
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
data from spreadsheets.
:::
This demo uses Vue 3 Table Lite and SheetJS to pull data from a spreadsheet and
display the content in a data table. We'll explore how to import data from files
into the data grid and how to export modified data from the grid to workbooks.
The demo creates a site that looks like the screenshot below:
The ["Demo"](#demo) section includes a complete example that displays data from
user-supplied sheets and exports data to XLSX workbooks:
![vue3-table-lite screenshot](pathname:///vtl/vtl1.png)
:::note Tested Deployments
This demo was tested in the following deployments:
| Browser | Version | Date |
|:-------------|:--------|:-----------|
| Chromium 125 | `1.4.0` | 2024-06-13 |
:::
## Integration Details
[The "Frameworks" section](/docs/getting-started/installation/frameworks) covers
installation in ViteJS projects using Vue 3 Table Lite.
Using the `npm` tool, this command installs SheetJS and Vue 3 Table Lite:
<CodeBlock language="bash">{`\
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz vue3-table-lite@1.4.0`}
</CodeBlock>
#### Rows and Columns Bindings
`vue3-table-lite` presents two attribute bindings: an array of column metadata
Vue 3 Table Lite presents two attribute bindings: an array of column metadata
(`columns`) and an array of objects representing the displayed data (`rows`).
Typically both are `ref` objects:
@ -114,7 +136,7 @@ cd sheetjs-vtl
2) Install dependencies:
<CodeBlock language="bash">{`\
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz vue3-table-lite@1.3.9`}
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz vue3-table-lite@1.4.0`}
</CodeBlock>
3) Download [`src/App.vue`](pathname:///vtl/App.vue) and replace the contents:
@ -131,5 +153,5 @@ npm run dev
5) Load the displayed URL (typically `http://localhost:5173`) in a web browser.
When the page loads, it will try to fetch <https://sheetjs.com/pres.numbers>
When the page loads, it will try to fetch https://docs.sheetjs.com/pres.numbers
and display the data. Click "Export" to generate a workbook.

@ -26,7 +26,11 @@ user-supplied sheets and exports data to XLSX workbooks:
:::note Tested Deployments
This demo was last tested on 2023 December 04 with Glide Data Grid 5.3.2
This demo was tested in the following environments:
| Browser | Version | Date |
|:-------------|:--------|:-----------|
| Chromiun 125 | `5.3.2` | 2024-06-09 |
:::
@ -367,7 +371,7 @@ curl -L -o src/App.tsx https://docs.sheetjs.com/gdg/App.tsx
![glide-data-grid initial view](pathname:///gdg/pre.png)
The demo downloads and processes <https://sheetjs.com/pres.numbers>.
The demo downloads and processes https://docs.sheetjs.com/pres.numbers .
6) Make some changes to the grid data.
@ -384,7 +388,8 @@ values should be 41, 42, 43, 44, and 45, as shown in the screenshot below:
![glide-data-grid after edits](pathname:///gdg/post.png)
7) Click on the "Export" button to create a XLSX file (`sheetjs-gdg.xlsx`).
7) Click on the "Export" button. The browser should attempt to download a XLSX
file (`sheetjs-gdg.xlsx`). Save the file.
Open the generated file and verify the contents match the grid.

@ -28,15 +28,15 @@ This demo was tested in the following environments:
| Version | Date | Notes |
|:----------------|:-----------|:---------------------|
| `7.0.0-beta.19` | 2023-12-04 | |
| `7.0.0-beta.41` | 2023-12-04 | Editing did not work |
| `7.0.0-beta.19` | 2024-06-09 | |
| `7.0.0-beta.44` | 2024-06-09 | Editing did not work |
:::
:::warning pass
:::danger pass
When this demo was last tested, the grid correctly displayed data but could not
be edited by the user.
When this demo was last tested against the latest version, the grid correctly
displayed data but data could not be edited by the user.
The current recommendation is to use version `7.0.0-beta.19`.
@ -50,7 +50,7 @@ installation with Yarn and other package managers.
Using the `npm` tool, this command installs SheetJS and React Data Grid:
<CodeBlock language="bash">{`\
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz react-data-grid@7.0.0-beta.41`}
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz react-data-grid@7.0.0-beta.19`}
</CodeBlock>
Methods and components in both libraries can be loaded in pages using `import`:
@ -146,17 +146,17 @@ function rdg_to_ws(rows: Row[]): WorkSheet {
## Demo
1) Create a new TypeScript `create-react-app` app:
1) Create a new ViteJS app using the `react-ts` template:
```bash
npx create-react-app sheetjs-rdg --template typescript
npm create vite@latest -- sheetjs-rdg --template react-ts
cd sheetjs-rdg
```
2) Install dependencies:
<CodeBlock language="bash">{`\
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz react-data-grid@7.0.0-beta.41`}
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz react-data-grid@7.0.0-beta.19`}
</CodeBlock>
3) Download [`App.tsx`](pathname:///rdg/App.tsx) and replace `src/App.tsx`.
@ -168,12 +168,24 @@ curl -L -o src/App.tsx https://docs.sheetjs.com/rdg/App.tsx
4) Start the development server:
```bash
npm start
npm run dev
```
The terminal window will display a URL (typically `http://localhost:5173`).
Open the URL with a web browser and confirm that a page loads.
#### Testing
5) When the page loads, it will fetch <https://sheetjs.com/pres.numbers>, parse
5) Confirm the table shows a list of Presidents.
When the page loads, it will fetch https://docs.sheetjs.com/pres.numbers, parse
with SheetJS, and load the data in the data grid.
6) Click one of the "export" buttons to export the grid data to file.
6) Click the "export [.xlsx]" button to export the grid data to XLSX. It should
attempt to download `SheetJSRDG.xlsx`.
7) Open the generated file in a spreadsheet editor. Set cell A7 to "SheetJS Dev"
and set cell B7 to 47. Save the file.
8) Use the file picker to select the modified file. The table will refresh and
show the new data.

@ -5,44 +5,174 @@ pagination_next: demos/net/index
---
import current from '/version.js';
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeBlock from '@theme/CodeBlock';
This demo covers the traditional Material UI Table as well as the MUI Data Grid.
Material UI is a collection of ReactJS Components that follows the
[Google Material Design system](https://material.io/)
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
data from spreadsheets.
This demo uses Material UI and SheetJS to pull data from a spreadsheet and
display the data. We'll explore how to import data from spreadsheets and export
data to spreadsheets. The following Material UI components will be tested:
- ["Table"](#material-ui-table) is based on the core HTML TABLE element.
- ["Data Grid"](#material-ui-data-grid) is a data grid for larger datasets.
:::note pass
The [ReactJS demo](/docs/demos/frontend/react) covers basic ReactJS concepts.
It should be perused before reading this demo.
:::
## Integration Details
[The "Frameworks" section](/docs/getting-started/installation/frameworks) covers
installation in projects using Material UI.
After installing the SheetJS module in a ReactJS project, `import` statements
can load relevant parts of the library.
```js
import { read, utils, writeFileXLSX } from 'xlsx';
```
## Material UI Table
The `Table` component abstracts the `<table>` element in HTML. `table_to_book`
can process a `ref` attached to the `Table` element:
The `Table` component abstracts the `<table>` element in HTML.
```tsx
import TableContainer from '@mui/material/TableContainer';
import Table from '@mui/material/Table';
// ...
### Importing Data
Starting from a SheetJS worksheet object[^1], the `sheet_to_json` method[^2]
generates an array of row objects.
In the [ReactJS "Array of Objects" demo](/docs/demos/frontend/react), the array
of objects is rendered by manually mapping over data. For example, starting from
the following spreadsheet and data:
<table>
<thead><tr><th>Spreadsheet</th><th>State</th></tr></thead>
<tbody><tr><td>
![`pres.xlsx` data](pathname:///pres.png)
</td><td>
```js
[
{ Name: "Bill Clinton", Index: 42 },
{ Name: "GeorgeW Bush", Index: 43 },
{ Name: "Barack Obama", Index: 44 },
{ Name: "Donald Trump", Index: 45 },
{ Name: "Joseph Biden", Index: 46 }
]
```
</td></tr></tbody></table>
The HTML table elements map to MUI components:
| HTML | MUI |
|:--------|:------------|
| `TABLE` | `Table` |
| `THEAD` | `TableHead` |
| `TBODY` | `TableBody` |
| `TR` | `TableRow` |
| `TD` | `TableCell` |
The library requires a `TableContainer` container component.
The following example JSX shows a table using HTML and using MUI components:
<Tabs>
<TabItem value="ReactJS" label="ReactJS">
```jsx title="Example JSX for displaying arrays of objects"
<table>
{/* The `thead` section includes the table header row */}
<thead><tr><th>Name</th><th>Index</th></tr></thead>
{/* The `tbody` section includes the data rows */}
<tbody>
{/* generate row (TR) for each president */}
// highlight-start
{pres.map(row => (
<tr>
{/* Generate cell (TD) for name / index */}
<td>{row.Name}</td>
<td>{row.Index}</td>
</tr>
))}
// highlight-end
</tbody>
</table>
```
</TabItem>
<TabItem value="MUI" label="Material UI">
```jsx title="Example JSX for displaying arrays of objects"
<TableContainer><Table>
{/* The `TableHead` section includes the table header row */}
<TableHead><TableRow><TableCell>Name</TableCell><TableCell>Index</TableCell></TableRow></TableHead>
{/* The `TableBody` section includes the data rows */}
<TableBody>
{/* generate row (TableRow) for each president */}
// highlight-start
{pres.map((row, idx) => (
<TableRow key={idx}>
{/* Generate cell (TableCell) for name / index */}
<TableCell>{row.Name}</TableCell>
<TableCell>{row.Index}</TableCell>
</TableRow>
))}
// highlight-end
</TableBody>
</Table></TableContainer>
```
</TabItem>
</Tabs>
### Exporting Data
The SheetJS `table_to_book` method[^3] can parse data from a DOM element.
The MUI `Table` element is really a HTML TABLE element under the hood. A `ref`
attached to the `Table` element can be processed by `table_to_book`.
The following snippet uses the `writeFileXLSX` method[^4] to generate and
download a XLSX workbook:
```tsx title="Skeleton Component for exporting a Material UI Table"
// highlight-start
import { utils, writeFileXLSX } from "xlsx";
import { useRef } from "react";
// highlight-end
// ...
export default function BasicTable() {
export default function MUITableSheetJSExport() {
/* This ref will be attached to the <Table> component */
// highlight-next-line
const tbl = useRef<HTMLTableElement>(null);
const xport = () => {
/* the .current field will be a TABLE element */
const table_elt = tbl.current;
/* generate SheetJS workbook */
// highlight-next-line
const wb = utils.table_to_book(table_elt);
/* export to XLSX */
writeFileXLSX(wb, "SheetJSMaterialUI.xlsx");
};
return ( <>
<button onClick={() => {
// highlight-next-line
const wb = utils.table_to_book(tbl.current);
writeFileXLSX(wb, "SheetJSMaterialUI.xlsx");
}}>Export</button>
<TableContainer {...}>
<button onClick={xport}>Export</button>
<TableContainer>
// highlight-next-line
<Table {...} ref={tbl}>
{/* ... material ui table machinations ... */}
</Table>
<Table ref={tbl}>{/* ... */}</Table>
</TableContainer>
<>);
}
@ -52,8 +182,11 @@ export default function BasicTable() {
:::note Tested Deployments
This demo was last run on 2023 December 04 against Material UI 5.14.19 paired
with Emotion 11.11.1
This demo was tested in the following deployments:
| Material UI | Emotion | Date |
|:------------|:----------|:-----------|
| `5.15.20` | `11.11.4` | 2024-06-12 |
:::
@ -67,7 +200,7 @@ cd sheetjs-mui
2) Install dependencies:
<CodeBlock language="bash">{`\
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @mui/material@5.14.19 @emotion/react@11.11.1 @emotion/styled@11.11.0`}
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @mui/material@5.15.20 @emotion/react@11.11.4 @emotion/styled@11.11.5`}
</CodeBlock>
3) Download [`App.tsx`](pathname:///mui/table/App.tsx) and replace `src/App.tsx`.
@ -195,8 +328,11 @@ export default function App() {
:::note Tested Deployments
This demo was last run on 2023 December 04 against MUI data grid 6.18.3 paired
with Emotion 11.11.1
This demo was tested in the following deployments:
| Data Grid | Emotion | Date |
|:----------|:----------|:-----------|
| `7.6.2` | `11.11.4` | 2024-06-12 |
:::
@ -210,7 +346,7 @@ cd sheetjs-muidg
2) Install dependencies:
<CodeBlock language="bash">{`\
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @mui/x-data-grid@6.18.3 @emotion/react@11.11.1 @emotion/styled@11.11.0`}
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @mui/x-data-grid@7.6.2 @emotion/react@11.11.4 @emotion/styled@11.11.5`}
</CodeBlock>
3) Download [`App.tsx`](pathname:///mui/dg/App.tsx) and replace `src/App.tsx`.
@ -225,4 +361,9 @@ curl -L -o src/App.tsx https://docs.sheetjs.com/mui/dg/App.tsx
npm run dev
```
When the page loads, it will fetch and process <https://sheetjs.com/pres.numbers>
When the page loads, it will process https://docs.sheetjs.com/pres.numbers
[^1]: See ["Sheet Objects"](/docs/csf/sheet)
[^2]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
[^3]: See [`table_to_book` in "HTML" Utilities](/docs/api/utilities/html#create-new-sheet)
[^4]: See [`writeFileXLSX` in "Writing Files"](/docs/api/write-options)

@ -30,8 +30,7 @@ The `sheet_to_json` utility function generates arrays of objects, which is
suitable for a number of libraries. When more advanced shapes are needed,
it is easier to process an array of arrays.
### x-spreadsheet
#### x-spreadsheet
With a familiar UI, `x-spreadsheet` is an excellent choice for a modern editor.
@ -39,7 +38,7 @@ With a familiar UI, `x-spreadsheet` is an excellent choice for a modern editor.
**[The exposition has been moved to a separate page.](/docs/demos/grid/xs)**
### Canvas Datagrid
#### Canvas Datagrid
After extensive testing, `canvas-datagrid` stood out as a high-performance grid
with a straightforward API.
@ -48,15 +47,16 @@ with a straightforward API.
**[The exposition has been moved to a separate page.](/docs/demos/grid/cdg)**
### Tabulator
#### Tabulator
[Tabulator](https://tabulator.info/docs/5.4/download#xlsx) includes deep support
through a special Export button. It handles the SheetJS operations internally.
**[The exposition has been moved to a separate page.](/docs/demos/grid/tabulator)**
### Angular UI Grid
#### Angular UI Grid
:::warning pass
:::danger pass
This UI Grid is for AngularJS, not the modern Angular. New projects should not
use AngularJS. This demo is included for legacy applications.
@ -67,7 +67,8 @@ The [AngularJS demo](/docs/demos/frontend/angularjs) covers more general strateg
[Click here for a live integration demo.](pathname:///angularjs/ui-grid.html)
<details><summary><b>Notes</b> (click to show)</summary>
<details>
<summary><b>Notes</b> (click to show)</summary>
The library does not provide any way to modify the import button, so the demo
includes a simple directive for a File Input HTML element. It also includes a
@ -92,21 +93,21 @@ and idioms. The same `sheet_to_json` and `json_to_sheet` / `aoa_to_sheet`
methods are used, but they pull from a shared state object that can be mutated
with other buttons and components on the page.
### React Data Grid
#### React Data Grid
**[The exposition has been moved to a separate page.](/docs/demos/grid/rdg)**
### Glide Data Grid
#### Glide Data Grid
**[The exposition has been moved to a separate page.](/docs/demos/grid/gdg)**
### Material UI Data Grid
#### Material UI Data Grid
**[The exposition has been moved to a separate page.](/docs/demos/grid/mui#material-ui-data-grid)**
<!-- spellchecker-disable -->
### vue3-table-lite
#### vue3-table-lite
<!-- spellchecker-enable -->
@ -133,7 +134,7 @@ TABLE elements and when writing to XLSX and other spreadsheet formats.
:::
### Fixed Tables
#### Fixed Tables
When the page has a raw HTML table, the easiest solution is to attach an `id`:
@ -170,10 +171,10 @@ XLSX.writeFile(wb, "HTMLFlicker.xlsx");
document.body.removeChild(tbl);
```
### React
#### React
**[The exposition has been moved to a separate page.](/docs/demos/frontend/react#html)**
### Material UI Table
#### Material UI Table
**[The exposition has been moved to a separate page.](/docs/demos/grid/mui#material-ui-table)**

@ -57,8 +57,8 @@ The lines are automatically added if `sheets` plugin is enabled during setup.
Spreadsheet files added in the `_data` subdirectory are accessible from template
files using the name stem.
For example, [`pres.xlsx`](https://sheetjs.com/pres.xlsx) can be accessed using
the variable `pres` in a template.
For example, [`pres.xlsx`](https://docs.sheetjs.com/pres.xlsx) can be accessed
using the variable `pres` in a template.
#### Single-Sheet Workbooks
@ -168,11 +168,11 @@ The `nunjucks` plugin was included by default in Lume version 1.
:::
2) Download <https://sheetjs.com/pres.xlsx> and place in a `_data` subfolder:
2) Download https://docs.sheetjs.com/pres.xlsx and place in a `_data` subfolder:
```bash
mkdir -p _data
curl -L -o _data/pres.xlsx https://sheetjs.com/pres.xlsx
curl -L -o _data/pres.xlsx https://docs.sheetjs.com/pres.xlsx
```
3) Create a `index.njk` file that references the file:
@ -216,7 +216,9 @@ After saving the spreadsheet, the page will refresh and show the new contents.
### Static Site
6) Stop the server (press `CTRL+C` in the terminal window) and run
6) Stop the server (press <kbd>CTRL</kbd>+<kbd>C</kbd> in the terminal window).
7) Build the static site:
```bash
deno task lume
@ -224,7 +226,7 @@ deno task lume
This will create a static site in the `_site` folder
7) Test the generated site by running
7) Test the generated site by starting a web server:
```bash
npx http-server _site

@ -13,6 +13,9 @@ import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeBlock from '@theme/CodeBlock';
export const r = {style: {color:"red"}};
export const R = {style: {backgroundColor:"darkred"}};
GatsbyJS is a framework for creating websites. It uses React components for page
templates and GraphQL for loading data.
@ -48,7 +51,7 @@ overridden through a `package.json` override in the latest versions of NodeJS:
:::
:::warning Telemetry
:::danger Telemetry
GatsbyJS collects telemetry by default. The `telemetry` subcommand can disable it:
@ -176,7 +179,7 @@ This demo was tested in the following environments:
| GatsbyJS | Date |
|:---------|:-----------|
| `5.12.1` | 2023-12-04 |
| `5.13.4` | 2024-05-04 |
| `4.25.8` | 2024-03-27 |
:::
@ -189,6 +192,25 @@ This demo was tested in the following environments:
npx gatsby telemetry --disable
```
:::info pass
In NodeJS 22, the process displayed an error:
<pre>
<span {...R}> ERROR </span><span {...r}> UNKNOWN</span>
{`\n`}
{`\n`}
(node:25039) [DEP0040] DeprecationWarning: The `punycode` module is deprecated.
Please use a userland alternative instead.
(Use `node --trace-deprecation ...` to show where the warning was created)
</pre>
**This is a false report!**
The error can be safely ignored.
:::
1) Create a template site:
```bash
@ -255,12 +277,12 @@ npm i --save gatsby-transformer-excel@4 gatsby-source-filesystem@4
:::
5) Make a `src/data` directory, download <https://sheetjs.com/pres.xlsx>, and
5) Make a `src/data` directory, download https://docs.sheetjs.com/pres.xlsx, and
move the downloaded file into the new folder:
```bash
mkdir -p src/data
curl -L -o src/data/pres.xlsx https://sheetjs.com/pres.xlsx
curl -L -o src/data/pres.xlsx https://docs.sheetjs.com/pres.xlsx
```
6) Edit `gatsby-config.js` and add the following lines to the `plugins` array:
@ -333,11 +355,60 @@ Press the Execute Query button (`▶`) and data should show up in the right pane
![GraphiQL Screenshot](pathname:///gatsby/graphiql.png)
<details>
<summary><b>Sample Output</b> (click to show)</summary>
In GatsbyJS version `5.13.4`, the raw output was:
```json title="GraphQL query result from GatsbyJS 5.13.4"
{
"data": {
"allPresXlsxSheet1": {
"edges": [
{
"node": {
"Name": "Bill Clinton",
"Index": 42
}
},
{
"node": {
"Name": "GeorgeW Bush",
"Index": 43
}
},
{
"node": {
"Name": "Barack Obama",
"Index": 44
}
},
{
"node": {
"Name": "Donald Trump",
"Index": 45
}
},
{
"node": {
"Name": "Joseph Biden",
"Index": 46
}
}
]
}
},
"extensions": {}
}
```
</details>
### React page
8) Create a new file `src/pages/pres.js` that uses the query and displays the result:
```jsx title="src/pages/pres.js"
```jsx title="src/pages/pres.js (create new file)"
import { graphql } from "gatsby"
import * as React from "react"

@ -32,8 +32,8 @@ importing the SheetJS library in a browser script.
## ESBuild Loader
ESBuild supports custom loader plugins. The loader receives an absolute path to
the spreadsheet on the filesystem.
ESBuild releases starting from `0.9.1` support custom loader plugins. The loader
receives an absolute path to the spreadsheet on the filesystem.
The [SheetJS NodeJS module](/docs/getting-started/installation/nodejs) can be
imported from ESBuild loader plugins.
@ -200,7 +200,23 @@ document.body.appendChild(elt);
:::note Tested Deployments
This demo was last tested on 2023 December 04 against ESBuild 0.19.8
This demo was tested in the following environments:
| `esbuild` | Date |
|:----------|:-----------|
| `0.20.2` | 2024-04-07 |
| `0.19.12` | 2024-04-07 |
| `0.18.20` | 2024-04-07 |
| `0.17.19` | 2024-04-07 |
| `0.16.17` | 2024-04-07 |
| `0.15.18` | 2024-04-07 |
| `0.14.54` | 2024-04-07 |
| `0.13.15` | 2024-04-07 |
| `0.12.29` | 2024-04-07 |
| `0.11.23` | 2024-04-07 |
| `0.10.2` | 2024-04-07 |
| `0.9.7` | 2024-04-07 |
| `0.9.1` | 2024-04-07 |
:::
@ -212,7 +228,7 @@ This demo was last tested on 2023 December 04 against ESBuild 0.19.8
mkdir sheetjs-esb
cd sheetjs-esb
npm init -y
npm i --save esbuild@0.19.8
npm i --save esbuild@0.20.2
```
1) Install the SheetJS NodeJS module:
@ -255,10 +271,10 @@ document.body.appendChild(elt);
curl -LO https://docs.sheetjs.com/esbuild/build.mjs
```
5) Download <https://sheetjs.com/pres.numbers> to the project folder:
5) Download https://docs.sheetjs.com/pres.numbers to the project folder:
```bash
curl -LO https://sheetjs.com/pres.numbers
curl -LO https://docs.sheetjs.com/pres.numbers
```
### Static Site Test

@ -12,15 +12,15 @@ sidebar_custom_props:
import current from '/version.js';
import CodeBlock from '@theme/CodeBlock';
[ViteJS](https://vitejs.dev/) is a modern build tool for generating static sites.
It has a robust JavaScript-powered plugin system[^1]
[ViteJS](https://vitejs.dev/) is a build tool for generating static websites. It
has a robust JavaScript-powered plugin system[^1].
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
data from spreadsheets.
This demo uses ViteJS and SheetJS to pull data from a spreadsheet and display
the content in an HTML table. We'll explore how to load SheetJS in a ViteJS
plugin and compare a few different data loading strategies.
plugin and evaluate data loading strategies.
The ["Complete Demo"](#complete-demo) section creates a complete website powered
by a XLSX spreadsheet.
@ -32,16 +32,97 @@ suitable for end of week or end of month (EOM) reports published in HTML tables.
For processing user-submitted files in the browser, the
[ViteJS "Bundlers" demo](/docs/demos/frontend/bundler/vitejs) shows client-side
bundling of the SheetJS library. The ["ReactJS" demo](/docs/demos/frontend/react)
bundling of SheetJS libraries. The ["ReactJS" demo](/docs/demos/frontend/react)
shows example sites using ViteJS with the ReactJS starter.
:::
## Plugins
ViteJS supports static asset imports[^2], but the default raw loader interprets data
as UTF-8 strings. This corrupts binary formats like XLSX and XLS, but a custom
loader can override the default behavior.
ViteJS supports static asset imports[^2], but the default raw loader interprets
data as UTF-8 strings. This corrupts binary formats including XLSX and XLS. A
custom loader can bypass the raw loader and directly read files.
Since a custom loader must be used, some data processing work can be performed
by the loader. Three approaches are explored in this demo.
The following diagrams show the ViteJS data flow. The pink "Main Script import"
boxes represent the division between the loader and the main script. The green
"SheetJS Operations" boxes represent the steps performed by SheetJS libraries.
<table>
<tr>
<th>[HTML](#html-plugin)</th>
<th>[Data](#pure-data-plugin)</th>
<th>[Base64](#base64-plugin)</th>
</tr>
<tr>
<td style={{verticalAlign: "top"}}>
```mermaid
flowchart TB
file[(workbook\nfile)]
buffer(NodeJS\nBuffer)
sheetjs[[SheetJS Operations]]
tabeller{{HTML\nString}}
handoff[[Main Script import]]
html{{HTML\nTABLE}}
style handoff fill:#FFC7CE
style sheetjs fill:#C6EFCE
file --> buffer
buffer --> sheetjs
sheetjs --> tabeller
tabeller --> handoff
handoff --------> html
```
</td>
<td style={{verticalAlign: "top"}}>
```mermaid
flowchart TB
file[(workbook\nfile)]
buffer(NodeJS\nBuffer)
sheetjs[[SheetJS Operations]]
aoo(array of\nobjects)
handoff[[Main Script import]]
import(array of\nobjects)
html{{HTML\nTABLE}}
style handoff fill:#FFC7CE
style sheetjs fill:#C6EFCE
file --> buffer
buffer --> sheetjs
sheetjs --> aoo
aoo --> handoff
handoff ------> import
import --> html
```
</td>
<td style={{verticalAlign: "top"}}>
```mermaid
flowchart TB
file[(workbook\nfile)]
base64(Base64\nString)
handoff[[Main Script import]]
import(Base64\nString)
sheetjs[[SheetJS Operations]]
aoo(array of\nobjects)
html{{HTML\nTABLE}}
style handoff fill:#FFC7CE
style sheetjs fill:#C6EFCE
file --> base64
base64 ------> handoff
handoff --> import
import --> sheetjs
sheetjs --> aoo
aoo --> html
```
</td>
</tr>
</table>
For simple tables of data, ["Pure Data Plugin"](#pure-data-plugin) is strongly
recommended. The file processing is performed at build time and the generated
@ -51,6 +132,9 @@ For more complex parsing or display logic, ["Base64 Plugin"](#base64-plugin) is
preferable. Since the raw parsing logic is performed in the page, the library
will be included in the final bundle.
The ["HTML Plugin"](#html-plugin) generates HTML in the loader script. The
SheetJS HTML writer renders merged cells and other features.
### Pure Data Plugin
For a pure static site, a plugin can load data into an array of row objects. The
@ -72,7 +156,7 @@ flowchart LR
```
This ViteJS plugin will read spreadsheets using the SheetJS `read` method[^3]
and generate arrays of row objects with `sheet_to_json`[^4]:
and generate arrays of row objects with the SheetJS `sheet_to_json`[^4] method:
```js title="vite.config.js"
import { readFileSync } from 'fs';
@ -89,13 +173,23 @@ export default defineConfig({
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)}')`;
return `export default JSON.parse('${JSON.stringify(data).replace(/\\/g, "\\\\")}')`;
}
}
]
});
```
:::info pass
ViteJS plugins are expected to return strings representing ECMAScript modules.
The plugin uses `JSON.stringify` to encode the array of objects. The generated
string is injected into the new module code. When ViteJS processes the module,
`JSON.parse` recovers the array of objects.
:::
In frontend code, the loader will look for all modules with a `?sheetjs`
query string. The default export is an array of row objects.
@ -115,9 +209,75 @@ document.querySelector('#app').innerHTML = `<table>
</table>`;
```
### HTML Plugin
A plugin can generate raw HTML strings that can be added to a page. The SheetJS
libraries are used in the plugin but will not be added to the site.
The following diagram depicts the workbook waltz:
```mermaid
flowchart LR
file[(workbook\nfile)]
subgraph SheetJS operations
buffer(NodeJS\nBuffer)
tavolo{{HTML\nString}}
end
html{{HTML\nTABLE}}
file --> |vite.config.js\ncustom plugin| buffer
buffer --> |vite.config.js\ncustom plugin| tavolo
tavolo --> |main.js\nfrontend code| html
```
This ViteJS plugin will read spreadsheets using the SheetJS `read` method[^5]
and generate HTML using the SheetJS `sheet_to_html`[^6] method:
```js title="vite.config.js"
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 ?html tags
name: "vite-sheet-html",
transform(code, id) {
if(!id.match(/\?html/)) return;
var wb = read(readFileSync(id.replace(/\?html/, "")));
var html = utils.sheet_to_html(wb.Sheets[wb.SheetNames[0]]);
return (`export default JSON.parse('${JSON.stringify(html).replace(/\\/g, "\\\\")}')`);
}
}
]
});
```
:::info pass
ViteJS plugins are expected to return strings representing ECMAScript modules.
The plugin uses `JSON.stringify` to encode the HTML string. The generated string
is injected into the new module code. When ViteJS processes the module,
`JSON.parse` recovers the original HTML string.
:::
In frontend code, the loader will look for all modules with a `?html` query
string. The default export is a string that can be directly added to the page.
The following example script sets the `innerHTML` property of the container:
```js title="main.js"
import html from './data/pres.xlsx?html';
document.querySelector('#app').innerHTML = html;
```
### Base64 Plugin
This plugin pulls in data as a Base64 string that can be read with `read`[^5].
This plugin pulls in data as a Base64 string that can be read with `read`[^7].
While this approach works, it is not recommended since it loads the library in
the front-end site.
@ -161,7 +321,7 @@ export default defineConfig({
```
When importing using the `b64` query, the raw Base64 string will be exposed.
`read` will process the Base64 string using the `base64` input type[^6]:
`read` will process the Base64 string using the `base64` input type[^8]:
```js title="main.js"
import { read, utils } from "xlsx";
@ -187,22 +347,22 @@ document.querySelector('#app').innerHTML = `<table>
## Complete Demo
The demo walks through the process of creating a new ViteJS website from scratch.
A Git repository with the completed site can be cloned[^9].
:::note Tested Deployments
This demo was tested in the following environments:
| ViteJS | Date |
|:---------|:-----------|
| `5.0.5` | 2023-12-04 |
| `4.5.0` | 2023-12-04 |
| `3.2.7` | 2023-12-04 |
| `2.9.16` | 2023-12-04 |
| `5.2.12` | 2024-06-02 |
| `4.5.3` | 2024-06-02 |
| `3.2.10` | 2024-06-02 |
| `2.9.18` | 2024-06-02 |
:::
The demo walks through the process of creating a new ViteJS website from scratch.
A Git repository with the completed site can be cloned[^7].
### Initial Setup
1) Create a new site with the `vue-ts` template and install the SheetJS package:
@ -227,11 +387,11 @@ npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
curl -O https://docs.sheetjs.com/vitejs/vite.config.ts
```
3) Make a `data` folder and download <https://sheetjs.com/pres.xlsx> :
3) Make a `data` folder and download https://docs.sheetjs.com/pres.xlsx :
```bash
mkdir -p data
curl -L -o data/pres.xlsx https://sheetjs.com/pres.xlsx
curl -L -o data/pres.xlsx https://docs.sheetjs.com/pres.xlsx
```
### Pure Data Test
@ -275,9 +435,30 @@ npx http-server dist/
The terminal will display a URL, typically `http://127.0.0.1:8080` . Access
that page with a web browser.
:::caution pass
When this demo was tested against ViteJS `2.9.18`, the build failed:
```
src/App.vue:8:3 - error TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
8 <img alt="Vue logo" src="./assets/logo.png" />
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
```
**As it affects the project template, this is a bug in ViteJS.**
The simplest workaround is to force upgrade the `vue-tsc` dependency:
```bash
npm i vue-tsc@latest
```
:::
7) To confirm that only the raw data is present in the page, view the page
source. The code will reference some script like `/assets/index-HASH.js`.
Open that script.
source. The code will reference a script `/assets/index-HASH.js` where `HASH` is
a string of characters. Open that script.
Searching for `Bill Clinton` reveals the following:
@ -291,11 +472,11 @@ included in the final site!
:::info pass
ViteJS also supports "Server-Side Rendering". In SSR, only the HTML table
would be added to the final page. Details are covered in the ViteJS docs[^8].
would be added to the final page. Details are covered in the ViteJS docs[^10].
:::
### Base64 Test
### HTML Test
8) Run the dev server:
@ -303,10 +484,88 @@ would be added to the final page. Details are covered in the ViteJS docs[^8].
npm run dev
```
Open a browser window to the displayed URL.
Open a browser window to the displayed URL (typically `http://localhost:5173` )
9) Replace the component `src/components/HelloWorld.vue` with:
```html title="src/components/HelloWorld.vue"
<script setup lang="ts">
// @ts-ignore
import html from '../../data/pres.xlsx?html';
</script>
<template>
<div v-html="html"></div>
</template>
```
Save and refresh the page. A data table should be displayed
10) Stop the dev server and build the site
```bash
npm run build
npx http-server dist/
```
The terminal will display a URL, typically `http://127.0.0.1:8080` . Access
that page with a web browser.
:::caution pass
When this demo was tested against ViteJS `2.9.18`, the build failed:
```
src/App.vue:8:3 - error TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
8 <img alt="Vue logo" src="./assets/logo.png" />
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
```
**As it affects the project template, this is a bug in ViteJS.**
The simplest workaround is to force upgrade the `vue-tsc` dependency:
```bash
npm i vue-tsc@latest
```
:::
11) To confirm that only the raw HTML is present in the page, view the page
source. The code will reference a script `/assets/index-HASH.js` where `HASH` is
a string of characters. Open that script.
Searching for `Bill Clinton` reveals the following encoded HTML element:
```
<td data-t=\\"s\\" data-v=\\"Bill Clinton\\" id=\\"sjs-A2\\">Bill Clinton</td>
```
Searching for `BESSELJ` should reveal no results. The SheetJS scripts are not
included in the final site!
:::info pass
The HTML code is still stored in a script and is injected dynamically.
ViteJS "Server-Side Rendering" offers the option to render the site at build
time, ensuring that the HTML table is directly added to the page.
:::
### Base64 Test
12) Run the dev server:
```bash
npm run dev
```
Open a browser window to the displayed URL (typically `http://localhost:5173` )
13) Replace the component `src/components/HelloWorld.vue` with:
```html title="src/components/HelloWorld.vue"
<script setup lang="ts">
// @ts-ignore
@ -330,7 +589,7 @@ const data = utils.sheet_to_json<IPresident>(ws);
</template>
```
10) Stop the dev server and build the site
14) Stop the dev server and build the site
```bash
npm run build
@ -340,9 +599,30 @@ npx http-server dist/
The terminal will display a URL ( `http://127.0.0.1:8080` ). Access that page
with a web browser.
11) To confirm that the object data is not present in the page, view the page
source. The code will reference some script like `/assets/index-HASH.js` with
a different hash from the previous test. Open that script.
:::caution pass
When this demo was tested against ViteJS `2.9.18`, the build failed:
```
src/App.vue:8:3 - error TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
8 <img alt="Vue logo" src="./assets/logo.png" />
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
```
**As it affects the project template, this is a bug in ViteJS.**
The simplest workaround is to force upgrade the `vue-tsc` dependency:
```bash
npm i vue-tsc@latest
```
:::
15) To confirm that the object data is not present in the page, view the page
source. The code will reference a script `/assets/index-HASH.js` where `HASH` is
a string of characters. Open that script.
Searching for `BESSELJ` should match the code:
@ -356,8 +636,10 @@ embedded in the final site and the data is parsed when the page is loaded.
[^1]: See ["Using Plugins"](https://vitejs.dev/guide/using-plugins.html) in the ViteJS documentation.
[^2]: See ["Static Asset Handling"](https://vitejs.dev/guide/assets.html) in the ViteJS documentation.
[^3]: See [`read` in "Reading Files"](/docs/api/parse-options)
[^4]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
[^4]: See [`sheet_to_html` in "Utilities"](/docs/api/utilities/html#html-table-output)
[^5]: See [`read` in "Reading Files"](/docs/api/parse-options)
[^6]: See [the "base64" type in "Reading Files"](/docs/api/parse-options#input-type)
[^7]: See [`SheetJS/sheetjs-vite`](https://git.sheetjs.com/sheetjs/sheetjs-vite/) on the SheetJS git server.
[^8]: See ["Server-Side Rendering"](https://vitejs.dev/guide/ssr.html) in the ViteJS documentation.
[^6]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
[^7]: See [`read` in "Reading Files"](/docs/api/parse-options)
[^8]: See [the "base64" type in "Reading Files"](/docs/api/parse-options#input-type)
[^9]: See [`examples/sheetjs-vite`](https://git.sheetjs.com/examples/sheetjs-vite/) on the SheetJS git server.
[^10]: See ["Server-Side Rendering"](https://vitejs.dev/guide/ssr.html) in the ViteJS documentation.

@ -57,9 +57,23 @@ flowchart LR
### Webpack Config
A special rule should be added to `module.rules`:
The Webpack configuration is normally saved to `webpack.config.js`.
```js title="webpack.config.js"
#### Required Settings
`module.rules` is an array of rule objects that controls module synthesis.[^2]
For the SheetJS Webpack integration, the following properties are required:
- `test` describes whether the rule is relevant. If the property is a regular
expression, Webpack will test the filename against the `test` property.
- `use` lists the loaders that will process files matching the `test`. The
loaders are specified using the `loader` property of the loader object.
The following example instructs Webpack to use the `sheetjs-loader.js` script
when the file name ends in `.numbers` or `.xls` or `.xlsx` or `.xlsb`:
```js title="webpack.config.js (define loader)"
// ...
module.exports = {
// ...
@ -68,7 +82,7 @@ module.exports = {
// highlight-start
{
/* `test` matches file extensions */
test: /\.(numbers|xls|xlsx|xlsb)/,
test: /\.(numbers|xls|xlsx|xlsb)$/,
/* use the loader script */
use: [ { loader: './sheetjs-loader' } ]
}
@ -78,24 +92,21 @@ module.exports = {
};
```
Hot Module Replacement enables reloading when files are updated:
#### Recommended Settings
```js title="webpack.config.js"
// ...
module.exports = {
// ...
// highlight-start
devServer: {
static: './dist',
hot: true,
}
// highlight-end
};
```
It is strongly recommended to enable other Webpack features:
It is strongly recommended to add an alias to simplify imports:
- `resolve.alias` defines path aliases. If data files are stored in one folder,
an alias ensures that each page can reference the files using the same name[^3].
```js title="webpack.config.js"
- `devServer.hot` enables "hot module replacement"[^4], ensuring that pages will
refresh in development mode when spreadsheets are saved.
The following example instructs Webpack to treat `~` as the root of the project
(so `~/data/pres.xlsx` refers to `pres.xlsx` in the data folder) and to enable
live reloading:
```js title="webpack.config.js (other recommended settings)"
// ...
module.exports = {
// ...
@ -107,21 +118,32 @@ module.exports = {
}
},
// highlight-end
// ...
// highlight-start
/* enable live reloading in development mode */
devServer: { static: './dist', hot: true }
// highlight-end
};
```
### SheetJS Loader
The SheetJS loader script must export a `raw` property that is set to `true`.
The SheetJS loader script must be saved to the script referenced in the Webpack
configuration (`sheetjs-loader.js`).
As with [ViteJS](/docs/demos/static/vitejs), Webpack will interpret data as
UTF-8 strings. This corrupts binary formats including XLSX and XLS. To suppress
this behavior and instruct Webpack to pass a NodeJS `Buffer` object, the loader
script must export a `raw` property that is set to `true`[^5].
The base export is expected to be the loader function. The loader receives the
file bytes as a Buffer, which can be parsed with the SheetJS `read` method[^2].
`read` returns a SheetJS workbook object[^3].
file bytes as a Buffer, which can be parsed with the SheetJS `read` method[^6].
`read` returns a SheetJS workbook object[^7].
The loader in this demo will parse the workbook, pull the first worksheet, and
generate an array of row objects using the `sheet_to_json` method[^4]:
generate an array of row objects using the `sheet_to_json` method[^8]:
```js title="sheetjs-loader.js"
```js title="sheetjs-loader.js (Webpack loader)"
const XLSX = require("xlsx");
function loader(content) {
@ -131,8 +153,11 @@ function loader(content) {
var data = XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]);
return `export default JSON.parse('${JSON.stringify(data)}')`;
}
/* ensure the function receives a Buffer */
loader.raw = true;
/* export the loader */
module.exports = loader;
```
@ -141,7 +166,7 @@ module.exports = loader;
Spreadsheets can be imported using the plugin. Assuming `pres.xlsx` is stored
in the `data` subfolder, `~/data/pres.xlsx` can be imported from any script:
```js title="src/index.js"
```js title="src/index.js (main script)"
import data from '~/data/pres.xlsx';
/* `data` is an array of objects from data/pres.xlsx */
@ -159,7 +184,7 @@ document.body.appendChild(elt);
:::note Tested Deployments
This demo was last tested on 2023 December 04 against Webpack 5.89.0
This demo was last tested on 2024 April 06 against Webpack 5.91.0
:::
@ -171,7 +196,7 @@ This demo was last tested on 2023 December 04 against Webpack 5.89.0
mkdir sheetjs-wp5
cd sheetjs-wp5
npm init -y
npm install webpack@5.89.0 webpack-cli@5.1.4 webpack-dev-server@4.15.1 --save
npm install webpack@5.91.0 webpack-cli@5.1.4 webpack-dev-server@5.0.4 --save
mkdir -p dist
mkdir -p src
mkdir -p data
@ -235,7 +260,7 @@ module.exports = {
module: {
rules: [
{
test: /\.(numbers|xls|xlsx|xlsb)/,
test: /\.(numbers|xls|xlsx|xlsb)$/,
use: [ { loader: './sheetjs-loader' } ]
}
]
@ -260,10 +285,10 @@ loader.raw = true;
module.exports = loader;
```
6) Download <https://sheetjs.com/pres.xlsx> and save to the `data` folder:
6) Download https://docs.sheetjs.com/pres.xlsx and save to the `data` folder:
```bash
curl -L -o data/pres.xlsx https://sheetjs.com/pres.xlsx
curl -L -o data/pres.xlsx https://docs.sheetjs.com/pres.xlsx
```
### Live Reload Test
@ -287,9 +312,10 @@ The terminal will print URLs for the development server:
It should display a table of Presidents with "Name" and "Index" columns
10) Add a new row to the spreadsheet and save the file.
10) Add a new row to the spreadsheet (set `A7` to "SheetJS Dev" and `B7` to 47)
and save the file.
Upon saving, the page should refresh with the new data.
After saving the file, the page should automatically refresh with the new data.
### Static Site Test
@ -320,6 +346,10 @@ To verify that the data was added to the page, append `main.js` to the URL
president names. It will not include SheetJS library references!
[^1]: See ["Plugins"](https://webpack.js.org/concepts/plugins/) in the Webpack documentation.
[^2]: See [`read` in "Reading Files"](/docs/api/parse-options)
[^3]: See ["Workbook Object"](/docs/csf/book)
[^4]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
[^2]: See [`module.rules`](https://webpack.js.org/configuration/module/#modulerules) in the Webpack documentation.
[^3]: See [`resolve.alias`](https://webpack.js.org/configuration/resolve/#resolvealias) in the Webpack documentation.
[^4]: See ["Hot Module Replacement"](https://webpack.js.org/concepts/hot-module-replacement/) in the Webpack documentation.
[^5]: See ["Raw" Loader](https://webpack.js.org/api/loaders/#raw-loader) in the Webpack documentation.
[^6]: See [`read` in "Reading Files"](/docs/api/parse-options)
[^7]: See ["Workbook Object"](/docs/csf/book)
[^8]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)

@ -91,8 +91,8 @@ module.exports = (eleventyConfig) => {
Spreadsheet files added in the `_data` subdirectory are accessible from template
files using the name stem.
For example, [`pres.numbers`](https://sheetjs.com/pres.numbers) can be accessed
using the variable `pres` in a template:
For example, [`pres.numbers`](https://docs.sheetjs.com/pres.numbers) can be
accessed using the variable `pres` in a template:
```liquid title="index.njk"
<table><thead><tr><th>Name</th><th>Index</th></tr></thead>
@ -151,11 +151,11 @@ npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @11ty/e
</Tabs>
3) Make a new `_data` subdirectory in the project. Download the example file
[`pres.xlsx`](https://sheetjs.com/pres.xlsx) into `_data`:
[`pres.xlsx`](https://docs.sheetjs.com/pres.xlsx) into `_data`:
```bash
mkdir _data
curl -Lo _data/pres.xlsx https://sheetjs.com/pres.xlsx
curl -Lo _data/pres.xlsx https://docs.sheetjs.com/pres.xlsx
```
4) Download the following files to the project folder:

@ -31,18 +31,18 @@ The ["Demo"](#demo) uses NextJS and SheetJS to pull data from a spreadsheet.
We'll explore how to create asset modules that process spreadsheet data at build
time and how to read files on the server in NextJS lifecycle methods.
:::warning Telemetry
:::danger Telemetry
NextJS collects telemetry by default. The `telemetry` subcommand can disable it:
```js
npx next@13.5.6 telemetry disable
npx -y next@13.5.6 telemetry disable
```
The setting can be verified by running
```js
npx next@13.5.6 telemetry status
npx -y next@13.5.6 telemetry status
```
:::
@ -75,12 +75,26 @@ This demo was tested in the following environments:
| NextJS | NodeJS | Date |
|:----------|:----------|:-----------|
| ` 9.5.5` | `16.20.2` | 2023-12-04 |
| `10.2.3` | `16.20.2` | 2023-12-04 |
| `11.1.4` | `16.20.2` | 2023-12-04 |
| `12.3.4` | `20.10.0` | 2023-12-04 |
| `13.5.6` | `20.10.0` | 2023-12-04 |
| `14.0.3` | `20.10.0` | 2023-12-04 |
| ` 9.5.5` | `16.20.2` | 2024-06-07 |
| `10.2.3` | `16.20.2` | 2024-06-07 |
| `11.1.4` | `16.20.2` | 2024-06-07 |
| `12.3.4` | `20.14.0` | 2024-06-07 |
| `13.5.6` | `20.14.0` | 2024-06-07 |
| `14.2.3` | `20.14.0` | 2024-06-07 |
:::
:::info pass
SheetJS libraries work in legacy NextJS apps. Older versions of this demo have
been tested against versions `3.2.3`, `4.2.3`, `5.1.0`, `6.1.2` and `7.0.3`.
NextJS has made a number of breaking changes over the years. Older versions of
NextJS use legacy versions of ReactJS that do not support function components
and other idioms.
[`examples/reactjs-legacy`](https://git.sheetjs.com/examples/reactjs-legacy) on
the SheetJS git server includes code samples for legacy NextJS versions.
:::
@ -196,7 +210,7 @@ export async function getStaticProps() {
The [SheetJS NodeJS module](/docs/getting-started/installation/nodejs) can be
imported from page scripts.
:::warning pass
:::danger pass
[The SheetJS ESM build](/docs/getting-started/installation/nodejs#esm-import)
does not load NodeJS native modules directly. The Installation section includes
@ -243,7 +257,7 @@ export async function getServerSideProps() {
}
```
:::warning Reading and writing files during the build process
:::danger Reading and writing files during the build process
As the NextJS workaround is non-traditional, it bears repeating:
@ -554,13 +568,13 @@ When upgrading NextJS is not an option, NodeJS should be downgraded to v16.
0) Disable NextJS telemetry:
```js
npx next@13.5.6 telemetry disable
npx -y next@13.5.6 telemetry disable
```
Confirm it is disabled by running
```js
npx next@13.5.6 telemetry status
npx -y next@13.5.6 telemetry status
```
1) Set up folder structure. At the end, a `pages` folder with a `sheets`
@ -583,7 +597,7 @@ curl -LO https://docs.sheetjs.com/next/sheetjs.xlsx
:::note pass
The `next@13.5.6` dependency can be adjusted to pick a different version. For
The `next@13.5.6` depefndency can be adjusted to pick a different version. For
example, NextJS `12.3.4` is installed with
<CodeBlock language="bash">{`\
@ -699,6 +713,13 @@ As explained in the summary, the `/getStaticPaths` and `/getStaticProps` routes
are completely static. 2 `/sheets/#` pages were generated, corresponding to 2
worksheets in the file. `/getServerSideProps` is server-rendered.
:::info pass
NextJS historically used lowercase Lambda (`λ`) to denote dynamic paths. This
was changed to a stylized lowercase F (`ƒ`) in recent versions of NextJS.
:::
9) Try to build a static site:
<Tabs groupId="nextver">
@ -711,7 +732,7 @@ npx next export
</TabItem>
<TabItem value="14" label="NextJS 14">
:::warning NextJS breaking changes
:::danger NextJS breaking changes
**NextJS 14 removed the `export` subcommand!**
@ -741,7 +762,13 @@ This build will fail. A static page cannot be generated at this point because
### Static Site
10) Delete `pages/getServerSideProps.js` and rebuild:
10) Delete `pages/getServerSideProps.js`:
```bash
rm -f pages/getServerSideProps.js
```
11) Rebuild the static site:
<Tabs groupId="nextver">
<TabItem value="13" label="NextJS 9 - 13">
@ -758,17 +785,16 @@ module.exports = {
webpack: (config) => {
```
After editing `next.config.js`:
After editing `next.config.js`, run the build command:
</TabItem>
</Tabs>
```bash
rm -f pages/getServerSideProps.js
npx next build
```
Inspecting the output, there should be no lines with the `λ` symbol:
Inspecting the output, there should be no lines with `λ` or `ƒ`:
```
Route (pages) Size First Load JS
@ -782,7 +808,7 @@ Route (pages) Size First Load JS
└ /sheets/1
```
11) Generate the static site:
12) Generate the static site:
<Tabs groupId="nextver">
<TabItem value="13" label="NextJS 9 - 13">
@ -814,7 +840,7 @@ npx next build
The static site will be written to the `out` subfolder
12) Serve the static site:
13) Serve the static site:
```bash
npx http-server out
@ -825,16 +851,16 @@ testing the generated site. Note that `/getServerSideProps` will 404 since the
page was removed.
[^1]: See the ["Webpack" asset module demo](/docs/demos/static/webpack) for more details.
[^2]: See [`read` in "Reading Files"](/docs/api/parse-options)
[^2]: See [`read` in "Reading Files"](/docs/api/parse-options).
[^3]: See ["SheetJS Data Model"](/docs/csf/) for more details.
[^4]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
[^5]: See [`readFile` in "Reading Files"](/docs/api/parse-options)
[^6]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
[^4]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output).
[^5]: See [`readFile` in "Reading Files"](/docs/api/parse-options).
[^6]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output).
[^7]: See [`getStaticProps`](https://nextjs.org/docs/pages/building-your-application/data-fetching/get-static-props) in the NextJS documentation.
[^8]: See [`getStaticPaths`](https://nextjs.org/docs/pages/building-your-application/data-fetching/get-static-paths) in the NextJS documentation.
[^9]: See [`getServerSideProps`](https://nextjs.org/docs/pages/building-your-application/data-fetching/get-server-side-props) in the NextJS documentation.
[^10]: See [`fallback` in getStaticPaths](https://nextjs.org/docs/pages/api-reference/functions/get-static-paths#fallback-false) in the NextJS documentation.
[^11]: See [`sheet_to_html` in "Utilities"](/docs/api/utilities/html#html-table-output)
[^12]: [`dangerouslySetInnerHTML`](https://react.dev/reference/react-dom/components/common#common-props) is a ReactJS prop supported for all built-in components.
[^13]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
[^14]: See ["Array of Objects" in the ReactJS demo](/docs/demos/frontend/react#rendering-data)
[^13]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output).
[^14]: See ["Array of Objects" in the ReactJS demo](/docs/demos/frontend/react#rendering-data).

@ -48,12 +48,12 @@ This demo was tested in the following environments:
| Nuxt Content | Nuxt | Date |
|:-------------|:---------|:-----------|
| `1.15.1` | `2.17.2` | 2023-12-04 |
| `2.9.0` | `3.8.2` | 2023-12-04 |
| `1.15.1` | `2.17.3` | 2024-06-04 |
| `2.12.1` | `3.11.2` | 2024-06-04 |
:::
:::warning Telemetry
:::danger Telemetry
Nuxt embeds telemetry. According to the docs, it can be disabled with:
@ -61,7 +61,7 @@ Nuxt embeds telemetry. According to the docs, it can be disabled with:
npx nuxt telemetry disable
```
**At the time the demo was last tested, this command did not work.**
**When the demo was last tested, this command did not work.**
Disabling telemetry requires a few steps:
@ -100,6 +100,14 @@ Click "OK" in each window (3 windows) and restart your computer.
telemetry.enabled=false
```
The following command can be run in the Linux / MacOS terminal:
```bash
cat >~/.nuxtrc <<EOF
telemetry.enabled=false
EOF
```
3) For Nuxt 3 sites, set the `telemetry` option in the Nuxt config file (either `nuxt.config.ts` or `nuxt.config.js`):
```js title="nuxt.config.js"
@ -265,18 +273,18 @@ npx create-nuxt-app@4.0.0 sheetjs-nuxt
When prompted, enter the following options:
- `Project name`: press Enter (use default `sheetjs-nuxt`)
- `Programming language`: press Down Arrow (`TypeScript` selected) then Enter
- `Package manager`: select `Npm` and press Enter
- `UI framework`: select `None` and press Enter
- `Nuxt.js modules`: scroll to `Content`, select with Space, then press Enter
- `Linting tools`: press Enter (do not select any Linting tools)
- `Testing framework`: select `None` and press Enter
- `Rendering mode`: select `Universal (SSR / SSG)` and press Enter
- `Deployment target`: select `Static (Static/Jamstack hosting)` and press Enter
- `Development tools`: press Enter (do not select any Development tools)
- `What is your GitHub username?`: press Enter
- `Version control system`: select `None`
- `Project name`: press <kbd>Enter</kbd> (use default `sheetjs-nuxt`)
- `Programming language`: press <kbd></kbd> (`TypeScript` selected) then <kbd>Enter</kbd>
- `Package manager`: select `Npm` and press <kbd>Enter</kbd>
- `UI framework`: select `None` and press <kbd>Enter</kbd>
- `Nuxt.js modules`: scroll to `Content`, select with <kbd>Space</kbd>, then press <kbd>Enter</kbd>
- `Linting tools`: press <kbd>Enter</kbd> (do not select any Linting tools)
- `Testing framework`: select `None` and press <kbd>Enter</kbd>
- `Rendering mode`: select `Universal (SSR / SSG)` and press <kbd>Enter</kbd>
- `Deployment target`: select `Static (Static/Jamstack hosting)` and press <kbd>Enter</kbd>
- `Development tools`: press <kbd>Enter</kbd> (do not select any Development tools)
- `What is your GitHub username?`: press <kbd>Enter</kbd> (use default)
- `Version control system`: select `None` and press <kbd>Enter</kbd>
The project will be configured and modules will be installed.
@ -296,17 +304,17 @@ When the build finishes, the terminal will display a URL like:
The server is listening on that URL. Open the link in a web browser.
3) Download <https://sheetjs.com/pres.xlsx> and move to the `content` folder.
3) Download https://docs.sheetjs.com/pres.xlsx and move to the `content` folder.
```bash
curl -L -o content/pres.xlsx https://sheetjs.com/pres.xlsx
curl -L -o content/pres.xlsx https://docs.sheetjs.com/pres.xlsx
```
4) Modify `nuxt.config.js` as follows:
- Add the following to the top of the script:
```js
```js title="nuxt.config.js (add to top)"
import { readFile, utils } from 'xlsx';
// This will be called when the files change
@ -327,7 +335,7 @@ const parseSheet = (file, { path }) => {
Replace the property with the following definition:
```js
```js title="nuxt.config.js (replace content key in object)"
// content.extendParser allows us to hook into the parsing step
content: {
extendParser: {
@ -391,21 +399,31 @@ The page should automatically refresh with the new content:
![Nuxt Demo end of step 6](pathname:///nuxt/nuxt6.png)
7) Stop the server (press `CTRL+C` in the terminal window) and run
7) Stop the server (press <kbd>CTRL</kbd>+<kbd>C</kbd> in the terminal window).
8) Build the static site:
```bash
npm run generate
```
This will create a static site in the `dist` folder, which can be served with:
This will create a static site in the `dist` folder.
9) Serve the static site:
```bash
npx http-server dist
```
Accessing the page `http://localhost:8080` will show the page contents. Verifying
the static nature is trivial: make another change in Excel and save. The page
will not change.
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 v2
@ -485,7 +503,8 @@ The data is stored in the `body` property of the final object.
NuxtJS modules are the main mechanism for adding transformers to the pipeline.
<details><summary><b>Module Details</b> (click to show)</summary>
<details>
<summary><b>Module Details</b> (click to show)</summary>
Due to the structure of the NuxtJS system, modules must be defined in separate
script files. The module script is expected to export a module configured with
@ -590,7 +609,7 @@ The recommended solution is to switch to Node 18.
1) Create a stock app and install dependencies:
```bash
npx -y nuxi init -t content sheetjs-nc2
npx -y nuxi init -t content --packageManager yarn --no-gitInit sheetjs-nc2
cd sheetjs-nc2
npx -y yarn install
npx -y yarn add --dev @types/node
@ -612,10 +631,10 @@ When the build finishes, the terminal will display a URL like:
The server is listening on that URL. Open the link in a web browser.
3) Download <https://sheetjs.com/pres.xlsx> and move to the `content` folder.
3) Download https://docs.sheetjs.com/pres.xlsx and move to the `content` folder.
```bash
curl -L -o content/pres.xlsx https://sheetjs.com/pres.xlsx
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:
@ -650,7 +669,7 @@ export default defineNuxtConfig({
});
```
Restart the dev server by exiting the process (Control+C) and running:
Stop the dev server (<kbd>CTRL</kbd>+<kbd>C</kbd>) and run the following:
```bash
npx -y nuxi clean
@ -683,7 +702,8 @@ Loading `http://localhost:3000/pres` should show some JSON data:
```bash
curl -o pages/pres.vue https://docs.sheetjs.com/nuxt/3/pres.vue
```
Restart the dev server by exiting the process (Control+C) and running:
Stop the dev server (<kbd>CTRL</kbd>+<kbd>C</kbd>) and run the following:
```bash
npx -y nuxi clean
@ -694,13 +714,15 @@ npx -y yarn run dev
The browser should now display an HTML table.
6) To verify that hot loading works, open `pres.xlsx` from the `content` folder
with Excel or another spreadsheet editor.
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.
7) Stop the server (press `CTRL+C` in the terminal window) and run
7) Stop the server (press <kbd>CTRL</kbd>+<kbd>C</kbd> in the terminal window).
8) Build the static site:
```bash
npx -y yarn run generate
@ -712,9 +734,15 @@ This will create a static site in `.output/public`, which can be served with:
npx -y http-server .output/public
```
Accessing `http://localhost:8080/pres` will show the page contents. Verifying
the static nature is trivial: make another change in Excel and save. The page
will not change.
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>
```
[^1]: See [`readFile` in "Reading Files"](/docs/api/parse-options)
[^2]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)

@ -49,10 +49,10 @@ flowchart LR
This demo was tested in the following environments:
| Svelte | Kit | Date |
|:----------------|:---------|:-----------|
| `4.2.8` | `1.27.6` | 2023-12-04 |
| `5.0.0-next.17` | `1.27.6` | 2023-12-04 |
| SvelteJS | Kit | Date |
|:-----------------|:---------|:-----------|
| `4.2.17` | `2.5.10` | 2024-06-03 |
| `5.0.0-next.149` | `2.5.10` | 2024-06-03 |
:::
@ -178,6 +178,12 @@ a simple HTML table without any reference to the existing spreadsheet file!
## Complete Example
:::caution pass
When this demo was last tested, SvelteKit required NodeJS major version 20.
:::
### Initial Setup
1) Create a new site:
@ -190,11 +196,11 @@ When prompted:
- `Which Svelte app template?` select `Skeleton Project`
- `Add type checking with TypeScript?` select `Yes, using JavaScript with JSDoc`
- `Select additional options` press Enter (do not select options)
- `Select additional options` press <kbd>Enter</kbd> (do not select options)
:::note pass
To test the Svelte 5 beta, select "Try out Svelte 5 beta" before pressing Enter.
To test the Svelte 5 beta, select `Try the Svelte 5 preview (unstable!)`
:::
@ -205,12 +211,12 @@ cd sheetjs-svelte
npm i
```
3) Fetch the example file [`pres.xlsx`](https://sheetjs.com/pres.xlsx) and move
to a `data` subdirectory in the root of the project:
3) Fetch the example file [`pres.xlsx`](https://docs.sheetjs.com/pres.xlsx) and
move to a `data` subdirectory in the root of the project:
```bash
mkdir -p data
curl -Lo data/pres.xlsx https://sheetjs.com/pres.xlsx
curl -Lo data/pres.xlsx https://docs.sheetjs.com/pres.xlsx
```
4) Install the SheetJS library:
@ -219,7 +225,7 @@ curl -Lo data/pres.xlsx https://sheetjs.com/pres.xlsx
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
</CodeBlock>
5) Replace the contents of `vite.config.js` with the following:
5) Replace the contents of `vite.config.js` with the following codeblock:
```js title="vite.config.js"
import { sveltekit } from '@sveltejs/kit/vite';
@ -249,7 +255,8 @@ declare global {
}
```
7) Replace the contents of `src/routes/+page.server.js` with following:
7) Replace the contents of `src/routes/+page.server.js` with the following code.
Create the file if it does not exist.
```js title="src/routes/+page.server.js"
import b64 from "../../data/pres.xlsx";
@ -266,9 +273,8 @@ export async function load({ params }) {
}
```
If the file does not exist, create a new file.
8) Replace the contents of `src/routes/+page.svelte` with the following:
8) Replace the contents of `src/routes/+page.svelte` with the following code.
Create the file if it does not exist.
```html title="src/routes/+page.svelte"
<script>

@ -36,7 +36,7 @@ flowchart LR
aoo --> |index.astro\ntemplate body| html
```
:::warning Telemetry
:::danger Telemetry
AstroJS enables telemetry by default. The tool has an option to disable telemetry:
@ -50,10 +50,10 @@ npx astro telemetry disable
This demo was tested in the following environments:
| AstroJS | Date |
|:---------------|:-----------|
| `3.6.4` | 2023-12-04 |
| `4.0.0-beta.4` | 2023-12-04 |
| AstroJS | Template | Date |
|:--------|:-----------------|:-----------|
| `3.6.5` | Starlight 0.14.0 | 2024-04-14 |
| `4.6.1` | Starlight 0.21.5 | 2024-04-14 |
:::
@ -69,7 +69,7 @@ AstroJS has introduced a number of breaking changes in minor releases.
:::info pass
This demo uses ["Base64 Loader"](/docs/demos/static/vitejs#base64-loader)
This demo uses ["Base64 Loader"](/docs/demos/static/vitejs#base64-plugin)
from the ViteJS demo.
The ViteJS demo used the query `?b64` to identify files. To play nice with
@ -212,10 +212,13 @@ cd sheetjs-astro
:::note pass
To test the AstroJS 4 beta release, run the following command:
To test an older version of AstroJS, install the specific version of `astro` and
a supported starter template after creating the project.
For major version 3, Starlight must be version `0.14.0`:
```bash
npm install --force @astrojs/starlight@^0.14.0 astro@4.0.0-beta.4
npm install --force astro@3.6.5 @astrojs/starlight@0.14.0
```
The version can be verified by running:
@ -226,11 +229,11 @@ npx astro --version
:::
2) Fetch the example file [`pres.numbers`](https://sheetjs.com/pres.numbers):
2) Fetch the example file [`pres.numbers`](https://docs.sheetjs.com/pres.numbers):
```bash
mkdir -p src/data
curl -Lo src/data/pres.numbers https://sheetjs.com/pres.numbers
curl -Lo src/data/pres.numbers https://docs.sheetjs.com/pres.numbers
```
3) Install the SheetJS library:
@ -251,7 +254,7 @@ declare module '*.xlsx' { const data: string; export default data; }
- At the top of the script, import `readFileSync`:
```js title="astro.config.mjs"
```js title="astro.config.mjs (add higlighted lines)"
// highlight-start
/* import `readFileSync` at the top of the script*/
import { readFileSync } from 'fs';
@ -261,7 +264,7 @@ import { defineConfig } from 'astro/config';
- In the object argument to `defineConfig`, add a `vite` section:
```js title="astro.config.mjs"
```js title="astro.config.mjs (add highlighted lines)"
export default defineConfig({
// highlight-start
/* this vite section should be added as a property of the object */

@ -1,5 +1,5 @@
---
title: React Native
title: Sheets on the Go with React Native
sidebar_label: React Native
description: Build data-intensive mobile apps with React Native. Seamlessly integrate spreadsheets into your app using SheetJS. Securely process and generate Excel files in the field.
pagination_prev: demos/static/index
@ -9,8 +9,6 @@ sidebar_custom_props:
summary: React + Native Rendering
---
# Sheets on the Go with React Native
import current from '/version.js';
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
@ -69,7 +67,9 @@ imported from any component or script in the app.
For simplicity, this demo uses an "Array of Arrays"[^2] as the internal state.
<table><thead><tr><th>Spreadsheet</th><th>Array of Arrays</th></tr></thead><tbody><tr><td>
<table>
<thead><tr><th>Spreadsheet</th><th>Array of Arrays</th></tr></thead>
<tbody><tr><td>
![`pres.xlsx` data](pathname:///pres.png)
@ -207,11 +207,11 @@ row. This neatly skips the first header row.
React Native versions starting from `0.72.0`[^5] support binary data in `fetch`.
This snippet downloads and parses <https://sheetjs.com/pres.xlsx>:
This snippet downloads and parses https://docs.sheetjs.com/pres.xlsx:
```js
/* fetch data into an ArrayBuffer */
const ab = await (await fetch("https://sheetjs.com/pres.xlsx")).arrayBuffer();
const ab = await (await fetch("https://docs.sheetjs.com/pres.xlsx")).arrayBuffer();
/* parse data */
const wb = XLSX.read(ab);
```
@ -222,14 +222,6 @@ const wb = XLSX.read(ab);
This demo was tested in the following environments:
**Simulators**
| OS | Device | RN | Dev Platform | Date |
|:-----------|:------------------|:---------|:-------------|:-----------|
| Android 34 | Pixel 3a | `0.73.6` | `darwin-x64` | 2024-03-13 |
| iOS 17.4 | iPhone 15 Pro Max | `0.73.6` | `darwin-x64` | 2024-03-13 |
| Android 34 | Pixel 3a | `0.73.5` | `win10-x64` | 2024-03-05 |
**Real Devices**
| OS | Device | RN | Date |
@ -237,12 +229,53 @@ This demo was tested in the following environments:
| iOS 15.1 | iPhone 12 Pro Max | `0.73.6` | 2024-03-13 |
| Android 29 | NVIDIA Shield | `0.73.6` | 2024-03-13 |
**Simulators**
| OS | Device | RN | Dev Platform | Date |
|:-----------|:------------------|:---------|:-------------|:-----------|
| Android 34 | Pixel 3a | `0.73.6` | `darwin-x64` | 2024-03-13 |
| iOS 17.4 | iPhone 15 Pro Max | `0.73.6` | `darwin-x64` | 2024-03-13 |
| Android 34 | Pixel 3a | `0.73.5` | `win10-x64` | 2024-03-05 |
| Android 34 | Pixel 3a | `0.73.7` | `linux-x64` | 2024-04-29 |
:::
0) Install React Native dependencies
<details>
<summary><b>Installation Notes</b> (click to show)</summary>
On the Steam Deck, JDK17 was installed using `pacman`:
```bash
sudo pacman -Syu jdk17-openjdk
```
[The Android Studio tarball](https://developer.android.com/studio) was
downloaded and extracted. After extracting:
```bash
cd ./android-studio/bin
./studio.sh
```
In Android Studio, select "SDK Manager" and switch to the "SDK Tools" tab. Check
"Show Package Details" and install "Android SDK Command-line Tools (latest)".
When this demo was last tested, the following environment variables were used:
```bash
export ANDROID_HOME=~/Android/Sdk
export PATH=$PATH:$ANDROID_HOME/emulator:$ANDROID_HOME/platform-tools
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk
```
</details>
1) Create project:
```bash
npx -y react-native@0.73.6 init SheetJSRNFetch --version="0.73.6"
npx -y react-native@0.73.7 init SheetJSRNFetch --version="0.73.7"
```
2) Install shared dependencies:
@ -282,6 +315,20 @@ OpenJDK 64-Bit Server VM Temurin-17.0.10+7 (build 17.0.10+7, mixed mode)
npx react-native run-android
```
:::info pass
On Linux, the command may silently stall. It is strongly recommended to launch
the interactive CLI tool:
```bash
npx react-native start
```
Once the dev server is ready, the terminal will display a few options. Press `a`
to run on Android.
:::
:::caution pass
If the initial launch fails with an error referencing the emulator, manually
@ -298,6 +345,55 @@ This error can be resolved by installing and switching to the requested version.
:::
:::caution pass
When this demo was last tested on Linux, the process failed to launch the emulator:
<pre>
<b {...y}>warn</b> Please launch an emulator manually or connect a device. Otherwise app may fail to launch.
</pre>
**This is a known bug in React Native!**
If an emulator is installed, run the following command:
```bash
npx react-native doctor
```
Under `Android`, there will be one error:
<pre>
<span {...gr}>Android</span> {`\n`}
{` `}<span {...r}></span> Adb - No devices and/or emulators connected. Please create emulator with Android Studio or connect Android device.
</pre>
Press `f` and a list of available emulators will be shown. Select the emulator
(typically the last line) and press Enter.
<pre>
<span {...g}></span> <b>Select the device / emulator you want to use</b> <span {...gr}></span> <b>Emulator</b> <span {...g}>Pixel_3a_API_34_extension_level_7_x86_64</span> (disconnected)
</pre>
The text in green is the name of the virtual device
(`Pixel_3a_API_34_extension_level_7_x86_64` in this example).
Run the following command to manually start the emulator:
```bash
$ANDROID_HOME/tools/emulator -avd Pixel_3a_API_34_extension_level_7_x86_64
```
(replace `Pixel_3a_API_34_extension_level_7_x86_64` with the name of the virtual device)
To confirm React Native detects the emulator, run the following command again:
```bash
npx react-native doctor
```
:::
6) When opened, the app should look like the "Before" screenshot below. After
tapping "Import data from a spreadsheet", verify that the app shows new data:
@ -316,7 +412,7 @@ tapping "Import data from a spreadsheet", verify that the app shows new data:
**iOS Testing**
:::warning pass
:::danger pass
**iOS testing can only be performed on Apple hardware running macOS!**
@ -376,7 +472,8 @@ If the device asks to trust the computer, tap "Trust" and enter the passcode.
15) Enable developer code signing certificates[^7].
<details open><summary><b>Enabling Code Signing</b> (click to show)</summary>
<details open>
<summary><b>Enabling Code Signing</b> (click to show)</summary>
These instructions were verified against Xcode 15.3.
@ -541,7 +638,7 @@ Click on the icon and select the real device from the list.
## Local Files
:::warning pass
:::danger pass
React Native does not provide a native file picker or a method for reading and
writing data from documents on the devices. A third-party library must be used.
@ -566,7 +663,8 @@ The following table lists tested file plugins. "OS" lists tested platforms
Due to privacy concerns, apps must request file access. There are special APIs
for accessing data and are subject to change in future platform versions.
<details><summary><b>Technical Details</b> (click to show)</summary>
<details>
<summary><b>Technical Details</b> (click to show)</summary>
**iOS**
@ -698,7 +796,8 @@ is the continuation of other libraries that date back to 2016.
The `ascii` type returns an array of numbers corresponding to the raw bytes.
A `Uint8Array` from the data is compatible with the `buffer` type.
<details><summary><b>Reading and Writing snippets</b> (click to hide)</summary>
<details>
<summary><b>Reading and Writing snippets</b> (click to hide)</summary>
_Reading Data_
@ -779,7 +878,8 @@ is a filesystem API that uses modern iOS and Android development patterns.
The `base64` encoding returns strings compatible with the `base64` type:
<details><summary><b>Reading and Writing snippets</b> (click to hide)</summary>
<details>
<summary><b>Reading and Writing snippets</b> (click to hide)</summary>
_Reading Data_
@ -839,7 +939,8 @@ The [`expo-document-picker`](#expo-document-picker) snippet makes a local copy.
The `EncodingType.Base64` encoding is compatible with `base64` type.
<details><summary><b>Reading and Writing snippets</b> (click to hide)</summary>
<details>
<summary><b>Reading and Writing snippets</b> (click to hide)</summary>
_Reading Data_
@ -903,14 +1004,6 @@ try {
This demo was tested in the following environments:
**Simulators**
| OS | Device | RN | Dev Platform | Date |
|:-----------|:------------------|:---------|:-------------|:-----------|
| Android 34 | Pixel 3a | `0.73.6` | `darwin-x64` | 2024-03-31 |
| iOS 17.4 | iPhone 15 Pro Max | `0.73.6` | `darwin-x64` | 2024-03-31 |
| Android 34 | Pixel 3a | `0.73.6` | `win10-x64` | 2024-03-31 |
**Real Devices**
| OS | Device | RN | Date |
@ -918,9 +1011,18 @@ This demo was tested in the following environments:
| iOS 15.5 | iPhone 13 Pro Max | `0.73.6` | 2024-03-31 |
| Android 29 | NVIDIA Shield | `0.73.6` | 2024-03-31 |
**Simulators**
| OS | Device | RN | Dev Platform | Date |
|:-----------|:------------------|:---------|:-------------|:-----------|
| Android 34 | Pixel 3a | `0.73.6` | `darwin-x64` | 2024-03-31 |
| iOS 17.4 | iPhone 15 Pro Max | `0.73.6` | `darwin-x64` | 2024-03-31 |
| Android 34 | Pixel 3a | `0.73.6` | `win10-x64` | 2024-03-31 |
| Android 34 | Pixel 3a | `0.73.6` | `linux-x64` | 2024-03-31 |
:::
:::warning pass
:::danger pass
There are many moving parts and pitfalls with React Native apps. It is strongly
recommended to follow the official React Native tutorials for iOS and Android
@ -1196,7 +1298,7 @@ npx react-native doctor
:::
8) Download <https://sheetjs.com/pres.numbers> and open the Downloads folder.
8) Download https://docs.sheetjs.com/pres.numbers and open the Downloads folder.
9) Click and drag `pres.numbers` from the Downloads folder into the simulator.
@ -1254,7 +1356,7 @@ npx xlsx-cli sheetjsw.xlsx
**iOS Testing**
:::warning pass
:::danger pass
**iOS testing can only be performed on Apple hardware running macOS!**
@ -1276,7 +1378,7 @@ cd ios; pod install; cd -
npx react-native run-ios
```
17) Download <https://sheetjs.com/pres.numbers> and open the Downloads folder.
17) Download https://docs.sheetjs.com/pres.numbers and open the Downloads folder.
18) In the simulator, click the Home icon to return to the home screen.
@ -1352,7 +1454,7 @@ If the device asks to allow USB debugging, tap "Allow".
npx react-native run-android
```
30) Download <https://sheetjs.com/pres.numbers> on the device.
30) Download https://docs.sheetjs.com/pres.numbers on the device.
31) Switch back to the "SheetJSRN" app.
@ -1441,7 +1543,7 @@ npx react-native run-ios
If the build fails, some troubleshooting notes are included in the "iOS Device
Testing" part of the [Fetch Demo](#fetch-demo) (step 17).
41) Download <https://sheetjs.com/pres.numbers> on the device.
41) Download https://docs.sheetjs.com/pres.numbers on the device.
42) Switch back to the "SheetJSRN" app.
@ -1471,7 +1573,7 @@ The Numbers app will load the spreadsheet, confirming that the file is valid.
[^2]: See ["Array of Arrays" in the API reference](/docs/api/utilities/array#array-of-arrays)
[^3]: See ["Array Output" in "Utility Functions"](/docs/api/utilities/array#array-output)
[^4]: See ["Array of Arrays Input" in "Utility Functions"](/docs/api/utilities/array#array-of-arrays-input)
[^5]: React-Native commit [`5b597b5`](https://github.com/facebook/react-native/commit/5b597b5ff94953accc635ed3090186baeecb3873) added the final piece required for `fetch` support. It landed in version `0.72.0-rc.1` and is available in official releases starting from `0.72.0`.
[^5]: React-Native commit [`5b597b5`](https://github.com/facebook/react-native/commit/5b597b5ff94953accc635ed3090186baeecb3873) added the final piece required for `fetch` support. It is available in official releases starting from `0.72.0`.
[^6]: When the demo was last tested, the Temurin distribution of Java 17 was installed through the macOS Brew package manager by running `brew install temurin17`. [Direct downloads are available at `adoptium.net`](https://adoptium.net/temurin/releases/?version=17)
[^7]: See ["Running On Device"](https://reactnative.dev/docs/running-on-device) in the React Native documentation
[^8]: See [`UIFileSharingEnabled`](https://developer.apple.com/documentation/bundleresources/information_property_list/uifilesharingenabled) in the Apple Developer Documentation.

@ -13,7 +13,7 @@ import CodeBlock from '@theme/CodeBlock';
export const g = {style: {color:"green"}};
export const r = {style: {color:"red"}};
export const y = {style: {color:"yellow"}};
export const y = {style: {color:"gold"}};
[NativeScript](https://nativescript.org/) is a mobile app framework. It builds
iOS and Android apps that use JavaScript for describing layouts and events.
@ -25,7 +25,8 @@ This demo uses NativeScript and SheetJS to process and generate spreadsheets.
We'll explore how to load SheetJS in a NativeScript app; parse and generate
spreadsheets stored on the device; and fetch and parse remote files.
The "Complete Example" creates an app that looks like the screenshots below:
The ["Complete Example"](#complete-example) creates an app that looks like the
screenshots below:
<table><thead><tr>
<th><a href="#complete-example">iOS</a></th>
@ -51,16 +52,24 @@ Angular and TypeScript is assumed.
This demo was tested in the following environments:
| OS | Type | Device | NS | Date |
|:-----------|:-----|:--------------------|:---------|:-----------|
| Android 34 | Sim | Pixel 3a | `8.6.1` | 2023-12-04 |
| iOS 17.0.1 | Sim | iPhone SE (3rd gen) | `8.6.1` | 2023-12-04 |
| Android 29 | Real | NVIDIA Shield | `8.6.1` | 2023-12-04 |
| iOS 15.1 | Real | iPad Pro | `8.6.1` | 2023-12-04 |
**Real Devices**
| OS | Device | NS | Date |
|:-----------|:--------------------|:---------|:-----------|
| 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 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 34 | Pixel 3a | `8.6.5` | `win10-x64` | 2024-04-07 |
:::
:::warning Telemetry
:::danger Telemetry
Before starting this demo, manually disable telemetry.
@ -88,9 +97,9 @@ imported from any component or script in the app.
The `@nativescript/core/file-system` package provides classes for file access.
The `File` class does not support binary data, but the file access singleton
from `@nativescript/core` does support reading and writing `ArrayBuffer`.
from `@nativescript/core` does support reading and writing `ArrayBuffer` data.
Reading and writing data require a URL. The following snippet searches typical
Reading and writing data require a URL. The following snippet searches typical
document folders for a specified filename:
```ts
@ -102,6 +111,69 @@ function get_url_for_filename(filename: string): string {
}
```
### App Configuration
Due to privacy concerns, apps must request file access. There are special APIs
for accessing data and are subject to change in future platform versions.
<details>
<summary><b>Technical Details</b> (click to show)</summary>
**Android**
Android security has evolved over the years. In newer Android versions, the
following workarounds were required:
- `READ_EXTERNAL_STORAGE` and `WRITE_EXTERNAL_STORAGE` allow apps to access
files outside of the app scope. These are required for scoped storage access.
When the demo was last tested, this option was enabled by default.
- `android:requestLegacyExternalStorage="true"` enabled legacy behavior in some
older releases.
The manifest is saved to `App_Resources/Android/src/main/AndroidManifest.xml`:
```xml title="App_Resources/Android/src/main/AndroidManifest.xml (add highlighted lines)"
<application
<!-- highlight-next-line -->
android:requestLegacyExternalStorage="true"
android:name="com.tns.NativeScriptApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme"
android:hardwareAccelerated="true">
```
- Permissions must be explicitly requested.
`@nativescript-community/perms` is a community module for managing permissions:
```ts title="App script or component"
import { request } from '@nativescript-community/perms';
import { File } from '@nativescript/core/file-system';
```
Storage access must be requested before writing data:
```ts title="App script or component (before writing file)"
/* request permissions */
const res = await request('storage');
```
The external paths can be resolved using the low-level APIs:
```ts title="App script or component (writing to downloads folder)"
/* find Downloads folder */
const dl_dir = android.os.Environment.DIRECTORY_DOWNLOADS;
const dl = android.os.Environment.getExternalStoragePublicDirectory(dl_dir).getAbsolutePath();
/* write to file */
File.fromPath(dl + "/SheetJSNS.xls").writeSync(data);
```
</details>
### Reading Local Files
`getFileAccess().readBufferAsync` can read data into an `ArrayBuffer` object.
@ -176,7 +248,7 @@ import { read } from 'xlsx';
const temp: string = path.join(knownFolders.temp().path, "pres.xlsx");
/* download file */
const file = await getFile("https://sheetjs.com/pres.xlsx", temp)
const file = await getFile("https://docs.sheetjs.com/pres.xlsx", temp)
/* get data */
const ab: ArrayBuffer = await getFileAccess().readBufferAsync(file.path);
@ -204,10 +276,9 @@ When the demo was last tested, the latest version of the Android API was 34.
NativeScript did not support that API level. The exact error message from
`npx -p nativescript ns doctor ios` clearly stated supported versions:
(x is red, body text is yellow)
```
✖ 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>
<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: '&gt;=23 &lt;=33'.
</pre>
The SDK Platform `Android 13.0 ("Tiramisu")` was compatible with NativeScript.
Until NativeScript properly supports API level 34, "Tiramisu" must be used.
@ -226,20 +297,21 @@ npx -p nativescript ns doctor android
In the last macOS test, the following output was displayed:
<details open><summary><b>Expected output</b> (click to hide)</summary>
<details open>
<summary><b>Expected output</b> (click to hide)</summary>
<pre>
<span {...g}></span> Getting environment information{'\n'}
{'\n'}
<b>No issues were detected.</b>{'\n'}
<span {...g}></span> Your ANDROID_HOME environment variable is set and points to correct directory.{'\n'}
<span {...g}></span> Your adb from the Android SDK is correctly installed.{'\n'}
<span {...g}></span> The Android SDK is installed.{'\n'}
<span {...g}></span> A compatible Android SDK for compilation is found.{'\n'}
<span {...g}></span> Javac is installed and is configured properly.{'\n'}
<span {...g}></span> The Java Development Kit (JDK) is installed and is configured properly.{'\n'}
<span {...g}></span> Getting NativeScript components versions information...{'\n'}
<span {...g}></span> Component nativescript has 8.6.1 version and is up to date.
<span {...g}></span> Getting environment information
<b>No issues were detected.</b>
<span {...g}></span> Your ANDROID_HOME environment variable is set and points to correct directory.
<span {...g}></span> Your adb from the Android SDK is correctly installed.
<span {...g}></span> The Android SDK is installed.
<span {...g}></span> A compatible Android SDK for compilation is found.
<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.7.2 version and is up to date.
</pre>
</details>
@ -252,23 +324,23 @@ npx -p nativescript ns doctor ios
In the last macOS test, the following output was displayed:
<details open><summary><b>Expected output</b> (click to hide)</summary>
<details open>
<summary><b>Expected output</b> (click to hide)</summary>
<pre>
<span {...g}></span> Getting environment information{'\n'}
{'\n'}
No issues were detected.{'\n'}
<span {...g}></span> Xcode is installed and is configured properly.{'\n'}
<span {...g}></span> xcodeproj is installed and is configured properly.{'\n'}
<span {...g}></span> CocoaPods are installed.{'\n'}
<span {...g}></span> CocoaPods update is not required.{'\n'}
<span {...g}></span> CocoaPods are configured properly.{'\n'}
<span {...g}></span> Your current CocoaPods version is newer than 1.0.0.{'\n'}
<span {...g}></span> Python installed and configured correctly.{'\n'}
<span {...g}></span> The Python 'six' package is found.{'\n'}
<span {...g}></span> Xcode version 15.0.1 satisfies minimum required version 10.{'\n'}
<span {...g}></span> Getting NativeScript components versions information...{'\n'}
<span {...g}></span> Component nativescript has 8.6.1 version and is up to date.
<span {...g}></span> Getting environment information
<b>No issues were detected.</b>
<span {...g}></span> Xcode is installed and is configured properly.
<span {...g}></span> xcodeproj is installed and is configured properly.
<span {...g}></span> CocoaPods are installed.
<span {...g}></span> CocoaPods update is not required.
<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 15.4.0 satisfies minimum required version 10.
<span {...g}></span> Getting NativeScript components versions information...
<span {...g}></span> Component nativescript has 8.7.2 version and is up to date.
</pre>
</details>
@ -291,7 +363,8 @@ 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 entering the key sequence `CTRL + C`
selecting the terminal and pressing <kbd>CTRL</kbd>+<kbd>C</kbd>. On Windows, if
prompted to `Terminate batch job`, type `y` and press Enter.
:::note pass
@ -303,12 +376,6 @@ Emulator start failed with: No emulator image available for device identifier 'u
:::
6) From the project folder, install the library:
<CodeBlock language="bash">{`\
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
</CodeBlock>
### Add SheetJS
:::note pass
@ -317,10 +384,16 @@ The goal of this section is to display the SheetJS library version number.
:::
6) From the project folder, install the SheetJS NodeJS module:
<CodeBlock language="bash">{`\
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
</CodeBlock>
7) Edit `src/app/item/items.component.ts` so that the component imports the
SheetJS version string and adds it to a `version` variable in the component:
```ts title="src/app/item/items.component.ts"
```ts title="src/app/item/items.component.ts (add highlighted lines)"
// highlight-next-line
import { version } from 'xlsx';
import { Component, OnInit } from '@angular/core'
@ -339,7 +412,7 @@ export class ItemsComponent implements OnInit {
8) Edit the template `src/app/item/items.component.html` to reference `version`
in the title of the action bar:
```xml title="src/app/item/items.component.html"
```xml title="src/app/item/items.component.html (edit highlighted line)"
<!-- highlight-next-line -->
<ActionBar [title]="version"></ActionBar>
@ -347,7 +420,7 @@ in the title of the action bar:
<!-- ... -->
```
9) Relaunch the app in the Android simulator:
9) End the script and relaunch the app in the Android simulator:
```bash
npx -p nativescript ns run android
@ -361,7 +434,7 @@ The title bar should show the version.
10) Add the Import and Export buttons to the template:
```xml title="src/app/item/items.component.html"
```xml title="src/app/item/items.component.html (add highlighted lines)"
<ActionBar [title]="version"></ActionBar>
<!-- highlight-start -->
@ -424,13 +497,19 @@ export class ItemsComponent implements OnInit {
}
```
12) Restart the app process. Two buttons should show up at the top:
12) End the script and relaunch the app in the Android simulator:
```bash
npx -p nativescript ns run android
```
Two buttons should appear just below the header:
![NativeScript Step 5](pathname:///nativescript/step5.png)
13) Implement import and export by adding the highlighted lines:
```ts title="src/app/item/items.component.ts"
```ts title="src/app/item/items.component.ts (add highlighted lines)"
/* Import button */
async import() {
// highlight-start
@ -486,21 +565,22 @@ export class ItemsComponent implements OnInit {
```bash
npx -p nativescript ns run android
````
```
If the app does not automatically launch, manually open the `SheetJSNS` app.
15) Tap "Export File". A dialog will print where the file was written. Typically
the URL is `/data/user/0/org.nativescript.SheetJSNS/files/SheetJSNS.xls`
16) Pull the file from the simulator:
16) Pull the file from the simulator. The following commands should be run in a
new terminal or PowerShell window:
```bash
adb root
adb pull /data/user/0/org.nativescript.SheetJSNS/files/SheetJSNS.xls SheetJSNS.xls
```
If the emulator cannot be rooted:
If the emulator cannot be rooted, the following command works in macOS:
```bash
adb shell "run-as org.nativescript.SheetJSNS cat /data/user/0/org.nativescript.SheetJSNS/files/SheetJSNS.xls" > SheetJSNS.xls
@ -510,8 +590,9 @@ adb shell "run-as org.nativescript.SheetJSNS cat /data/user/0/org.nativescript.S
After the header row, insert a row with cell A2 = 0, B2 = SheetJS, C2 = Library:
```
```text
id | name | role
# highlight-next-line
0 | SheetJS | Library
1 | Ter Stegen | Goalkeeper
3 | Piqué | Defender
@ -524,7 +605,7 @@ id | name | role
adb push SheetJSNS.xls /data/user/0/org.nativescript.SheetJSNS/files/SheetJSNS.xls
```
If the emulator cannot be rooted:
If the emulator cannot be rooted, the following command works in macOS:
```bash
dd if=SheetJSNS.xls | adb shell "run-as org.nativescript.SheetJSNS dd of=/data/user/0/org.nativescript.SheetJSNS/files/SheetJSNS.xls"
@ -533,8 +614,20 @@ dd if=SheetJSNS.xls | adb shell "run-as org.nativescript.SheetJSNS dd of=/data/u
19) Tap "Import File". A dialog will print the path of the file that was read.
The first item in the list will change.
![NativeScript Step 6](pathname:///nativescript/step6.png)
### iOS
:::danger pass
**iOS testing can only be performed on Apple hardware running macOS!**
Xcode and iOS simulators are not available on Windows or Linux.
Scroll down to ["Fetching Files"](#android-device) for Android device testing.
:::
20) Launch the app in the iOS Simulator:
```bash
@ -547,8 +640,9 @@ npx -p nativescript ns run ios
After the header row, insert a row with cell A2 = 0, B2 = SheetJS, C2 = Library:
```
```text
id | name | role
# highlight-next-line
0 | SheetJS | Library
1 | Ter Stegen | Goalkeeper
3 | Piqué | Defender
@ -566,7 +660,7 @@ The first item in the list will change:
25) In `src/app/item/items.component.ts`, make `ngOnInit` asynchronous:
```ts title="src/app/item/items.component.ts"
```ts title="src/app/item/items.component.ts (replace existing function)"
async ngOnInit(): Promise<void> {
this.items = await this.itemService.getItems()
}
@ -589,9 +683,9 @@ export class ItemService {
private items: Array<Item>;
async getItems(): Promise<Array<Item>> {
/* fetch https://sheetjs.com/pres.xlsx */
/* fetch https://docs.sheetjs.com/pres.xlsx */
const temp: string = path.join(knownFolders.temp().path, "pres.xlsx");
const ab = await getFile("https://sheetjs.com/pres.xlsx", temp)
const ab = await getFile("https://docs.sheetjs.com/pres.xlsx", temp)
/* read the temporary file */
const wb = read(await getFileAccess().readBufferAsync(ab.path));
/* translate the first worksheet to the required Item type */
@ -605,11 +699,11 @@ export class ItemService {
}
```
27) Relaunch the app in the Android simulator:
27) End the script and relaunch the app in the Android simulator:
```bash
npx -p nativescript ns run android
````
```
The app should show Presidential data.
@ -621,7 +715,61 @@ If the device asks to allow USB debugging, tap "Allow".
29) Close any Android / iOS emulators.
30) Build APK and run on device:
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)"
<application
<!-- highlight-next-line -->
android:requestLegacyExternalStorage="true"
android:name="com.tns.NativeScriptApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme"
android:hardwareAccelerated="true">
```
31) Install the `@nativescript-community/perms` dependency:
```bash
npm i --save @nativescript-community/perms
```
32) Add the highlighted lines to `items.component.ts`:
- Import `File` from NativeScript core and `request` from the new dependency:
```ts title="items.component.ts (add highlighted lines)"
import { Dialogs, getFileAccess, Utils } from '@nativescript/core';
// highlight-start
import { request } from '@nativescript-community/perms';
import { Folder, knownFolders, path, File } from '@nativescript/core/file-system';
// highlight-end
import { Component, OnInit } from '@angular/core'
// ...
```
- Add a new write operation to the `export` method:
```ts title="items.component.ts (add highlighted lines)"
/* attempt to save Uint8Array to file */
await getFileAccess().writeBufferAsync(url, global.isAndroid ? (Array.from(u8) as any) : u8);
await Dialogs.alert(`Wrote to SheetJSNS.xls at ${url}`);
/* highlight-start */
if(global.isAndroid) {
/* request permissions */
const res = await request('storage');
/* write to Downloads folder */
const dl = android.os.Environment.getExternalStoragePublicDirectory(android.os.Environment.DIRECTORY_DOWNLOADS).getAbsolutePath();
File.fromPath(dl + "/SheetJSNS.xls").writeSync(Array.from(u8));
}
/* highlight-end */
} catch(e) { await Dialogs.alert(e.message); }
```
33) Build APK and run on device:
```bash
npx -p nativescript ns run android
@ -630,26 +778,54 @@ npx -p nativescript ns run android
If the Android emulators are closed and an Android device is connected, the last
command will build an APK and install on the device.
<details open>
<summary><b>Android Device Testing</b> (click to hide)</summary>
When the app launches, if the SheetJS library is loaded and if the device is
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 open the "Downloads" folder. There should be a new
file named `SheetJSNS.xls`.
</details>
### iOS Device
31) Connect an iOS device using a USB cable
34) Connect an iOS device using a USB cable
32) Close any Android / iOS emulators.
35) Close any Android / iOS emulators.
33) Enable developer code signing certificates[^9]
36) Enable developer code signing certificates:
34) Run on device:
Open `platforms/ios/SheetJSNS.xcodeproj/project.xcworkspace` in Xcode. Select
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 -p nativescript ns run ios
```
<details open>
<summary><b>iOS Device Testing</b> (click to hide)</summary>
When the app launches, if the SheetJS library is loaded and if the device is
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 open the "Downloads" folder. There should be a new
file named `SheetJSNS.xls`.
</details>
[^1]: See [`read` in "Reading Files"](/docs/api/parse-options)
[^2]: See ["Workbook Object"](/docs/csf/book)
[^3]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
[^4]: See [`write` in "Writing Files"](/docs/api/write-options)
[^5]: See [`json_to_sheet` in "Utilities"](/docs/api/utilities/array#array-of-objects-input)
[^6]: See ["Workbook Helpers" in "Utilities"](/docs/api/utilities/wb) for details on `book_new` and `book_append_sheet`.
[^7]: See [`read` in "Reading Files"](/docs/api/parse-options)
[^8]: See ["Local setup"](https://docs.nativescript.org/setup/#local-setup) in the NativeScript documentation. For Windows and Linux, follow the "Android" instructions. For macOS, follow both the iOS and Android instructions.
[^9]: The [Flutter documentation](https://docs.flutter.dev/get-started/install/macos?tab=ios15#enable-developer-code-signing-certificates) covers the instructions in more detail. The correct workspace is `platforms/ios/SheetJSNS.xcodeproj/project.xcworkspace`
[^7]: See [`read` in "Reading Files"](/docs/api/parse-options)

@ -41,10 +41,19 @@ The ["Demo"](#demo) creates an app that looks like the screenshots below:
This demo was tested in the following environments:
| OS | Type | Device | Quasar | Date |
|:-----------|:-----|:--------------------|:---------|:-----------|
| Android 34 | Sim | Pixel 3a | `2.14.1` | 2023-12-04 |
| iOS 17.0.1 | Sim | iPhone SE (3rd gen) | `2.14.1` | 2023-12-04 |
**Real Devices**
| OS | Device | Quasar | Date |
|:-----------|:--------------------|:---------|:-----------|
| Android 30 | NVIDIA Shield | `2.16.4` | 2024-06-09 |
| iOS 15.1 | iPad Pro | `2.16.4` | 2024-06-09 |
**Simulators**
| OS | Device | Quasar | Dev Platform | Date |
|:-----------|:--------------------|:---------|:-------------|:-----------|
| Android 34 | Pixel 3a | `2.16.4` | `darwin-arm` | 2024-06-09 |
| iOS 17.5 | iPhone SE (3rd gen) | `2.16.4` | `darwin-arm` | 2024-06-09 |
:::
@ -165,6 +174,13 @@ npm i -g @quasar/cli cordova
(you may need to run `sudo npm i -g` if there are permission issues)
<details>
<summary><b>Installation Notes</b> (click to show)</summary>
Quasar requires Java 17
</details>
1) Create a new app:
```bash
@ -175,19 +191,19 @@ npm init quasar
When prompted:
- "What would you like to build?": `App with Quasar CLI`
- "What would you like to build?": `App with Quasar CLI, let's go!`
- "Project folder": `SheetJSQuasar`
- "Pick Quasar version": `Quasar v2 (Vue 3 | latest and greatest)`
- "Pick script type": `Typescript`
- "Pick Quasar App CLI variant": `Quasar App CLI with Vite`
- "Package name": (just press enter, it will use the default `sheetjsquasar`
- "Package name": (press <kbd>Enter</kbd>, it will use the default `sheetjsquasar`)
- "Project product name": `SheetJSQuasar`
- "Project description": `SheetJS + Quasar`
- "Author": (just press enter, it will use your git config settings)
- "Author": (press <kbd>Enter</kbd>, it will use your git config settings)
- "Pick a Vue component style": `Composition API`
- "Pick your CSS preprocessor": `None`
- "Check the features needed for your project": Deselect everything
- "Install project dependencies": `No`
- "Check the features needed for your project": Deselect everything (scroll down to each selected item and press <kbd>Space</kbd>)
- "Install project dependencies": `Yes, use npm`
2) Install dependencies:
@ -234,7 +250,7 @@ Return to the project directory:
cd ..
```
11) Enable file sharing and make the documents folder visible in the iOS app.
4) Enable file sharing and make the documents folder visible in the iOS app.
The following lines must be added to `src-cordova/platforms/ios/SheetJSQuasar/SheetJSQuasar-Info.plist`:
```xml title="src-cordova/platforms/ios/SheetJSQuasar/SheetJSQuasar-Info.plist (add to file)"
@ -258,6 +274,8 @@ The following lines must be added to `src-cordova/platforms/ios/SheetJSQuasar/Sh
quasar dev -m ios
```
If prompted to select an external IP, press <kbd>Enter</kbd>.
:::caution pass
If the app is blank or not refreshing, delete the app and close the simulator,
@ -267,7 +285,7 @@ then restart the development process.
6) Add the Dialog plugin to `quasar.config.js`:
```js title="quasar.config.js"
```js title="quasar.config.js (add highlighted line)"
framework: {
config: {},
// ...
@ -280,7 +298,7 @@ then restart the development process.
7) In the template section of `src/pages/IndexPage.vue`, replace the example
with a Table, Save button and Load file picker component:
```html title="src/pages/IndexPage.vue"
```html title="src/pages/IndexPage.vue (change highlighted lines)"
<template>
<q-page class="row items-center justify-evenly">
<!-- highlight-start -->
@ -296,7 +314,7 @@ then restart the development process.
This uses two functions that should be added to the component script:
```ts title="src/pages/IndexPage.vue"
```ts title="src/pages/IndexPage.vue (add highlighted lines)"
const meta = ref<Meta>({
totalCount: 1200
});
@ -324,7 +342,7 @@ then restart the development process.
8) Wire up the `updateFile` function:
```ts title="src/pages/IndexPage.vue"
```ts title="src/pages/IndexPage.vue (add highlighted lines)"
import { defineComponent, ref } from 'vue';
// highlight-start
import { read, write, utils } from 'xlsx';
@ -356,7 +374,7 @@ export default defineComponent({
To test that reading works:
- Download <https://sheetjs.com/pres.numbers>
- Download https://docs.sheetjs.com/pres.numbers
- In the simulator, click the Home icon to return to the home screen
- Click on the "Files" icon
- Click and drag `pres.numbers` from a Finder window into the simulator.
@ -373,7 +391,7 @@ Once selected, the screen should refresh with new contents.
9) Wire up the `saveFile` function:
```ts title="src/pages/IndexPage.vue"
```ts title="src/pages/IndexPage.vue (add highlighted lines)"
function saveFile() {
// highlight-start
/* generate workbook from state */
@ -471,6 +489,8 @@ cd ..
quasar dev -m android
```
If prompted to select an external IP, press <kbd>Enter</kbd>.
To test that reading works:
- Click and drag `pres.numbers` from a Finder window into the simulator.
@ -487,6 +507,80 @@ adb exec-out run-as org.sheetjs.quasar cat files/files/SheetJSQuasar.xlsx > /tmp
npx xlsx-cli /tmp/SheetJSQuasar.xlsx
```
**iOS Device**
12) Close all open emulators and simulators.
13) Disconnect any iOS or Android devices connected to the computer.
14) Connect the iOS device to the computer.
15) Open the Xcode project:
```bash
open src-cordova/platforms/ios/SheetJSQuasar.xcodeproj
```
Select "SheetJSQuasar" in the Navigator. In the main pane, select "Signing &amp;
Capabilities" and ensure a Team is selected. Save and close the project.
16) Start the dev process:
```bash
quasar dev -m ios
```
If prompted to select an external IP, press <kbd>Enter</kbd>.
17) Test the application:
- Press the Home button (or swipe up with one finger) and switch to Safari.
- Download https://docs.sheetjs.com/pres.numbers
- Press the Home button (or swipe up with one finger) and select the `SheetJSQuasar` app
- Tap the "Load" button, then select "Choose File" and select the downloaded `pres.numbers`
The table will update with new data.
- Tap "Save File"
- Press the Home button (or swipe up with one finger) and switch to Files.
- Tap `<` until the main "Browse" window is displayed, then select "On My iPhone"
- Look for the "SheetJSQuasar" folder and tap `SheetJSQuasar.xlsx`.
If Numbers is installed on the device, it will display the contents of the new file.
**Android Device**
18) Close all open emulators and simulators.
19) Disconnect any iOS or Android devices connected to the computer.
20) Connect the Android device to the computer.
21) Start the dev process:
```bash
quasar dev -m android
```
If prompted to select an external IP, press <kbd>Enter</kbd>.
22) Test the application:
- Press the Home button (or swipe up with one finger) and switch to Browser.
- Download https://docs.sheetjs.com/pres.numbers
- Press the Home button (or swipe up with one finger) and select the `SheetJSQuasar` app
- Tap the "Load" button, then select "Choose File" and select the downloaded `pres.numbers`
The table will update with new data.
:::warning pass
The "Save File" process will write files. However, Android 30+ requires special
methods ("Storage Access Framework") that are not implemented in Quasar.
:::
[^1]: See ["File Picker"](https://quasar.dev/vue-components/file-picker) in the Quasar documentation.
[^2]: See [`read` in "Reading Files"](/docs/api/parse-options)
[^3]: See ["SheetJS Data Model"](/docs/csf/) for more details on workbooks, worksheets, and other concepts.

@ -49,21 +49,34 @@ The [CapacitorJS demo](/docs/demos/mobile/capacitor) covers CapacitorJS apps.
This demo was tested in the following environments:
| OS | Type | Device | Date |
|:-----------|:-----|:--------------------|:-----------|
| Android 34 | Sim | Pixel 3a | 2023-12-04 |
| iOS 17.0.1 | Sim | iPhone SE (3rd gen) | 2023-12-04 |
**Real Devices**
`ionic info` showed:
| OS | Device | Config | Date |
|:-----------|:--------------------|:-------|:-----------|
| Android 30 | NVIDIA Shield | A | 2024-05-30 |
| iOS 15.1 | iPad Pro | A | 2024-05-30 |
- Ionic: `@ionic/angular 7.5.7`, `@ionic/angular-toolkit 9.0.0`
- Cordova: `cordova-lib@12.0.1`, `android 12.0.1, ios 7.0.1`
**Simulators**
The file integration uses `@awesome-cordova-plugins/file` version `6.4.0`.
| OS | Device | Config | Dev Platform | Date |
|:-----------|:--------------------|:-------|:-------------|:-----------|
| Android 34 | Pixel 3a | A | `darwin-arm` | 2024-05-30 |
| iOS 17.5 | iPhone SE (3rd gen) | A | `darwin-arm` | 2024-05-30 |
<details>
<summary><b>Configurations</b> (click to show)</summary>
Configuration A:
- Ionic: `@ionic/angular 8.2.0`, `@ionic/angular-toolkit 11.0.1`
- Cordova: `cordova-lib@12.0.1`, `android 13.0.0, ios 7.1.0`
- File Integration: `@awesome-cordova-plugins/file` version `6.7.0`
</details>
:::
:::warning Telemetry
:::danger Telemetry
Before starting this demo, manually disable telemetry. On Linux and MacOS:
@ -132,7 +145,18 @@ simplifies iteration over the array of arrays:
### File Operations
`@awesome-cordova-plugins/file` reads and writes files on devices.
The `cordova-plugin-file` plugin reads and writes files on devices.
:::caution pass
For Android 30+, due to scoped storage rules, the standard file module writes
private files that cannot be accessed from the Files app.
A Storage Access Framework plugin must be used to write external files.
:::
`@awesome-cordova-plugins/file` is a wrapper designed for Ionic + Angular apps.
:::info pass
@ -193,7 +217,7 @@ this.file.writeFile(url, filename, blob, {replace: true});
The app in this demo will display data in a table.
On load, a [test file](https://sheetjs.com/pres.numbers) will be processed.
On load, a [test file](https://docs.sheetjs.com/pres.numbers) will be processed.
When a document is selected with the file picker, it will be processed and the
table will refresh to show the contents.
@ -211,12 +235,29 @@ known location. After writing, an alert will display the location of the file.
1) Follow the official instructions for iOS and Android development[^9].
<details>
<summary><b>Installation Notes</b> (click to show)</summary>
Ionic requires Java 17.
</details>
2) Install required global dependencies:
```bash
npm i -g cordova cordova-res @angular/cli native-run @ionic/cli
```
:::note pass
In some systems, the command must be run as the root user:
```bash
sudo npm i -g cordova cordova-res @angular/cli native-run @ionic/cli
```
:::
### Base Project
3) Create a new project:
@ -233,9 +274,9 @@ If a prompt asks about creating an Ionic account, enter `N` to opt out.
:::caution pass
Due to conflicts in the dependency tree, the command failed in the last test.
Due to conflicts in the dependency tree, the command failed in some test runs.
The fix is to force install all modules:
If the package installation fails, forcefully install all modules:
```bash
cd SheetJSIonic
@ -471,7 +512,7 @@ brew install gradle
:::
:::warning pass
:::danger pass
When the demo was last tested on Android, reading files worked as expected.
However, the generated files were not externally visible from the Files app.
@ -488,4 +529,5 @@ However, the generated files were not externally visible from the Files app.
[^6]: See [`aoa_to_sheet` in "Utilities"](/docs/api/utilities/array#array-of-arrays-input)
[^7]: See ["Workbook Helpers" in "Utilities"](/docs/api/utilities/wb) for details on `book_new` and `book_append_sheet`.
[^8]: See [`write` in "Writing Files"](/docs/api/write-options)
[^9]: See ["Developing for iOS"](https://ionicframework.com/docs/v6/developing/ios) and ["Developing for Android"](https://ionicframework.com/docs/v6/developing/android) in the v6 Ionic framework documentation.
[^9]: See ["Developing for iOS"](https://ionic-docs-o31kiyk8l-ionic1.vercel.app/docs/v6/developing/ios) and ["Developing for Android"](https://ionic-docs-o31kiyk8l-ionic1.vercel.app/docs/v6/developing/android). The Ionic team removed these pages from the official docs site and recommend the `vercel.app` docs site.
[^10]: See the [JDK Archive](https://jdk.java.net/archive/) for Java 17 JDK download links.

@ -1,5 +1,6 @@
---
title: CapacitorJS
title: Storing Sheets with CapacitorJS
sidebar_label: CapacitorJS
pagination_prev: demos/static/index
pagination_next: demos/desktop/index
sidebar_position: 5
@ -10,10 +11,17 @@ sidebar_custom_props:
import current from '/version.js';
import CodeBlock from '@theme/CodeBlock';
The [SheetJS NodeJS Module](/docs/getting-started/installation/nodejs) can be
imported from any component or script in the app.
[CapacitorJS](https://capacitorjs.com/) is a mobile app runtime for building iOS
and Android apps.
The "Complete Example" creates an app that looks like the screenshots below:
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
data from spreadsheets.
This demo uses CapacitorJS and SheetJS to process data and export spreadsheets.
We'll explore how to load SheetJS in an CapacitorJS app and use APIs and plugins
to extract data from, and write data to, spreadsheet files on the device.
The ["Demo"](#demo) creates an app that looks like the screenshots below:
<table><thead><tr>
<th><a href="#demo">iOS</a></th>
@ -32,16 +40,26 @@ The "Complete Example" creates an app that looks like the screenshots below:
This demo was tested in the following environments:
| OS | Type | Device | CapacitorJS + FS | Date |
|:-----------|:-----|:--------------------|:------------------|:-----------|
| Android 34 | Sim | Pixel 3a | `5.5.1` / `5.1.4` | 2023-12-04 |
| iOS 17.0.1 | Sim | iPhone 15 Pro Max | `5.5.1` / `5.1.4` | 2023-12-04 |
| Android 29 | Real | NVIDIA Shield | `5.5.1` / `5.1.4` | 2023-12-04 |
| iOS 15.1 | Real | iPad Pro | `5.5.1` / `5.1.4` | 2023-12-04 |
**Real Devices**
| OS | Device | CapacitorJS + FS | Date |
|:-----------|:--------------------|:------------------|:-----------|
| Android 30 | NVIDIA Shield | `6.0.0` / `6.0.0` | 2024-06-02 |
| iOS 15.1 | iPad Pro | `6.0.0` / `6.0.0` | 2024-06-02 |
**Simulators**
| OS | Device | CapacitorJS + FS | Dev Platform | Date |
|:-----------|:--------------------|:------------------|:-------------|:-----------|
| Android 34 | Pixel 3a | `6.0.0` / `6.0.0` | `darwin-x64` | 2024-06-02 |
| iOS 17.5 | iPhone 15 Pro Max | `6.0.0` / `6.0.0` | `darwin-x64` | 2024-06-02 |
| Android 34 | Pixel 3a | `6.0.0` / `6.0.0` | `darwin-arm` | 2024-06-02 |
| iOS 17.5 | iPhone 15 Pro Max | `6.0.0` / `6.0.0` | `darwin-arm` | 2024-06-02 |
| Android 34 | Pixel 3a | `6.0.0` / `6.0.0` | `win10-x64` | 2024-05-28 |
:::
:::warning Telemetry
:::danger Telemetry
Before starting this demo, manually disable telemetry. On Linux and MacOS:
@ -59,13 +77,26 @@ npx @capacitor/cli telemetry
## Integration Details
This example uses Svelte, but the same principles apply to other frameworks.
The [SheetJS NodeJS Module](/docs/getting-started/installation/nodejs) can be
imported from any component or script in the app.
This demo uses [SvelteJS](/docs/demos/frontend/svelte), but the same principles
apply to other frameworks.
#### Reading data
The standard HTML5 File Input element logic works in CapacitorJS:
The standard [HTML5 File Input](/docs/demos/local/file#file-api) API works as
expected in CapacitorJS.
```html
Apps will typically include an `input type="file"` element. When the element is
activated, CapacitorJS will show a file picker. After the user selects a file,
the element will receive a `change` event.
The following example parses the selected file using the SheetJS `read`[^1]
method, generates a HTML table from the first sheet using `sheet_to_html`[^2],
and displays the table by setting the `innerHTML` attribute of a `div` element:
```html title="Sample component for data import"
<script>
import { read, utils } from 'xlsx';
@ -91,28 +122,39 @@ async function importFile(evt) {
#### Writing data
`@capacitor/filesystem` can write Base64 strings:
Starting from a SheetJS workbook object[^3], the `write` method with the option
`type: "base64"`[^4] will generate Base64-encoded files.
```html
The `@capacitor/filesystem` plugin can write Base64 strings to the device.
The following example uses the SheetJS `table_to_book` method[^5] to create a
workbook object from a HTML table. The workbook object is exported to the XLSX
format and written to the device.
```html title="Sample component for data export"
<script>
import { Filesystem, Directory } from '@capacitor/filesystem';
import { utils, writeXLSX } from 'xlsx';
import { utils, write } from 'xlsx';
let html = "";
let tbl;
/* get state data and export to XLSX */
async function exportFile() {
/* generate workbook object from HTML table */
const elt = tbl.getElementsByTagName("TABLE")[0];
const wb = utils.table_to_book(elt);
/* generate Base64 string for Capacitor */
// highlight-start
const data = writeXLSX(wb, { type: "base64" });
/* export to XLSX encoded in a Base64 string */
const data = write(wb, { bookType: "xlsx", type: "base64" });
/* attempt to write to the device */
await Filesystem.writeFile({
data,
path: "SheetJSCap.xlsx",
directory: Directory.Documents
}); // write file
});
// highlight-end
}
@ -124,11 +166,45 @@ async function exportFile() {
</main>
```
:::caution pass
`Filesystem.writeFile` cannot overwrite existing files. Production apps should
attempt to delete the file before writing:
```js
/* attempt to delete file first */
try {
await Filesystem.deleteFile({
path: "SheetJSCap.xlsx",
directory: Directory.Documents
});
} catch(e) {}
/* attempt to write to the device */
await Filesystem.writeFile({
data,
path: "SheetJSCap.xlsx",
directory: Directory.Documents
});
```
:::
## Demo
The app in this demo will display data in a table.
When the app is launched, a [test file](https://docs.sheetjs.com/pres.numbers)
will be fetched and processed.
When a document is selected with the file picker, it will be processed and the
table will refresh to show the contents.
"Export XLSX" will attempt to export the table data to `SheetJSCap.xlsx` in the
app Documents folder. An alert will display the location of the file.
### Base Project
0) Follow the official "Environment Setup"[^1] instructions to set up Android
0) Follow the official "Environment Setup"[^6] instructions to set up Android
and iOS targets
:::caution pass
@ -137,7 +213,8 @@ iOS development is only supported on macOS.
:::
<details><summary><b>Installation Notes</b> (click to show)</summary>
<details>
<summary><b>Installation Notes</b> (click to show)</summary>
CapacitorJS requires Java 17.
@ -180,7 +257,7 @@ npm run build
:::note pass
If prompted to create an Ionic account, type `N` and press Enter.
If prompted to create an Ionic account, type `N` and press <kbd>Enter</kbd>.
:::
@ -215,7 +292,7 @@ the `Permissions` comment:
<uses-permission android:name="android.permission.INTERNET" />
```
8) Run the app in the simulator
8) Run the app in the simulator:
```bash
npm run build
@ -223,27 +300,70 @@ npx cap sync
npx cap run android
```
9) Test the app
The app should look like the screenshot at the top of the page.
Open the app and observe that presidents are listed in the table.
9) Test the export functionality.
Touch "Export XLSX" and the emulator will ask for permission:
Touch "Export XLSX" and the emulator will ask for permission. Tap "Allow" and a
popup will be displayed with a path.
Tap "Allow" and a popup will be displayed with a path.
Open the "Files" app in the simulator, tap the `≡` icon and tap "Documents". Tap
the "Documents" folder to find `SheetJSCap.xlsx`.
To see the generated file, switch to the "Files" app in the simulator, tap the
`≡` icon and tap "Documents". Tap "Documents" folder to find `SheetJSCap.xlsx`.
<details open>
<summary><b>Downloading the generated file</b> (click to hide)</summary>
`adb` must be run from the root user:
```bash
adb root
```
The file location can be found by searching for `SheetJSCap.xlsx`:
```bash
adb exec-out find / -name SheetJSCap.xlsx
```
The first line of the output starting with `/` is the desired path:
```text
find: /proc/8533/task/8533/exe: No such file or directory
find: /proc/8533/exe: No such file or directory
// highlight-next-line
/data/media/0/Documents/SheetJSCap.xlsx
/storage/emulated/0/Documents/SheetJSCap.xlsx
```
`adb pull` can download the file:
```bash
adb pull "/data/media/0/Documents/SheetJSCap.xlsx" SheetJSCap.xlsx
```
`SheetJSCap.xlsx` can be opened with a spreadsheet editor such as Excel.
</details>
10) Test the import functionality.
Create a spreadsheet or find an existing file. Click and drag the file into the
Android emulator window. The file will be uploaded to a Downloads folder in the
emulator.
Tap on "Choose File" in the app. In the selector, tap `≡` and select "Downloads"
to find the uploaded file. After selecting the file, the table will refresh.
### iOS
10) Create iOS app
11) Create iOS app
```bash
npm i --save @capacitor/ios
npx cap add ios
```
11) Enable file sharing and make the documents folder visible in the iOS app.
12) Enable file sharing and make the documents folder visible in the iOS app.
The following lines must be added to `ios/App/App/Info.plist`:
```xml title="ios/App/App/Info.plist (add to file)"
@ -260,7 +380,7 @@ The following lines must be added to `ios/App/App/Info.plist`:
(The root element of the document is `plist` and it contains one `dict` child)
12) Run the app in the simulator
13) Run the app in the simulator
```bash
npm run build
@ -270,24 +390,45 @@ npx cap run ios
If prompted to select a target device, select "iPhone 15 Pro Max (simulator)".
13) Test the app
The app should look like the screenshot at the top of the page.
Open the app and observe that presidents are listed in the table.
14) Test the export functionality.
Touch "Export XLSX" and a popup will be displayed.
To see the generated file, switch to the "Files" app in the simulator and look
for `SheetJSCap.xlsx` in "On My iPhone" > "`sheetjs-cap`"
<details open>
<summary><b>Downloading the generated file</b> (click to hide)</summary>
The app files are available in the filesystem in `~/Library/Developer`. Open a
terminal and run the following command to find the file:
```bash
find ~/Library/Developer -name SheetJSCap.xlsx
```
</details>
15) Test the import functionality.
Create a spreadsheet or find an existing file. Click and drag the file into the
iOS simulator window. The simulator will show a picker for saving the file.
Select the `sheetjs-cap` folder and tap "Save".
Tap on "Choose File" in the app and "Choose File" in the popup. In the picker,
tap "Recents" and select the new file. After selecting the file, the table will refresh.
### Android Device
14) Connect an Android device using a USB cable.
16) Connect an Android device using a USB cable.
If the device asks to allow USB debugging, tap "Allow".
15) Close any Android / iOS emulators.
17) Close any Android / iOS emulators.
16) Build APK and run on device:
18) Build APK and run on device:
```bash
npm run build
@ -298,6 +439,13 @@ npx cap run android
If the Android emulators are closed and an Android device is connected, the last
command will build an APK and install on the device.
:::note pass
In some tests, the last command asked for a target device. Select the Android
device in the list and press <kbd>Enter</kbd>
:::
:::caution pass
For real devices running API level 29 or below, the following line must be added
@ -319,13 +467,17 @@ to the `application` open tag in `android/app/src/main/AndroidManifest.xml`:
### iOS Device
17) Connect an iOS device using a USB cable
19) Connect an iOS device using a USB cable
18) Close any Android / iOS emulators.
20) Close any Android / iOS emulators.
19) Enable developer code signing certificates[^2]
21) Enable developer code signing certificates.
19) Run on device:
Open `ios/App/App.xcworkspace` in Xcode. Select the "Project Navigator" and
select the "App" project. In the main view, select "Signing & Capabilities".
Under "Signing", select a team in the dropdown menu.
22) Run on device:
```bash
npm run build
@ -335,5 +487,9 @@ npx cap run ios
When prompted to select a target device, select the real device in the list.
[^1]: See ["Environment Setup"](https://capacitorjs.com/docs/getting-started/environment-setup) in the CapacitorJS documentation.
[^2]: The [Flutter documentation](https://docs.flutter.dev/get-started/install/macos?tab=ios15#enable-developer-code-signing-certificates) covers the instructions in more detail. The correct workspace is `ios/App/App.xcworkspace`
[^1]: See [`read` in "Reading Files"](/docs/api/parse-options)
[^2]: See [`sheet_to_html` in "Utilities"](/docs/api/utilities/html#html-table-output)
[^3]: See ["Workbook Object"](/docs/csf/book)
[^4]: See [the "base64" type in "Writing Files"](/docs/api/write-options#output-type)
[^5]: See [`table_to_book` in "HTML" Utilities](/docs/api/utilities/html#create-new-sheet)
[^6]: See ["Environment Setup"](https://capacitorjs.com/docs/getting-started/environment-setup) in the CapacitorJS documentation.

@ -10,6 +10,8 @@ sidebar_custom_props:
---
import current from '/version.js';
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeBlock from '@theme/CodeBlock';
Dart[^1] + Flutter[^2] is a popular cross-platform app framework. JavaScript
@ -41,16 +43,23 @@ The "Demo" creates an app that looks like the screenshots below:
This demo was tested in the following environments:
| OS | Type | Device | Dart | Flutter | Date |
|:-----------|:-----|:------------------|:--------|:---------|:-----------|
| Android 34 | Sim | Pixel 3a | `3.2.2` | `3.16.2` | 2023-12-04 |
| iOS 17.0.1 | Sim | iPhone 15 Pro Max | `3.2.2` | `3.16.2` | 2023-12-04 |
| Android 29 | Real | NVIDIA Shield | `3.2.2` | `3.16.2` | 2023-12-04 |
| iOS 15.1 | Real | iPad Pro | `3.2.2` | `3.16.2` | 2023-12-04 |
**Real Devices**
| OS | Device | Dart | Flutter | Date |
|:-----------|:------------------|:--------|:---------|:-----------|
| Android 30 | NVIDIA Shield | `3.4.3` | `3.22.2` | 2024-06-09 |
| iOS 15.1 | iPad Pro | `3.4.3` | `3.22.2` | 2024-06-09 |
**Simulators**
| OS | Device | Dart | Flutter | Dev Platform | Date |
|:-----------|:------------------|:--------|:---------|:-------------|:-----------|
| Android 34 | Pixel 3a | `3.4.3` | `3.22.2` | `darwin-x64` | 2024-06-09 |
| iOS 17.5 | iPhone 15 Pro Max | `3.4.3` | `3.22.2` | `darwin-x64` | 2024-06-09 |
:::
:::warning Telemetry
:::danger Telemetry
Before starting this demo, manually disable telemetry. On MacOS:
@ -212,15 +221,35 @@ class SheetJSFlutterState extends State<SheetJSFlutter> {
Run `flutter doctor` and confirm the following items are checked:
<Tabs groupId="os">
<TabItem value="linux" label="Linux">
```
[✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
[✓] Xcode - develop for iOS and macOS (Xcode 15.0.1)
[✓] Android Studio (version 2022.3)
```
</TabItem>
<TabItem value="macos" label="macOS" default>
```
[✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
[✓] Xcode - develop for iOS and macOS (Xcode 15.4)
```
</TabItem>
<TabItem value="win" label="Windows">
```
[✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
```
</TabItem>
</Tabs>
(the actual version numbers may differ)
<details open><summary><b>Installation Notes</b> (click to hide)</summary>
<details open>
<summary><b>Installation Notes</b> (click to hide)</summary>
:::note pass
@ -282,6 +311,22 @@ If Chromium is installed, the environment variable should be manually assigned:
export CHROME_EXECUTABLE=/Applications/Chromium.app/Contents/MacOS/Chromium
```
<Tabs groupId="os">
<TabItem value="linux" label="Linux">
</TabItem>
<TabItem value="macos" label="macOS">
```bash
export CHROME_EXECUTABLE=/Applications/Chromium.app/Contents/MacOS/Chromium
```
</TabItem>
<TabItem value="win" label="Windows">
</TabItem>
</Tabs>
:::
</details>
@ -289,17 +334,18 @@ export CHROME_EXECUTABLE=/Applications/Chromium.app/Contents/MacOS/Chromium
Run `flutter emulators` and check for both `ios` and `android` emulators:
```
apple_ios_simulator • iOS Simulator • Apple • ios
Pixel_3a_API_34 • Pixel 3a API 34 • Google • android
Id • Name • Manufacturer • Platform
apple_ios_simulator • iOS Simulator • Apple • ios
Pixel_3a_API_34 • Pixel 3a API 34 • Google • android
```
1) Disable telemetry.
1) Disable telemetry. The following commands were confirmed to work:
```bash
dart --disable-telemetry
dart --disable-analytics
flutter config --no-analytics
flutter config --disable-telemetry
```
### Base Project
@ -313,7 +359,8 @@ cd sheetjs_flutter
3) Start the Android emulator.
<details open><summary><b>Details</b> (click to hide)</summary>
<details open>
<summary><b>Details</b> (click to hide)</summary>
**Android Studio**
@ -360,7 +407,8 @@ emulator -avd Pixel_3a_API_34
flutter run
```
<details><summary><b>If emulator is not detected</b> (click to show)</summary>
<details>
<summary><b>If emulator is not detected</b> (click to show)</summary>
In some test runs, `flutter run` did not automatically detect the emulator.
@ -440,12 +488,12 @@ curl -L -o lib/main.dart https://docs.sheetjs.com/flutter/main.dart
flutter run
```
The app fetches <https://sheetjs.com/pres.numbers>, parses, converts data to an
array of arrays, and presents the data in a Flutter `Table` widget.
The app fetches https://docs.sheetjs.com/pres.numbers, parses, converts data to
an array of arrays, and presents the data in a Flutter `Table` widget.
:::caution pass
:::info pass
When the demo was last run, there was a build error:
In some demo runs, the build failed with an Android SDK error:
```
│ The plugin flutter_js requires a higher Android SDK version. │
@ -485,8 +533,8 @@ Searching for `minSdkVersion` should reveal the following line:
flutter run
```
The app fetches <https://sheetjs.com/pres.numbers>, parses, converts data to an
array of arrays, and presents the data in a Flutter `Table` widget.
The app fetches https://docs.sheetjs.com/pres.numbers, parses, converts data to
an array of arrays, and presents the data in a Flutter `Table` widget.
### Android Device
@ -503,7 +551,7 @@ flutter devices
The list should include the device:
```
SheetJS (mobile) • 1234567890 • android-arm64 • Android 10 (API 29)
SheetJS (mobile) • 726272627262726272 • android-arm64 • Android 11 (API 30)
^^^^^^^--- the first column is the name
```
@ -519,7 +567,9 @@ flutter build apk --release
flutter install
```
The script will ask for a device:
:::note pass
The script may ask for a device:
```
[1]: SheetJS (1234567890)
@ -531,7 +581,9 @@ Please choose one (or "q" to quit):
Select the number corresponding to the device.
18) Launch the installed `sheetjs_flutter` app on the device
:::
18) Launch the installed `sheetjs_flutter` app on the device.
:::caution pass
@ -554,7 +606,7 @@ flutter devices
The list should include the device:
```
SheetPad (mobile) • 00000000-0000000000000000 ios • iOS 15.1 19B74
SheetPad (mobile) • 00000000-0000000000000000 • ios • iOS 15.1 19B74
^^^^^^^^--- the first column is the name
```
@ -564,8 +616,57 @@ The list should include the device:
flutter run -d SheetPad
```
[^1]: <https://dart.dev/> is the official site for the Dart Programming Language.
[^2]: <https://flutter.dev/> is the official site for the Flutter Framework.
In debug mode, "Flutter tools" will attempt to connect to the running app. The
device will ask for permission:
> "Sheetjs Flutter" would like to find and connect to devices on your local network.
Tap "OK" to continue.
:::caution pass
When this demo was last tested, the build failed with an error:
```text
Could not build the precompiled application for the device.
Error (Xcode): No profiles for 'com.example.sheetjsFlutter' were found: Xcode couldn't find any iOS App Development provisioning profiles matching 'com.example.sheetjsFlutter'. Automatic signing is disabled and unable to generate a profile. To enable automatic signing, pass -allowProvisioningUpdates to xcodebuild.
```
The message includes a hint:
```
Verify that the Bundle Identifier in your project is your signing id in Xcode
open ios/Runner.xcworkspace
```
Open the workspace and select the "Runner" project in the Navigator. In the main
pane, select "Signing &amp; Capabilities" and ensure a Team is selected. From
the menu bar, select "Product" > "Run" to run the app.
:::
:::info pass
If there is an "Untrusted Developer" error, the certificate must be trusted on
the device. The following steps were verified in iOS 15.1:
1) Open the "Settings" app on the device
In the "APPS FROM DEVELOPER" section of the new screen, "Sheetjs Flutter" should
be displayed. If it is missing, tap the "&lt;" button near the top of the screen
and select a different certificate from the list.
2) Select "General" > "VPN &amp; Device Management".
3) In the "DEVELOPER APP" section, tap the certificate that is "Not Trusted".
4) After confirming "Sheetjs Flutter" is in the list, tap the "Trust" button and
tap "Trust" in the popup.
:::
[^1]: https://dart.dev/ is the official site for the Dart Programming Language.
[^2]: https://flutter.dev/ is the official site for the Flutter Framework.
[^3]: [The `flutter_js` package](https://pub.dev/packages/flutter_js) is hosted on the Dart package repository.
[^4]: See [the dedicated "Swift + JavaScriptCore" demo](/docs/demos/engines/jsc) for more details.
[^5]: See [the dedicated "C + QuickJS" demo](/docs/demos/engines/quickjs) for more details.

@ -1,18 +1,28 @@
---
title: iOS and Android Apps
title: Tables on Tablets and Mobile Devices
sidebar_label: iOS and Android Apps
pagination_prev: demos/static/index
pagination_next: demos/desktop/index
hide_table_of_contents: true
---
import EngineData from '/data/mobile.js'
import DocCardList from '@theme/DocCardList';
import {useCurrentSidebarCategory} from '@docusaurus/theme-common';
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
data from spreadsheets.
Many mobile app frameworks mix JavaScript / CSS / HTML5 concepts with native
extensions and libraries to create a hybrid development experience. Developers
well-versed in web technologies can now build actual mobile applications that
run on iOS and Android!
:::warning pass
The demos in this section showcase a number of mobile frameworks. In each case,
we will build a sample app that loads SheetJS library scripts and processes
on-device and remote spreadsheet files.
:::danger pass
**The ecosystem has broken backwards-compatibility many times!**
@ -24,10 +34,6 @@ MacOS is required for the iOS demos. The Android demos were tested on MacOS.
:::
The ["JavaScript Engines"](/docs/demos/engines) section includes samples for JS
engines used in the mobile app frameworks. SheetJS libraries have been tested
in the relevant engines and should "just work" with some caveats.
Demos for common tools are included in separate pages. Each demo section will
mention test dates and platform versions.
@ -40,6 +46,14 @@ mention test dates and platform versions.
</li>);
})}</ul>
:::info pass
The ["JavaScript Engines"](/docs/demos/engines) section includes samples for JS
engines used in the mobile app frameworks. SheetJS libraries have been tested
in the relevant engines.
:::
:::note Recommendation
React Native is extremely popular and is the recommended choice for greenfield
@ -47,9 +61,9 @@ projects that can use community modules. However, its "lean core" approach
forces developers to learn iOS/Android programming or use community modules to
provide basic app features.
The original Web View framework was PhoneGap/Cordova. The modern frameworks
are built atop Cordova. Cordova is waning in popularity but it has a deep
library of community modules to solve many problems.
The original Web View framework was PhoneGap/Cordova. The modern frameworks are
built atop Cordova. Cordova is waning in popularity but it has a deep library of
community modules to solve many problems.
Before creating a new app, it is important to identify what features the app
should support and investigate community modules. If there are popular modules
@ -57,3 +71,16 @@ for features that must be included, or for teams that are comfortable with
native app development, React Native is the obvious choice.
:::
### Platforms
The following frameworks have been tested:
<EngineData/>
:::info pass
When this table was last updated, it was not possible to build an iOS app from
Linux or Windows. Android tooling runs on MacOS, Linux and Windows.
:::

@ -189,11 +189,11 @@ This demo was tested in the following environments:
| OS and Version | Architecture | Electron | Date |
|:---------------|:-------------|:---------|:-----------|
| macOS 14.4 | `darwin-x64` | `29.1.4` | 2024-03-15 |
| macOS 14.1.2 | `darwin-arm` | `27.1.3` | 2023-12-01 |
| macOS 14.5 | `darwin-arm` | `30.0.8` | 2024-05-28 |
| Windows 10 | `win10-x64` | `28.2.0` | 2024-03-04 |
| Windows 11 | `win11-arm` | `27.1.3` | 2023-12-01 |
| Windows 11 | `win11-arm` | `30.0.8` | 2024-05-28 |
| Linux (HoloOS) | `linux-x64` | `29.1.4` | 2024-03-21 |
| Linux (Debian) | `linux-arm` | `27.1.3` | 2023-12-01 |
| Linux (Debian) | `linux-arm` | `30.0.8` | 2024-05-28 |
:::
@ -268,15 +268,18 @@ The program will run on ARM64 Windows.
### Testing
5) Download [the test file `pres.numbers`](https://sheetjs.com/pres.numbers)
5) Download [the test file `pres.numbers`](https://docs.sheetjs.com/pres.numbers)
6) Launch the generated application:
| Architecture | Command |
|:-------------|:--------------------------------------------------------------|
| `darwin-x64` | `open ./out/sheetjs-electron-darwin-x64/sheetjs-electron.app` |
| `win10-x64` | `.\out\sheetjs-electron-win32-x64\sheetjs-electron.exe` |
| `linux-x64` | `./out/sheetjs-electron-linux-x64/sheetjs-electron` |
| `darwin-x64` |`open ./out/sheetjs-electron-darwin-x64/sheetjs-electron.app` |
| `darwin-arm` |`open ./out/sheetjs-electron-darwin-arm64/sheetjs-electron.app`|
| `win10-x64` |`.\out\sheetjs-electron-win32-x64\sheetjs-electron.exe` |
| `win11-arm` |`.\out\sheetjs-electron-win32-x64\sheetjs-electron.exe` |
| `linux-x64` |`./out/sheetjs-electron-linux-x64/sheetjs-electron` |
| `linux-x64` |`./out/sheetjs-electron-linux-arm64/sheetjs-electron` |
#### Electron API
@ -319,7 +322,7 @@ and select `pres.numbers`.
## Electron Breaking Changes
The first version of this demo used Electron 1.7.5. The current demo includes
the required changes for Electron 28.2.0.
the required changes for Electron 30.0.8.
There are no Electron-specific workarounds in the library, but Electron broke
backwards compatibility multiple times. A summary of changes is noted below.

@ -111,16 +111,14 @@ input.click();
This demo was tested in the following environments:
| OS and Version | Architecture | NW.js | Date |
|:---------------|:-------------|:---------|:-----------|
| macOS 14.3.1 | `darwin-x64` | `0.85.0` | 2024-03-12 |
| macOS 14.1.2 | `darwin-arm` | `0.82.0` | 2023-12-01 |
| Windows 10 | `win10-x64` | `0.83.0` | 2024-03-04 |
| Windows 11 | `win11-arm` | `0.82.0` | 2023-12-01 |
| Linux (HoloOS) | `linux-x64` | `0.85.0` | 2024-03-12 |
There is no official Linux ARM64 release. The community release[^1] was tested
and verified on 2023-09-27.
| OS and Version | Architecture | NW.js | Date | Notes |
|:---------------|:-------------|:---------|:-----------|:---------------------|
| macOS 14.3.1 | `darwin-x64` | `0.85.0` | 2024-03-12 | |
| macOS 14.5 | `darwin-arm` | `0.88.0` | 2024-05-28 | |
| Windows 10 | `win10-x64` | `0.83.0` | 2024-03-04 | |
| Windows 11 | `win11-arm` | `0.88.0` | 2024-05-28 | |
| Linux (HoloOS) | `linux-x64` | `0.85.0` | 2024-03-12 | |
| Linux (Debian) | `linux-arm` | `0.60.0` | 2024-05-23 | Unofficial build[^1] |
:::
@ -140,7 +138,7 @@ cd sheetjs-nwjs
"version": "0.0.0",
"main": "index.html",
"dependencies": {
"nw": "0.85.0",
"nw": "0.88.0",
"xlsx": "https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz"
}
}`}
@ -173,13 +171,18 @@ npm i
npx nw .
```
The app will show and you should be able to verify reading and writing by using
the file input element to select a spreadsheet and clicking the export button.
On launch, the app will fetch and parse https://docs.sheetjs.com/pres.numbers .
Using the file input element, a file can be selected from the filesystem and the
table will refresh with the contents of the selected file.
Click "Export Data!" and save the generated file to `SheetJSNWDemo.xlsx`. This
file can be opened in Excel or another spreadsheet editor.
5) To build a standalone app, run the builder:
```bash
npx -p nw-builder nwbuild --mode=build --version=0.85.0 --glob=false --outDir=../out ./
npx -p nw-builder nwbuild --mode=build --version=0.88.0 --glob=false --outDir=../out ./
```
This will generate the standalone app in the `..\out\` folder.

@ -298,17 +298,18 @@ This demo was tested in the following environments:
| OS and Version | Architecture | Wails | Date |
|:---------------|:-------------|:---------|:-----------|
| macOS 14.4 | `darwin-x64` | `v2.8.0` | 2024-03-15 |
| macOS 14.1.2 | `darwin-arm` | `v2.6.0` | 2023-12-01 |
| macOS 14.5 | `darwin-arm` | `v2.8.2` | 2024-05-28 |
| Windows 10 | `win10-x64` | `v2.8.0` | 2024-03-24 |
| Windows 11 | `win11-arm` | `v2.6.0` | 2023-12-01 |
| Windows 11 | `win11-arm` | `v2.8.2` | 2024-05-28 |
| Linux (HoloOS) | `linux-x64` | `v2.8.0` | 2024-03-21 |
| Linux (Debian) | `linux-arm` | `v2.6.0` | 2023-12-01 |
| Linux (Debian) | `linux-arm` | `v2.8.2` | 2024-05-28 |
:::
0) Read the Wails "Getting Started" guide[^14] and install dependencies.
<details><summary><b>Installation Notes</b> (click to show)</summary>
<details>
<summary><b>Installation Notes</b> (click to show)</summary>
Wails will require:
@ -333,7 +334,7 @@ On macOS and Linux, the `PATH` environment variable must include `~/go/bin`. If
`wails` cannot be found, run the following command in the terminal session:
```bash
export PATH="$PATH:~/go/bin"
export PATH="$PATH":~/go/bin
```
:::
@ -403,7 +404,7 @@ It will print the path to the generated program (typically in `build/bin/`).
**Testing**
The program will download [`pres.xlsx`](https://sheetjs.com/pres.xlsx) and
The program will download [`pres.xlsx`](https://docs.sheetjs.com/pres.xlsx) and
display the contents of the first worksheet in a table.
To test export features, click "Export XLSX". The app will ask for a file name

@ -9,13 +9,15 @@ sidebar_custom_props:
summary: Webview + Rust Backend
---
# Data Wrangling in Tauri Apps
import current from '/version.js';
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeBlock from '@theme/CodeBlock';
export const y = {style: {color:"gold"}};
export const g = {style: {color:"green"}};
export const B = {style: {fontWeight:"bold"}};
[Tauri](https://tauri.app/) is a modern toolkit for building desktop apps. Tauri
apps leverage platform-native browser engines to build lightweight programs.
@ -210,7 +212,7 @@ function SheetJSImportKaioponent() {
<table><tbody>{data.map((row) =>
<tr>{row.map((cell) => <td>{cell}</td>)}</tr>
)}</tbody></table>
</>);
</> );
}
```
@ -329,7 +331,6 @@ function SheetJSExportKaioponent() {
return ( <button type="button" onclick={save_callback}>Save Data</button> );
}
```
</TabItem>
@ -343,18 +344,19 @@ This demo was tested in the following environments:
| OS and Version | Architecture | Tauri | Date |
|:---------------|:-------------|:----------|:-----------|
| macOS 14.4 | `darwin-x64` | `v1.5.11` | 2024-03-15 |
| macOS 14.0 | `darwin-arm` | `v1.5.2` | 2023-10-18 |
| macOS 14.4 | `darwin-x64` | `v1.5.11` | 2024-04-20 |
| macOS 14.5 | `darwin-arm` | `v1.5.14` | 2024-05-26 |
| Windows 10 | `win10-x64` | `v1.5.11` | 2024-03-24 |
| Windows 11 | `win11-arm` | `v1.5.7` | 2023-12-01 |
| Windows 11 | `win11-arm` | `v1.5.14` | 2024-05-28 |
| Linux (HoloOS) | `linux-x64` | `v1.5.11` | 2024-03-21 |
| Linux (Debian) | `linux-arm` | `v1.5.7` | 2023-12-01 |
| Linux (Debian) | `linux-arm` | `v1.5.14` | 2024-05-28 |
:::
0) Read Tauri "Getting Started" guide and install prerequisites.[^16]
<details><summary><b>Installation Notes</b> (click to show)</summary>
<details>
<summary><b>Installation Notes</b> (click to show)</summary>
At a high level, the following software is required for building Tauri apps:
@ -371,18 +373,18 @@ npx @tauri-apps/cli info
If required dependencies are installed, the output will show a checkmark next to
"Environment". The output from the most recent macOS test is shown below:
```
[✔] Environment
- OS: Mac OS 14.4.0 X64
✔ Xcode Command Line Tools: installed
✔ rustc: 1.76.0 (07dca489a 2024-02-04)
✔ cargo: 1.76.0 (c84b36747 2024-01-18)
✔ rustup: 1.27.0 (bbb9276d2 2024-03-08)
✔ Rust toolchain: stable-x86_64-apple-darwin (default)
- node: 20.11.1
- npm: 10.2.4
- bun: 1.0.31
```
<pre>
<span {...g}>[✔]</span> <span style={{...y.style,...B.style}}>Environment</span>
{` `}<span {...g}>-</span> <span {...B}>OS</span>: Mac OS 14.4.1 X64
{` `}<span {...g}></span> <span {...B}>Xcode Command Line Tools</span>: installed
{` `}<span {...g}></span> <span {...B}>rustc</span>: 1.77.2 (25ef9e3d8 2024-04-09)
{` `}<span {...g}></span> <span {...B}>cargo</span>: 1.77.2 (e52e36006 2024-03-26)
{` `}<span {...g}></span> <span {...B}>rustup</span>: 1.27.0 (bbb9276d2 2024-03-08)
{` `}<span {...g}></span> <span {...B}>Rust toolchain</span>: stable-x86_64-apple-darwin (default)
{` `}<span {...g}>-</span> <span {...B}>node</span>: 20.12.1
{` `}<span {...g}>-</span> <span {...B}>npm</span>: 10.5.0
{` `}<span {...g}>-</span> <span {...B}>bun</span>: 1.1.4
</pre>
:::caution pass
@ -434,6 +436,8 @@ npm i --save-dev @tauri-apps/cli`}
</TabItem>
<TabItem value="kaioken" label="Kaioken" default>
Install the Kaioken dependencies:
```bash
npm add kaioken --save
npm add vite-plugin-kaioken -D --save
@ -463,14 +467,22 @@ npm add vite-plugin-kaioken -D --save
// highlight-end
```
In the same file, look for the `"identifier"` key and replace the value with `com.sheetjs.tauri`:
In the same file, look for `"title"` and change the value to `SheetJS x Tauri`:
```json title="src-tauri/tauri.conf.json (edit highlighted line)"
"icons/icon.ico"
],
{
// highlight-next-line
"title": "SheetJS x Tauri",
"width": 800,
```
In the same file, look for `"identifier"` and change the value to `com.sheetjs.tauri`:
```json title="src-tauri/tauri.conf.json (edit highlighted line)"
"targets": "all",
// highlight-next-line
"identifier": "com.sheetjs.tauri",
"longDescription": "",
"icon": [
```
<Tabs groupId="framework">
@ -493,19 +505,11 @@ curl -o src/App.vue https://docs.sheetjs.com/tauri/App.vue
```ts title="vite.config.ts (add highlighted lines)"
import { defineConfig } from "vite";
// highlight-next-line
import kaioken from "vite-plugin-kaioken"
import kaioken from "vite-plugin-kaioken";
// https://vitejs.dev/config/
export default defineConfig(async () => ({
// highlight-start
esbuild: {
jsxInject: `import * as kaioken from "kaioken"`,
jsx: "transform",
jsxFactory: "kaioken.createElement",
jsxFragment: "kaioken.fragment",
loader: "tsx",
include: ["**/*.tsx", "**/*.ts", "**/*.jsx", "**/*.js"],
},
plugins: [kaioken()],
// highlight-end
```
@ -625,7 +629,7 @@ or
The following features should be manually verified:
- When it is loaded, the app will download <https://sheetjs.com/pres.numbers>
- When it is loaded, the app will download https://docs.sheetjs.com/pres.numbers
and display the data in a table.
- Clicking "Save Data" will show a save dialog. After selecting a path and name,
the app will write a file. That file can be opened in a spreadsheet editor.
@ -665,7 +669,7 @@ sudo pacman -S openssl
:::note pass
During the last macOS test, the build failed with the following error message:
In some macOS tests, the build failed with the following error message:
```
Error failed to bundle project: error running bundle_dmg.sh
@ -690,6 +694,14 @@ select "Automation" in the body. Look for "Terminal", expand the section, and en
:::
:::note pass
In some tests, the fonts did not match the screenshots.
**The Inter font static TTFs must be manually downloaded and installed.**[^17]
:::
[^1]: See ["Security"](https://tauri.app/v1/references/architecture/security#allowing-api) in the Tauri documentation
[^2]: See [`FsAllowlistConfig`](https://tauri.app/v1/api/config/#fsallowlistconfig) in the Tauri documentation
[^3]: See [`DialogAllowlistConfig`](https://tauri.app/v1/api/config/#dialogallowlistconfig) in the Tauri documentation
@ -705,4 +717,5 @@ select "Automation" in the body. Look for "Terminal", expand the section, and en
[^13]: See [`fs`](https://tauri.app/v1/api/js/fs#writebinaryfile) in the Tauri documentation
[^14]: See ["Array of Arrays Input" in "Utility Functions"](/docs/api/utilities/array#array-of-arrays-input)
[^15]: See ["Workbook Helpers" in "Utility Functions"](/docs/api/utilities/wb)
[^16]: See ["Prerequisites"](https://tauri.app/v1/guides/getting-started/prerequisites) in the Tauri documentation
[^16]: See ["Prerequisites"](https://tauri.app/v1/guides/getting-started/prerequisites) in the Tauri documentation
[^17]: Click "Get font" in the [Inter Google Fonts listing](https://fonts.google.com/specimen/Inter)

@ -193,18 +193,22 @@ This demo was tested in the following environments:
| OS and Version | Architecture | Server | Client | Date |
|:---------------|:-------------|:---------|:---------|:-----------|
| macOS 14.4 | `darwin-x64` | `5.0.0` | `5.0.1` | 2024-03-15 |
| macOS 14.0 | `darwin-arm` | `4.14.1` | `3.12.0` | 2023-10-18 |
| macOS 14.5 | `darwin-arm` | `5.1.0` | `5.1.0` | 2024-05-25 |
| Windows 10 | `win10-x64` | `5.1.0` | `5.1.0` | 2024-03-24 |
| Windows 11 | `win11-arm` | `4.14.1` | `3.12.0` | 2023-12-01 |
| Windows 11 | `win11-arm` | `5.1.0` | `5.1.1` | 2024-05-28 |
| Linux (HoloOS) | `linux-x64` | `5.0.0` | `5.0.1` | 2024-03-21 |
| Linux (Debian) | `linux-arm` | `4.14.1` | `3.12.0` | 2023-12-01 |
| Linux (Debian) | `linux-arm` | `5.1.0` | `5.1.1` | 2024-05-28 |
On `win11-arm`, the Electron runner is a proper ARM64 binary but the binaries
generated by Electron Forge are x64. The x64 binaries run in Windows on ARM.
:::
The app core state will be the HTML table. Reading files will add the table to
the window. Writing files will parse the table into a spreadsheet.
<details><summary><b>Installation Notes</b> (click to show)</summary>
<details>
<summary><b>Installation Notes</b> (click to show)</summary>
NeutralinoJS uses `portable-file-dialogs`[^12] to show open and save dialogs. On
Linux, Zenity or KDialog are require.
@ -302,7 +306,7 @@ table {
6) Print the version number in the `showInfo` method of `resources/js/main.js`:
```js title="resources/js/main.js"
```js title="resources/js/main.js (add highlighted lines)"
function showInfo() {
document.getElementById('info').innerHTML = `
${NL_APPID} is running on port ${NL_PORT} inside ${NL_OS}
@ -328,14 +332,18 @@ npx @neutralinojs/neu run
```js title="resources/js/main.js (add to end)"
(async() => {
const ab = await (await fetch("https://sheetjs.com/pres.numbers")).arrayBuffer();
const ab = await (await fetch("https://docs.sheetjs.com/pres.numbers")).arrayBuffer();
const wb = XLSX.read(ab);
const ws = wb.Sheets[wb.SheetNames[0]];
document.getElementById('info').innerHTML = XLSX.utils.sheet_to_html(ws);
})();
```
9) Close the app and relaunch the app with `npx @neutralinojs/neu run`
9) Close the app. Run the app again:
```bash
npx @neutralinojs/neu run
```
When the app loads, a table should show in the main screen.
@ -370,7 +378,11 @@ async function exportData() {
}
```
11) Close the app and re-run with `npx @neutralinojs/neu run`
11) Close the app. Run the app again:
```bash
npx @neutralinojs/neu run
```
When the app loads, click the "Import File" button and select a spreadsheet to
see the contents.
@ -397,8 +409,15 @@ save as `SheetJSNeu` will not automatically add the `.xlsx` extension!
npx @neutralinojs/neu build
```
Platform-specific programs will be created in the `dist` folder. For example,
the `darwin-arm` program will be `./dist/sheetjs-neu/sheetjs-neu-mac_arm64`
Platform-specific programs will be created in the `dist` folder:
| Platform | Path to binary |
|:-------------|:---------------------------------------------|
| `darwin-arm` | `./dist/sheetjs-neu/sheetjs-neu-mac_arm64` |
| `win11-arm` | `.\dist\sheetjs-neu\sheetjs-neu-win_x64.exe` |
| `linux-arm` | `.\dist\sheetjs-neu\sheetjs-neu-linux_arm64` |
Run the generated app and confirm that Presidential data is displayed.
[^1]: See [`nativeAllowList`](https://neutralino.js.org/docs/configuration/neutralino.config.json#nativeallowlist-string) in the NeutralinoJS documentation
[^2]: See [`os.showOpenDialog`](https://neutralino.js.org/docs/api/os#osshowopendialogtitle-options) in the NeutralinoJS documentation

@ -46,11 +46,11 @@ This demo was tested in the following environments:
| OS and Version | Architecture | RN Platform | Date |
|:---------------|:-------------|:------------|:-----------|
| Windows 10 | `win10-x64` | `v0.73.11` | 2024-03-24 |
| Windows 11 | `win11-x64` | `v0.72.12` | 2023-10-14 |
| Windows 11 | `win11-arm` | `v0.72.20` | 2023-12-01 |
| Windows 10 | `win10-x64` | `v0.74.6` | 2024-05-28 |
| Windows 11 | `win11-x64` | `v0.74.6` | 2024-05-28 |
| Windows 11 | `win11-arm` | `v0.74.5` | 2024-05-25 |
| MacOS 14.4 | `darwin-x64` | `v0.73.22` | 2024-03-24 |
| MacOS 14.1.2 | `darwin-arm` | `v0.72.11` | 2023-12-01 |
| MacOS 14.5 | `darwin-arm` | `v0.73.30` | 2024-05-28 |
:::
@ -61,7 +61,7 @@ applications, [check the mobile demo](/docs/demos/mobile/reactnative)
:::
:::warning Telemetry
:::danger Telemetry
**React Native for Windows + macOS commands include telemetry without proper**
**disclaimer or global opt-out.**
@ -84,7 +84,9 @@ imported from any component or script in the app.
For simplicity, this demo uses an "Array of Arrays"[^2] as the internal state.
<table><thead><tr><th>Spreadsheet</th><th>Array of Arrays</th></tr></thead><tbody><tr><td>
<table>
<thead><tr><th>Spreadsheet</th><th>Array of Arrays</th></tr></thead>
<tbody><tr><td>
![`pres.xlsx` data](pathname:///pres.png)
@ -133,7 +135,8 @@ function update_state(wb) {
The demos use native `View` elements from `react-native` to display data.
<details><summary><b>Explanation</b> (click to show)</summary>
<details>
<summary><b>Explanation</b> (click to show)</summary>
Since some spreadsheets may have empty cells between cells containing data,
looping over the rows may skip values!
@ -379,7 +382,7 @@ RCT_EXPORT_METHOD(PickAndRead:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromi
## Windows Demo
:::warning pass
:::danger pass
There is no simple standalone executable file at the end of the process.
@ -404,7 +407,8 @@ used to switch the NodeJS version.
:::
<details><summary><b>Installation Notes</b> (click to show)</summary>
<details>
<summary><b>Installation Notes</b> (click to show)</summary>
When the demo was last tested, a PowerShell script installed dependencies:
@ -420,10 +424,10 @@ setup instructions" to find instructions for manual installation.
### Project Setup
1) Create a new project using React Native `0.73.6`:
1) Create a new project using React Native `0.74.1`:
```bash
npx react-native init SheetJSWin --template react-native@0.73.6
npx react-native init SheetJSWin --template react-native@0.74.1
cd SheetJSWin
```
@ -478,7 +482,7 @@ Specific Windows SDK versions can be installed through Visual Studio Installer.
npx react-native run-windows --no-telemetry --arch=X86
```
:::warning pass
:::danger pass
The ARM64 binary is normally built with
@ -612,7 +616,7 @@ npx react-native run-windows --no-telemetry
npx react-native run-windows --no-telemetry --arch=X86
```
:::warning pass
:::danger pass
The ARM64 binary is normally built with
@ -629,7 +633,7 @@ When this demo was last tested on Windows 11 ARM, the build failed.
</TabItem>
</Tabs>
9) Download <https://sheetjs.com/pres.xlsx>.
9) Download https://docs.sheetjs.com/pres.xlsx.
10) In the app, click "Click here to Open File!" and use the file picker to
select `pres.xlsx` . The app will refresh and display the data from the file.
@ -669,10 +673,10 @@ npx -y react-native-macos-init --no-telemetry
:::caution pass
In the most recent x64 test, the build failed due to `visionos` errors:
In some macOS tests, the build failed due to `visionos` errors:
```
[!] Failed to load 'React-RCTFabric' podspec:
[!] Failed to load 'React-RCTFabric' podspec:
[!] Invalid `React-RCTFabric.podspec` file: undefined method `visionos' for #<Pod::Specification name="React-RCTFabric">.
```
@ -682,6 +686,14 @@ This error was resolved by upgrading CocoaPods to `1.15.2`:
sudo gem install cocoapods
```
After upgrading CocoaPods, reinstall the project pods:
```bash
cd macos
pod install
cd ..
```
:::
3) Install the SheetJS library:
@ -698,16 +710,16 @@ npx react-native run-macos
Close the running app from the dock and close the Metro terminal window.
:::warning pass
:::danger pass
When the demo was last tested on x64, the app failed with a warning
When the demo was last tested, the app failed with a warning
> No bundle URL present.
**As this affects the default app, this is a bug in React Native macOS!**
The production builds work as expected. If there are errors, the recommended
approach is to [make a release build](#production) every time.
The production builds work as expected. If there are errors, click "Dismiss" to
dismiss the error, close the app, and [make a release build](#production).
:::
@ -785,7 +797,6 @@ B) Copy the highlighted lines and paste under `/* Begin PBXFileReference section
4717DC6828CC495400A9BE56 /* RCTDocumentPicker.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = RCTDocumentPicker.h; path = "SheetJSMacOS-macOS/RCTDocumentPicker.h"; sourceTree = "<group>"; };
4717DC6928CC499A00A9BE56 /* RCTDocumentPicker.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = RCTDocumentPicker.m; path = "SheetJSMacOS-macOS/RCTDocumentPicker.m"; sourceTree = "<group>"; };
// highlight-end
008F07F21AC5B25A0029DE68 /* main.jsbundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = main.jsbundle; sourceTree = "<group>"; };
```
:::
@ -912,7 +923,7 @@ Close the running app from the dock and close the Metro terminal window.
curl -LO https://docs.sheetjs.com/reactnative/rnm/App.tsx
```
10) Download <https://sheetjs.com/pres.xlsx> to the Downloads folder.
10) Download https://docs.sheetjs.com/pres.xlsx to the Downloads folder.
#### Development
@ -955,9 +966,9 @@ xcodebuild -workspace macos/SheetJSmacOS.xcworkspace -scheme SheetJSmacOS-macOS
```
When the demo was last tested, the path to the generated app was displayed in
the terminal. Search for `Release/SheetJSmacOS.app` and look for `touch`:
the terminal. Search for `Release/SheetJSmacOS.app` and look for `touch -c`:
```
```text title="Sample result when searching for 'touch -c'"
/usr/bin/touch -c /Users/sheetjs/Library/Developer/Xcode/DerivedData/SheetJSmacOS-abcdefghijklmnopqrstuvwxyzab/Build/Products/Release/SheetJSmacOS.app
```

@ -2,13 +2,43 @@
title: Desktop Applications
pagination_prev: demos/mobile/index
pagination_next: demos/cli/index
hide_table_of_contents: true
---
import DocCardList from '@theme/DocCardList';
import {useCurrentSidebarCategory} from '@docusaurus/theme-common';
import FrameworkData from '/data/desktop.js'
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
data from spreadsheets.
Web technologies including JavaScript and HTML can power traditional software.
This demo covers a number of desktop app frameworks. In each demo, we will build
an app that uses SheetJS libraries to read and write spreadsheet files.
## Strategies
There are two different integration strategies. The "WebView" strategy embeds a
mini web browser and adds supporting native components. The "Engine" strategy
uses an embedded JavaScript engine that fits into the desktop app.
### WebView
WebViews are special web browser components designed to be embedded within apps.
As the browser components are available across all major platforms, desktop apps
can use the WebView as the main user interface. This approach allows small teams
to build software that works across operating systems and architectures.
The app is designed in HTML and CSS. [Web Frameworks](/docs/demos/frontend) can
be used but are typically not required.
### Engine
JavaScript engines including [V8](/docs/demos/engines/v8) can be directly added
to traditional desktop software. This approach is explored in greater detail in
the ["JavaScript Engines" demo](/docs/demos/engines/).
## Desktop Apps
Desktop app frameworks bundle a JavaScript engine and a windowing framework to
@ -38,6 +68,12 @@ Frameworks like React Native generate applications that use native UI elements.
:::
## Command-Line Tools
#### Platform Support
The following frameworks have been tested on the following platforms:
<FrameworkData/>
#### Command-Line Tools
**[The exposition has been moved to a separate page.](/docs/demos/cli)**

@ -0,0 +1,187 @@
---
title: Spreadsheet Tools with Nexe
sidebar_label: nexe
pagination_prev: demos/desktop/index
pagination_next: demos/data/index
sidebar_custom_props:
summary: Prebuilt NodeJS packages
---
import current from '/version.js';
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeBlock from '@theme/CodeBlock';
export const r = {style: {color:"red"}};
export const B = {style: {fontWeight:"bold"}};
`nexe`[^1] is a tool for generating command-line tools that embed scripts.
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
data from spreadsheets.
This demo uses `nexe` and SheetJS to create a standalone CLI tool for parsing
spreadsheets and converting to other formats.
:::info pass
The latest prebuilt package matches NodeJS version `14.15.3`.
`nexe` can build the required packages for newer NodeJS versions.
:::
:::note Tested Deployments
This demo was tested in the following deployments:
| Architecture | Version | NodeJS | Source | Date |
|:-------------|:-------------|:----------|:----------|:-----------|
| `darwin-x64` | `4.0.0-rc.6` | `14.15.3` | Pre-built | 2024-05-28 |
| `darwin-arm` | `4.0.0-rc.6` | `18.20.3` | Compiled | 2024-05-25 |
| `win10-x64` | `4.0.0-rc.4` | `14.15.3` | Pre-built | 2024-04-18 |
| `win11-arm` | `4.0.0-rc.6` | `20.10.0` | Compiled | 2024-05-28 |
| `linux-x64` | `4.0.0-rc.4` | `14.15.3` | Pre-built | 2024-03-21 |
| `linux-arm` | `4.0.0-rc.6` | `18.20.3` | Compiled | 2024-05-26 |
:::
## Integration Details
The [SheetJS NodeJS module](/docs/getting-started/installation/nodejs) can be
required from scripts. `nexe` will automatically handle packaging.
### Script Requirements
Scripts that exclusively use SheetJS libraries and NodeJS built-in modules can
be bundled using `nexe`.
The demo script [`xlsx-cli.js`](pathname:///cli/xlsx-cli.js) runs in NodeJS. It
is a simple command-line tool for reading and writing spreadsheets.
## Complete Example
0) Download the test file https://docs.sheetjs.com/pres.numbers:
```bash
curl -o pres.numbers https://docs.sheetjs.com/pres.numbers
```
1) Download [`xlsx-cli.js`](pathname:///cli/xlsx-cli.js)
```bash
curl -o xlsx-cli.js https://docs.sheetjs.com/cli/xlsx-cli.js
```
2) Install the dependencies:
<Tabs groupId="pm">
<TabItem value="npm" label="npm">
<CodeBlock language="bash">{`\
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz exit-on-epipe commander@2`}
</CodeBlock>
</TabItem>
<TabItem value="pnpm" label="pnpm">
<CodeBlock language="bash">{`\
pnpm install --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz exit-on-epipe commander@2`}
</CodeBlock>
</TabItem>
<TabItem value="yarn" label="Yarn" default>
<CodeBlock language="bash">{`\
yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz exit-on-epipe commander@2`}
</CodeBlock>
</TabItem>
</Tabs>
3) Create the standalone program:
```bash
npx nexe -t 14.15.3 xlsx-cli.js
```
<details open>
<summary><b>Building from source (click to hide)</b></summary>
When the demo was tested on ARM targets, the Nexe pre-built packages were
missing. For unsupported NodeJS versions, packages must be built from source:
<Tabs groupId="os">
<TabItem value="unix" label="Linux/MacOS">
```bash
npx nexe xlsx-cli.js --build --python=$(which python3) --make="-j8"
```
</TabItem>
<TabItem value="win" label="Windows">
On Windows x64, the `--build` flag suffices:
```bash
npx nexe xlsx-cli.js --build --make="-j8"
```
On Windows ARM, the target `windows-arm64-20.10.0` must be specified:
```bash
npx nexe xlsx-cli.js --build --make="-j8" --target=windows-arm64-20.10.0
```
**`vcbuild.bat` issues**
The Windows ARM build may fail with a `vcbuild.bat` error:
```
Error: vcbuild.bat nosign release arm64 exited with code: 1
```
Pass the `-v` flag for more details. In the most recent test, the error stemmed
from a Python version mismatch:
```
Node.js configure: found Python 2.7.18
Please use python3.11 or python3.10 or python3.9 or python3.8 or python3.7 or python3.6
```
The resolved version of Python can be found with
```cmd
where python
```
In the most recent test, a Python 2 version appeared first. This was fixed by
finding the Python 3 location and prepending it to `PATH`:
```cmd
set PATH="C:\correct\path\to\python\three";%PATH%
```
</TabItem>
</Tabs>
</details>
This generates `xlsx-cli` or `xlsx-cli.exe` depending on platform.
5) Run the generated program, passing `pres.numbers` as the argument:
<Tabs groupId="os">
<TabItem value="unix" label="Linux/MacOS">
```bash
./xlsx-cli pres.numbers
```
</TabItem>
<TabItem value="win" label="Windows">
```powershell
.\xlsx-cli.exe pres.numbers
```
</TabItem>
</Tabs>
[^1]: The project does not have a website. The [source repository](https://github.com/nexe/nexe) is publicly available.
[^2]: The NodeJS website hosts [prebuilt installers](https://nodejs.org/en/download/prebuilt-installer).

@ -0,0 +1,169 @@
---
title: Packing Sheets with pkg
sidebar_label: pkg
pagination_prev: demos/desktop/index
pagination_next: demos/data/index
sidebar_custom_props:
summary: Prebuilt NodeJS packages
---
import current from '/version.js';
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeBlock from '@theme/CodeBlock';
export const r = {style: {color:"red"}};
export const B = {style: {fontWeight:"bold"}};
`pkg`[^1] is a tool for generating command-line tools that embed scripts.
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
data from spreadsheets.
This demo uses `pkg` and SheetJS to create a standalone CLI tool for parsing
spreadsheets and converting to other formats.
:::caution pass
With the official release of [NodeJS SEA](/docs/demos/cli/nodesea), Vercel opted
to deprecate `pkg`. It is still useful for deploying apps embedding NodeJS v18
or earlier since those versions do not support NodeJS SEA.
:::
:::note Tested Deployments
This demo was tested in the following deployments:
| Architecture | Version | NodeJS | Date |
|:-------------|:--------|:---------|:-----------|
| `darwin-x64` | `5.8.1` | `18.5.0` | 2024-05-28 |
| `darwin-arm` | `5.8.1` | `18.5.0` | 2024-05-25 |
| `win10-x64` | `5.8.1` | `18.5.0` | 2024-04-18 |
| `win11-arm` | `5.8.1` | `18.5.0` | 2024-05-28 |
| `linux-x64` | `5.8.1` | `18.5.0` | 2024-03-21 |
| `linux-arm` | `5.8.1` | `18.5.0` | 2024-05-26 |
:::
## Integration Details
The [SheetJS NodeJS module](/docs/getting-started/installation/nodejs) can be
required from scripts. `pkg` will automatically handle packaging.
### Script Requirements
Scripts that exclusively use SheetJS libraries and NodeJS built-in modules can
be bundled using `pkg`.
The demo script [`xlsx-cli.js`](pathname:///cli/xlsx-cli.js) runs in NodeJS. It
is a simple command-line tool for reading and writing spreadsheets.
### Limitations
:::danger pass
When this demo was last tested, `pkg` failed with an error referencing `node20`:
```
> Targets not specified. Assuming:
node20-linux-arm64, node20-macos-arm64, node20-win-arm64
> Error! No available node version satisfies 'node20'
```
**`pkg` does not support NodeJS 20 or 22!**
The local NodeJS version must be rolled back to version 18.
If `nvm` or `nvm-windows` was used to install NodeJS:
```bash
nvm install 18
nvm use 18
```
Otherwise, on macOS and Linux, `n` can manage the global installation:
```bash
sudo npm i -g n
sudo n 18
```
On Windows, it is recommended to use a prebuilt installer[^2]
:::
## Complete Example
0) Downgrade NodeJS to major version 18 or earlier.
1) Download the test file https://docs.sheetjs.com/pres.numbers:
```bash
curl -o pres.numbers https://docs.sheetjs.com/pres.numbers
```
2) Download [`xlsx-cli.js`](pathname:///cli/xlsx-cli.js)
```bash
curl -o xlsx-cli.js https://docs.sheetjs.com/cli/xlsx-cli.js
```
3) Install the dependencies:
<Tabs groupId="pm">
<TabItem value="npm" label="npm">
<CodeBlock language="bash">{`\
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz exit-on-epipe commander@2`}
</CodeBlock>
</TabItem>
<TabItem value="pnpm" label="pnpm">
<CodeBlock language="bash">{`\
pnpm install --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz exit-on-epipe commander@2`}
</CodeBlock>
</TabItem>
<TabItem value="yarn" label="Yarn" default>
<CodeBlock language="bash">{`\
yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz exit-on-epipe commander@2`}
</CodeBlock>
</TabItem>
</Tabs>
4) Create the standalone program:
```bash
npx pkg xlsx-cli.js
```
This generates `xlsx-cli-linux`, `xlsx-cli-macos`, and `xlsx-cli-win.exe` .
5) Run the generated program, passing `pres.numbers` as the argument:
<Tabs groupId="os">
<TabItem value="linux" label="Linux">
```bash
./xlsx-cli-linux pres.numbers
```
</TabItem>
<TabItem value="macos" label="macOS">
```bash
./xlsx-cli-macos pres.numbers
```
</TabItem>
<TabItem value="win" label="Windows">
```powershell
.\xlsx-cli-win.exe pres.numbers
```
</TabItem>
</Tabs>
[^1]: The project does not have a website. The [source repository](https://github.com/vercel/pkg) is publicly available.
[^2]: The NodeJS website hosts [prebuilt installers](https://nodejs.org/en/download/prebuilt-installer).

@ -0,0 +1,214 @@
---
title: Sheets in a Box with boxednode
sidebar_label: boxednode
pagination_prev: demos/desktop/index
pagination_next: demos/data/index
sidebar_custom_props:
summary: NodeJS binaries with scripts, built from source
---
import current from '/version.js';
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeBlock from '@theme/CodeBlock';
export const r = {style: {color:"red"}};
export const B = {style: {fontWeight:"bold"}};
`boxednode`[^1] is a tool for generating command-line tools that embed scripts.
It automates the process of building NodeJS from source.
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
data from spreadsheets.
This demo uses `boxednode` and SheetJS to create a standalone CLI tool for
parsing spreadsheets and converting to other formats.
:::note Tested Deployments
This demo was tested in the following deployments:
| Architecture | Version | NodeJS | Date |
|:-------------|:--------|:----------|:-----------|
| `darwin-x64` | `2.4.0` | `22.2.0` | 2024-05-28 |
| `darwin-arm` | `2.4.3` | `22.2.0` | 2024-05-25 |
| `win10-x64` | `2.4.2` | `16.20.2` | 2024-04-18 |
| `linux-x64` | `2.4.0` | `21.7.1` | 2024-03-21 |
| `linux-arm` | `2.4.3` | `20.13.1` | 2024-05-26 |
:::
## Integration Details
The [SheetJS NodeJS module](/docs/getting-started/installation/nodejs) can be
required from scripts. `boxednode` will automatically handle packaging.
### Script Requirements
Scripts that exclusively use SheetJS libraries and NodeJS built-in modules can
be bundled using `boxednode`
The demo script [`xlsx-cli.js`](pathname:///cli/xlsx-cli.js) runs in NodeJS. It
is a simple command-line tool for reading and writing spreadsheets.
## Complete Example
0) Download the test file https://docs.sheetjs.com/pres.numbers:
```bash
curl -o pres.numbers https://docs.sheetjs.com/pres.numbers
```
1) Download [`xlsx-cli.js`](pathname:///cli/xlsx-cli.js)
```bash
curl -o xlsx-cli.js https://docs.sheetjs.com/cli/xlsx-cli.js
```
2) Install the dependencies:
<Tabs groupId="pm">
<TabItem value="npm" label="npm">
<CodeBlock language="bash">{`\
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz exit-on-epipe commander@2`}
</CodeBlock>
</TabItem>
<TabItem value="pnpm" label="pnpm">
<CodeBlock language="bash">{`\
pnpm install --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz exit-on-epipe commander@2`}
</CodeBlock>
</TabItem>
<TabItem value="yarn" label="Yarn" default>
<CodeBlock language="bash">{`\
yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz exit-on-epipe commander@2`}
</CodeBlock>
</TabItem>
</Tabs>
3) Create the standalone program:
<Tabs groupId="os">
<TabItem value="unix" label="Linux/MacOS">
```bash
npx boxednode@2.4.3 -s xlsx-cli.js -t xlsx-cli
```
:::caution pass
When this demo was last tested in `linux-arm`, the build failed with an error:
<pre>
../deps/v8/src/base/small-vector.h: In instantiation of <span {...B}>class v8::base::SmallVector&lt;std::pair&lt;const v8::internal::compiler::turboshaft::PhiOp*, const v8::internal::compiler::turboshaft::OpIndex&gt;, 16&gt;</span>:
<span {...B}>../deps/v8/src/compiler/turboshaft/loop-unrolling-reducer.h:444:11:</span> required from here
<span {...B}>../deps/v8/src/base/macros.h:206:55:</span> <span style={{...r.style,...B.style}}>error:</span> static assertion failed: T should be trivially copyable
{" 206 |"} static_assert(::v8::base::is_trivially_copyable&lt;T&gt;::<span style={{...r.style,...B.style}}>value</span>, \\
{" |"} ^~~~~
</pre>
This affects NodeJS `22.2.0`, but does not affect `20.13.1`. It affects the
[V8 JavaScript Engine](/docs/demos/engines/v8#build-v8).
The `-n` flag controls the target NodeJS version. For this demo, the following
command uses NodeJS `20.13.1`:
```bash
npx boxednode@2.4.3 -s xlsx-cli.js -t xlsx-cli -n 20.13.1
```
:::
</TabItem>
<TabItem value="win" label="Windows">
```bash
npx boxednode@2.4.3 -s xlsx-cli.js -t xlsx-cli.exe -n 16.20.2
```
:::info pass
The Windows 10 build requires Visual Studio with "Desktop development with C++"
workload, Python 3.11, and NASM[^2].
**The build command must be run in "x64 Native Tools Command Prompt"**
:::
:::caution pass
When the demo was last tested, the build failed:
```
Not an executable Python program
Could not find Python.
```
By default, Windows aliases `python` to a Microsoft Store installer. If the
official installer was used, the alias should be disabled manually:
1) Open Start menu and type "app alias". Click "Manage app execution aliases".
2) Disable the App Installer for all items with `python` in the name.
Using Python 3.12, the build fails with an error:
```
Please use python3.11 or python3.10 or python3.9 or python3.8 or python3.7 or python3.6.
```
In the most recent test, Python 3.11.8 was installed from the official site.
:::
:::caution pass
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 `v142` build tools through the Visual
Studio installer.
:::
:::caution pass
In the most recent Windows test against NodeJS `20.8.0`, the build failed due
to an issue in the OpenSSL dependency:
```
...\node-v20.8.0\deps\openssl\openssl\crypto\cversion.c(75,33): error C2153: integer literals must have at least one digit [...\node-v20.8.0\deps\openssl\openssl.vcxproj]
```
SheetJS libraries are compatible with NodeJS versions dating back to `v0.8`. The
workaround is to select NodeJS `v16.20.2` using the `-n` flag. This version was
was chosen since NodeJS `v18` upgraded the OpenSSL dependency.
:::
</TabItem>
</Tabs>
4) Run the generated program, passing `pres.numbers` as the argument:
<Tabs groupId="os">
<TabItem value="unix" label="Linux/MacOS">
```bash
./xlsx-cli pres.numbers
```
</TabItem>
<TabItem value="win" label="Windows">
```powershell
.\xlsx-cli.exe pres.numbers
```
</TabItem>
</Tabs>
[^1]: The project does not have a website. The [source repository](https://github.com/mongodb-js/boxednode) is publicly available.
[^2]: Downloads can be found [at the main NASM project website](https://www.nasm.us/)

@ -158,9 +158,20 @@ This demo was tested in the following deployments:
| Architecture | NodeJS | Date |
|:-------------|:----------|:-----------|
| `darwin-x64` | `20.11.1` | 2024-03-17 |
| `darwin-x64` | `22.2.0` | 2024-05-28 |
| `darwin-arm` | `22.2.0` | 2024-05-29 |
| `win10-x64` | `20.12.0` | 2024-03-26 |
| `win11-x64` | `20.13.1` | 2024-05-22 |
| `win11-arm` | `20.14.0` | 2024-06-11 |
| `linux-x64` | `20.11.1` | 2024-03-18 |
| `linux-arm` | `20.14.0` | 2024-06-10 |
:::
:::caution pass
NodeJS on Windows on ARM uses the X64 compatibility layer. It does not generate
a native ARM64 binary!
:::
@ -222,10 +233,10 @@ local NodeJS platform.
:::
4) Download the test file <https://sheetjs.com/pres.numbers>:
4) Download the test file https://docs.sheetjs.com/pres.numbers:
```bash
curl -o pres.numbers https://sheetjs.com/pres.numbers
curl -o pres.numbers https://docs.sheetjs.com/pres.numbers
```
5) Run the script and pass `pres.numbers` as the first argument:
@ -236,7 +247,7 @@ node sheet2csv.js pres.numbers
The script should display CSV contents from the first sheet:
```
```text title="Expected Output"
Name,Index
Bill Clinton,42
GeorgeW Bush,43
@ -290,7 +301,7 @@ Application node.exe 20.12.0.0 C:
Copy the program (listed in the "Source" column) to `sheet2csv.exe`:
```powershell
PS C:\sheetjs-sea> copy "C:\Program Files\nodejs\node.exe" sheet2csv.exe
copy "C:\Program Files\nodejs\node.exe" sheet2csv.exe
```
9) Remove the code signature.
@ -299,6 +310,12 @@ PS C:\sheetjs-sea> copy "C:\Program Files\nodejs\node.exe" sheet2csv.exe
signtool remove /s .\sheet2csv.exe
```
:::info pass
`signtool` is included in the Windows SDK[^4].
:::
</TabItem>
<TabItem value="linux-x64" label="Linux">
@ -416,3 +433,4 @@ This error is expected.
[^1]: See ["Single Executable Applications"](https://nodejs.org/api/single-executable-applications.html) in the NodeJS documentation.
[^2]: See [`readFile` in "Reading Files"](/docs/api/parse-options)
[^3]: See [`sheet_to_csv` in "CSV and Text"](/docs/api/utilities/csv#delimiter-separated-output)
[^4]: See [Windows SDK](https://developer.microsoft.com/en-us/windows/downloads/windows-sdk/) in the Windows Dev Center documentation.

@ -0,0 +1,153 @@
---
title: BunJS SEA
pagination_prev: demos/desktop/index
pagination_next: demos/data/index
sidebar_custom_props:
summary: BunJS Single-file Executables
---
import current from '/version.js';
import CodeBlock from '@theme/CodeBlock';
[BunJS](https://bun.sh/docs/bundler/executables) is a JavaScript runtime with
support for compiling scripts into self-contained executables.
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
data from spreadsheets.
This demo uses the Bun compiler and SheetJS to create a standalone CLI tool for
parsing spreadsheets and generating CSV rows.
:::info pass
It is strongly recommended to install BunJS on systems using SheetJS libraries
in command-line tools. This workaround should only be considered if a standalone
binary is considered desirable.
:::
:::caution BunJS support is considered experimental.
Great open source software grows with user tests and reports. Any issues should
be reported to the BunJS project for further diagnosis.
:::
## Integration Details
The [SheetJS BunJS module](/docs/getting-started/installation/bun) can be
imported from BunJS scripts.
`bun build --compile` generates a standalone executable that includes the BunJS
runtime, user JS code and supporting scripts and assets
### Script Requirements
Scripts that exclusively use SheetJS libraries and BunJS built-in modules can be
bundled using BunJS. The module should be required directly:
<CodeBlock language="ts">{`\
const XLSX = require("xlsx");`}
</CodeBlock>
For example, the following script accepts one command line argument, parses the
specified file using the SheetJS `readFile` method[^1], generates CSV text from
the first worksheet using `sheet_to_csv`[^2], and prints to terminal:
<CodeBlock language="ts" title="sheet2csv.ts">{`\
const XLSX = require("xlsx");
\n\
/* process.argv[2] is the first argument to the script */
const filename = process.argv[2];
\n\
/* read file */
const wb = XLSX.readFile(filename);
\n\
/* generate CSV of first sheet */
const ws = wb.Sheets[wb.SheetNames[0]];
const csv = XLSX.utils.sheet_to_csv(ws);
\n\
/* print to terminal */
console.log(csv);`}
</CodeBlock>
## Complete Example
:::note Tested Deployments
This demo was last tested in the following deployments:
| Architecture | BunJS | Date |
|:-------------|:---------|:-----------|
| `darwin-x64` | `1.1.10` | 2024-05-28 |
| `darwin-arm` | `1.1.10` | 2024-05-29 |
| `win10-x64` | `1.1.12` | 2024-06-10 |
| `linux-x64` | `1.1.12` | 2024-06-09 |
| `linux-arm` | `1.1.12` | 2024-06-10 |
:::
0) Install or update BunJS.[^3]
1) Download the test file https://docs.sheetjs.com/pres.numbers:
```bash
curl -o pres.numbers https://docs.sheetjs.com/pres.numbers
```
2) Save the [contents of the `sheet2csv.ts` code block](#script-requirements)
to `sheet2csv.ts` in the project folder.
3) Install the SheetJS dependency:
<CodeBlock language="bash">{`\
bun install https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
</CodeBlock>
:::caution pass
On Windows, the command failed with a `ENOTEMPTY` error:
```
error: InstallFailed extracting tarball for https://cdn.sheetjs.com/xlsx-0.20.1/xlsx-0.20.1.tgz
error: moving "https://cdn.sheetjs.com/xlsx-0.20.1/xlsx-0.20.1.tgz" to cache dir failed
ENOTEMPTY: Directory not empty (NtSetInformationFile())
```
The workaround is to prepend `xlsx@` to the URL:
<CodeBlock language="bash">{`\
bun install xlsx@https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
</CodeBlock>
:::
4) Test the script with `bun run`:
```bash
bun run sheet2csv.ts pres.numbers
```
The script should display CSV contents from the first sheet:
```text title="Expected Output"
Name,Index
Bill Clinton,42
GeorgeW Bush,43
Barack Obama,44
Donald Trump,45
Joseph Biden,46
```
5) Compile and run `sheet2csv`:
```bash
bun build ./sheet2csv.ts --compile --outfile sheet2csv
./sheet2csv pres.numbers
```
The program should display the same CSV contents as the script (from step 2)
[^1]: See [`readFile` in "Reading Files"](/docs/api/parse-options)
[^2]: See [`sheet_to_csv` in "CSV and Text"](/docs/api/utilities/csv#delimiter-separated-output)
[^3]: See ["Installation"](https://bun.sh/docs/installation) in the BunJS documentation for instructions.

@ -1,5 +1,5 @@
---
title: Deno Compiler
title: Deno SEA
pagination_prev: demos/desktop/index
pagination_next: demos/data/index
sidebar_custom_props:
@ -69,7 +69,7 @@ const wb = XLSX.readFile(filename);
\n\
/* generate CSV of first sheet */
const ws = wb.Sheets[wb.SheetNames[0]];
const csv = utils.sheet_to_csv(ws);
const csv = XLSX.utils.sheet_to_csv(ws);
\n\
/* print to terminal */
console.log(csv);`}
@ -99,22 +99,22 @@ This demo was last tested in the following deployments:
| Architecture | Deno | Date |
|:-------------|:---------|:-----------|
| `darwin-x64` | `1.41.3` | 2024-03-15 |
| `darwin-arm` | `1.37.2` | 2023-10-18 |
| `darwin-x64` | `1.43.6` | 2024-05-28 |
| `darwin-arm` | `1.43.6` | 2024-05-23 |
| `win10-x64` | `1.41.3` | 2024-03-24 |
| `win11-x64` | `1.37.2` | 2023-10-14 |
| `win11-arm` | `1.38.4` | 2023-12-01 |
| `win11-x64` | `1.43.6` | 2024-05-25 |
| `win11-arm` | `1.43.6` | 2024-05-25 |
| `linux-x64` | `1.41.3` | 2024-03-18 |
| `linux-arm` | `1.38.4` | 2023-12-01 |
| `linux-arm` | `1.43.6` | 2024-05-25 |
:::
0) Install Deno.[^6]
1) Download the test file <https://sheetjs.com/pres.numbers>:
1) Download the test file https://docs.sheetjs.com/pres.numbers:
```bash
curl -o pres.numbers https://sheetjs.com/pres.numbers
curl -o pres.numbers https://docs.sheetjs.com/pres.numbers
```
2) Test the script with `deno run`:
@ -125,7 +125,7 @@ deno run -r --allow-read https://docs.sheetjs.com/cli/sheet2csv.ts pres.numbers
The script should display CSV contents from the first sheet:
```
```text title="Expected Output"
Name,Index
Bill Clinton,42
GeorgeW Bush,43

@ -1,5 +1,7 @@
---
title: Command-Line Tools
title: Sheets on the Command Line
sidebar_label: Command-Line Tools
hide_table_of_contents: true
pagination_prev: demos/desktop/index
pagination_next: demos/data/index
---
@ -9,6 +11,10 @@ import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeBlock from '@theme/CodeBlock';
import {useCurrentSidebarCategory} from '@docusaurus/theme-common';
import FrameworkData from '/data/cli.js'
export const r = {style: {color:"red"}};
export const B = {style: {fontWeight:"bold"}};
With the availability of JS engines and the success of server-side platforms,
it is possible to build standalone command-line tools from JavaScript code.
@ -21,9 +27,8 @@ processors. The ultimate goal is to use SheetJS libraries to generate CSV output
from arbitrary spreadsheet files. The generated command-line tool will accept an
argument, parse the specified workbook, and print CSV rows to the terminal.
>**Sample terminal session**
```bash
$ xlsx-cli.exe pres.numbers
```bash title="Sample terminal session"
> xlsx-cli.exe pres.numbers
Name,Index
Bill Clinton,42
GeorgeW Bush,43
@ -43,10 +48,16 @@ Demos for common standalone CLI tools are included in separate pages:
</li>);
})}</ul>
#### Platform Support
The following frameworks have been tested on the following platforms:
<FrameworkData/>
:::tip pass
The [`xlsx-cli`](https://cdn.sheetjs.com/xlsx-cli/) NodeJS script is available
as a package on the SheetJS CDN. It is an easy-to-use command-line tool for
as a package on the SheetJS CDN. It is a straightforward command-line tool for
translating files between supported spreadsheet file formats.
:::
@ -63,419 +74,33 @@ server-side scripting platform cannot be installed on the target computer.
:::
## NodeJS
#### NodeJS
There are a few popular tools for compiling NodeJS scripts to CLI programs.
This demo has been organized by framework:
The demo script presents a friendly command line interface including flags:
- [`boxednode`](/docs/demos/cli/boxednode)
- [`nexe`](/docs/demos/cli/nexe)
- [`pkg`](/docs/demos/cli/pkg)
```bash
$ ./xlsx-cli -h
Usage: xlsx-cli [options] <file> [sheetname]
#### V8
Options:
-V, --version output the version number
-f, --file <file> use specified workbook
-s, --sheet <sheet> print specified sheet (default first sheet)
...
```
**[The exposition has been moved to the "V8" demo.](/docs/demos/engines/v8#snapshots)**
:::note Tested Deployments
#### BunJS
This demo was tested in the following deployments:
<Tabs groupId="njs">
<TabItem value="nexe" label="Nexe">
| Architecture | Version | NodeJS | Source | Date |
|:-------------|:-------------|:----------|:----------|:-----------|
| `darwin-x64` | `4.0.0-rc.4` | `14.15.3` | Pre-built | 2024-03-15 |
| `darwin-arm` | `4.0.0-rc.2` | `18.18.0` | Compiled | 2023-12-01 |
| `win10-x64` | `4.0.0-rc.4` | `14.15.3` | Pre-built | 2024-03-04 |
| `win11-arm` | `4.0.0-rc.2` | `20.10.0` | Compiled | 2023-12-01 |
| `linux-x64` | `4.0.0-rc.4` | `14.15.3` | Pre-built | 2024-03-21 |
| `linux-arm` | `4.0.0-rc.2` | `20.10.0` | Compiled | 2023-12-01 |
</TabItem>
<TabItem value="pkg" label="pkg">
| Architecture | Version | NodeJS | Date |
|:-------------|:--------|:---------|:-----------|
| `darwin-x64` | `5.8.1` | `18.5.0` | 2024-03-15 |
| `darwin-arm` | `5.8.1` | `18.5.0` | 2023-12-01 |
| `win10-x64` | `5.8.1` | `18.5.0` | 2024-03-24 |
| `win11-arm` | `5.8.1` | `18.5.0` | 2023-12-01 |
| `linux-x64` | `5.8.1` | `18.5.0` | 2024-03-21 |
| `linux-arm` | `5.8.1` | `18.5.0` | 2023-12-01 |
</TabItem>
<TabItem value="boxednode" label="boxednode">
| Architecture | Version | NodeJS | Date |
|:-------------|:--------|:----------|:-----------|
| `darwin-x64` | `2.4.0` | `21.7.1` | 2024-03-15 |
| `darwin-arm` | `2.3.0` | `21.3.0` | 2023-12-01 |
| `win10-x64` | `2.4.0` | `16.20.2` | 2024-03-24 |
| `linux-x64` | `2.4.0` | `21.7.1` | 2024-03-21 |
| `linux-arm` | `2.3.0` | `21.3.0` | 2023-12-01 |
</TabItem>
</Tabs>
:::
0) Download the test file <https://sheetjs.com/pres.numbers>:
```bash
curl -o pres.numbers https://sheetjs.com/pres.numbers
```
1) Download [`xlsx-cli.js`](pathname:///cli/xlsx-cli.js)
```bash
curl -o xlsx-cli.js https://docs.sheetjs.com/cli/xlsx-cli.js
```
2) Install the dependencies:
<Tabs groupId="pm">
<TabItem value="npm" label="npm">
<CodeBlock language="bash">{`\
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz exit-on-epipe commander@2`}
</CodeBlock>
</TabItem>
<TabItem value="pnpm" label="pnpm">
<CodeBlock language="bash">{`\
pnpm install --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz exit-on-epipe commander@2`}
</CodeBlock>
</TabItem>
<TabItem value="yarn" label="Yarn" default>
<CodeBlock language="bash">{`\
yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz exit-on-epipe commander@2`}
</CodeBlock>
</TabItem>
</Tabs>
3) Follow tooling steps:
<Tabs groupId="njs">
<TabItem value="nexe" label="Nexe">
Run `nexe` and manually specify NodeJS version 14.15.3
```bash
npx nexe -t 14.15.3 xlsx-cli.js
```
This generates `xlsx-cli` or `xlsx-cli.exe` depending on platform.
:::caution pass
When the demo was tested on ARM targets, the Nexe pre-built packages were
missing. The package must be built from source:
```bash
npx nexe xlsx-cli.js --build --python=$(which python3) --make="-j8"
```
:::
:::caution pass
On Windows ARM, the target `windows-arm64-20.10.0` must be specified:
```bash
npx nexe xlsx-cli.js --build --make="-j8" --target=windows-arm64-20.10.0
```
The Windows build may fail with a `vcbuild.bat` error:
```
Error: vcbuild.bat nosign release arm64 exited with code: 1
```
Pass the `-v` flag for more details. In the most recent test, the error stemmed
from a Python version mismatch:
```
Node.js configure: found Python 2.7.18
Please use python3.11 or python3.10 or python3.9 or python3.8 or python3.7 or python3.6
```
The resolved version of Python can be found with
```cmd
where python
```
In the most recent test, a Python 2 version appeared first. This was fixed by
finding the Python 3 location and prepending it to `PATH`:
```cmd
set PATH="C:\correct\path\to\python\three";%PATH%
```
:::
</TabItem>
<TabItem value="pkg" label="pkg">
:::warning pass
When this demo was last tested, `pkg` failed with an error referencing `node20`:
```
> Targets not specified. Assuming:
node20-linux-arm64, node20-macos-arm64, node20-win-arm64
> Error! No available node version satisfies 'node20'
```
**`pkg` does not support NodeJS 20!**
The local NodeJS version must be rolled back to version 18.
If `n` is installed:
```bash
sudo n 18
```
If `nvm` was used to install NodeJS:
```bash
nvm install 18
nvm use 18
```
:::
Run `pkg`:
```bash
npx pkg xlsx-cli.js
```
This generates `xlsx-cli-linux`, `xlsx-cli-macos`, and `xlsx-cli-win.exe` .
</TabItem>
<TabItem value="boxednode" label="boxednode">
Run `boxednode`:
<Tabs groupId="os">
<TabItem value="unix" label="Linux/MacOS">
```bash
npx boxednode@2.4.0 -s xlsx-cli.js -t xlsx-cli
```
</TabItem>
<TabItem value="win" label="Windows">
```bash
npx boxednode@2.4.0 -s xlsx-cli.js -t xlsx-cli.exe -n 16.20.2
```
:::info pass
The Windows 10 build requires Visual Studio with "Desktop development with C++"
workload, Python 3.11, and NASM[^1].
**The build command must be run in "x64 Native Tools Command Prompt"**
:::
:::caution pass
When the demo was last tested, the build failed:
```
Not an executable Python program
Could not find Python.
```
By default, Windows aliases `python` to a Microsoft Store installer. If the
official installer was used, the alias should be disabled manually:
1) Open Start menu and type "app alias". Click "Manage app execution aliases".
2) Disable the App Installer for all items with `python` in the name.
Using Python 3.12, the build fails with an error:
```
Please use python3.11 or python3.10 or python3.9 or python3.8 or python3.7 or python3.6.
```
In the most recent test, Python 3.11.8 was installed from the official site.
:::
:::caution pass
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 `v142` build tools through the Visual
Studio installer.
:::
:::caution pass
In the most recent Windows test against NodeJS `20.8.0`, the build failed due
to an issue in the OpenSSL dependency:
```
...\node-v20.8.0\deps\openssl\openssl\crypto\cversion.c(75,33): error C2153: integer literals must have at least one digit [...\node-v20.8.0\deps\openssl\openssl.vcxproj]
```
SheetJS libraries are compatible with NodeJS versions dating back to `v0.8`. The
workaround is to select NodeJS `v16.20.2` using the `-n` flag. This version was
was chosen since NodeJS `v18` upgraded the OpenSSL dependency.
:::
</TabItem>
</Tabs>
</TabItem>
</Tabs>
4) Run the generated program, passing `pres.numbers` as the argument. For
example, `nexe` generates `xlsx-cli` in macOS so the command is:
```bash
./xlsx-cli pres.numbers
```
`nexe` generates `xlsx-cli.exe` in Windows, so the command is:
```powershell
.\xlsx-cli.exe pres.numbers
```
## V8
The [V8](/docs/demos/engines/v8) demo covers standalone programs that embed the
V8 engine. This demo uses the Rust integration to generate a command line tool.
:::note Tested Deployments
This demo was last tested in the following deployments:
| Architecture | V8 Version | Crate | Date |
|:-------------|:--------------|:---------|:-----------|
| `darwin-x64` | `12.3.219.9` | `0.88.0` | 2024-03-15 |
| `darwin-arm` | `11.8.172.13` | `0.79.2` | 2023-10-18 |
| `win10-x64` | `12.3.219.9` | `0.88.0` | 2024-03-24 |
| `win11-x64` | `11.8.172.13` | `0.79.2` | 2023-10-14 |
| `linux-x64` | `12.3.219.9` | `0.88.0` | 2024-03-18 |
| `linux-arm` | `12.0.267.8` | `0.82.0` | 2023-12-01 |
:::
0) Make a new folder for the project:
```bash
mkdir sheetjs2csv
cd sheetjs2csv
```
1) Download the following scripts:
- [`Cargo.toml`](pathname:///cli/Cargo.toml)
- [`snapshot.rs`](pathname:///cli/snapshot.rs)
- [`sheet2csv.rs`](pathname:///cli/sheet2csv.rs)
```bash
curl -o Cargo.toml https://docs.sheetjs.com/cli/Cargo.toml
curl -o snapshot.rs https://docs.sheetjs.com/cli/snapshot.rs
curl -o sheet2csv.rs https://docs.sheetjs.com/cli/sheet2csv.rs
```
2) Download the SheetJS Standalone script and move to the project directory:
<ul>
<li><a href={`https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js`}>xlsx.full.min.js</a></li>
</ul>
<CodeBlock language="bash">{`\
curl -o xlsx.full.min.js https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js`}
</CodeBlock>
3) Build the V8 snapshot:
```bash
cargo build --bin snapshot
cargo run --bin snapshot
```
:::caution pass
With some versions of the `v8` crate, the Linux AArch64 build failed with an error:
```
error[E0080]: evaluation of constant value failed
|
1715 | assert!(size_of::<TypeId>() == size_of::<u64>());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'assertion failed: size_of::<TypeId>() == size_of::<u64>()'
```
Versions `0.75.1` and `0.82.0` are known to work.
:::
4) Build `sheet2csv` (`sheet2csv.exe` in Windows):
```bash
cargo build --release --bin sheet2csv
```
5) Download the test file <https://sheetjs.com/pres.numbers>:
```bash
curl -o pres.numbers https://sheetjs.com/pres.numbers
```
6) Test the application:
<Tabs groupId="os">
<TabItem value="unix" label="Linux/MacOS">
```bash
mv target/release/sheet2csv .
./sheet2csv pres.numbers
```
</TabItem>
<TabItem value="win" label="Windows">
```bash
mv target/release/sheet2csv.exe .
.\sheet2csv.exe pres.numbers
```
</TabItem>
</Tabs>
**[The exposition has been moved to a separate page.](/docs/demos/cli/bunsea)**
#### Deno
**[The exposition has been moved to a separate page.](/docs/demos/cli/deno)**
**[The exposition has been moved to a separate page.](/docs/demos/cli/denosea)**
## Dedicated Engines
#### Dedicated Engines
The following demos for JS engines produce standalone programs:
- [V8](/docs/demos/engines/v8)
- [Duktape](/docs/demos/engines/duktape)
- [ChakraCore](/docs/demos/engines/chakra)
- [QuickJS](/docs/demos/engines/quickjs)
- [Goja](/docs/demos/engines/goja)
- [JavaScriptCore](/docs/demos/engines/jsc)
[^1]: Downloads can be found [at the main NASM project website](https://www.nasm.us/)

@ -9,12 +9,18 @@ sidebar_custom_props:
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
Structured Query Language ("SQL") is a popular declarative language for issuing
commands to database servers.
## Raw SQL Operations
### Generating Tables
This example will fetch <https://sheetjs.com/data/cd.xls>, scan the columns of the
This example will fetch https://docs.sheetjs.com/cd.xls, scan the columns of the
first worksheet to determine data types, and generate 6 PostgreSQL statements.
<details><summary><b>Explanation</b> (click to show)</summary>
<details>
<summary><b>Explanation</b> (click to show)</summary>
The relevant `generate_sql` function takes a worksheet name and a table name:
@ -118,7 +124,7 @@ function SheetJSQLWriter() {
if(fields.length) return `INSERT INTO \`${wsname}\` (${fields.join(", ")}) VALUES (${values.join(", ")})`;
})).filter(x => x).slice(0, 6);
}
const [url, setUrl] = React.useState("https://sheetjs.com/data/cd.xls");
const [url, setUrl] = React.useState("https://docs.sheetjs.com/cd.xls");
const set_url = (evt) => setUrl(evt.target.value);
const [out, setOut] = React.useState("");
const xport = React.useCallback(async() => {
@ -147,7 +153,7 @@ types and other database minutiae.
### Other SQL Databases
The `generate_sql` function from ["Building Schemas from Worksheets"](#building-schemas-from-worksheets)
The `generate_sql` function from ["Generating Tables"](#generating-tables)
can be adapted to generate SQL statements for a variety of databases, including:
**PostgreSQL**

Some files were not shown because too many files have changed in this diff Show More