vite moved fast and broke things

This commit is contained in:
SheetJS 2025-11-16 02:08:21 -05:00
parent f39d01eb84
commit 1327b2f98c
44 changed files with 1929 additions and 500 deletions

@ -1165,9 +1165,9 @@ 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.
dedicated the content to the public domain. When this demo was last tested,
[^2]: See ["The Executive Branch"](https://github.com/unitedstates/congress-legislators#the-executive-branch)
in the dataset documentation.
[^3]: See [`json_to_sheet` in "Utilities"](/docs/api/utilities/array#array-of-objects-input)

@ -35,17 +35,19 @@ This demo was tested in the following configurations:
| Platform | Architecture | Date |
|:------------------------------------------------------------------|:-------------|:-----------|
| NVIDIA RTX 5090 (32 GB VRAM) + Ryzen Z1 Extreme (24 GB RAM) | `win11-x64` | 2025-06-17 |
| NVIDIA RTX 5090 (32 GB VRAM) + Ryzen Z1 Extreme (16 GB RAM) | `linux-x64` | 2025-06-20 |
| NVIDIA RTX 4090 (24 GB VRAM) + Ryzen Z1 Extreme (16 GB RAM) | `win11-x64` | 2025-04-17 |
| NVIDIA RTX 4090 (24 GB VRAM) + Ryzen Z1 Extreme (16 GB RAM) | `linux-x64` | 2025-06-20 |
| AMD RX 7900 XTX (24 GB VRAM) + Ryzen Z1 Extreme (16 GB RAM) | `win11-x64` | 2025-06-20 |
| AMD RX 7900 XTX (24 GB VRAM) + Ryzen Z1 Extreme (16 GB RAM) | `linux-x64` | 2025-06-21 |
| Intel Arc B580 (12 GB VRAM) + Ryzen Z1 Extreme (24 GB RAM) | `win11-x64` | 2025-06-20 |
| Intel Arc B580 (12 GB VRAM) + Ryzen Z1 Extreme (16 GB RAM) | `linux-x64` | 2025-06-21 |
| Apple M4 Max 16-Core CPU + 40-Core GPU (48 GB unified memory) | `darwin-arm` | 2025-03-06 |
| Apple M3 Ultra 28-Core CPU + 60-Core GPU (96 GB unified memory) | `darwin-arm` | 2025-06-24 |
| Apple M2 Max 12-Core CPU + 30-Core GPU (32 GB unified memory) | `darwin-arm` | 2025-03-25 |
| NVIDIA RTX PRO 6000 (96 GB VRAM) + Ryzen Z2 Go (32 GB RAM) | `win11-x64` | 2025-11-15 |
| NVIDIA RTX PRO 6000 (96 GB VRAM) + Ryzen Z2 Go (32 GB RAM) | `linux-x64` | 2025-11-15 |
| NVIDIA RTX 5090 (32 GB VRAM) + Ryzen Z2 Go (32 GB RAM) | `win11-x64` | 2025-11-15 |
| NVIDIA RTX 5090 (32 GB VRAM) + Ryzen Z2 Go (32 GB RAM) | `linux-x64` | 2025-11-15 |
| AMD RX 7900 XTX (24 GB VRAM) + Ryzen Z1 Extreme (16 GB RAM) | `win11-x64` | 2025-11-15 |
| AMD RX 7900 XTX (24 GB VRAM) + Ryzen Z1 Extreme (16 GB RAM) | `linux-x64` | 2025-11-15 |
| AMD RYZEN AI MAX+ 395 + Radeon 8060S (128 GB unified memory) | `linux-x64` | 2025-11-15 |
| AMD RYZEN AI MAX+ 395 + Radeon 8060S (128 GB unified memory) | `win11-x64` | 2025-11-15 |
| Intel Arc B580 (12 GB VRAM) + Ryzen Z1 Extreme (24 GB RAM) | `win11-x64` | 2025-11-15 |
| Intel Arc B580 (12 GB VRAM) + Ryzen Z1 Extreme (16 GB RAM) | `linux-x64` | 2025-11-15 |
| Apple M4 Max 16-Core CPU + 40-Core GPU (48 GB unified memory) | `darwin-arm` | 2025-11-15 |
| Apple M3 Ultra 28-Core CPU + 60-Core GPU (96 GB unified memory) | `darwin-arm` | 2025-11-15 |
| Apple M2 Max 12-Core CPU + 30-Core GPU (32 GB unified memory) | `darwin-arm` | 2025-11-15 |
SheetJS users have verified this demo in other configurations:
@ -55,6 +57,8 @@ SheetJS users have verified this demo in other configurations:
| Platform | Architecture | Demo |
|:---------------------------------------------------------------------|:-------------|:------------|
| NVIDIA L40 (48 GB VRAM) + i9-13900K (32 GB RAM) | `linux-x64` | LangChainJS |
| NVIDIA RTX 4090 (24 GB VRAM) + Ryzen Z2 Go (32 GB RAM) | `win11-x64` | LangChainJS |
| NVIDIA RTX 4090 (24 GB VRAM) + Ryzen Z2 Go (32 GB RAM) | `linux-x64` | LangChainJS |
| NVIDIA RTX 4080 SUPER (16 GB VRAM) + Ryzen Z1 Extreme (16 GB RAM) | `win11-x64` | LangChainJS |
| NVIDIA RTX 4080 SUPER (16 GB VRAM) + Ryzen Z2 Go (32 GB RAM) | `linux-x64` | LangChainJS |
| NVIDIA RTX 4070 Ti SUPER (16 GB VRAM) + Ryzen Z1 Extreme (16 GB RAM) | `win11-x64` | LangChainJS |
@ -878,7 +882,7 @@ npm i --save https://sheet.lol/balls/xlsx-${current}.tgz`}
4) Install dependencies:
```bash
npm i --save @langchain/core@0.3.59 langchain@0.3.28 @langchain/ollama@0.2.2 peggy@3.0.2
npm i --save @langchain/core@0.3.78 langchain@0.3.36 @langchain/ollama@0.2.4 peggy@3.0.2
```
:::note pass
@ -887,7 +891,7 @@ In some test runs, there were error messages relating to dependency and peer
dependency versions. The `--force` flag will suppress version mismatch errors:
```bash
npm i --save @langchain/core@0.3.59 langchain@0.3.28 @langchain/ollama@0.2.2 peggy@3.0.2 --force
npm i --save @langchain/core@0.3.78 langchain@0.3.36 @langchain/ollama@0.2.4 peggy@3.0.2 --force
```
:::
@ -921,7 +925,7 @@ ollama pull phi4:14b
```
<details>
<summary><b>Additional steps for Intel GPUs</b> (click to show)</summary>
<summary><b>Additional steps for Intel GPUs and AMD Strix Halo</b> (click to show)</summary>
A different embedding model must be used on Intel GPUs:

@ -44,14 +44,15 @@ The NodeJS demo was tested in the following environments:
| NodeJS | TF.js | Date |
|:------------|:----------|:-----------|
| `22.14.0` | `4.22.0` | 2025-04-21 |
| `20.18.0` | `4.22.0` | 2025-04-21 |
| `24.11.1` | `4.22.0` | 2025-11-15 |
| `22.21.1` | `4.22.0` | 2025-11-15 |
| `20.18.0` | `4.22.0` | 2025-11-15 |
The Kaioken demo was tested in the following environments:
| Kaioken | TF.js | Date |
|:------------|:----------|:-----------|
| `0.37.0` | `4.22.0` | 2025-04-21 |
| `0.44.3` | `4.22.0` | 2025-11-15 |
:::
@ -417,7 +418,7 @@ The SheetJS team strongly recommends using Kaioken in projects using TF.js.
1) Create a new site.
```bash
npm create vite sheetjs-tfjs-kaioken -- --template vanilla-ts
npm create vite sheetjs-tfjs-kaioken -- --template vanilla-ts --no-rolldown --no-interactive
cd sheetjs-tfjs-kaioken
npm add --save kaioken
npm add --save vite-plugin-kaioken -D
@ -443,7 +444,14 @@ export default defineConfig({
"jsx": "preserve",
```
4) Replace `src/main.ts` with the following codeblock:
4) Edit `tsconfig.json` and remove any lines referencing `verbatimModuleSyntax`.
When the demo was last tested, the file had the following line:
```js title="tsconfig.json (remove this line if present)"
"verbatimModuleSyntax": true,
```
5) Replace `src/main.ts` with the following codeblock:
```js title="src/main.ts"
import { mount } from "kaioken";
@ -453,19 +461,19 @@ const root = document.getElementById("app");
mount(App, root!);
```
5) Download [`SheetJSTF.tsx`](pathname:///tfjs/SheetJSTF.tsx) to the `src` directory:
6) Download [`SheetJSTF.tsx`](pathname:///tfjs/SheetJSTF.tsx) to the `src` directory:
```bash
curl -L -o src/SheetJSTF.tsx https://docs.sheetjs.com/tfjs/SheetJSTF.tsx
```
6) Install SheetJS and TF.js dependencies:
7) Install SheetJS and TF.js dependencies:
<CodeBlock language="bash">{`\
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @tensorflow/tfjs`}
</CodeBlock>
7) Start the development server:
8) Start the development server:
```bash
npm run dev

@ -360,9 +360,9 @@ export default function SheetJSKaiokenAoO() {
{ /* error message */
!pres && !loading && ( <tr><td colSpan="2">{error.message}</td></tr> )
}
</tbody><tfoot><td colSpan={2}>
</tbody><tfoot><tr><td colSpan={2}>
<button onclick={exportFile}>Export XLSX</button>
</td></tfoot></table>);
</td></tr></tfoot></table>);
}
```
@ -429,9 +429,9 @@ export default function SheetJSKaiokenAoO() {
<td>{pres.Index}</td>
</tr>))
}
</tbody><tfoot><td colSpan={2}>
</tbody><tfoot><tr><td colSpan={2}>
<button onclick={exportFile}>Export XLSX</button>
</td></tfoot></table>);
</td></tr></tfoot></table>);
}
```
@ -457,7 +457,7 @@ This demo was tested in the following environments:
1) Create a new site.
```bash
npm create vite@latest sheetjs-kaioken -- --template vanilla-ts
npm create vite@latest sheetjs-kaioken -- --template vanilla-ts --no-rolldown --no-interactive
cd sheetjs-kaioken
npm add --save kaioken
npm add --save vite-plugin-kaioken -D
@ -604,7 +604,7 @@ This demo was tested in the following environments:
1) Create a new site.
```bash
npm create vite@latest sheetjs-kaioken -- --template vanilla-ts
npm create vite@latest sheetjs-kaioken -- --template vanilla-ts --no-rolldown --no-interactive
cd sheetjs-kaioken
npm add --save kaioken
npm add --save vite-plugin-kaioken -D

@ -341,7 +341,7 @@ This demo was tested in the following environments:
1) Create a new site:
```bash
npm create vite@latest sheetjs-react -- --template react
npm create vite@latest sheetjs-react -- --template react --no-rolldown --no-interactive
```
2) Install the SheetJS dependency and start the dev server:
@ -845,7 +845,7 @@ This demo was tested in the following environments:
1) Create a new site:
```bash
npm create vite@latest sheetjs-react -- --template react
npm create vite@latest sheetjs-react -- --template react --no-rolldown --no-interactive
```
2) Install the SheetJS dependency and start the dev server:

@ -59,14 +59,15 @@ 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.
Typically, users will create a spreadsheet with data that should be imported to
the site. The data sheets will typically store headers in the first row. In some
applications, the data will follow a standardized template.
#### State
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, where the keys are specified in the first row:
object for each data row, where the values in the first row are used as keys:
<table>
<thead><tr><th>Spreadsheet</th><th>State</th></tr></thead>
@ -114,7 +115,8 @@ const pres = ref<any[]>([]);
</script>
```
When the spreadsheet header row is known ahead of time, row typing is possible:
`ref` is a generic method in TypeScript. If the spreadsheet header row is known
ahead of time, each row object can be typed:
```html
<script setup lang="ts">
@ -461,18 +463,19 @@ and test the page.
### HTML
The main disadvantage of the Array of Objects approach is the specific nature
The main disadvantage of the "Array of Objects" approach is the specific nature
of the columns. For more general use, passing around an Array of Arrays works.
However, this does not handle merge cells[^5] well!
However, this does not handle merged cells[^5] well!
The [`sheet_to_html`](/docs/api/utilities/html#html-table-output) function
generates HTML that is aware of merges and other worksheet features. VueJS
`v-html`[^6] attribute allows code to set the `innerHTML` attribute, effectively
inserting the code into the page.
In this example, the component attaches a `ref` to the `DIV` container. During
export, the first `TABLE` child element can be parsed with [`table_to_book`](/docs/api/utilities/html#html-table-input) to
generate a workbook object.
In this example, the component attaches a `ref` to the `DIV` container. When the
"Export XLSX" button is clicked, the first `TABLE` child element will be parsed
with the [`table_to_book`](/docs/api/utilities/html#html-table-input) method.
The generated workbook object will be exported to XLSX using `writeFile`.
```html title="src/SheetJSVueHTML.vue"
<script setup>

@ -143,14 +143,14 @@ This demo was tested in the following environments:
| SvelteJS | ViteJS | Date |
|:---------|:---------|:-----------|
| `5.38.6` | `7.1.1` | 2025-09-03 |
| `5.43.7` | `7.2.2` | 2025-11-15 |
:::
1) Create a new project:
```bash
npm create vite@latest sheetjs-svelte -- --template svelte-ts
npm create vite@latest sheetjs-svelte -- --template svelte-ts --no-rolldown --no-interactive
```
2) Install the SheetJS dependency and start the dev server:
@ -245,14 +245,14 @@ This demo was tested in the following environments:
| SvelteJS | ViteJS | Date |
|:---------|:---------|:-----------|
| `5.25.3` | `6.2.3` | 2025-03-30 |
| `5.43.7` | `7.2.2` | 2025-11-15 |
:::
1) Create a new project:
```bash
npm create vite@latest sheetjs-svelte -- --template svelte-ts
npm create vite@latest sheetjs-svelte -- --template svelte-ts --no-rolldown --no-interactive
```
2) Install the SheetJS dependency and start the dev server:

@ -41,10 +41,11 @@ This demo was tested in the following environments:
| ViteJS | Date |
|:---------|:-----------|
| `6.2.3` | 2025-03-30 |
| `5.4.15` | 2025-03-30 |
| `4.5.10` | 2025-03-30 |
| `3.2.11` | 2025-03-30 |
| `7.2.2` | 2025-11-15 |
| `6.4.1` | 2025-11-15 |
| `5.4.21` | 2025-11-15 |
| `4.5.14` | 2025-11-15 |
| `3.2.11` | 2025-11-15 |
:::
@ -74,7 +75,7 @@ It is strongly recommended to [upgrade to the latest version](/docs/getting-star
1) Create a new ViteJS project:
```bash
npm create vite@latest sheetjs-vite -- --template vue-ts
npm create vite@latest sheetjs-vite -- --template vue-ts --no-rolldown --no-interactive
cd sheetjs-vite
npm i
```

@ -47,23 +47,25 @@ This demo was tested in the following environments:
| ESBuild | Date |
|:----------|:-----------|
| `0.25.5` | 2025-06-02 |
| `0.24.2` | 2025-06-02 |
| `0.23.1` | 2025-06-02 |
| `0.22.0` | 2025-06-02 |
| `0.21.5` | 2025-06-02 |
| `0.20.2` | 2025-06-02 |
| `0.19.12` | 2025-06-02 |
| `0.18.20` | 2025-06-02 |
| `0.17.19` | 2025-06-02 |
| `0.16.17` | 2025-06-02 |
| `0.15.18` | 2025-06-02 |
| `0.14.54` | 2025-06-02 |
| `0.13.15` | 2025-06-02 |
| `0.12.29` | 2025-06-02 |
| `0.11.23` | 2025-06-02 |
| `0.10.2` | 2025-06-02 |
| `0.9.7` | 2025-06-02 |
| `0.27.0` | 2025-11-15 |
| `0.26.0` | 2025-11-15 |
| `0.25.12` | 2025-11-15 |
| `0.24.2` | 2025-11-15 |
| `0.23.1` | 2025-11-15 |
| `0.22.0` | 2025-11-15 |
| `0.21.5` | 2025-11-15 |
| `0.20.2` | 2025-11-15 |
| `0.19.12` | 2025-11-15 |
| `0.18.20` | 2025-11-15 |
| `0.17.19` | 2025-11-15 |
| `0.16.17` | 2025-11-15 |
| `0.15.18` | 2025-11-15 |
| `0.14.54` | 2025-11-15 |
| `0.13.15` | 2025-11-15 |
| `0.12.29` | 2025-11-15 |
| `0.11.23` | 2025-11-15 |
| `0.10.2` | 2025-11-15 |
| `0.9.7` | 2025-11-15 |
:::
@ -91,7 +93,7 @@ Assuming the primary source file is `in.js`, the following command will bundle
the script and generate `out.js`:
```bash
npx -y esbuild@0.25.5 in.js --bundle --outfile=out.js
npx -y esbuild@0.25.9 in.js --bundle --outfile=out.js
```
### Browser Demo
@ -140,7 +142,7 @@ curl -LO https://docs.sheetjs.com/esbuild/esbrowser.js
4) Create bundle:
```bash
npx -y esbuild@0.25.5 esbrowser.js --bundle --outfile=esb.browser.js
npx -y esbuild@0.25.9 esbrowser.js --bundle --outfile=esb.browser.js
```
5) Start a local HTTP server:
@ -180,7 +182,7 @@ Assuming the primary source file is `in.js`, the following command will bundle
the script for NodeJS and generate `out.js`:
```bash
npx -y esbuild@0.25.5 in.js --bundle --platform=node --outfile=out.js
npx -y esbuild@0.25.9 in.js --bundle --platform=node --outfile=out.js
```
### NodeJS Demo
@ -229,7 +231,7 @@ curl -LO https://docs.sheetjs.com/esbuild/esbnode.js
3) Create bundle:
```bash
npx -y esbuild@0.25.5 esbnode.js --bundle --platform=node --outfile=esb.node.js
npx -y esbuild@0.25.9 esbnode.js --bundle --platform=node --outfile=esb.node.js
```
4) Run the bundle:

@ -39,12 +39,12 @@ which covers SheetJS library usage in more detail.
This demo was tested in the following environments:
| Version | Date | Required Workarounds |
|:---------|:-----------|:------------------------------------|
| `5.97.1` | 2025-01-03 | |
| `4.47.0` | 2025-01-03 | |
| `3.12.0` | 2025-01-03 | Import `xlsx/dist/xlsx.full.min.js` |
| `2.7.0` | 2025-01-03 | Import `xlsx/dist/xlsx.full.min.js` |
| Version | Date | Required Workarounds |
|:----------|:-----------|:------------------------------------|
| `5.102.1` | 2025-11-15 | |
| `4.47.0` | 2025-11-15 | |
| `3.12.0` | 2025-11-15 | Import `xlsx/dist/xlsx.full.min.js` |
| `2.7.0` | 2025-11-15 | Import `xlsx/dist/xlsx.full.min.js` |
:::
@ -329,8 +329,6 @@ npx -y http-server .
Click on "Click here to export" to generate a file.
## Miscellany
[^1]: See [`read` in "Reading Files"](/docs/api/parse-options)
[^2]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
[^3]: See [`json_to_sheet` in "Utilities"](/docs/api/utilities/array#array-of-objects-input)

@ -34,21 +34,21 @@ This demo was tested in the following environments:
| Browserify | Date |
|:-----------|:-----------|
| `17.0.1` | 2025-06-18 |
| `16.5.2` | 2025-06-18 |
| `15.2.0` | 2025-06-18 |
| `14.5.0` | 2025-06-18 |
| `13.3.0` | 2025-06-18 |
| `12.0.2` | 2025-06-18 |
| `11.2.0` | 2025-06-18 |
| `10.2.6` | 2025-06-18 |
| `9.0.8` | 2025-06-18 |
| `8.1.3` | 2025-06-18 |
| `7.1.0` | 2025-06-18 |
| `6.3.4` | 2025-06-18 |
| `5.13.1` | 2025-06-18 |
| `4.2.3` | 2025-06-18 |
| `3.46.1` | 2025-06-18 |
| `17.0.1` | 2025-11-15 |
| `16.5.2` | 2025-11-15 |
| `15.2.0` | 2025-11-15 |
| `14.5.0` | 2025-11-15 |
| `13.3.0` | 2025-11-15 |
| `12.0.2` | 2025-11-15 |
| `11.2.0` | 2025-11-15 |
| `10.2.6` | 2025-11-15 |
| `9.0.8` | 2025-11-15 |
| `8.1.3` | 2025-11-15 |
| `7.1.0` | 2025-11-15 |
| `6.3.4` | 2025-11-15 |
| `5.13.1` | 2025-11-15 |
| `4.2.3` | 2025-11-15 |
| `3.46.1` | 2025-11-15 |
:::

@ -40,8 +40,8 @@ This demo was tested in the following environments:
| RequireJS | Date |
|:----------|:-----------|
| `2.3.7` | 2025-01-07 |
| `2.1.22` | 2025-01-07 |
| `2.3.7` | 2025-11-15 |
| `2.1.22` | 2025-11-15 |
:::

@ -46,11 +46,11 @@ This demo was tested in the following environments:
| Version | Platform | Date |
|:----------|:---------|:-----------|
| `0.19.47` | NodeJS | 2025-01-03 |
| `0.20.16` | Browser | 2025-01-03 |
| `0.20.19` | NodeJS | 2025-03-03 |
| `0.21.6` | NodeJS | 2025-03-03 |
| `6.15.1` | NodeJS | 2025-01-03 |
| `0.19.47` | NodeJS | 2025-11-15 |
| `0.20.16` | Browser | 2025-11-15 |
| `0.20.19` | NodeJS | 2025-11-15 |
| `0.21.6` | NodeJS | 2025-11-15 |
| `6.15.1` | NodeJS | 2025-11-15 |
:::

@ -34,10 +34,10 @@ This demo was tested in the following environments:
| Version | Date |
|:---------|:-----------|
| `4.29.1` | 2025-01-03 |
| `3.29.5` | 2025-01-03 |
| `2.79.1` | 2025-01-03 |
| `1.32.1` | 2025-01-03 |
| `4.53.2` | 2025-11-15 |
| `3.29.5` | 2025-11-15 |
| `2.79.2` | 2025-11-15 |
| `1.32.1` | 2025-11-15 |
:::

@ -0,0 +1,195 @@
---
title: Packing Sheets with Rspack
sidebar_label: Rspack
pagination_prev: demos/index
pagination_next: demos/grid/index
sidebar_position: 18
---
import current from '/version.js';
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeBlock from '@theme/CodeBlock';
[Rspack](https://rspack.rs/) is a fast module bundler for JavaScript.
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
data from spreadsheets.
This demo uses Rspack and SheetJS to export data. We'll explore how to add
SheetJS to a site using Rspack and how to export data to spreadsheets.
:::note pass
This demo focuses on integration details with the Rspack bundler.
The demos follow the ["Export Tutorial"](/docs/getting-started/examples/export),
which covers SheetJS library usage in more detail.
:::
:::note Tested Deployments
This demo was tested in the following environments:
| Rspack | Date |
|:--------|:-----------|
| `1.6.3` | 2025-11-15 |
:::
## Integration Details
[The "Frameworks" section](/docs/getting-started/installation/frameworks) covers
installation with Yarn and other package managers.
After installing the SheetJS module in a Rspack project, `import` statements can
load relevant parts of the library.
Projects that import data will use methods such as `read`[^1] to parse workbooks
and `sheet_to_json`[^2] to generate usable data from files. As `sheet_to_json`
is part of the `utils` object, the required import is:
```js
import { read, utils } from 'xlsx';
```
Projects that export data will use methods such as `json_to_sheet`[^3] to
generate worksheets and `writeFile`[^4] to export files. As `json_to_sheet` is
part of the `utils` object, the required import is:
```js
import { utils, writeFile } from 'xlsx';
```
## Complete Example
This demo follows the [Export Example](/docs/getting-started/examples/export).
0) Initialize a new project:
```bash
mkdir sheetjs-rspack
cd sheetjs-rspack
npm init -y
```
1) Install the dependencies using a package manager:
<Tabs groupId="pm">
<TabItem value="npm" label="npm">
<CodeBlock language="bash">{`\
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @rspack/core @rspack/cli`}
</CodeBlock>
</TabItem>
<TabItem value="pnpm" label="pnpm">
<CodeBlock language="bash">{`\
pnpm install --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @rspack/core @rspack/cli`}
</CodeBlock>
</TabItem>
<TabItem value="yarn" label="Yarn" default>
<CodeBlock language="bash">{`\
yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @rspack/core @rspack/cli`}
</CodeBlock>
</TabItem>
</Tabs>
2) Save the following to `src/index.js`:
```js title="src/index.js"
import { utils, writeFileXLSX } from 'xlsx';
document.getElementById("xport").addEventListener("click", async() => {
/* fetch JSON data and parse */
const url = "https://docs.sheetjs.com/executive.json";
const raw_data = await (await fetch(url)).json();
/* filter for the Presidents */
const prez = raw_data.filter(row => row.terms.some(term => term.type === "prez"));
/* sort by first presidential term */
prez.forEach(row => row.start = row.terms.find(term => term.type === "prez").start);
prez.sort((l,r) => l.start.localeCompare(r.start));
/* flatten objects */
const rows = prez.map(row => ({
name: row.name.first + " " + row.name.last,
birthday: row.bio.birthday
}));
/* generate worksheet and workbook */
const worksheet = utils.json_to_sheet(rows);
const workbook = utils.book_new();
utils.book_append_sheet(workbook, worksheet, "Dates");
/* fix headers */
utils.sheet_add_aoa(worksheet, [["Name", "Birthday"]], { origin: "A1" });
/* calculate column width */
const max_width = rows.reduce((w, r) => Math.max(w, r.name.length), 10);
worksheet["!cols"] = [ { wch: max_width } ];
/* create an XLSX file and try to save to Presidents.xlsx */
writeFileXLSX(workbook, "Presidents.xlsx");
});
```
3) Create bundle:
```bash
npx -p @rspack/cli rspack build
```
:::caution pass
This build may fail with a module error:
```
ERROR in ./src/index.js
× Module parse failed:
╰─▶ × JavaScript parse error: 'import', and 'export' cannot be used outside of module code
```
Some versions of the `npm` tool create `package.json` files with the option
`"type": "commonjs"`. Rspack will fail if that option is specified. Edit
`package.json` and remove the property:
```js title="package.json (remove highlighted line)"
"license": "ISC",
// highlight-next-line
"type": "commonjs",
"dependencies": {
```
:::
This command will create the script `dist/main.js`
4) Create a small HTML page that loads the generated script:
```html title="index.html"
<!DOCTYPE html>
<html lang="en">
<head></head>
<body>
<h1>SheetJS Presidents Demo</h1>
<button id="xport">Click here to export</button>
<script src="dist/main.js"></script>
</body>
</html>
```
5) Start a local HTTP server:
```bash
npx -y http-server .
```
Access the displayed URL (typically `http://localhost:8080`) with a web browser.
Click the "Click here to export" button to generate `Presidents.xlsx`
[^1]: See [`read` in "Reading Files"](/docs/api/parse-options)
[^2]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
[^3]: See [`json_to_sheet` in "Utilities"](/docs/api/utilities/array#array-of-objects-input)
[^4]: See [`writeFile` in "Writing Files"](/docs/api/write-options)

@ -34,8 +34,8 @@ This demo was tested in the following environments:
| Version | Date |
|:---------|:-----------|
| `2.14.4` | 2025-05-07 |
| `1.12.3` | 2025-05-07 |
| `2.16.1` | 2025-11-15 |
| `1.12.3` | 2025-11-15 |
:::
@ -44,7 +44,7 @@ This demo was tested in the following environments:
[The "Frameworks" section](/docs/getting-started/installation/frameworks) covers
installation with Yarn and other package managers.
After installing the SheetJS module in a RollupJS project, `import` statements
After installing the SheetJS module in a ParcelJS project, `import` statements
can load relevant parts of the library:
```js

@ -35,7 +35,7 @@ This demo was tested in the following environments:
| Version | Date |
|:----------|:-----------|
| `1.21.1` | 2025-06-18 |
| `1.15.2` | 2025-11-15 |
:::
@ -75,8 +75,8 @@ thread '<unnamed>' panicked at 'cannot access a scoped thread local variable wit
This bug is known to affect versions `1.3.100`, `1.4.17`, and `1.10.6`.
This bug was fixed in version `1.21.1`. It is strongly recommended to upgrade
existing projects to use `1.21.1` or to downgrade to `1.2.246`.
This bug was fixed in version `1.12.1`. It is strongly recommended to upgrade
existing projects to use `1.12.1` or to downgrade to `1.2.246`.
:::
@ -95,17 +95,17 @@ npm init -y
<Tabs groupId="pm">
<TabItem value="npm" label="npm">
<CodeBlock language="bash">{`\
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz regenerator-runtime @swc/cli @swc/core@1.21.1`}
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz regenerator-runtime @swc/cli @swc/core@1.13.5`}
</CodeBlock>
</TabItem>
<TabItem value="pnpm" label="pnpm">
<CodeBlock language="bash">{`\
pnpm install --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz regenerator-runtime @swc/cli @swc/core@1.21.1`}
pnpm install --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz regenerator-runtime @swc/cli @swc/core@1.13.5`}
</CodeBlock>
</TabItem>
<TabItem value="yarn" label="Yarn" default>
<CodeBlock language="bash">{`\
yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz regenerator-runtime @swc/cli @swc/core@1.21.1`}
yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz regenerator-runtime @swc/cli @swc/core@1.13.5`}
</CodeBlock>
</TabItem>
</Tabs>

@ -62,7 +62,7 @@ This demo was tested in the following environments:
| Version | Date |
|:--------|:-----------|
| `3.8.8` | 2025-01-07 |
| `3.8.8` | 2025-11-15 |
:::
@ -149,7 +149,7 @@ writeFileXLSX(workbook, "Presidents.xlsx");
</html>
```
:::note pass
:::danger pass
Unlike other bundlers, Snowpack requires a full page including `HEAD` element.
@ -194,7 +194,7 @@ This demo was tested in the following environments:
| Version | Date |
|:--------|:-----------|
| `3.8.0` | 2025-01-07 |
| `3.8.0` | 2025-11-15 |
:::

@ -356,11 +356,12 @@ This demo was tested in the following environments:
| ViteJS | Date |
|:---------|:-----------|
| `6.2.3` | 2025-03-30 |
| `5.4.15` | 2025-03-30 |
| `4.5.10` | 2025-03-30 |
| `3.2.11` | 2025-03-30 |
| `2.9.18` | 2025-03-30 |
| `7.2.2` | 2025-11-15 |
| `6.4.1` | 2025-11-15 |
| `5.4.21` | 2025-11-15 |
| `4.5.14` | 2025-11-15 |
| `3.2.11` | 2025-11-15 |
| `2.9.18` | 2025-11-15 |
:::
@ -376,7 +377,7 @@ major version. For example, `npm create vite@3` will use ViteJS major version 3.
:::
<CodeBlock language="bash">{`\
npm create vite@5 sheetjs-vite -- --template vue-ts
npm create vite@5 sheetjs-vite -- --template vue-ts --no-rolldown --no-interactive
cd sheetjs-vite
npm i
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}

@ -242,7 +242,7 @@ npx -y @capacitor/cli telemetry
2) Create a new Svelte project:
```bash
npm create vite@latest sheetjs-cap -- --template svelte
npm create vite@latest sheetjs-cap -- --template svelte --no-rolldown --no-interactive
cd sheetjs-cap
```

@ -291,7 +291,7 @@ following screenshot was taken in Chrome 126.0.6478.127:
This is a browser limitation and no pure JavaScript library can work around the
issue. See [Issue #3145](https://git.sheetjs.com/sheetjs/sheetjs/issues/3145) in
the SheetJS bug tracker for more details.
the SheetJS CE bug tracker for more details.
:::
@ -466,13 +466,13 @@ This browser demo was tested in the following environments:
| Browser | Date |
|:-------------|:-----------|
| Chromium 137 | 2025-06-20 |
| Chromium 142 | 2025-11-15 |
Some lesser-used browsers do not support File System Access API:
| Browser | Date |
|:-------------|:-----------|
| Safari 18.5 | 2025-06-20 |
| Safari 26.1 | 2025-11-15 |
| Konqueror 22 | 2025-04-23 |
| Firefox 139 | 2025-06-20 |
@ -836,4 +836,4 @@ Desktop and mobile apps have their own specific APIs covered in separate demos:
[^1]: See ["Input Type" in "Reading Files"](/docs/api/parse-options#input-type)
[^2]: See ["Supported Output Formats" type in "Writing Files"](/docs/api/write-options#supported-output-formats)
[^3]: See ["Buffers and TypedArrays"](https://nodejs.org/api/buffer.html#buffers-and-typedarrays) in the NodeJS documentation.
[^4]: See [issue 3145 in the SheetJS bug tracker](https://git.sheetjs.com/sheetjs/sheetjs/issues/3145#issuecomment-11074) for more details. Special thanks to `@sjoenH`!
[^4]: See [issue 3145 in the SheetJS CE bug tracker](https://git.sheetjs.com/sheetjs/sheetjs/issues/3145#issuecomment-11074) for more details. Special thanks to `@sjoenH`!

@ -28,7 +28,7 @@ This demo was tested by SheetJS users in the following deployments:
| Architecture | Ghidra | Date |
|:-------------|:----------|:-----------|
| `darwin-x64` | `11.13.1` | 2025-04-17 |
| `darwin-arm` | `11.13.1` | 2025-03-17 |
| `darwin-arm` | `11.13.1` | 2025-10-19 |
:::
@ -255,30 +255,18 @@ Rows will be generated for each block and the final dataset will be exported.
### System Setup
0) Install Ghidra, Xcode, and Apple Numbers.
0) Install Java, Ghidra, Xcode, and Apple Numbers.
<details>
<summary><b>Installation Notes</b> (click to show)</summary>
On macOS, Ghidra was installed using Homebrew:
When this demo was last tested, Java and Ghidra were installed using Homebrew:
```bash
brew install zulu@21
brew install --cask ghidra
```
:::note pass
Ghidra requires Java to run. If Ghidra returns `JDK 21+ (64-bit) could not be found and must be manually chosen!`:
```bash
brew install zulu@21
```
:::
</details>
1) Add the base Ghidra folder to the PATH variable. The following shell command

@ -180,25 +180,25 @@ be reported to the Bun project for further diagnosis.
This demo was tested in the following deployments:
| Node Version | Date | Node Status when tested |
|:-------------|:-----------|:------------------------|
| `0.12.18` | 2025-04-24 | End-of-Life |
| `4.9.1` | 2025-04-24 | End-of-Life |
| `6.17.1` | 2025-04-24 | End-of-Life |
| `8.17.0` | 2025-04-24 | End-of-Life |
| `10.24.1` | 2025-04-24 | End-of-Life |
| `12.22.12` | 2025-04-24 | End-of-Life |
| `14.15.5` | 2025-04-24 | End-of-Life |
| `16.20.2` | 2025-04-24 | End-of-Life |
| `18.20.8` | 2025-04-24 | Maintenance LTS |
| `20.18.0` | 2025-04-24 | Active LTS |
| `22.14.0` | 2025-04-24 | Current |
| Platform | Date | Status when tested |
|:------------------|:-----------|:-------------------|
| NodeJS `0.12.18` | 2025-11-15 | End-of-Life |
| NodeJS `4.9.1` | 2025-11-15 | End-of-Life |
| NodeJS `6.17.1` | 2025-11-15 | End-of-Life |
| NodeJS `8.17.0` | 2025-11-15 | End-of-Life |
| NodeJS `10.24.1` | 2025-11-15 | End-of-Life |
| NodeJS `12.22.12` | 2025-11-15 | End-of-Life |
| NodeJS `14.21.3` | 2025-11-15 | End-of-Life |
| NodeJS `16.20.2` | 2025-11-15 | End-of-Life |
| NodeJS `18.20.8` | 2025-11-15 | End-of-Life |
| NodeJS `20.18.0` | 2025-11-15 | Maintenance LTS |
| NodeJS `22.21.1` | 2025-11-15 | Active LTS |
| NodeJS `24.11.1` | 2025-11-15 | Current |
| BunJS `1.3.2` | 2025-11-15 | Current |
While streaming methods work in End-of-Life versions of NodeJS, production
deployments should upgrade to a Current or LTS version of NodeJS.
This demo was also tested against BunJS `1.2.10` on 2025-04-24.
:::
1) Install the [NodeJS module](/docs/getting-started/installation/nodejs)

@ -29,7 +29,6 @@ var workbook = XLSX.read(data, opts);
The `read` method can extract data from spreadsheet bytes stored in a JS string,
"binary string", NodeJS buffer or typed array (`Uint8Array` or `ArrayBuffer`).
_Read spreadsheet bytes from a local file and extract data_
```js
@ -38,8 +37,8 @@ var workbook = XLSX.readFile(filename, opts);
The `readFile` method attempts to read a spreadsheet file at the supplied path.
The second `opts` argument is optional. ["Parsing Options"](/docs/api/parse-options)
covers the supported properties and behaviors.
The ["Reading Files"](/docs/api/parse-options) section covers the supported
properties and behaviors.
:::danger pass
@ -48,7 +47,6 @@ security risk), and running `XLSX.readFile` in the browser will throw an error.
Deno scripts must be invoked with `--allow-read` to read from the filesystem.
:::
#### Examples
@ -165,7 +163,6 @@ const workbook = read(Buffer.from(readFileSync(path)));
</TabItem>
</Tabs>
### Example: User Submissions
This example focuses on user-submitted files through a drag-and-drop event, HTML
@ -188,7 +185,6 @@ Assume `drop_dom_element` is the DOM element that will listen for changes:
The event property is `e.dataTransfer`. The code snippet highlights the
difference between the drag-and-drop example and the file input example:
```js
// XLSX is a global from the standalone script
@ -238,7 +234,6 @@ input_dom_element.addEventListener("change", handleFileAsync, false);
https://oss.sheetjs.com/sheetjs/ demonstrates the FileReader technique.
**For maximal compatibility (IE10+)**, the `FileReader` approach is recommended:
<Tabs>
@ -387,11 +382,9 @@ curl -X POST -F "file=@test.xlsx" http://localhost:7262/
:::
</TabItem>
</Tabs>
### Example: Remote File
This example focuses on fetching files ("Ajax" in browser parlance) using APIs
@ -587,7 +580,6 @@ other tools.
:::
<Tabs>
<TabItem value="browser" label="Browser">
@ -731,7 +723,6 @@ var worksheet = XLSX.utils.aoa_to_sheet([
["Array of Arrays Input"](/docs/api/utilities/array#array-of-arrays-input) describes
the function and the optional `opts` argument in more detail.
_Create a worksheet from an array of JS objects_
```js
@ -765,7 +756,6 @@ databases and query results.
</details>
## Processing HTML Tables
#### API
@ -781,8 +771,6 @@ through the rows to generate a worksheet. The `opts` argument is optional.
["HTML Table Input"](/docs/api/utilities/html#html-table-input) describes the
function in more detail.
_Create a workbook by scraping an HTML TABLE in the page_
```js

@ -4,6 +4,9 @@ sidebar_label: Cell Comments
sidebar_position: 4
---
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
<details>
<summary><b>File Format Support</b> (click to show)</summary>
@ -34,7 +37,7 @@ support Excel styled comments or Excel legacy notes.
The letter R (R) marks features parsed but not written in the format.
:::note pass
:::tip pass
[SheetJS Pro](https://sheetjs.com/pro) supports comment rich text and styling.
@ -43,25 +46,66 @@ The letter R (R) marks features parsed but not written in the format.
</details>
Comments and notes are cell annotations. Cells with comments or notes are marked
with a small triangle or `¬` in the upper-right corner.
with `◥` or `¬` in the upper-right corner.
Excel notes are standalone text boxes with adjustable background colors and
support for rich text. Historically people "replied" to comments by adding text
to the end of existing comments.
Excel comments are simple text boxes that allow users to enter plain text. Users
can reply to comments.
can reply to comments. The replies are typically shown in a vertical stack.
The following screenshot shows a spreadsheet with comments and a note.
#### Live Example
The following live example shows a spreadsheet with comments and a note.
- The note is associated with cell A1 (the cell with the red triangle). It has
a green gradient background fill.
- The comments are associated with cell A2 (the cell with the blue `¬`). There
are 2 comments from different authors. A "Reply" box appears below the thread.
:::caution pass
Excel for Mac and other software that lack full support for threaded comments
will show a fallback note that includes each reply on a separate line.
:::
![Excel comments and notes](pathname:///comments/types.png)
:::info pass
<details open>
<summary><b>Live Example</b> (click to hide)</summary>
```jsx live
function SheetJSThreadedComments() {
return ( <button onClick={() => {
var ws = XLSX.utils.aoa_to_sheet([["SheetJS"], [5433795]], {dense: true});
var cell_A1 = (ws["!data"][0]??=[])[0]??={t:"z"}; // Cell A1
var cell_A2 = (ws["!data"][1]??=[])[0]??={t:"z"}; // Cell A2
/* normal comment in cell A1 */
cell_A1.c ??= [];
cell_A1.c.push({a:"SheetJS", t:"This is not threaded"});
/* threaded comment in cell A2 */
cell_A2.c ??= [];
/* add parts */
cell_A2.c.push({a:"SheetJS", t:"This is threaded", T: true});
var part = {a:"JSSheet", t:"This is also threaded"};
cell_A2.c.push({...part, T: true});
/* create workbook and export */
var wb = XLSX.utils.book_new(ws, "Sheet1");
XLSX.writeFile(wb, "SheetJSThreadedComments.xlsx");
}}>Click me to generate a sample file</button> );
}
```
</details>
:::info Application Support
Google Sheets "notes" do not currently support rich text or background colors.
@ -71,19 +115,18 @@ Apple Numbers supports "comments" but does not support "notes".
## Basic Structure
Cell comments are objects stored in the `c` array of cell objects.
The comment content is split into parts based on the comment author.
The `a` field of each comment part is the author of the comment and the `t`
field is the plain text representation.
Cell comments are stored in the `c` property of cell objects. They are expected
to be arrays of fragments objects that include text and metadata.
For example, the following snippet appends a cell comment into cell `A1`:
```js
<Tabs groupId="sheetrep">
<TabItem value="sparse" label="Sparse Sheets">
```js title="Add cell comment to cell A1 in a sparse worksheet"
/* get cell A1, creating an empty cell if necessary */
var cell = ws["A1"];
if(!ws["A1"]) ws["A1"] = { t: "z" };
if(!cell) cell = ws["A1"] = { t: "z" };
/* create comment array if it does not exist */
if(!cell.c) cell.c = [];
@ -98,6 +141,78 @@ var comment_part = {
cell.c.push(comment_part);
```
</TabItem>
<TabItem value="dense" label="Dense Sheets">
:::info pass
[`read`](/docs/api/parse-options), [`aoa_to_sheet`](/docs/api/utilities/array),
and other SheetJS methods will generate sparse worksheets by default.
[Dense mode worksheets](/docs/csf/sheet#dense-mode) require explicit opt-in.
:::
```js title="Add cell comment to cell A1 in a dense worksheet"
/* get cell A1, creating an empty cell if necessary */
var row = ws["!data"][0];
if(!row) row = ws["!data"][0] = [];
var cell = row[0];
if(!row[0]) cell = row[0] = { t: "z" };
/* create comment array if it does not exist */
if(!cell.c) cell.c = [];
/* create a comment part */
var comment_part = {
a: "SheetJS",
t: "I'm a little comment, short and stout!"
};
/* Add comment part to the comment array */
cell.c.push(comment_part);
```
</TabItem>
</Tabs>
### Fragment Properties
Fragments support the following properties:
| Key | Description |
|:----|:-------------------------------------------------------------------|
| `t` | [Text](#fragment-text) |
| `a` | [Author](#fragment-author) |
| `T` | If true, fragment is a [threaded comment](#threaded-comments) part |
#### Fragment Text
Each fragment must include text.
Note and comment fragment texts are concatenated to form the full text.
[Threaded comment](#threaded-comments) fragments are visually distinct.
:::tip pass
SheetJS CE supports plaintext comments.
[SheetJS Pro](https://sheetjs.com/pro) supports comment styling and rich text.
Google Sheets and Excel threaded comments currently do not support styling.
:::
#### Fragment Author
The fragment author is optional.
Note and comment fragment authors are not displayed in Excel.
[Threaded comment](#threaded-comments) fragment authors are displayed in the
thread in Excel.
:::note XLSB Author limits
XLSB enforces a 54 character limit on the Author name. Names longer than 54
@ -105,6 +220,46 @@ characters may cause issues with other formats.
:::
### Comment Properties
#### Visibility
The `hidden` property of the comment block indicates comment visibility. If set
to `true`, the comment will not be visible until users hover over the comment.
```js title="Mark a cell comment as hidden"
if(!cell.c) cell.c = [];
// highlight-next-line
cell.c.hidden = true;
cell.c.push({a:"SheetJS", t:"This comment will be hidden"});
```
<details>
<summary><b>Live Example</b> (click to show)</summary>
The following demo creates a worksheet with two comments. The comment in cell A1
will be visibile and the comment in cell A2 will be hidden.
```jsx live
function SheetJSComments2() {
return (<button onClick={() => {
var ws = XLSX.utils.aoa_to_sheet([["SheetJS"], [5433795]]);
if(!ws.A1.c) ws.A1.c = [];
ws.A1.c.push({a:"SheetJS", t:"This comment is visible"});
if(!ws.A2.c) ws.A2.c = [];
ws.A2.c.hidden = true;
ws.A2.c.push({a:"SheetJS", t:"This comment will be hidden"});
var wb = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(wb, ws, "Sheet1");
XLSX.writeFile(wb, "SheetJSComments2.xlsx");
}}>Click me to generate a sample file</button>);
}
```
</details>
## Demos
#### Export
@ -172,44 +327,6 @@ function SheetJSParseComments(props) {
</details>
## Visibility
The `hidden` property of the comment block indicates comment visibility. If set
to `true`, the comment will not be visible until users hover over the comment.
```js
if(!cell.c) cell.c = [];
// highlight-next-line
cell.c.hidden = true;
cell.c.push({a:"SheetJS", t:"This comment will be hidden"});
```
<details>
<summary><b>Live Example</b> (click to show)</summary>
The following demo creates a worksheet with two comments. The comment in cell A1
will be visibile and the comment in cell A2 will be hidden.
```jsx live
function SheetJSComments2() {
return (<button onClick={() => {
var ws = XLSX.utils.aoa_to_sheet([["SheetJS"], [5433795]]);
if(!ws.A1.c) ws.A1.c = [];
ws.A1.c.push({a:"SheetJS", t:"This comment is visible"});
if(!ws.A2.c) ws.A2.c = [];
ws.A2.c.hidden = true;
ws.A2.c.push({a:"SheetJS", t:"This comment will be hidden"});
var wb = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(wb, ws, "Sheet1");
XLSX.writeFile(wb, "SheetJSComments2.xlsx");
}}>Click me to generate a sample file</button>);
}
```
</details>
## Threaded Comments
Threaded comments are plain text comment snippets with author metadata and
@ -217,7 +334,7 @@ parent references. They are supported in XLSX, XLSB, and NUMBERS files.
To mark a comment as threaded, each comment part must have a true `T` property:
```js
```js title="Create a threaded comment with a reply"
if(!cell.c) cell.c = [];
var part1 = {
@ -237,34 +354,8 @@ var part2 = {
cell.c.push({ ...part2, T: true});
```
:::caution pass
There is no Active Directory or Office 365 metadata associated with authors.
<details open>
<summary><b>Live Example</b> (click to hide)</summary>
```jsx live
function SheetJSThreadedComments() {
return ( <button onClick={() => {
var ws = XLSX.utils.aoa_to_sheet([["SheetJS"], [5433795]]);
/* normal comment */
if(!ws.A1.c) ws.A1.c = [];
ws.A1.c.push({a:"SheetJS", t:"This is not threaded"});
/* threaded comment */
if(!ws.A2.c) ws.A2.c = [];
/* add parts */
ws.A2.c.push({a:"SheetJS", t:"This is threaded", T: true});
var part = {a:"JSSheet", t:"This is also threaded"};
ws.A2.c.push({...part, T: true});
/* create workbook and export */
var wb = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(wb, ws, "Sheet1");
XLSX.writeFile(wb, "SheetJSThreadedComments.xlsx");
}}>Click me to generate a sample file</button> );
}
```
</details>
:::

@ -15,31 +15,31 @@ is stored as the formatted value. The formatted values can be generated from
many different values and number formats. SheetJS parsers expose options to
control value parsing and number format speculation.
| Formats | Basic | Storage Representation |
|:------------------|:-----:|:-----------------------|
| XLSX / XLSM | ✔ | Number Format Code |
| XLSB | ✔ | Number Format Code |
| XLS | ✔ | Number Format Code |
| XLML | ✔ | Number Format Code |
| SYLK | R | Number Format Code |
| ODS / FODS / UOS | ✔ | XML Tokens |
| NUMBERS | | Binary encoding |
| WK1 | + | Fixed set of formats |
| WK3 / WK4 | | Binary encoding |
| WKS Lotus | + | Fixed set of formats |
| WKS Works | + | Fixed set of formats |
| WQ1 | + | Fixed set of formats |
| WQ2 | | Binary encoding |
| WB1 / WB2 / WB3 | | Binary encoding |
| QPW | + | Binary encoding |
| DBF | | Implied by field types |
| HTML | ! | Special override |
| CSV | * | N/A |
| PRN | * | N/A |
| DIF | * | N/A |
| RTF | * | N/A |
| Formats | Basic | Storage Representation |
|:------------------|:-----:|:--------------------------|
| XLSX / XLSM | ✔ | Number Format Code |
| XLSB | ✔ | Number Format Code |
| XLS | ✔ | Number Format Code |
| XLML | ✔ | Number Format Code |
| SYLK | R | Number Format Code |
| ODS / FODS / UOS | ✔ | XML Tokens |
| NUMBERS | | Binary encoding |
| WK1 | + | Fixed set of formats |
| WK3 / WK4 | | Binary encoding |
| WKS Lotus | + | Fixed set of formats |
| WKS Works | + | Fixed set of formats |
| WQ1 | + | Fixed set of formats |
| WQ2 | | Binary encoding |
| WB1 / WB2 / WB3 | | Binary encoding |
| QPW | + | Binary encoding |
| DBF | | Inferred from field types |
| HTML | ! | Special override |
| CSV | * | N/A |
| PRN | * | N/A |
| DIF | * | N/A |
| RTF | * | N/A |
(+) mark formats with limited support. The QPW (Quattro Pro Workbooks) parser
Plus symbols (+) mark formats with limited support. The QPW (Quattro Pro) parser
supports the built-in date and built-in time formats but does not support
custom number formats. [Date and Time support](/docs/csf/features/dates) in
modern Excel formats requires limited number format support to distinguish date

@ -5,7 +5,7 @@ hide_table_of_contents: true
---
The main SheetJS method for reading files is `read`. It expects developers to
supply the actual data in a supported representation.
supply the actual data in a [supported representation](#input-type).
The `readFile` helper method accepts a filename and tries to read the specified
file using standard APIs. *It does not work in web browsers!*
@ -38,64 +38,130 @@ includes additional instructions for non-standard use cases.
:::
:::tip pass
The SheetJS file format import codecs focus on raw data. Not all codecs support
all features. Features not described in the documentation may not be extracted.
[SheetJS Pro](https://sheetjs.com/pro) offers support for additional features,
including styling, images, graphs, and PivotTables.
:::
## Parsing Options
The read functions accept an options argument:
| Option Name | Default | Description |
|:------------|:--------|:-----------------------------------------------------|
|`type` | | [Input data representation](#input-type) |
|`raw` | `false` | If true, plain text parsing will not parse values ** |
|`dense` | `false` | If true, use a [dense sheet representation](#dense) |
|`codepage` | | If specified, use code page when appropriate ** |
|`cellFormula`| `true` | Save [formulae to the `.f` field](#formulae) |
|`cellHTML` | `true` | Parse rich text and save HTML to the `.h` field |
|`cellNF` | `false` | Save number format string to the `.z` field |
|`cellStyles` | `false` | Save style/theme info to the `.s` field |
|`cellText` | `true` | Generated formatted text to the `.w` field |
|`cellDates` | `false` | Store dates as type `d` (default is `n`) |
|`dateNF` | | If specified, use the string for date code 14 ** |
|`sheetStubs` | `false` | Create cell objects of type `z` for stub cells |
|`sheetRows` | `0` | If >0, read the [specified number of rows](#range) |
|`bookDeps` | `false` | If true, parse calculation chains |
|`bookFiles` | `false` | If true, add raw files to book object ** |
|`bookProps` | `false` | If true, only parse enough to get book metadata ** |
|`bookSheets` | `false` | If true, only parse enough to get the sheet names |
|`bookVBA` | `false` | If true, generate [VBA blob](#vba) |
|`password` | `""` | If defined and file is encrypted, use password ** |
|`WTF` | `false` | If true, throw errors on unexpected file features ** |
|`sheets` | | If specified, only parse specified sheets ** |
|`nodim` | `false` | If true, calculate [worksheet ranges](#range) |
|`PRN` | `false` | If true, allow parsing of PRN files ** |
|`xlfn` | `false` | If true, [preserve prefixes](#formulae) in formulae |
|`FS` | | DSV Field Separator override |
|`UTC` | `true` | If explicitly false, parse text dates in local time |
| Option Name | Default | Description |
|:--------------|:--------|:---------------------------------------------------|
| `type` | | [Input data representation](#input-type) |
| `raw` | `false` | Disable [value parsing in plaintext formats](#raw) |
| `dense` | `false` | If true, [generate dense worksheets](#dense) |
| `codepage` | | Use specified [code page encoding](#codepage) |
| `cellFormula` | `true` | Save [formulae to the `f` field](#formulae) |
| `cellHTML` | `true` | Parse text and [save HTML to the `h` field](#html) |
| `cellNF` | `false` | Save [number format to the `z` field](#text) |
| `cellStyles` | `false` | Save [style/theme info to the `s` field](#style) |
| `cellText` | `true` | Save [formatted text to the `w` field](#text) |
| `cellDates` | `false` | [Generate proper date (type `d`) cells](#dates) |
| `dateNF` | | If specified, [override date code 14](#dates) |
| `sheetStubs` | `false` | [Create cells of type `z` for stub cells](#stubs) |
| `sheetRows` | `0` | If >0, read the [specified number of rows](#range) |
| `bookDeps` | `false` | If true, parse calculation chains |
| `bookFiles` | `false` | Add [raw files](#files) to book object |
| `bookProps` | `false` | If true, [only parse book metadata](#metadata) |
| `bookSheets` | `false` | If true, [only parse sheet names](#metadata) |
| `bookVBA` | `false` | If true, generate [VBA blob](#vba) |
| `password` | `""` | If specified, [decrypt workbook](#password) |
| `WTF` | `false` | [Do not suppress worksheet parsing errors](#wtf) |
| `sheets` | | Only parse [specified sheets](#sheets) |
| `nodim` | `false` | If true, calculate [worksheet ranges](#range) |
| `PRN` | `false` | If true, [allow parsing of PRN files](#prn) |
| `xlfn` | `false` | Use [raw formula function names](#formulae) |
| `FS` | | [DSV Field Separator override](#dsv) |
| `UTC` | `true` | Parse [text dates and times using UTC](#tz) |
- Even if `cellNF` is false, formatted text will be generated and saved to `.w`
- In some cases, sheets may be parsed even if `bookSheets` is false.
- Excel aggressively tries to interpret values from CSV and other plain text.
This leads to surprising behavior! The `raw` option suppresses value parsing.
- `bookSheets` and `bookProps` combine to give both sets of information
- `Deps` will be an empty object if `bookDeps` is false
- `bookFiles` behavior depends on file type:
* `keys` array (paths in the ZIP) for ZIP-based formats
* `files` hash (mapping paths to objects representing the files) for ZIP
* `cfb` object for formats using CFB containers
- By default all worksheets are parsed. `sheets` restricts based on input type:
* number: zero-based index of worksheet to parse (`0` is first worksheet)
* string: name of worksheet to parse (case insensitive)
* array of numbers and strings to select multiple worksheets.
- `codepage` is applied to BIFF2 - BIFF5 files without `CodePage` records and to
CSV files without BOM in `type:"binary"`. BIFF8 XLS always defaults to 1200.
- `PRN` affects parsing of text files without a common delimiter character.
- Currently only XOR encryption is supported. Unsupported error will be thrown
for files employing other encryption methods.
- `WTF` is mainly for development. By default, the parser will suppress read
errors on single worksheets, allowing you to read from the worksheets that do
parse properly. Setting `WTF:true` forces those errors to be thrown.
- `UTC` applies to CSV, Text and HTML formats. When explicitly set to `false`,
the parsers will assume the files are specified in local time. By default, as
is the case for other file formats, dates and times are interpreted in UTC.
### Cell-Level Options
#### Dates
By default, for consistency with spreadsheet applications, date cells are stored
as numeric cells (type `n`) with special number formats. If `cellDates` is
enabled, date codes are converted to proper Date objects.
Excel file formats (including XLSX, XLSB, and XLS) support a locale-specific
date format, typically stored as date code 14 or the string `m/d/yy`. The
formatted text for some cells will change based on the computer locale. SheetJS
parsers use the `en-US` form by default. If the `dateNF` option is set, that
number format string will be used.
["Dates and Times"](/docs/csf/features/dates) covers features in more detail.
#### Formulae
For some file formats, the `cellFormula` option must be explicitly enabled to
ensure that formulae are extracted.
Newer Excel functions are serialized with the `_xlfn.` prefix, hidden from the
user. By default, the file parsers will strip `_xlfn.` and similar prefixes.
If the `xlfn` option is enabled, the prefixes will be preserved.
[The "Formulae" docs](/docs/csf/features/formulae#prefixed-future-functions)
covers this in more detail.
#### Formatted Text {#text}
Many common spreadsheet formats (including XLSX, XLSB, and XLS) store numeric
values and number formats. Applications are expected to use the number formats
to display currency strings, dates, and other values.
Under the hood, parsers use the [SSF Number Formatter](/docs/constellation/ssf)
library to generated formatted text.
By default, formatted text is generated. If the `cellText` option is false,
formatted text will not be written.
By default, cell number formats are not preserved. If the `cellNF` option is
enabled, number format strings will be saved to the `z` field of cell objects.
["Number Formats"](/docs/csf/features/nf) covers the features in more detail.
:::note pass
Even if `cellNF` is false, formatted text will be generated and saved to `w`.
:::
#### Text and Cell Styling {#style}
By default, SheetJS CE parsers focus on data extraction.
If the `cellStyles` option is `true`, other styling metadata including
[row](/docs/csf/features/rowprops) and [column](/docs/csf/features/colprops)
properties will be parsed.
:::tip pass
[SheetJS Pro](https://sheetjs.com/pro) offers cell / text styling, conditional
formatting and additional styling options.
:::
#### HTML Formatted Text {#html}
Spreadsheet applications support a limited form of rich text styling.
If the `cellHTML` option is `true`, some file parsers will attempt to translate
the rich text to standard HTML with inner tags for bold text and other styles.
:::tip pass
[SheetJS Pro](https://sheetjs.com/pro) offers additional styling options,
conversions for all supported file formats, and whole-worsheet HTML generation.
:::
### Sheet-Level Options
#### Dense
@ -130,17 +196,16 @@ The `nodim` option instructs the parser to ignore self-reported ranges and use
the actual cells in the worksheet to determine the range. This addresses known
issues with non-compliant third-party exporters.
#### Formulae
#### Stubs
For some file formats, the `cellFormula` option must be explicitly enabled to
ensure that formulae are extracted.
Some file formats, including XLSX and XLS, can specify cells without cell data.
For example, cells covered by a [merged cell block](/docs/csf/features/merges)
are technically invalid but files may include metadata.
Newer Excel functions are serialized with the `_xlfn.` prefix, hidden from the
user. SheetJS will strip `_xlfn.` normally. The `xlfn` option preserves them.
[The "Formulae" docs](/docs/csf/features/formulae#prefixed-future-functions)
covers this in more detail.
By default, the cells are skipped. If the `sheetStubs` option is `true`, these
cells will be parsed as [stub cells](/docs/csf/cell#cell-types)
["Formulae"](/docs/csf/features/formulae) covers the features in more detail.
### Book-Level Options
#### VBA
@ -160,18 +225,166 @@ new blob from the XLS CFB container that works in XLSM and XLSB files.
</details>
#### Workbook Metadata {#metadata}
By default, the data from each worksheet is parsed.
If any of the following options are passed, parsers will not parse sheet data.
They will parse enough of the workbook to extract the requested information.
| Option | Extracted Data |
|:-------------|:--------------------|
| `bookProps` | Workbook properties |
| `bookSheets` | Worksheet names |
The options apply to XLSX, XLSB, XLS and XLML parsers.
#### Worksheets {#sheets}
By default, all worksheets are parsed. The `sheets` option limits which sheets
are parsed.
If the `sheets` option is a number, the number is interpreted as a zero-based
index. For example, `sheets: 2` instructs the parser to read the third sheet.
If the `sheets` option is text, the string is interpreted as a worksheet name.
The name is case-insensitive. `sheets: "Sheet1"` instructs the parser to read
the worksheet named "Sheet1".
If the `sheets` option is an array of numbers and text, each worksheets will
be parsed. `sheets: [2, "Sheet1"]` instructs the parser to read the third sheet
and the sheet named "Sheet1". If the third worksheet is coincidentally named
"Sheet1", only one worksheet will be parsed
### File-Level Options
#### Password Protection {#password}
SheetJS CE currently supports XOR encryption in XLS files. Errors will be thrown
when trying to parse files using unsupported encryption methods.
:::tip pass
[SheetJS Pro](https://sheetjs.com/pro) offers support for additional encryption
schemes, including the AES-CBC schemes used in XLSX / XLSM / XLSB files and the
RC4 schemes used in newer XLS files.
:::
#### Lotus Formatted Text (PRN) {#prn}
Lotus Formatted Text (`PRN`) worksheets are plain text files that do not include
delimiter characters. Each cell in a column has the same width.
If the `PRN` option is set, the plaintext parser will attempt to parse some
plaintext files as if they follow the `PRN` format.
:::note pass
If the `PRN` option is set, text files that do not include commas or semicolons
or other common delimiters may not be parsed as expected.
This option should not be enabled unless it is known that the file was exported
from Lotus 1-2-3 or from Excel using the "Lotus Formatted Text (`PRN`)" format.
:::
#### Value Parsing {#raw}
Spreadsheet software including Excel aggressively try to interpret values from
CSV and other plain text. This leads to surprising behavior[^1]!
If the `raw` option is true, value parsing will be suppressed. All cells values
are treated as strings.
The `raw` option affects the following formats: HTML, CSV, PRN, DIF, RTF.
The `raw` option does not affect XLSX, XLSB, XLS and other file formats that
support explicit value typing.
:::note pass
See [Issue #3331](https://git.sheetjs.com/sheetjs/sheetjs/issues/3145) in the
SheetJS CE bug tracker for more details.
:::
#### Code Page Encoding {#codepage}
Spreadsheet applications support a number of legacy encodings. Plaintext files
will appear different when opened in different computers in different regions.
By default, the parsers use the most common "English (United States)" encodings.
The `codepage` option controls the encoding in BIFF2 - BIFF5 XLS files without
`CodePage` records, some legacy formats including DBF, and in CSV files without
BOM in `type: "binary"`. BIFF8 XLS always defaults to 1200.
The `codepage` support library is not guaranteed to be loaded by default. The
["Installation"](/docs/getting-started/installation/) section describes how to
install and load the support library.
See ["Legacy Codepages"](/docs/constellation/codepage) for more details.
#### Date Processing {#tz}
Plaintext formats may include date and time values without timezone info. The
time `12:30 AM` is ambiguous.
In the wild, there are two popular approaches:
A) Spreadsheet software typically interpret time values using local timezones.
When opening a file in New York, `12:30 AM` will be parsed as `12:30 AM ET`.
When opening a file in Los Angeles, the time will be parsed as `12:30 AM PT`.
B) APIs use [UTC](https://en.wikipedia.org/wiki/Coordinated_Universal_Time), the
most popular global time standard. `12:30 AM` will be parsed as the absolute
moment in time corresponding to `8:30 PM EDT` or `7:30 PM EST`.
By default, the parsers assume files are specified in UTC. When the `UTC` option
is explicitly set to `false`, dates and times are interpreted in timezone of the
web browser or JavaScript engine.
#### Delimiter-Separated Values {#dsv}
The plaintext parser applies a number of heuristics to determine if files are
CSV (fields separated by commas), TSV (fields separated by tabs), PSV (fields
separated by `|`) or SSV (fields separated by `;`). The heuristics are based on
the presence of characters not in a double-quoted value.
The `FS` option instructs the parser to use the specified delimiter if multiple
delimiter characters are in the text. This bypasses the default heuristics.
#### Internal Files {#files}
Some file formats are structured as larger containers that include sub-files.
For example, XLSX files are ZIP files with XML sub-files.
If the `bookFiles` option is `true`, each sub-file will be preserved in the
workbook. The behavior depends on file type:
- `keys` array (paths in the ZIP) for ZIP-based formats
- `files` hash (mapping paths to objects representing the files) for ZIP
- `cfb` object for formats using CFB containers
#### Parsing Errors {#wtf}
By default, the workbook parser will suppress errors when parsing worksheets.
This ensures the valid worksheets from a multi-sheet workbook are parsed.
If the `WTF` option is enabled, the errors will not be suppressed.
### Input Type
The `type` parameter for `read` controls how data is interpreted:
| `type` | expected input |
|:-----------|:----------------------------------------------------------------|
| `"base64"` | string: Base64 encoding of the file |
| `"binary"` | string: binary string (byte `n` is `data.charCodeAt(n)`) |
| `"string"` | string: JS string (only appropriate for UTF-8 text formats) |
| `"buffer"` | nodejs Buffer |
| `"array"` | array: array of 8-bit unsigned integers (byte `n` is `data[n]`) |
| `"file"` | string: path of file that will be read (nodejs only) |
| `type` | expected input |
|:---------|:----------------------------------------------------------------|
| `base64` | string: Base64 encoding of the file |
| `binary` | string: binary string (byte `n` is `data.charCodeAt(n)`) |
| `string` | string: JS string (only appropriate for UTF-8 text formats) |
| `buffer` | nodejs Buffer |
| `array` | array: array of 8-bit unsigned integers (byte `n` is `data[n]`) |
| `file` | string: path of file that will be read (nodejs only) |
Some common types are automatically deduced from the data input type, including
NodeJS `Buffer` objects, `Uint8Array` and `ArrayBuffer` objects, and arrays of
@ -246,3 +459,6 @@ were CSV or TSV. SheetJS attempts to replicate that behavior.
</details>
[^1]: The gene [`SEPT1`](https://en.wikipedia.org/wiki/SEPTIN1) was renamed to
`SEPTIN1` to avoid Excel value interpretations: the string `SEPT1` is parsed as
the date "September 1".

@ -9,12 +9,13 @@ import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeBlock from '@theme/CodeBlock';
The main SheetJS method for writing workbooks is `write`. Scripts receive common
[JavaScript data representations](#output-type) and are expected to write or
share files using platform-specific APIs.
The main SheetJS method for writing workbooks is `write`. It accepts a SheetJS
[workbook object](/docs/csf/book) and returns the file data stored in common
[JavaScript data representations](#output-type). Scripts calling `write` are
expected to write or share files using platform-specific APIs.
The `writeFile` helper method accepts a filename and tries to write to a local
file using [standard APIs](/docs/demos/local/file).
file using [standard local file APIs](/docs/demos/local/file).
**Export a SheetJS workbook object in a specified file format**
@ -24,9 +25,9 @@ var file_data = XLSX.write(wb, opts);
`write` attempts to write the workbook `wb` and return the file.
The `options` argument is required. It must specify
- [`bookType`](#supported-output-formats) (file format of the exported file)
- [`type`](#output-type) (return value type)
The `options` argument is required. It must specify
- [`bookType`](#bookType): file format of the exported file
- [`type`](#output-type): return value type
**Export a SheetJS workbook object and attempt to write a local file**
@ -39,17 +40,18 @@ XLSX.writeFile(wb, filename, options);
In browser-based environments, it will attempt to force a client-side download.
It also supports NodeJS, ExtendScript applications, and Chromium extensions.
The `options` argument is optional.
If `options` is omitted or if `bookType` is missing from the `options` object,
the output file format will be deduced from the filename extension.
the output file format will be [inferred from the filename](#extension).
**Special functions for exporting data in the XLSX format**
```js
// limited form of `write`
/* `write` (XLSX only) */
var file_data = XLSX.writeXLSX(wb, options);
// limited form of `writeFile`
/* `writeFile` (XLSX only) */
XLSX.writeFileXLSX(wb, filename, options);
```
@ -60,111 +62,357 @@ For websites that exclusively export to XLSX, these functions can reduce the
size of the production site. The general `write` and `writeFile` functions are
more appropriate when exporting to XLS or XLSB or other formats.
<details>
<summary><b>NodeJS-specific methods</b> (click to show)</summary>
**Export a workbook and attempt to write a local file using `fs.writeFile`**
```js
// callback equivalent of `XLSX.writeFile`
/* callback equivalent of `XLSX.writeFile(wb, filename)` */
XLSX.writeFileAsync(filename, wb, cb);
// callback equivalent with options argument
/* callback equivalent of `XLSX.writeFile(wb, filename, options)` */
XLSX.writeFileAsync(filename, wb, options, cb);
```
`writeFileAsync` attempts to write `wb` to `filename` and invoke the callback
`cb` on completion.
When an `options` object is specified, it is expected to be the third argument.
This method only works in NodeJS and uses `fs.writeFile` under the hood.
This method only works in NodeJS. It uses `fs.writeFile` under the hood.
</details>
:::note Recommendation
`writeFile` wraps a number of export techniques, making it suitable for browser
downloads, NodeJS, ExtendScript apps, and Chromium extensions. It does not work
in other environments with more advanced export methods.
`writeFile` wraps a number of export techniques, making it suitable for web
browsers, [Photoshop and InDesign](/docs/demos/extensions/extendscript), and
server-side platforms including NodeJS. It does not work in other environments
with more advanced export methods.
The `write` method returns raw bytes or strings that can be exported in
[Desktop apps](/docs/demos/desktop/) , [Mobile apps](/docs/demos/mobile) , and
[Servers](/docs/demos/net/server).
`write` returns raw bytes or strings that can be exported with platform-specific
APIs in [Desktop apps](/docs/demos/desktop), [Mobile apps](/docs/demos/mobile),
[Servers](/docs/demos/net/server), and [extensions](/docs/demos/extensions).
The [demos](/docs/demos) preferentially use `writeFile`. When `writeFile` is not
supported, the demos show file creation using `write` and platform APIs.
:::
:::tip pass
The SheetJS file format export codecs focus on raw data. Not all codecs support
all features. Features not described in the documentation may not be serialized.
[SheetJS Pro](https://sheetjs.com/pro) offers support for additional features,
including styling, images, graphs, and PivotTables.
:::
## Writing Options
The write functions accept an options argument:
| Option Name | Default | Description |
| :---------- | -------: | :------------------------------------------------- |
|`type` | | Output data encoding (see Output Type below) |
|`cellDates` | `false` | Store dates as type `d` (default is `n`) |
|`cellStyles` | `false` | Save style/theme info to the `.s` field |
|`codepage` | | If specified, use code page when appropriate ** |
|`bookSST` | `false` | Generate Shared String Table ** |
|`bookType` | `"xlsx"` | Type of Workbook (see below for supported formats) |
|`bookVBA` | | Add VBA blob from workbook object to the file ** |
|`WTF` | `false` | If true, throw errors on unexpected features ** |
|`sheet` | `""` | Name of Worksheet for single-sheet formats ** |
|`compression`| `false` | Use ZIP compression for ZIP-based formats ** |
|`Props` | | Override workbook properties when writing ** |
|`themeXLSX` | | Override theme XML when writing XLSX/XLSB/XLSM ** |
|`ignoreEC` | `true` | Suppress "number as text" errors ** |
|`numbers` | | Payload for NUMBERS export ** |
|`FS` | `","` | "Field Separator" delimiter between fields ** |
|`RS` | `"\n"` | "Record Separator" delimiter between rows ** |
| Option Name | Default | Description |
|:--------------|:---------|:---------------------------------------------------|
| `type` | | [Output data encoding](#output-type) |
| `cellDates` | `false` | Convert [numeric date codes to strings](#dates) |
| `cellStyles` | `false` | Export [style/theme info](#style) |
| `codepage` | | Use specified [code page encoding](#codepage) |
| `bookSST` | `false` | Generate [Shared String Table](#sst) |
| `bookType` | `"xlsx"` | [Type of Workbook](#supported-output-formats) |
| `bookVBA` | | [Add VBA blob from workbook object](#vba) |
| `WTF` | `false` | [Do not suppress warnings and errors](#wtf) |
| `sheet` | `""` | [Export the named sheet](#sheet) |
| `compression` | `false` | [Try to reduce file size](#compression) |
| `Props` | | Override [workbook properties](#props) |
| `themeXLSX` | | Override [theme XML for XLSX/XLSB/XLSM](#theme) |
| `ignoreEC` | `true` | [Suppress "number as text" errors](#ec) |
| `numbers` | | [Payload for NUMBERS export](#numbers) |
| `FS` | `","` | [Field Separator](#dsv) for CSV and Text exports |
| `RS` | `"\n"` | [Record Separator](#dsv) for CSV and Text exports |
- `bookSST` is slower and more memory intensive, but has better compatibility
with older versions of iOS Numbers
- The raw data is the only thing guaranteed to be saved. Features not described
in this README may not be serialized.
- `cellDates` only applies to XLSX output and is not guaranteed to work with
third-party readers. Excel itself does not usually write cells with type `d`
so non-Excel tools may ignore the data or error in the presence of dates.
- `codepage` is applied to legacy formats including DBF. Characters missing
from the encoding will be replaced with underscore characters (`_`).
- `Props` is an object mirroring the workbook `Props` field. See the table from
the [Workbook File Properties](/docs/csf/book#file-properties) section.
- if specified, the string from `themeXLSX` will be saved as the primary theme
for XLSX/XLSB/XLSM files (to `xl/theme/theme1.xml` in the ZIP)
- Due to a bug in the program, some features like "Text to Columns" will crash
Excel on worksheets where error conditions are ignored. The writer will mark
files to ignore the error by default. Set `ignoreEC` to `false` to suppress.
- `FS` and `RS` apply to CSV and Text output formats. The options are discussed
in ["CSV and Text"](/docs/api/utilities/csv#delimiter-separated-output)
- `bookVBA` only applies to supported formats. ["VBA"](/docs/csf/features/vba)
section explains the feature in more detail.
- `WTF` is mainly for development.
## Supported Output Formats
<a name="bookType"></a>
For broad compatibility with third-party tools, SheetJS CE supports many output
formats. The writer will select the file type based on the `bookType` option:
| `bookType` | extension | sheets | Description |
|:-----------|:-----------|:-------|:--------------------------------|
| `xlsx` | `.xlsx` | multi | Excel 2007+ XML Format |
| `xlsm` | `.xlsm` | multi | Excel 2007+ Macro XML Format |
| `xlsb` | `.xlsb` | multi | Excel 2007+ Binary Format |
| `biff8` | `.xls` | multi | Excel 97-2004 Workbook Format |
| `biff5` | `.xls` | multi | Excel 5.0/95 Workbook Format |
| `biff4` | `.xls` | single | Excel 4.0 Worksheet Format |
| `biff3` | `.xls` | single | Excel 3.0 Worksheet Format |
| `biff2` | `.xls` | single | Excel 2.0 Worksheet Format |
| `xlml` | `.xls` | multi | Excel 2003-2004 (SpreadsheetML) |
| `numbers` | `.numbers` | multi | Numbers 3.0+ Spreadsheet |
| `ods` | `.ods` | multi | OpenDocument Spreadsheet |
| `fods` | `.fods` | multi | Flat OpenDocument Spreadsheet |
| `wk3` | `.wk3` | multi | Lotus Workbook (WK3) |
| `csv` | `.csv` | single | Comma Separated Values |
| `txt` | `.txt` | single | UTF-16 Unicode Text (TXT) |
| `sylk` | `.sylk` | single | Symbolic Link (SYLK) |
| `html` | `.html` | single | HTML Document |
| `dif` | `.dif` | single | Data Interchange Format (DIF) |
| `dbf` | `.dbf` | single | dBASE II + VFP Extensions (DBF) |
| `wk1` | `.wk1` | single | Lotus Worksheet (WK1) |
| `rtf` | `.rtf` | single | Rich Text Format (RTF) |
| `prn` | `.prn` | single | Lotus Formatted Text |
| `eth` | `.eth` | single | Ethercalc Record Format (ETH) |
If the output format supports multiple worksheets, the workbook writer will try
to export each worksheet. If the format only supports one worksheet, the writer
will export the first worksheet. If the [`sheet` option](#sheet) is a string,
the writer will use the named sheet.
#### Output Format inference from File Extension {#extension}
`writeFile` will automatically guess the output file format based on the file
extension if `bookType` is not specified.
| extension | Description |
|:-----------|:--------------------------------|
| `.csv` | Comma Separated Values |
| `.dbf` | dBASE II + VFP Extensions (DBF) |
| `.dif` | Data Interchange Format (DIF) |
| `.eth` | Ethercalc Record Format (ETH) |
| `.fods` | Flat OpenDocument Spreadsheet |
| `.html` | HTML Document |
| `.numbers` | Numbers 3.0+ Spreadsheet |
| `.ods` | OpenDocument Spreadsheet |
| `.prn` | Lotus Formatted Text |
| `.rtf` | Rich Text Format (RTF) |
| `.sylk` | Symbolic Link (SYLK) |
| `.txt` | UTF-16 Unicode Text (TXT) |
| `.wk1` | Lotus Worksheet (WK1) |
| `.wk3` | Lotus Workbook (WK3) |
| `.xls` | Excel 97-2004 Workbook Format |
| `.xlsb` | Excel 2007+ Binary Format |
| `.xlsm` | Excel 2007+ Macro XML Format |
| `.xlsx` | Excel 2007+ XML Format |
## Output Type
The `type` option specifies the JS form of the output:
| `type` | output |
|----------|-----------------------------------------------------------------|
| `base64` | string: Base64 encoding of the file |
| `binary` | string: binary string (byte `n` is `data.charCodeAt(n)`) |
| `string` | string: JS string (not compatible with binary formats) |
| `buffer` | NodeJS `Buffer` or `Uint8Array` |
| `array` | `ArrayBuffer` or array of 8-bit unsigned int |
| `file` | (attempt to download a file) |
:::note pass
For compatibility with Excel, `csv` output will always include the UTF-8 byte
order mark ("BOM").
The raw [`sheet_to_csv` method](/docs/api/utilities/csv#csv-output) will return
JavaScript strings without the UTF-8 BOM.
:::
## Other Options
### Cell-Level Options
#### Dates
Plaintext files store dates using formatted strings. XLS and most Excel file
formats store dates using numeric date codes.
XLSX and ODS/FODS support both numeric date codes and ISO 8601 date strings.
By default, when both numeric date codes and date strings are supported, the
writers will export date codes. Date cells (type `d`) will be converted to
numeric cells.
If the `cellDates` option is set, writers will export date strings. If number
formats include [date tokens](/docs/csf/features/nf#dates-and-times), numeric
cells (type `n`) will be converted to date cells.
:::caution pass
Generated files are not guaranteed to work with third-party spreadsheet readers!
Third-party tools may ignore cells or reject files that use proper date cells.
:::
["Dates and Times"](/docs/csf/features/dates) covers features in more detail.
#### Text and Cell Styling {#style}
By default, SheetJS CE writers focus on data preservation.
If the `cellStyles` option is `true`, other styling metadata including
[row](/docs/csf/features/rowprops) and [column](/docs/csf/features/colprops)
will be exported.
:::tip pass
[SheetJS Pro](https://sheetjs.com/pro) offers cell / text styling, conditional
formatting and additional styling options.
:::
### Sheet-Level Options
#### Error Checking {#ec}
By default, Excel warns users when creating text cells when the text could be
parsed as a number. People and software systems commonly use text to store US
ZIP Codes, as many ZIP Codes in New Jersey start with "0".
By default, SheetJS writers add special marks to instruct Excel not to elicit
warnings on cells containing text that look like numbers.
Due to a bug in Excel, "Text to Columns" and other features may lead to crashes
in files where error conditions are ignored. If the `ignoreEC` option is
explicitly set to `false`, SheetJS writers will not add the offending marks.
### Book-Level Options
#### VBA
When exporting to file formats that support VBA (XLSX, XLSB, XLS), macros are
not guaranteed to be exported by default. The `bookVBA` option should be set
to a truthy value (`true` or `1`).
The ["VBA"](/docs/csf/features/vba) section explains the feature in more detail.
#### Workbook Properties {#props}
When exporting to a file format that supports workbook properties, the SheetJS
export codecs will look in the workbook object for file properties.
If the `Props` option is specified, the export codecs will ignore properties
from the workbook object.
The ["File Properties"](/docs/csf/features/props) section lists the supported
file properties.
### File-Level Options
#### Compression
XLSX, XLSB, NUMBERS, and ODS files use ZIP containers. The ZIP container format
supports different levels of compression. Spreadsheet software typically use
compression when exporting files.
By default, SheetJS writers optimize for speed. Exports are faster but the
generated files are larger.
If the `compression` option is set, writers will optimize for file size. Exports
are smaller but will take more time.
#### Target Sheet {#sheet}
Some output formats support multiple worksheets. By default, the SheetJS export
codecs will export all worksheets.
Other output formats, including CSV and legacy Excel and Lotus worksheet files,
only support one worksheet. By default, the SheetJS export codecs will use the
first worksheet from the workbook.
If the `sheet` option is set to the name of a valid worksheet in the workbook,
the SheetJS writers will use the named sheet even if it is not the first sheet.
#### Shared String Table (SST) {#sst}
XLSX, XLS, and XLSB can store text cells in two ways.
By default, the writers use "inline" strings. The content of each text cell is
stored in the cell representation. This approach is conceptually simple, but it
uses features that may not be supported in iOS Numbers and other apps.
If the `bookSST` option is set, writers use the "shared string table" (SST)
feature. A separate lookup table houses each text string and cells store indices
into the SST. Multiple cells can point to the same SST entry.
Exporting with SST is typically slower and more memory intensive, but the files
may be smaller and have better compatibility with third-party software.
#### Delimiter-Separated Values {#dsv}
The SheetJS CSV writer uses commas to separate fields within a row and adds line
separators between rows.
The characters used to separate fields and rows can be controlled through the
`FS` ("field separator") and `RS` ("row separator") options.
["CSV and Text"](/docs/api/utilities/csv#delimiter-separated-output) discusses
the options in more detail.
#### Code Page Encoding {#codepage}
Spreadsheet applications support a number of legacy encodings. Plaintext files
will appear different when opened in different computers in different regions.
By default, the writers use the most common "English (United States)" encodings.
The `codepage` option controls the encoding in BIFF2 - BIFF5 XLS files without
`CodePage` records, some legacy formats including DBF, and in CSV files without
BOM in `type: "binary"`.
Characters missing from the specified encoding will be replaced with underscore
characters (`_`).
The `codepage` support library is not guaranteed to be loaded by default. The
["Installation"](/docs/getting-started/installation/) section describes how to
install and load the support library.
See ["Legacy Codepages"](/docs/constellation/codepage) for more details.
#### XLSX and XLSB Theme {#theme}
By default, the SheetJS XLSX and XLSB output codecs use a predetermined theme.
This simplifies [column widths](/docs/csf/features/colprops#column-widths) and
other features that depend on font metrics.
If the `themeXLSX` option is a string, the XLSB and XLSX codecs will suppress
the default theme. The default theme `xl/theme/theme1.xml` will be overridden.
#### Writing Errors {#wtf}
If the `WTF` option is enabled, workbook writers will show warnings and errors
when workbooks use features that may be considered unsafe. For example, some
file features are only supported in specific versions of Excel.
#### NUMBERS Exports {#numbers}
<details open>
<summary><b>Exporting NUMBERS files</b> (click to show)</summary>
<summary><b>Rationale</b> (click to hide)</summary>
The NUMBERS writer requires a fairly large base. The supplementary `xlsx.zahl`
scripts provide support. `xlsx.zahl.js` is designed for standalone and NodeJS
use, while `xlsx.zahl.mjs` is suitable for ESM.
Apple Numbers has broken backwards compatibility many times over the years.
Files generated by older versions of Numbers are not guaranteed to work with
newer versions of Numbers or third-party tools.
Adding NUMBERS export support involves two steps:
The NUMBERS exporter starts from a working file and rewrites the data blocks.
This ensures exports still work
1) Load the `xlsx.zahl` script
The current design allows for updating the base file without disrupting the rest
of the library.
2) Pass the payload into the `numbers` option to `write` or `writeFile`.
</details>
The `numbers` option is required for exporting NUMBERS files. It is expected to
be a Base64 string that encodes a valid Numbers file. The supplementary
`xlsx.zahl` scripts include the required string.
<Tabs>
<TabItem value="browser" label="Browser">
<TabItem value="standalone" label="Standalone">
<p><a href={"https://cdn.sheetjs.com/xlsx-" + current + "/package/dist/xlsx.zahl.js"}>{"https://cdn.sheetjs.com/xlsx-" + current + "/package/dist/xlsx.zahl.js"}</a> is the URL for {current}</p>
<CodeBlock language="html">{`\
<meta charset="utf8">\n\
<body>\n\
<CodeBlock language="html" title="Sample HTML">{`\
<html><head><meta charset="utf8"></head><body>\n\
<script src="https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js"></script>\n\
<script src="https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.zahl.js"></script>\n\
<script>\n\
@ -176,7 +424,7 @@ var wb = XLSX.utils.book_new(); var ws = XLSX.utils.aoa_to_sheet([\n\
]); XLSX.utils.book_append_sheet(wb, ws, "Sheet1");\n\
XLSX.writeFile(wb, "textport.numbers", {numbers: XLSX_ZAHL_PAYLOAD, compression: true});\n\
</script>\n\
</body>`}
</body></html>`}
</CodeBlock>
</TabItem>
@ -250,67 +498,3 @@ XLSX.writeFile(wb, "textport.numbers", {numbers: XLSX_ZAHL_PAYLOAD, compression:
</TabItem>
</Tabs>
</details>
## Supported Output Formats
For broad compatibility with third-party tools, this library supports many
output formats. The specific file type is controlled with `bookType` option:
| `bookType` | extension | sheets | Description |
|:-----------|:-----------|:-------|:--------------------------------|
| `xlsx` | `.xlsx` | multi | Excel 2007+ XML Format |
| `xlsm` | `.xlsm` | multi | Excel 2007+ Macro XML Format |
| `xlsb` | `.xlsb` | multi | Excel 2007+ Binary Format |
| `biff8` | `.xls` | multi | Excel 97-2004 Workbook Format |
| `biff5` | `.xls` | multi | Excel 5.0/95 Workbook Format |
| `biff4` | `.xls` | single | Excel 4.0 Worksheet Format |
| `biff3` | `.xls` | single | Excel 3.0 Worksheet Format |
| `biff2` | `.xls` | single | Excel 2.0 Worksheet Format |
| `xlml` | `.xls` | multi | Excel 2003-2004 (SpreadsheetML) |
| `numbers` | `.numbers` | multi | Numbers 3.0+ Spreadsheet |
| `ods` | `.ods` | multi | OpenDocument Spreadsheet |
| `fods` | `.fods` | multi | Flat OpenDocument Spreadsheet |
| `wk3` | `.wk3` | multi | Lotus Workbook (WK3) |
| `csv` | `.csv` | single | Comma Separated Values |
| `txt` | `.txt` | single | UTF-16 Unicode Text (TXT) |
| `sylk` | `.sylk` | single | Symbolic Link (SYLK) |
| `html` | `.html` | single | HTML Document |
| `dif` | `.dif` | single | Data Interchange Format (DIF) |
| `dbf` | `.dbf` | single | dBASE II + VFP Extensions (DBF) |
| `wk1` | `.wk1` | single | Lotus Worksheet (WK1) |
| `rtf` | `.rtf` | single | Rich Text Format (RTF) |
| `prn` | `.prn` | single | Lotus Formatted Text |
| `eth` | `.eth` | single | Ethercalc Record Format (ETH) |
- `compression` applies to ZIP-based formats (XLSX, XLSM, XLSB, NUMBERS, ODS)
- Formats that only support a single sheet require a `sheet` option specifying
the worksheet. If the string is empty, the first worksheet is used.
- `writeFile` will automatically guess the output file format based on the file
extension if `bookType` is not specified. It will choose the first format in
the aforementioned table that matches the extension.
## Output Type
The `type` option specifies the JS form of the output:
| `type` | output |
|------------|-----------------------------------------------------------------|
| `"base64"` | string: Base64 encoding of the file |
| `"binary"` | string: binary string (byte `n` is `data.charCodeAt(n)`) |
| `"string"` | string: JS string (not compatible with binary formats) |
| `"buffer"` | nodejs Buffer |
| `"array"` | ArrayBuffer, fallback array of 8-bit unsigned int |
| `"file"` | string: path of file that will be created (nodejs only) |
:::note pass
For compatibility with Excel, `csv` output will always include the UTF-8 byte
order mark ("BOM").
The raw [`sheet_to_csv` method](/docs/api/utilities/csv#csv-output) will return
JavaScript strings without the UTF-8 BOM.
:::

@ -91,7 +91,7 @@ export default App;
If you are starting from scratch, create a new ViteJS + ReactJS project:
```bash
npm create vite@latest -- sheetjs-react --template react --default
npm create vite@latest -- sheetjs-react --template react --no-rolldown --no-interactive
cd sheetjs-react
npm install
npm run dev
@ -123,19 +123,19 @@ yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
2) Ensure that your component script imports `useRef` from the `react` library:
```js
```js title="src/App.tsx (add useRef import if it does not exist)"
import { useRef } from "react";
```
3) Add the following line at the top of your component script:
```js
```js title="src/App.tsx (add import to the top of your component script)"
import { utils, writeFileXLSX } from "xlsx";
```
4) Create a ref in the body of your function component:
```jsx
```jsx title="src/App.tsx (add highlighted line)"
function App() {
// highlight-next-line
const tbl = useRef(null);
@ -144,7 +144,7 @@ function App() {
5) Attach the ref to the table element:
```jsx
```jsx title="src/App.tsx (add ref attribute to TABLE element)"
function App() {
// ...
return (
@ -156,7 +156,7 @@ function App() {
6) Add a button with a click handler that will export table data to XLSX:
```jsx
```jsx title="src/App.tsx (add button element to JSX)"
function App() {
// ...
return (
@ -172,6 +172,9 @@ function App() {
{/*...*/}
```
7) Start the development server (typically `npm run dev`) and open the app.
Click the "Export XLSX" button and open the generated file.
</TabItem>
</Tabs>

Binary file not shown.

@ -15,7 +15,7 @@ curl -LO https://docs.sheetjs.com/stream/SheetJSNodeJStream.js
curl -LO https://docs.sheetjs.com/pres.xlsx
# this version uses `nvm` to cycle through node versions
for n in 0.12 4 6 8 10 12 14 16 18 20 22; do
for n in 0.12 4 6 8 10 12 14 16 18 20 22 24; do
#sudo n $n
nvm use $n
node --version

@ -40,7 +40,7 @@ app.listen(7262, async() => {
});
EOF
for n in 0.{9..25}; do
for n in 0.{9..27}; do
npx -y esbuild@$n --version
## Browser Test

@ -87,7 +87,8 @@ npm i --save puppeteer express@4
node -e 'var pjson = JSON.parse(fs.readFileSync("./package.json")); console.log(pjson); delete pjson.main; fs.writeFileSync("package.json", JSON.stringify(pjson))'
for n in 2.14.4 1.12.3; do
### NOTE: there were semver issues with 1.12.4
for n in 2.16.1 1.12.3; do
npm i --save parcel@$n
npx -y parcel@$n build index.html
node test.js

@ -86,7 +86,7 @@ app.listen(7262, async() => {
await new Promise((res,rej) => setTimeout(res, 1000));
await page.click("#xport");
await new Promise((res,rej) => setTimeout(res, 1000));
await new Promise((res,rej) => setTimeout(res, 2000));
await browser.close();
process.exit();
});

119
tests/bundler/rollup.sh Executable file

@ -0,0 +1,119 @@
#!/bin/bash
# https://docs.sheetjs.com/docs/demos/frontend/bundler/rollup
cd /tmp
rm -rf sheetjs-rollup
mkdir sheetjs-rollup
cd sheetjs-rollup
npm init -y
npm i --save https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz
cat >index.js <<EOF
import { utils, version, writeFileXLSX } from 'xlsx';
document.getElementById("xport").addEventListener("click", async() => {
/* fetch JSON data and parse */
const url = "https://docs.sheetjs.com/executive.json";
const raw_data = await (await fetch(url)).json();
/* filter for the Presidents */
const prez = raw_data.filter(row => row.terms.some(term => term.type === "prez"));
/* sort by first presidential term */
prez.forEach(row => row.start = row.terms.find(term => term.type === "prez").start);
prez.sort((l,r) => l.start.localeCompare(r.start));
/* flatten objects */
const rows = prez.map(row => ({
name: row.name.first + " " + row.name.last,
birthday: row.bio.birthday
}));
/* generate worksheet and workbook */
const worksheet = utils.json_to_sheet(rows);
const workbook = utils.book_new();
utils.book_append_sheet(workbook, worksheet, "Dates");
/* fix headers */
utils.sheet_add_aoa(worksheet, [["Name", "Birthday"]], { origin: "A1" });
/* calculate column width */
const max_width = rows.reduce((w, r) => Math.max(w, r.name.length), 10);
worksheet["!cols"] = [ { wch: max_width } ];
/* create an XLSX file and try to save to Presidents.xlsx */
writeFileXLSX(workbook, "Presidents.xlsx");
});
EOF
cat >index.html <<EOF
<!DOCTYPE html>
<html lang="en">
<head></head>
<body>
<h1>SheetJS Presidents Demo</h1>
<button id="xport">Click here to export</button>
<script type="module" src="./bundle.js"></script>
</body>
</html>
EOF
npm i --save puppeteer express@4
cat >test.js <<EOF
const puppeteer = require('puppeteer');
const express = require('express');
const app = express();
app.use(express.static('./'));
app.listen(7262, async() => {
await new Promise((res,rej) => setTimeout(res, 1000));
const browser = await puppeteer.launch();
const page = await browser.newPage();
page.on("console", msg => console.log("PAGE LOG:", msg.text()));
await page.setViewport({width: 1920, height: 1080});
const client = await page.target().createCDPSession();
await client.send('Browser.setDownloadBehavior', {
behavior: 'allow',
downloadPath: require("path").resolve('./')
});
await page.goto('http://localhost:7262/');
await page.click("#xport");
await new Promise((res,rej) => setTimeout(res, 1000));
await browser.close();
process.exit();
});
EOF
## RollupJS 4.x
npm i --save rollup@4.x @rollup/plugin-node-resolve
npx -y rollup@4.x index.js --plugin @rollup/plugin-node-resolve --file bundle.js --format iife
node test.js
npx -y xlsx-cli Presidents.xlsx | head -n 3
rm -f bundle.js Presidents.xlsx
## RollupJS 3.x
npm i --save rollup@3.x @rollup/plugin-node-resolve
npx -y rollup@3.x index.js --plugin @rollup/plugin-node-resolve --file bundle.js --format iife
node test.js
npx -y xlsx-cli Presidents.xlsx | head -n 3
rm -f bundle.js Presidents.xlsx
## RollupJS 2.x
npm i --save rollup@2.x @rollup/plugin-node-resolve
npx -y rollup@2.x index.js --plugin @rollup/plugin-node-resolve --file bundle.js --format iife
node test.js
npx -y xlsx-cli Presidents.xlsx | head -n 3
rm -f bundle.js Presidents.xlsx
## RollupJS 1.x
npm i --save rollup@1.x rollup-plugin-node-resolve
npx -y rollup@1.x index.js --plugin rollup-plugin-node-resolve --file bundle.js --format iife
node test.js
npx -y xlsx-cli Presidents.xlsx | head -n 3
rm -f bundle.js Presidents.xlsx

95
tests/bundler/rspack.sh Executable file

@ -0,0 +1,95 @@
#!/bin/bash
# https://docs.sheetjs.com/docs/demos/frontend/bundler/rspack
cd /tmp
rm -rf sheetjs-rspack
mkdir sheetjs-rspack
cd sheetjs-rspack
npm init -y
sed -i.bak 's#"type": "commonjs"#"sheet": "js"#g' package.json
npm i --save https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz @rspack/core @rspack/cli
mkdir src
cat >src/index.js <<EOF
import { utils, writeFileXLSX } from 'xlsx';
document.getElementById("xport").addEventListener("click", async() => {
/* fetch JSON data and parse */
const url = "https://docs.sheetjs.com/executive.json";
const raw_data = await (await fetch(url)).json();
/* filter for the Presidents */
const prez = raw_data.filter(row => row.terms.some(term => term.type === "prez"));
/* sort by first presidential term */
prez.forEach(row => row.start = row.terms.find(term => term.type === "prez").start);
prez.sort((l,r) => l.start.localeCompare(r.start));
/* flatten objects */
const rows = prez.map(row => ({
name: row.name.first + " " + row.name.last,
birthday: row.bio.birthday
}));
/* generate worksheet and workbook */
const worksheet = utils.json_to_sheet(rows);
const workbook = utils.book_new();
utils.book_append_sheet(workbook, worksheet, "Dates");
/* fix headers */
utils.sheet_add_aoa(worksheet, [["Name", "Birthday"]], { origin: "A1" });
/* calculate column width */
const max_width = rows.reduce((w, r) => Math.max(w, r.name.length), 10);
worksheet["!cols"] = [ { wch: max_width } ];
/* create an XLSX file and try to save to Presidents.xlsx */
writeFileXLSX(workbook, "Presidents.xlsx");
});
EOF
npx -p @rspack/cli rspack build
cat >index.html <<EOF
<!DOCTYPE html>
<html lang="en">
<head></head>
<body>
<h1>SheetJS Presidents Demo</h1>
<button id="xport">Click here to export</button>
<script src="dist/main.js"></script>
</body>
</html>
EOF
npm i --save puppeteer express
cat >test.js <<EOF
const puppeteer = require('puppeteer');
const express = require('express');
const app = express();
app.use(express.static('./'));
app.listen(7262, async() => {
await new Promise((res,rej) => setTimeout(res, 1000));
const browser = await puppeteer.launch();
const page = await browser.newPage();
page.on("console", msg => console.log("PAGE LOG:", msg.text()));
await page.setViewport({width: 1920, height: 1080});
const client = await page.target().createCDPSession();
await client.send('Browser.setDownloadBehavior', {
behavior: 'allow',
downloadPath: require("path").resolve('./')
});
page.on('request', req => console.log(req.url()));
await page.goto('http://localhost:7262/');
await page.click("#xport");
await new Promise((res,rej) => setTimeout(res, 1000));
await browser.close();
process.exit();
});
EOF
node test.js
npx -y xlsx-cli Presidents.xlsx | head -n 3

92
tests/bundler/snowpack.sh Executable file

@ -0,0 +1,92 @@
#!/bin/bash
# https://docs.sheetjs.com/docs/demos/frontend/bundler/
cd /tmp
rm -rf sheetjs-snowpack
mkdir sheetjs-snowpack
cd sheetjs-snowpack
npm init -y
npm i --save https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz
cat >index.js <<EOF
import { utils, version, writeFileXLSX } from 'xlsx';
document.getElementById("xport").addEventListener("click", async() => {
/* fetch JSON data and parse */
const url = "https://docs.sheetjs.com/executive.json";
const raw_data = await (await fetch(url)).json();
/* filter for the Presidents */
const prez = raw_data.filter(row => row.terms.some(term => term.type === "prez"));
/* sort by first presidential term */
prez.forEach(row => row.start = row.terms.find(term => term.type === "prez").start);
prez.sort((l,r) => l.start.localeCompare(r.start));
/* flatten objects */
const rows = prez.map(row => ({
name: row.name.first + " " + row.name.last,
birthday: row.bio.birthday
}));
/* generate worksheet and workbook */
const worksheet = utils.json_to_sheet(rows);
const workbook = utils.book_new();
utils.book_append_sheet(workbook, worksheet, "Dates");
/* fix headers */
utils.sheet_add_aoa(worksheet, [["Name", "Birthday"]], { origin: "A1" });
/* calculate column width */
const max_width = rows.reduce((w, r) => Math.max(w, r.name.length), 10);
worksheet["!cols"] = [ { wch: max_width } ];
/* create an XLSX file and try to save to Presidents.xlsx */
writeFileXLSX(workbook, "Presidents.xlsx");
});
EOF
cat >index.html <<EOF
<!DOCTYPE html>
<html lang="en">
<head></head>
<body>
<h1>SheetJS Presidents Demo</h1>
<button id="xport">Click here to export</button>
<script type="module" src="./index.js"></script>
</body>
</html>
EOF
npx -y snowpack@3.8.8 build
npm i --save puppeteer express@4
cat >test.js <<EOF
const puppeteer = require('puppeteer');
const express = require('express');
const app = express();
app.use(express.static('./build'));
app.listen(7262, async() => {
await new Promise((res,rej) => setTimeout(res, 1000));
const browser = await puppeteer.launch();
const page = await browser.newPage();
page.on("console", msg => console.log("PAGE LOG:", msg.text()));
await page.setViewport({width: 1920, height: 1080});
const client = await page.target().createCDPSession();
await client.send('Browser.setDownloadBehavior', {
behavior: 'allow',
downloadPath: require("path").resolve('./')
});
await page.goto('http://localhost:7262/');
await page.click("#xport");
await new Promise((res,rej) => setTimeout(res, 1000));
await browser.close();
process.exit();
});
EOF
node test.js
npx -y xlsx-cli Presidents.xlsx | head -n 3
cd -

63
tests/bundler/systemjs.sh Executable file

@ -0,0 +1,63 @@
#!/bin/bash
# https://docs.sheetjs.com/docs/demos/frontend/bundler/systemjs
cd /tmp
rm -rf sheetjs-systemjs
mkdir sheetjs-systemjs
cd sheetjs-systemjs
npm init -y
## NodeJS Demo
npm i --save https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz
curl -LO https://docs.sheetjs.com/systemjs/SheetJSystem.js
for v in 6.x 0.21.6 0.20.19 0.19.47; do
echo "SystemJS NodeJS version $v"
npm i --save systemjs@$v
node SheetJSystem.js
npx -y xlsx-cli Presidents.xlsx | head -n 3
rm -f Presidents.xlsx
done
## Browser Demo
curl -LO https://docs.sheetjs.com/pres.xlsx
npm i --save puppeteer
cat >test.js << 'EOF'
const path = require('path');
const puppeteer = require('puppeteer');
(async() => {
await new Promise((res,rej) => setTimeout(res, 1000));
const browser = await puppeteer.launch();
const page = await browser.newPage();
page.on("console", msg => console.log("PAGE LOG:", msg.text()));
await page.setViewport({width: 1920, height: 1080});
const client = await page.target().createCDPSession();
await client.send('Browser.setDownloadBehavior', {
behavior: 'allow',
downloadPath: require("path").resolve('./')
});
await page.goto('https://docs.sheetjs.com/systemjs/systemjs');
const xlf = await page.$('#xlf');
await xlf.uploadFile(path.resolve('./pres.xlsx'));
await page.evaluate(() => {
const xlf = document.querySelector('#xlf');
const evt = new Event('change', { bubbles: true });
xlf.dispatchEvent(evt);
});
await new Promise((res,rej) => setTimeout(res, 1000));
const text = await page.$eval('#out', el => el.innerText);
console.log(text);
await browser.close();
process.exit();
})();
EOF
node test.js

@ -6,8 +6,8 @@ rm -rf sheetjs-vite-tests
mkdir sheetjs-vite-tests
cd sheetjs-vite-tests
for n in {3..6}; do
npm create -y vite@$n sheetjs-vite$n -- --template vue-ts
for n in {3..7}; do
npm create -y vite@$n sheetjs-vite$n -- --template vue-ts --no-rolldown --no-interactive
cd sheetjs-vite$n
npm i
npm i --save puppeteer express@4
@ -93,7 +93,7 @@ app.listen(7262, async() => {
page.on('request', req => console.log(req.url()));
await page.goto('http://localhost:7262/');
await page.click("button");
await new Promise((res,rej) => setTimeout(res, 1000));
await new Promise((res,rej) => setTimeout(res, 2000));
await browser.close();
process.exit();
});

136
tests/bundler/webpack.sh Executable file

@ -0,0 +1,136 @@
#!/bin/bash
# https://docs.sheetjs.com/docs/demos/frontend/bundler/webpack
cd /tmp
rm -rf sheetjs-webpack
mkdir sheetjs-webpack
cd sheetjs-webpack
npm init -y
npm i --save https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz
cat >index.js <<EOF
import { utils, version, writeFileXLSX } from 'xlsx/dist/xlsx.full.min.js';
document.getElementById("xport").addEventListener("click", function() {
/* fetch JSON data and parse */
var url = "https://docs.sheetjs.com/executive.json";
fetch(url).then(function(res) { return res.json(); }).then(function(raw_data) {
/* filter for the Presidents */
var prez = raw_data.filter(function(row) { return row.terms.some(function(term) { return term.type === "prez"; }); });
/* sort by first presidential term */
prez.forEach(function(row) {
row.start = row.terms.find(function(term) {
return term.type === "prez";
}).start;
});
prez.sort(function(l,r) { return l.start.localeCompare(r.start); });
/* flatten objects */
var rows = prez.map(function(row) { return {
name: row.name.first + " " + row.name.last,
birthday: row.bio.birthday
}; });
/* generate worksheet and workbook */
var worksheet = utils.json_to_sheet(rows);
var workbook = utils.book_new();
utils.book_append_sheet(workbook, worksheet, "Dates");
/* fix headers */
utils.sheet_add_aoa(worksheet, [["Name", "Birthday"]], { origin: "A1" });
/* calculate column width */
var max_width = rows.reduce(function(w, r) { return Math.max(w, r.name.length); }, 10);
worksheet["!cols"] = [ { wch: max_width } ];
/* create an XLSX file and try to save to Presidents.xlsx */
writeFileXLSX(workbook, "Presidents.xlsx");
});
});
EOF
cat >webpack.config.js <<EOF
module.exports = {
/* entry point index.js */
entry: './index.js',
/* write to index.min.js */
output: { path:__dirname, filename: './index.min.js' }
}
EOF
cat >index.html <<EOF
<!DOCTYPE html>
<html lang="en">
<head></head>
<body>
<h1>SheetJS Presidents Demo</h1>
<button id="xport">Click here to export</button>
<script src="./index.min.js"></script>
</body>
</html>
EOF
npm i --save puppeteer express@4
cat >test.js <<EOF
const puppeteer = require('puppeteer');
const express = require('express');
const app = express();
app.use(express.static('./'));
app.listen(7262, async() => {
await new Promise((res,rej) => setTimeout(res, 1000));
const browser = await puppeteer.launch();
const page = await browser.newPage();
page.on("console", msg => console.log("PAGE LOG:", msg.text()));
await page.setViewport({width: 1920, height: 1080});
const client = await page.target().createCDPSession();
await client.send('Browser.setDownloadBehavior', {
behavior: 'allow',
downloadPath: require("path").resolve('./')
});
await page.goto('http://localhost:7262/');
await page.click("#xport");
await new Promise((res,rej) => setTimeout(res, 1000));
await browser.close();
process.exit();
});
EOF
## Webpack 2.x
npx -y webpack@2.x -p
node test.js
npx -y xlsx-cli Presidents.xlsx | head -n 3
rm -f index.min.js Presidents.xlsx
## Webpack 3.x
npx -y webpack@3.x -p
node test.js
npx -y xlsx-cli Presidents.xlsx | head -n 3
rm -f index.min.js Presidents.xlsx
## Webpack 4.x and 5.x do not need to import the standalone build
sed -i.bak 's#/dist/xlsx.full.min.js##g' index.js
## Webpack 4.x
npm i --save webpack@4.x webpack-cli@4.x
npx -y webpack --mode=production
node test.js
npx -y xlsx-cli Presidents.xlsx | head -n 3
rm -f index.min.js Presidents.xlsx
## Webpack 5.x
sed -i.bak 's#"type": "commonjs"#"sheet": "js"#g' package.json
npm i --save webpack@5.x webpack-cli@5.x
npx -y webpack --mode=production
node test.js
npx -y xlsx-cli Presidents.xlsx | head -n 3
rm -f index.min.js Presidents.xlsx

92
tests/bundler/wmr.sh Executable file

@ -0,0 +1,92 @@
#!/bin/bash
# https://docs.sheetjs.com/docs/demos/frontend/bundler/
cd /tmp
rm -rf sheetjs-wmr
mkdir sheetjs-wmr
cd sheetjs-wmr
npm init -y
npm i --save https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz
cat >index.js <<EOF
import { utils, version, writeFileXLSX } from 'xlsx';
document.getElementById("xport").addEventListener("click", async() => {
/* fetch JSON data and parse */
const url = "https://docs.sheetjs.com/executive.json";
const raw_data = await (await fetch(url)).json();
/* filter for the Presidents */
const prez = raw_data.filter(row => row.terms.some(term => term.type === "prez"));
/* sort by first presidential term */
prez.forEach(row => row.start = row.terms.find(term => term.type === "prez").start);
prez.sort((l,r) => l.start.localeCompare(r.start));
/* flatten objects */
const rows = prez.map(row => ({
name: row.name.first + " " + row.name.last,
birthday: row.bio.birthday
}));
/* generate worksheet and workbook */
const worksheet = utils.json_to_sheet(rows);
const workbook = utils.book_new();
utils.book_append_sheet(workbook, worksheet, "Dates");
/* fix headers */
utils.sheet_add_aoa(worksheet, [["Name", "Birthday"]], { origin: "A1" });
/* calculate column width */
const max_width = rows.reduce((w, r) => Math.max(w, r.name.length), 10);
worksheet["!cols"] = [ { wch: max_width } ];
/* create an XLSX file and try to save to Presidents.xlsx */
writeFileXLSX(workbook, "Presidents.xlsx");
});
EOF
cat >index.html <<EOF
<!DOCTYPE html>
<html lang="en">
<head></head>
<body>
<h1>SheetJS Presidents Demo</h1>
<button id="xport">Click here to export</button>
<script type="module" src="./index.js"></script>
</body>
</html>
EOF
npx -y wmr@3.8.0 build
npm i --save puppeteer express@4
cat >test.js <<EOF
const puppeteer = require('puppeteer');
const express = require('express');
const app = express();
app.use(express.static('./dist'));
app.listen(7262, async() => {
await new Promise((res,rej) => setTimeout(res, 1000));
const browser = await puppeteer.launch();
const page = await browser.newPage();
page.on("console", msg => console.log("PAGE LOG:", msg.text()));
await page.setViewport({width: 1920, height: 1080});
const client = await page.target().createCDPSession();
await client.send('Browser.setDownloadBehavior', {
behavior: 'allow',
downloadPath: require("path").resolve('./')
});
await page.goto('http://localhost:7262/');
await page.click("#xport");
await new Promise((res,rej) => setTimeout(res, 1000));
await browser.close();
process.exit();
});
EOF
node test.js
npx -y xlsx-cli Presidents.xlsx | head -n 3
cd -

127
tests/frontend/svelte.sh Executable file

@ -0,0 +1,127 @@
#!/bin/bash
# https://docs.sheetjs.com/docs/demos/frontend/svelte
# NOTE: the svelte scripts explicitly attach id="xport" to the buttons
cd /tmp
rm -rf sheetjs-fe-svelte
mkdir sheetjs-fe-svelte
cd sheetjs-fe-svelte
rm -rf sheetjs-svelte
npm create -y vite@latest sheetjs-svelte -- --template svelte-ts --no-rolldown --no-interactive
cd sheetjs-svelte
npm i
npm i --save https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz
npm i --save puppeteer express@4
cat >test.cjs <<EOF
const puppeteer = require('puppeteer');
const express = require('express');
const app = express();
app.use(express.static('./dist/'));
app.listen(7262, async() => {
await new Promise((res,rej) => setTimeout(res, 1000));
const browser = await puppeteer.launch();
const page = await browser.newPage();
page.on("console", msg => console.log("PAGE LOG:", msg.text()));
await page.setViewport({width: 1920, height: 1080});
const client = await page.target().createCDPSession();
await client.send('Browser.setDownloadBehavior', {
behavior: 'allow',
downloadPath: require("path").resolve('./')
});
page.on('request', req => console.log(req.url()));
await page.goto('http://localhost:7262/');
await new Promise((res,rej) => setTimeout(res, 1000));
await page.click("#xport");
await new Promise((res,rej) => setTimeout(res, 1000));
await browser.close();
process.exit();
});
EOF
# Array of Objects
cat >src/App.svelte <<EOF
<script>
import { onMount } from 'svelte';
import { read, utils, writeFileXLSX } from 'xlsx';
/* the component state is an array of presidents */
let pres = [];
/* Fetch and update the state once */
onMount(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
pres = utils.sheet_to_json(ws); // generate objects and update state
});
/* get state data and export to XLSX */
function exportFile() {
const ws = utils.json_to_sheet(pres);
const wb = utils.book_new();
utils.book_append_sheet(wb, ws, "Data");
writeFileXLSX(wb, "SheetJSSvelteAoO.xlsx");
}
</script>
<main>
<table><thead><tr><th>Name</th><th>Index</th></tr></thead><tbody>
{#each pres as p}<tr>
<td>{p.Name}</td>
<td>{p.Index}</td>
</tr>{/each}
</tbody><tfoot><tr><td colSpan={2}>
<button id="xport" on:click={exportFile}>Export XLSX</button>
</td></tr></tfoot></table>
</main>
EOF
npm run build
node test.cjs
npx -y xlsx-cli SheetJSSvelteAoO.xlsx
# HTML
cat >src/App.svelte <<EOF
<script>
import { onMount } from 'svelte';
import { read, utils, writeFileXLSX } from 'xlsx';
let html = "";
let tbl;
/* Fetch and update the state once */
onMount(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
// highlight-start
html = utils.sheet_to_html(ws); // generate HTML and update state
// highlight-end
});
/* get state data and export to XLSX */
function exportFile() {
// highlight-start
const elt = tbl.getElementsByTagName("TABLE")[0];
const wb = utils.table_to_book(elt);
// highlight-end
writeFileXLSX(wb, "SheetJSSvelteHTML.xlsx");
}
</script>
<main>
<button id="xport" on:click={exportFile}>Export XLSX</button>
<!-- highlight-start -->
<div bind:this={tbl}>{@html html}</div>
<!-- highlight-end -->
</main>
EOF
npm run build
node test.cjs
npx -y xlsx-cli SheetJSSvelteHTML.xlsx

22
tests/math/tfjs-node.sh Executable file

@ -0,0 +1,22 @@
#!/bin/bash
# https://docs.sheetjs.com/docs/demos/math/tensorflow#nodejs-demo
. ~/.bashrc ## This is apparently needed in macos
cd /tmp
rm -rf sheetjs-tfjs-csv
mkdir sheetjs-tfjs-csv
cd sheetjs-tfjs-csv
npm init -y
npm i --save https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz @tensorflow/tfjs @tensorflow/tfjs-node
curl -LO https://docs.sheetjs.com/tfjs/SheetJSTF.js
# this version uses `nvm` to cycle through node versions
for n in 20 22 24; do
#sudo n $n
nvm use $n
node --version
node SheetJSTF.js
done

@ -6,8 +6,8 @@ rm -rf sheetjs-vite-static
mkdir sheetjs-vite-static
cd sheetjs-vite-static
for n in {2..6}; do
npm create -y vite@$n sheetjs-vite-$n -- --template vue-ts
for n in {2..7}; do
npm create -y vite@$n sheetjs-vite-$n -- --template vue-ts --no-rolldown --no-interactive
cd sheetjs-vite-$n
npm i
npm i --save https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz