This commit is contained in:
SheetJS 2023-12-04 22:46:54 -05:00
parent c76ec7f27d
commit e3d16d8108
69 changed files with 1958 additions and 516 deletions

@ -216,7 +216,7 @@ in the example JSX code:
```jsx title="Example JSX for displaying arrays of objects"
<table>
{/* The `thead` section includes the table header row */}
<thead><th>Name</th><th>Index</th></thead>
<thead><tr><th>Name</th><th>Index</th></tr></thead>
{/* The `tbody` section includes the data rows */}
<tbody>
{/* generate row (TR) for each president */}
@ -302,7 +302,7 @@ export default function SheetJSReactAoO() {
writeFileXLSX(wb, "SheetJSReactAoO.xlsx");
}, [pres]);
return (<table><thead><th>Name</th><th>Index</th></thead><tbody>
return (<table><thead><tr><th>Name</th><th>Index</th></tr></thead><tbody>
{ /* generate row for each president */
// highlight-start
pres.map(pres => (<tr>
@ -317,14 +317,18 @@ export default function SheetJSReactAoO() {
}
```
<details open><summary><b>How to run the example</b> (click to show)</summary>
<details open><summary><b>How to run the example</b> (click to hide)</summary>
<Tabs groupId="starter">
<TabItem name="vite" value="ViteJS">
:::note
:::note Tested Deployments
This demo was last tested on 2023 October 08 with ViteJS 4.4.1 and React 18.2.0
This demo was tested in the following environments:
| ReactJS | ViteJS | Date |
|:---------|:--------|:-----------|
| `18.2.0` | `5.0.5` | 2023-12-04 |
:::
@ -348,7 +352,7 @@ npm run dev`}
4) Replace `src/App.jsx` with the `src/SheetJSReactAoO.js` example.
The page will refresh and show a table with an Export button. Click the button
and the page will attempt to download `SheetJSReactAoA.xlsx`.
and the page will attempt to download `SheetJSReactAoO.xlsx`.
5) Build the site:
@ -370,7 +374,7 @@ and test the page.
</TabItem>
<TabItem name="CRA" value="create-react-app">
:::note
:::note Tested Deployments
This demo was last run on 2023 October 24 using `create-react-app@5.0.1` and
`react-scripts@5.0.1`.
@ -397,7 +401,7 @@ npm start`}
4) Replace `src/App.js` with the `src/SheetJSReactAoO.js` example.
The page will refresh and show a table with an Export button. Click the button
and the page will attempt to download `SheetJSReactAoA.xlsx`.
and the page will attempt to download `SheetJSReactAoO.xlsx`.
5) Build the site:
@ -474,12 +478,12 @@ export default function SheetJSReactHTML() {
}
```
<details open><summary><b>How to run the example</b> (click to show)</summary>
<details open><summary><b>How to run the example</b> (click to hide)</summary>
<Tabs groupId="starter">
<TabItem name="vite" value="ViteJS">
:::note
:::note Tested Deployments
This demo was last tested on 2023 October 08 with ViteJS 4.4.1 and React 18.2.0
@ -527,7 +531,7 @@ and test the page.
</TabItem>
<TabItem name="CRA" value="create-react-app">
:::note
:::note Tested Deployments
This demo was last run on 2023 October 24 using `create-react-app@5.0.1` and
`react-scripts@5.0.1`.

@ -241,7 +241,7 @@ const rows = ref([]);
<template>
<table>
<!-- The `thead` section includes the table header row -->
<thead><th>Name</th><th>Index</th></thead>
<thead><tr><th>Name</th><th>Index</th></tr></thead>
<!-- The `tbody` section includes the data rows -->
<tbody>
<!-- generate row (TR) for each president -->
@ -334,7 +334,7 @@ function exportFile() {
</script>
<template>
<table><thead><th>Name</th><th>Index</th></thead><tbody>
<table><thead><tr><th>Name</th><th>Index</th></tr></thead><tbody>
<tr v-for="(row, idx) in rows" :key="idx">
<td>{{ row.Name }}</td>
<td>{{ row.Index }}</td>
@ -352,8 +352,11 @@ function exportFile() {
:::note Tested Deployments
This demo was last run on 2023 November 09 using `vue@3.3.8`. When running
`npm init`, the package `create-vue@3.8.0` was installed.
This demo was tested in the following environments:
| VueJS | ViteJS | Date |
|:--------|:--------|:-----------|
| `3.3.9` | `4.5.1` | 2023-12-04 |
:::
@ -377,7 +380,7 @@ npm run dev`}
4) Replace `src/App.vue` with the `src/SheetJSVueAoO.vue` example.
The page will refresh and show a table with an Export button. Click the button
and the page will attempt to download `SheetJSVueAoA.xlsx`. There may be a delay
and the page will attempt to download `SheetJSVueAoO.xlsx`. There may be a delay
since Vite will try to optimize the SheetJS library on the fly.
5) Stop the dev server and build the site:
@ -427,7 +430,7 @@ npm run dev`}
4) Replace `App.vue` with the `src/SheetJSVueAoO.vue` example.
The page will refresh and show a table with an Export button. Click the button
and the page will attempt to download `SheetJSVueAoA.xlsx`.
and the page will attempt to download `SheetJSVueAoO.xlsx`.
5) Stop the dev server and build the site:

@ -219,7 +219,7 @@ storied syntax, instead opting for a `@for` block reminiscent of JavaScript[^4].
```html title="Example Template for displaying arrays of objects (Angular 2-16)"
<div class="content" role="main"><table>
<thead><th>Name</th><th>Index</th></thead>
<thead><tr><th>Name</th><th>Index</th></tr></thead>
<tbody>
// highlight-start
<tr *ngFor="let row of rows">
@ -236,7 +236,7 @@ storied syntax, instead opting for a `@for` block reminiscent of JavaScript[^4].
```html title="Example Template for displaying arrays of objects (Angular 17+)"
<div class="content" role="main"><table>
<thead><th>Name</th><th>Index</th></thead>
<thead><tr><th>Name</th><th>Index</th></tr></thead>
<tbody>
// highlight-start
@for(row of rows; track $index) { <tr>
@ -311,7 +311,7 @@ interface President { Name: string; Index: number };
selector: 'app-root',
template: `
<div class="content" role="main"><table>
<thead><th>Name</th><th>Index</th></thead>
<thead><tr><th>Name</th><th>Index</th></tr></thead>
<tbody>
// highlight-start
<tr *ngFor="let row of rows">
@ -367,7 +367,7 @@ interface President { Name: string; Index: number };
standalone: true,
template: `
<div class="content" role="main"><table>
<thead><th>Name</th><th>Index</th></thead>
<thead><tr><th>Name</th><th>Index</th></tr></thead>
<tbody>
// highlight-start
@for(row of rows; track $index) {
@ -414,11 +414,16 @@ export class AppComponent {
</TabItem>
</Tabs>
<details open><summary><b>How to run the example</b> (click to show)</summary>
<details open><summary><b>How to run the example</b> (click to hide)</summary>
:::note Tested Deployments
This demo was last run on 2023-11-18 using Angular 17.0.3 and CLI `17.0.1`
This demo was tested in the following environments:
| Angular | Date |
|:---------|:-----------|
| `17.0.5` | 2023-12-04 |
| `16.2.7` | 2023-10-22 |
:::
@ -431,7 +436,7 @@ npx @angular/cli analytics disable -g
1) Create a new project:
```bash
npx @angular/cli@17.0.1 new --minimal --defaults --no-interactive sheetjs-angular
npx @angular/cli@17.0.5 new --minimal --defaults --no-interactive sheetjs-angular
```
2) Install the SheetJS dependency and start the dev server:
@ -590,7 +595,7 @@ export class AppComponent {
</TabItem>
</Tabs>
<details open><summary><b>How to run the example</b> (click to show)</summary>
<details open><summary><b>How to run the example</b> (click to hide)</summary>
:::note Tested Deployments

@ -115,7 +115,7 @@ function exportFile() {
</script>
<main>
<table><thead><th>Name</th><th>Index</th></thead><tbody>
<table><thead><tr><th>Name</th><th>Index</th></tr></thead><tbody>
<!-- highlight-start -->
{#each pres as p}<tr>
<td>{p.Name}</td>
@ -128,16 +128,23 @@ function exportFile() {
</main>
```
<details open><summary><b>How to run the example</b> (click to show)</summary>
<details open><summary><b>How to run the example</b> (click to hide)</summary>
:::note
:::note Tested Deployments
This demo was last run on 2023 August 27 using `svelte@4.2.0`. When running
`npm create`, the package `create-vite@4.4.1` was installed.
This demo was tested in the following environments:
| Svelte | ViteJS | Date |
|:--------|:--------|:-----------|
| `4.2.8` | `5.0.5` | 2023-12-04 |
:::
1) Run `npm create vite@latest sheetjs-svelte -- --template svelte-ts`.
1) Create a new project:
```bash
npm create vite@latest sheetjs-svelte -- --template svelte-ts
```
2) Install the SheetJS dependency and start the dev server:
@ -156,8 +163,22 @@ The page will refresh and show a table with an Export button. Click the button
and the page will attempt to download `SheetJSSvelteAoA.xlsx`. There may be a
delay since Vite will try to optimize the SheetJS library on the fly.
5) Build the site with `npm run build`, then test with `npx http-server dist`.
Access `http://localhost:8080` with a web browser to test the bundled site.
5) Build the site:
```bash
npm run build
```
The generated site will be placed in the `dist` folder.
6) Start a local web server:
```bash
npx http-server dist
```
Access the displayed URL (typically `http://localhost:8080`) with a web browser
and test the page.
</details>
@ -206,16 +227,23 @@ function exportFile() {
</main>
```
<details open><summary><b>How to run the example</b> (click to show)</summary>
<details open><summary><b>How to run the example</b> (click to hide)</summary>
:::note
:::note Tested Deployments
This demo was last run on 2023 August 27 using `svelte@4.2.0`. When running
`npm create`, the package `create-vite@4.4.1` was installed.
This demo was tested in the following environments:
| Svelte | ViteJS | Date |
|:--------|:--------|:-----------|
| `4.2.8` | `5.0.5` | 2023-12-04 |
:::
1) Run `npm create vite@latest sheetjs-svelte -- --template svelte-ts`.
1) Create a new project:
```bash
npm create vite@latest sheetjs-svelte -- --template svelte-ts
```
2) Install the SheetJS dependency and start the dev server:
@ -234,7 +262,21 @@ The page will refresh and show a table with an Export button. Click the button
and the page will attempt to download `SheetJSSvelteHTML.xlsx`. There may be a
delay since Vite will try to optimize the SheetJS library on the fly.
5) Build the site with `npm run build`, then test with `npx http-server dist`.
Access `http://localhost:8080` with a web browser to test the bundled site.
5) Build the site:
```bash
npm run build
```
The generated site will be placed in the `dist` folder.
6) Start a local web server:
```bash
npx http-server dist
```
Access the displayed URL (typically `http://localhost:8080`) with a web browser
and test the page.
</details>

@ -32,7 +32,12 @@ models and data flow strategies.
:::note Tested Deployments
This demo was last run on 2023 November 19 using AngularJS `1.8.2`
This demo was tested in the following environments:
| Version | Date |
|:------------------|:-----------|
| `1.8.2` (latest) | 2023-12-04 |
| `1.2.32` (legacy) | 2023-12-04 |
:::
@ -294,7 +299,7 @@ However, this does not handle merge cells well!
The `sheet_to_html` function generates HTML that is aware of merges and other
worksheet features. The generated HTML does not contain any `<script>` tags,
and should therefore be safe to pass to an `ng-bind-html` binding. This approach
requires the `ngSanitize` plugin.
requires the `ngSanitize` plugin[^4].
```html
<div ng-controller="sheetjs">
@ -381,4 +386,5 @@ URL with a web browser (typically `http://localhost:8080`)
[^1]: See [`$http`](https://docs.angularjs.org/api/ng/service/$http) in the AngularJS documentation.
[^2]: See ["Workbook Object"](/docs/csf/book)
[^3]: See ["Creating Directives"](https://docs.angularjs.org/guide/directive#creating-a-directive-that-manipulates-the-dom) in the AngularJS documentation.
[^3]: See ["Creating Directives"](https://docs.angularjs.org/guide/directive#creating-a-directive-that-manipulates-the-dom) in the AngularJS documentation.
[^4]: See [`ngSanitize`](https://docs.angularjs.org/api/ngSanitize) in the AngularJS documentation.

@ -72,7 +72,7 @@ The script <https://docs.sheetjs.com/dojo/dojo.js> was fetched from the official
:::note Tested Deployments
The demos were last tested on 2023-11-27.
The demos were last tested on 2023-12-04.
Demos exclusively using Dojo Core were tested using Dojo Toolkit `1.17.3`.
@ -151,7 +151,7 @@ in the first worksheet, and assigns to a `dijit` UI Widget:
```html
<script>
require([
"dojo/ready", "dojo/request/xhr", "dojo/store/Memory" "dijit/registry", "xlsx"
"dojo/ready", "dojo/request/xhr", "dojo/store/Memory", "dijit/registry", "xlsx"
], function(ready, xhr, Memory, registry, _XLSX) {
ready(function() {
/* fetch test file */

@ -203,6 +203,12 @@ KnockoutJS was a popular MVVM framework.
The [Live demo](pathname:///knockout/knockout.html) shows a view model that is
updated with file data and exported to spreadsheets.
:::note Tested Deployments
This demo was last run on 2023 December 04 using KnockoutJS `3.4.2`
:::
<details><summary><b>Full Exposition</b> (click to show)</summary>
**State**

@ -17,7 +17,7 @@ import CodeBlock from '@theme/CodeBlock';
data from spreadsheets.
This demo uses ViteJS and SheetJS to export data. We'll explore how to add
SheetJS to a site using Browserify and how to export data to spreadsheets.
SheetJS to a site using ViteJS and how to export data to spreadsheets.
:::info pass
@ -26,9 +26,23 @@ loaders. They are ideal for static sites pulling data from sheets at build time.
:::
:::note
:::note pass
This demo was last tested on 2023 October 21 against ViteJS `4.5.0`
This demo focuses on integration details with the ViteJS 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:
| ViteJS | Date |
|:---------|:-----------|
| `5.0.5` | 2023-12-04 |
| `4.5.0` | 2023-12-04 |
:::
@ -89,6 +103,10 @@ const raw_data: President[] = 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,

@ -25,15 +25,6 @@ bundle with ESBuild for browser use.
- ["NodeJS"](#nodejs) explores how to import SheetJS libraries in a script and
bundle with ESBuild for NodeJS use.
:::note pass
This demo focuses on integration details with the ESBuild bundler.
The demos follow the ["Export Tutorial"](/docs/getting-started/examples/export).
The tutorial covers SheetJS library usage.
:::
:::info pass
The [ESBuild section of the Content demo](/docs/demos/static/esbuild) covers
@ -41,9 +32,23 @@ loaders. They are ideal for static sites pulling data from sheets at build time.
:::
:::note
:::note pass
This demo was last tested on 2023 October 19 against esbuild `0.19.5`
This demo focuses on integration details with the ESBuild 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:
| ESBuild | Date |
|:----------|:-----------|
| `0.14.14` | 2023-12-04 |
| `0.19.8` | 2023-12-04 |
:::
@ -71,7 +76,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.19.5 in.js --bundle --outfile=out.js
npx -y esbuild@0.19.8 in.js --bundle --outfile=out.js
```
### Browser Demo
@ -120,7 +125,7 @@ curl -LO https://docs.sheetjs.com/esbuild/esbrowser.js
4) Create bundle:
```bash
npx -y esbuild@0.19.5 esbrowser.js --bundle --outfile=esb.browser.js
npx -y esbuild@0.19.8 esbrowser.js --bundle --outfile=esb.browser.js
```
5) Start a local HTTP server:
@ -160,7 +165,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.19.5 in.js --bundle --platform=node --outfile=out.js
npx -y esbuild@0.19.8 in.js --bundle --platform=node --outfile=out.js
```
### NodeJS Demo
@ -209,7 +214,7 @@ curl -LO https://docs.sheetjs.com/esbuild/esbnode.js
3) Create bundle:
```bash
npx -y esbuild@0.19.5 esbnode.js --bundle --platform=node --outfile=esb.node.js
npx -y esbuild@0.19.8 esbnode.js --bundle --platform=node --outfile=esb.node.js
```
4) Run the bundle:

@ -26,16 +26,25 @@ loaders. They are ideal for static sites pulling data from sheets at build time.
:::
:::note
:::note pass
This demo was tested against the following Webpack versions:
This demo focuses on integration details with the Webpack 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:
| Version | Date | Required Workarounds |
|:---------|:-----------|:------------------------------------|
| `2.7.0` | 2023-10-17 | Import `xlsx/dist/xlsx.full.min.js` |
| `3.12.0` | 2023-10-17 | Import `xlsx/dist/xlsx.full.min.js` |
| `4.47.0` | 2023-10-17 | Downgrade NodeJS (tested v16.20.2) |
| `5.89.0` | 2023-10-17 | |
| `5.89.0` | 2023-12-04 | |
:::
@ -304,12 +313,14 @@ npx webpack --mode=production
</html>
```
6) Start a local HTTP server and go to `http://localhost:8080/`
6) Start a local HTTP server:
```bash
npx http-server .
```
7) Load the displayed URL (typically `http://localhost:8080/`) in a web browser.
Click on "Click here to export" to generate a file.
## Miscellany

@ -19,9 +19,23 @@ data from spreadsheets.
This demo uses Browserify and SheetJS to export data. We'll explore how to add
SheetJS to a site using Browserify and how to export data to spreadsheets.
:::note
:::note pass
This demo was last tested on 2023 October 21 against Browserify `17.0.0`
This demo focuses on integration details with the Browserify 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:
| Browserify | Date |
|:-----------|:-----------|
| `17.0.0` | 2023-12-04 |
| `3.46.1` | 2023-12-04 |
:::
@ -119,16 +133,21 @@ document.getElementById("xport").addEventListener("click", function() {
3) Bundle the scripts:
```bash
npx browserify app.js > browserify.js
npx browserify index.js > index.min.js
```
4) Spin up a local web server:
:::caution pass
Legacy `browserify` versions must use a local version. For version `3.46.1`:
```bash
npx http-server
npm install --save browserify@3.46.1
./node_modules/.bin/browserify index.js > index.min.js
```
5) Create a small HTML page that loads the script. Save to `index.html`:
:::
4) Create a small HTML page that loads the script. Save to `index.html`:
```html title="index.html"
<!DOCTYPE html>
@ -142,10 +161,12 @@ npx http-server
</html>
```
6) Start a local HTTP server and go to `http://localhost:8080/`
5) Start a local HTTP server:
```bash
npx http-server .
```
6) Load the displayed URL (typically `http://localhost:8080/`) in a web browser.
Click on "Click here to export" to generate a file.

@ -25,9 +25,23 @@ The [Live demo](pathname:///requirejs/requirejs.html) loads RequireJS from the
CDN, uses it to load the standalone script from the SheetJS CDN, and uses the
`XLSX` variable to create a button click handler that creates a workbook.
:::note
:::note pass
This demo was last tested on 2023 October 18 against RequireJS `2.3.6`
This demo focuses on integration details with the RequireJS loader.
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:
| RequireJS | Date |
|:----------|:-----------|
| `2.3.6` | 2023-12-04 |
| `2.1.22` | 2023-12-04 |
:::
@ -114,6 +128,7 @@ curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js`}
<body>
<h1>SheetJS Presidents Demo</h1>
<button id="xport">Click here to export</button>
<!-- highlight-next-line -->
<script src="http://requirejs.org/docs/release/2.3.6/comments/require.js"></script>
<script>
/* Wire up RequireJS */
@ -130,6 +145,17 @@ require.config({
</html>
```
:::note pass
To change the RequireJS version, change the version in the highlighted line. For
example, the following script corresponds to RequireJS `2.1.22`:
```html
<script src="http://requirejs.org/docs/release/2.1.22/comments/require.js"></script>
```
:::
2) Save the following to `SheetJSRequire.js`:
```js title="SheetJSRequire.js"
@ -142,6 +168,10 @@ require(["xlsx"], function(XLSX) {
/* 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,
@ -176,17 +206,19 @@ uses normal functions and traditional Promise chains.
:::
3) Start a local HTTP server, then go to `http://localhost:8080/`
3) Start a local HTTP server:
```bash
npx http-server .
```
4) Load the displayed URL (typically `http://localhost:8080/`) in a web browser.
Click on "Click here to export" to generate a file.
### r.js Optimizer
4) Create `build.js` configuration for the optimizer:
5) Create `build.js` configuration for the optimizer:
```js title="build.js"
({
@ -199,12 +231,23 @@ Click on "Click here to export" to generate a file.
});
```
5) Run the `r.js` optimizer to create `SheetJSRequire.min.js`:
6) Run the `r.js` optimizer to create `SheetJSRequire.min.js`:
```bash
npx -p requirejs@2.3.6 r.js -o build.js
```
:::note pass
To change the RequireJS version, change the version in the command. For example,
the following command uses RequireJS `2.1.22` to generate an optimized script:
```bash
npx -p requirejs@2.1.22 r.js -o build.js
```
:::
6) Save the following to `optimized.html`:
```html title="optimized.html"
@ -214,12 +257,24 @@ npx -p requirejs@2.3.6 r.js -o build.js
<body>
<h1>SheetJS Presidents Demo</h1>
<button id="xport">Click here to export</button>
<script src="http://requirejs.org/docs/release/2.3.3/comments/require.js"></script>
<!-- highlight-next-line -->
<script src="http://requirejs.org/docs/release/2.3.6/comments/require.js"></script>
<script src="SheetJSRequire.min.js"></script>
</body>
</html>
```
7) Open `http://localhost:8080/optimized.html`
:::note pass
To change the RequireJS version, change the version in the highlighted line. For
example, the following script corresponds to RequireJS `2.1.22`:
```html
<script src="http://requirejs.org/docs/release/2.1.22/comments/require.js"></script>
```
:::
7) Open `optimized.html` in the browser (`http://localhost:8080/optimized.html`)
Click on "Click here to export" to generate a file.

@ -31,16 +31,26 @@ other tools using SystemJS have switched to Webpack.
:::
:::note
:::note pass
This demo was tested against the following SystemJS versions:
This demo focuses on integration details with the SystemJS loader.
| Version | Date |
|:----------|:-----------|
| `0.19.47` | 2023-10-18 |
| `0.20.19` | 2023-10-18 |
| `0.21.6` | 2023-10-18 |
| `6.14.2` | 2023-10-18 |
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:
| Version | Platform | Date |
|:----------|:---------|:-----------|
| `0.19.47` | NodeJS | 2023-10-18 |
| `0.20.16` | Browser | 2023-12-04 |
| `0.20.19` | NodeJS | 2023-10-18 |
| `0.21.6` | NodeJS | 2023-10-18 |
| `6.14.2` | NodeJS | 2023-12-04 |
:::

@ -19,9 +19,23 @@ data from spreadsheets.
This demo uses RollupJS and SheetJS to export data. We'll explore how to bundle
SheetJS in a site using RollupJS and how to export data to spreadsheets.
:::note
:::note pass
This demo was last tested on 2023 October 21 against RollupJS 4.1.4
This demo focuses on integration details with the RollupJS 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:
| Version | Date |
|:--------|:-----------|
| `4.6.1` | 2023-12-04 |
| `4.1.4` | 2023-10-21 |
:::
@ -62,17 +76,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`} rollup@4.1.4 @rollup/plugin-node-resolve
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`} rollup@4.6.1 @rollup/plugin-node-resolve
</CodeBlock>
</TabItem>
<TabItem value="pnpm" label="pnpm">
<CodeBlock language="bash">{`\
pnpm install https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`} rollup@4.1.4 @rollup/plugin-node-resolve
pnpm install https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`} rollup@4.6.1 @rollup/plugin-node-resolve
</CodeBlock>
</TabItem>
<TabItem value="yarn" label="Yarn" default>
<CodeBlock language="bash">{`\
yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`} rollup@4.1.4 @rollup/plugin-node-resolve
yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`} rollup@4.6.1 @rollup/plugin-node-resolve
</CodeBlock>
</TabItem>
</Tabs>
@ -91,6 +105,10 @@ 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,

@ -19,9 +19,23 @@ data from spreadsheets.
This demo uses ParcelJS and SheetJS to export data. We'll explore how to bundle
SheetJS in a site using ParcelJS and how to export data to spreadsheets.
:::note
:::note pass
This demo was last tested on 2023 October 21 against parcel `2.10.0`
This demo focuses on integration details with the ParcelJS 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:
| Version | Date |
|:---------|:-----------|
| `2.10.3` | 2023-12-04 |
| `1.12.3` | 2023-12-04 |
:::
@ -78,6 +92,10 @@ document.getElementById("xport").onclick = async() => {
/* 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,
@ -103,6 +121,67 @@ document.getElementById("xport").onclick = async() => {
<body>
```
:::caution pass
**ParcelJS v1 did not support `import` statements within inline scripts.**
For ParcelJS version 1, the entire script should be copied to `index.js` and the
main `index.html` page should load the `index.js` script:
<details><summary><b>ParcelJS v1 example</b> (click to show)</summary>
```html title="index.html"
<body>
<h3>SheetJS <span id="vers"></span> export demo</h3>
<button id="xport">Click to Export!</button>
<script src="index.js"></script>
<body>
```
```js title="index.js"
// ESM-style import from "xlsx"
import { utils, version, writeFileXLSX } from 'xlsx';
document.getElementById("vers").innerText = version;
document.getElementById("xport").onclick = async() => {
/* fetch JSON data and parse */
const url = "https://sheetjs.com/data/executive.json";
const 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");
};
```
</details>
:::
2) Install the SheetJS NodeJS module:
<Tabs groupId="pm">
@ -128,7 +207,7 @@ yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
3) Run the ParcelJS development server:
```bash
npx -y parcel@2.10.0 index.html
npx -y parcel@2.10.3 index.html
```
The process will print a URL:

@ -20,9 +20,22 @@ data from spreadsheets.
This demo uses `spack` and SheetJS to export data. We'll explore how to bundle
SheetJS in a site using `spack` and how to export data to spreadsheets.
:::note
:::note pass
This demo was last tested on 2023 October 20 against SWC 1.2.246
This demo focuses on integration details with the `spack` 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:
| Version | Date |
|:----------|:-----------|
| `1.2.246` | 2023-12-04 |
:::
@ -52,12 +65,14 @@ import { utils, writeFile } from 'xlsx';
:::warning pass
When this demo was tested against the latest version, `spack` crashed:
When this demo was tested against the `@swc/core@1.3.100`, `spack` crashed:
```
thread '<unnamed>' panicked at 'cannot access a scoped thread local variable without calling `set` first',
```
**This is a bug in SWC**
Until the bug is fixed, it is strongly recommended to use `@swc/core@1.2.246`.
:::
@ -112,6 +127,10 @@ 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,
@ -174,7 +193,7 @@ This command will create the script `lib/web.js`
6) Start a local HTTP server, then go to `http://localhost:8080/`
```bash
npx http-server
npx http-server .
```
Click on "Click here to export" to generate a file.

@ -47,9 +47,13 @@ Snowpack works with no caveats.
<details><summary><b>Complete Example</b> (click to show)</summary>
:::note
:::note Tested Deployments
This demo was last tested on 2023 October 21 against Snowpack `3.8.8`
This demo was tested in the following environments:
| Version | Date |
|:--------|:-----------|
| `3.8.8` | 2023-12-04 |
:::
@ -95,6 +99,10 @@ 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,
@ -160,9 +168,13 @@ WMR works with no caveats.
<details><summary><b>Complete Example</b> (click to show)</summary>
:::note
:::note Tested Deployments
This demo was last tested on 2023 Oct 21 against WMR `3.8.0`
This demo was tested in the following environments:
| Version | Date |
|:--------|:-----------|
| `3.8.0` | 2023-12-04 |
:::
@ -208,6 +220,10 @@ 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,

@ -1,4 +1,4 @@
{
"label": "Web Frameworks",
"position": 1
"position": 2
}

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

@ -365,7 +365,7 @@ function SheetJSPreviewPSTSheets() {
<p>Use the file input to select a file, or click "Use a Sample PST"</p>
<input type="file" accept=".pst" onChange={chg}/>
<button onClick={doit}>Use a Sample PST!</button><br/><br/>
<table><thead><th colspan="3">Attachments</th></thead>
<table><thead><tr><th colspan="3">Attachments</th></tr></thead>
<tbody>{files.map((f,j) => (
<tr key={j}><th>{f.filename}</th>
<td><a onClick={()=>view(j)}>(preview)</a></td>

@ -14,9 +14,9 @@ With a familiar UI, `x-spreadsheet` is an excellent choice for a modern editor.
[Click here for a live standalone integration demo.](pathname:///xspreadsheet/)
:::note
:::note Tested Deployments
This demo was last verified on 2023 September 03.
This demo was last verified on 2023 December 04.
:::

@ -13,9 +13,9 @@ with a straightforward API.
[Click here for a live standalone integration demo.](pathname:///cdg/)
:::note
:::note Tested Deployments
This demo was last verified on 2023 September 03.
This demo was last verified on 2023 December 04.
:::

@ -9,8 +9,8 @@ import CodeBlock from '@theme/CodeBlock';
:::note Tested Deployments
This demo was tested against `vue3-table-lite 1.3.9`, VueJS `3.3.7` and ViteJS
`4.5.0` on 2023 November 03.
This demo was tested against `vue3-table-lite 1.3.9`, VueJS `3.3.10` and ViteJS
`5.0.5` on 2023 December 04.
:::
@ -114,7 +114,7 @@ cd sheetjs-vtl
2) Install dependencies:
<CodeBlock language="bash">{`\
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz vue3-table-lite@1.2.4`}
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz vue3-table-lite@1.3.9`}
</CodeBlock>
3) Download [`src/App.vue`](pathname:///vtl/App.vue) and replace the contents:

@ -24,9 +24,9 @@ user-supplied sheets and exports data to XLSX workbooks:
![Glide Data Grid example](pathname:///gdg/gdg.png)
:::note
:::note Tested Deployments
This demo was last tested on 2023 September 08 with Glide Data Grid 5.2.1
This demo was last tested on 2023 December 04 with Glide Data Grid 5.3.2
:::
@ -38,7 +38,7 @@ installation with Yarn and other package managers.
Using the `npm` tool, this command installs SheetJS and Glide Data Grid:
<CodeBlock language="bash">{`\
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @glideapps/glide-data-grid@5.2.1`}
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @glideapps/glide-data-grid@5.3.2`}
</CodeBlock>
Methods and components in both libraries can be loaded in pages using `import`:
@ -340,10 +340,10 @@ cd sheetjs-gdg
npm i
```
2) Install SheetJS and Glide Data Grid required dependencies:
2) Install SheetJS and Glide Data Grid libraries:
<CodeBlock language="bash">{`\
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @glideapps/glide-data-grid@5.2.1`}
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @glideapps/glide-data-grid@5.3.2`}
</CodeBlock>
3) Start dev server:

@ -7,19 +7,59 @@ pagination_next: demos/net/index
import current from '/version.js';
import CodeBlock from '@theme/CodeBlock';
:::note
[React Data Grid](https://adazzle.github.io/react-data-grid/) is a data grid
designed for the ReactJS web framework.
This demo was last tested on 2023 September 17 with `react-data-grid` version
`7.0.0-beta.39` and React 18.2.0.
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
data from spreadsheets.
:::
This demo uses React Data Grid and SheetJS to pull data from a spreadsheet and
display the content in a data grid. We'll explore how to import data from files
into the data grid and how to export modified data from the grid to workbooks.
The demo creates a site that looks like the screenshot below:
The ["Demo"](#demo) section includes a complete example that displays data from
user-supplied sheets and exports data to XLSX workbooks:
![react-data-grid screenshot](pathname:///rdg/rdg1.png)
:::note Tested Deployments
This demo was tested in the following environments:
| Version | Date | Notes |
|:----------------|:-----------|:---------------------|
| `7.0.0-beta.19` | 2023-12-04 | |
| `7.0.0-beta.41` | 2023-12-04 | Editing did not work |
:::
:::warning pass
When this demo was last tested, the grid correctly displayed data but could not
be edited by the user.
The current recommendation is to use version `7.0.0-beta.19`.
:::
## Integration Details
[The "Frameworks" section](/docs/getting-started/installation/frameworks) covers
installation with Yarn and other package managers.
Using the `npm` tool, this command installs SheetJS and React Data Grid:
<CodeBlock language="bash">{`\
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz react-data-grid@7.0.0-beta.41`}
</CodeBlock>
Methods and components in both libraries can be loaded in pages using `import`:
```js
import { read, utils, writeFile } from 'xlsx';
import DataGrid, { Column } from "react-data-grid";
```
#### Rows and Columns state
`react-data-grid` state consists of an Array of column metadata and an Array of
@ -116,7 +156,7 @@ cd sheetjs-rdg
2) Install dependencies:
<CodeBlock language="bash">{`\
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz react-data-grid@7.0.0-beta.39`}
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz react-data-grid@7.0.0-beta.41`}
</CodeBlock>
3) Download [`App.tsx`](pathname:///rdg/App.tsx) and replace `src/App.tsx`.

@ -9,6 +9,11 @@ import CodeBlock from '@theme/CodeBlock';
This demo covers the traditional Material UI Table as well as the MUI Data Grid.
## Integration Details
[The "Frameworks" section](/docs/getting-started/installation/frameworks) covers
installation in projects using Material UI.
## Material UI Table
The `Table` component abstracts the `<table>` element in HTML. `table_to_book`
@ -18,8 +23,10 @@ can process a `ref` attached to the `Table` element:
import TableContainer from '@mui/material/TableContainer';
import Table from '@mui/material/Table';
// ...
// highlight-next-line
// highlight-start
import { utils, writeFileXLSX } from "xlsx";
import { useRef } from "react";
// highlight-end
// ...
export default function BasicTable() {
@ -41,13 +48,11 @@ export default function BasicTable() {
}
```
#### MUI Table Demo
### MUI Table Demo
<details open><summary><b>Complete Example</b> (click to hide)</summary>
:::note Tested Deployments
:::note
This demo was last run on 2023 October 12 against Material UI 5.14.13 paired
This demo was last run on 2023 December 04 against Material UI 5.14.19 paired
with Emotion 11.11.1
:::
@ -62,7 +67,7 @@ cd sheetjs-mui
2) Install dependencies:
<CodeBlock language="bash">{`\
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @mui/material@5.14.13 @emotion/react@11.11.1 @emotion/styled@11.11.0`}
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @mui/material@5.14.19 @emotion/react@11.11.1 @emotion/styled@11.11.0`}
</CodeBlock>
3) Download [`App.tsx`](pathname:///mui/table/App.tsx) and replace `src/App.tsx`.
@ -80,8 +85,6 @@ npm run dev
The script should open the live demo in a web browser. Click the "Export" button
to save the file. Open the generated file in a spreadsheet editor.
</details>
## Material UI Data Grid
[A complete example is included below.](#muidg-demo)
@ -186,15 +189,13 @@ export default function App() {
<!-- spellchecker-disable -->
#### MUIDG Demo
### MUIDG Demo
<!-- spellchecker-enable -->
<details open><summary><b>Complete Example</b> (click to hide)</summary>
:::note Tested Deployments
:::note
This demo was last run on 2023 October 12 against MUI data grid 6.3.1 paired
This demo was last run on 2023 December 04 against MUI data grid 6.18.3 paired
with Emotion 11.11.1
:::
@ -209,7 +210,7 @@ cd sheetjs-muidg
2) Install dependencies:
<CodeBlock language="bash">{`\
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @mui/x-data-grid@6.16.1 @emotion/react@11.11.1 @emotion/styled@11.11.0`}
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @mui/x-data-grid@6.18.3 @emotion/react@11.11.1 @emotion/styled@11.11.0`}
</CodeBlock>
3) Download [`App.tsx`](pathname:///mui/dg/App.tsx) and replace `src/App.tsx`.
@ -225,5 +226,3 @@ npm run dev
```
When the page loads, it will fetch and process <https://sheetjs.com/pres.numbers>
</details>

@ -65,7 +65,7 @@ the variable `pres` in a template.
When a workbook has one worksheet, the data is an array of row objects:
```liquid title="single.njk"
<table><thead><th>Name</th><th>Index</th></thead>
<table><thead><tr><th>Name</th><th>Index</th></tr></thead>
<tbody>
{% for row in pres %}
<tr>
@ -99,7 +99,7 @@ named `"VicePresidents"`, then the following snippet would print data from the
`"Presidents"` sheet:
```liquid title="multi.njk"
<table><thead><th>Name</th><th>Index</th></thead>
<table><thead><tr><th>Name</th><th>Index</th></tr></thead>
<tbody>
{% for row in pres["Presidents"] %}
<tr>
@ -127,9 +127,9 @@ site.use(sheets({
## Complete Example
:::note
:::note Tested Deployments
This demo was last tested against `lume v1.19.1` on 2023 October 14.
This demo was last tested against Lume `v1.19.4` on 2023 December 04.
This example uses the Nunjucks template format. Lume plugins support additional
template formats, including Markdown and JSX.
@ -156,7 +156,7 @@ When prompted, enter the following options:
The project will be configured and modules will be installed.
2) Download <https://sheetjs.com/pres.xlsx> and place in a `_data` folder:
2) Download <https://sheetjs.com/pres.xlsx> and place in a `_data` subfolder:
```bash
mkdir -p _data
@ -167,7 +167,7 @@ curl -L -o _data/pres.xlsx https://sheetjs.com/pres.xlsx
```liquid title="index.njk"
<h2>Presidents</h2>
<table><thead><th>Name</th><th>Index</th></thead>
<table><thead><tr><th>Name</th><th>Index</th></tr></thead>
<tbody>
{% for row in pres %}
<tr>
@ -179,7 +179,11 @@ curl -L -o _data/pres.xlsx https://sheetjs.com/pres.xlsx
</table>
```
Since the file name is `pres.xlsx`, the parameter name is `pres`:
:::note pass
Since the file name is `pres.xlsx`, the parameter name is `pres`.
:::
### Live Refresh
@ -189,11 +193,12 @@ Since the file name is `pres.xlsx`, the parameter name is `pres`:
deno task serve --port 7262
```
To verify it works, access `http://localhost:7262` from your web browser. The
page will show the contents of the spreadsheet.
To verify the site, access the "Local" URL (typically `http://localhost:7262`)
from a web browser. The page will show the contents of the spreadsheet.
5) While the server is still running, open `_data/pres.xlsx` in a spreadsheet
editor and add a new row at the bottom of the sheet.
5) While the server is running, open `_data/pres.xlsx` in a spreadsheet editor.
Set cell `A7` to "SheetJS Dev" and set `B7` to `47`. Save the spreadsheet.
After saving the spreadsheet, the page will refresh and show the new contents.

@ -170,10 +170,10 @@ The following query pulls the `Name` and `Index` fields from each row:
## Complete Example
:::note
:::note Tested Deployments
This demo was tested on 2023 October 08 against `create-gatsby@3.12.0`. The
generated project used `gatsby@5.12.5` and `react@18.2.0`.
This demo was tested on 2023 December 04 against `create-gatsby@3.12.3`. The
generated project used `gatsby@5.12.11` and `react@18.2.0`.
:::
@ -202,7 +202,7 @@ Open a web browser to the displayed URL (typically `http://localhost:8000/`)
3) Edit `package.json` and add the highlighted lines in the JSON object:
<CodeBlock language="json">{`\
<CodeBlock language="json" title="package.json (add highlighted lines)">{`\
{
// highlight-start
"overrides": {
@ -232,7 +232,7 @@ curl -L -o src/data/pres.xlsx https://sheetjs.com/pres.xlsx
6) Edit `gatsby-config.js` and add the following lines to the `plugins` array:
```js title="gatsby-config.js"
```js title="gatsby-config.js (add highlighted lines)"
module.exports = {
siteMetadata: {
title: `sheetjs-gatsby`,
@ -257,11 +257,12 @@ Stop and restart the development server process (`npm run develop`).
### GraphiQL test
7) Open the GraphiQL editor at `http://localhost:8000/___graphql`
7) Open the GraphiQL editor. The output of the previous step displayed the URL
(typically `http://localhost:8000/___graphql` )
There is an editor in the left pane. Paste the following query into the editor:
```graphql
```graphql title="GraphQL Query (paste into editor)"
{
allPresXlsxSheet1 {
edges {
@ -274,7 +275,7 @@ There is an editor in the left pane. Paste the following query into the editor:
}
```
Press the Execute Query button and data should show up in the right pane:
Press the Execute Query button (`▶`) and data should show up in the right pane:
![GraphiQL Screenshot](pathname:///gatsby/graphiql.png)
@ -306,7 +307,7 @@ export default PageComponent;
After saving the file, access `http://localhost:8000/pres` in the browser. The
displayed JSON is the data that the component receives:
```js
```js title="Expected contents of /pres"
{
"allPresXlsxSheet1": {
"edges": [
@ -358,21 +359,27 @@ Going back to the browser, `http://localhost:8000/pres` will show a table:
### Live refresh
10) Open the file `src/data/pres.xlsx` in Excel or LibreOffice or Numbers.
Add a new row at the end of the file:
10) Open the file `src/data/pres.xlsx` in Excel or another spreadsheet editor.
Add a new row at the end of the file, setting cell `A7` to "SheetJS Dev" and
cell `B7` to `47`. The sheet should look like the following screenshot:
![New Row in File](pathname:///gatsby/pres2.png)
Save the file and notice that the table has refreshed with the new data:
Save the file and observe that the table has refreshed with the new data:
![Updated Table](pathname:///gatsby/table2.png)
### Static site
11) Stop the development server and run `npm run build`. Once the build is
finished, the output will confirm that the `/pres` route is static:
11) Stop the development server and build the site:
```bash
npm run build
```
The build output will confirm that the `/pres` route is static:
```text title="Output from GatsbyJS build process"
Pages
┌ src/pages/404.js
@ -398,7 +405,7 @@ The generated page will be placed in `public/pres/index.html`.
12) Open `public/pres/index.html` with a text editor and search for "SheetJS".
There will be a HTML row:
```html title="public/pres/index.html"
```html title="public/pres/index.html (Expected contents)"
<tr><td>SheetJS Dev</td><td>47</td></tr>
```

@ -135,7 +135,7 @@ recover the Date objects in the generated code module.
:::
```js
```js title="build.mjs (plugin implementation)"
import * as XLSX from 'xlsx';
import * as fs from 'fs';
XLSX.set_fs(fs);
@ -198,9 +198,9 @@ document.body.appendChild(elt);
## Demo
:::note
:::note Tested Deployments
This demo was last tested on 2023 October 21 against ESBuild 0.19.5
This demo was last tested on 2023 December 04 against ESBuild 0.19.8
:::
@ -212,7 +212,7 @@ This demo was last tested on 2023 October 21 against ESBuild 0.19.5
mkdir sheetjs-esb
cd sheetjs-esb
npm init -y
npm i --save esbuild@0.19.5
npm i --save esbuild@0.19.8
```
1) Install the SheetJS NodeJS module:
@ -249,58 +249,13 @@ elt.innerHTML = "<table><tr><th>Name</th><th>Index</th></tr>" +
document.body.appendChild(elt);
```
4) Save the following to `build.mjs`:
4) Download [`build.mjs`](pathname:///esbuild/build.mjs) to the project folder:
```js title="build.mjs"
import * as esbuild from 'esbuild'
import * as XLSX from 'xlsx';
import * as fs from 'fs';
XLSX.set_fs(fs);
let sheetjsPlugin = {
name: 'sheetjs',
setup(build) {
/* match NUMBERS, XLSX, XLS, and XLSB files */
const EXTS = /.(numbers|xlsx|xls|xlsb)$/;
/* this method will be called once for each referenced file */
build.onLoad({ filter: EXTS }, (args) => {
/* parse file from filesystem */
const wb = XLSX.readFile(args.path);
/* get first worksheet */
const ws = wb.Sheets[wb.SheetNames[0]];
/* workaround for JSON limitation */
Date.prototype.toJSON2 = Date.prototype.toJSON;
Date.prototype.toJSON = function() { return {d:this.toISOString()}; };
/* generate row objects */
const data = XLSX.utils.sheet_to_json(ws);
/* generate final module code */
const res = JSON.stringify(data);
Date.prototype.toJSON = Date.prototype.toJSON2;
const contents = `const data = ${res};
data.forEach(row => {
Object.keys(row).forEach(k => {
if(row[k]?.d) row[k] = new Date(row[k].d);
})
});
export default data;`
return { contents, loader: 'js' };
});
},
};
await esbuild.build({
entryPoints: ['app.js'],
bundle: true,
outfile: 'out.js',
plugins: [sheetjsPlugin],
});
```bash
curl -LO https://docs.sheetjs.com/esbuild/build.mjs
```
5) Download <https://sheetjs.com/pres.numbers> and save to the project folder:
5) Download <https://sheetjs.com/pres.numbers> to the project folder:
```bash
curl -LO https://sheetjs.com/pres.numbers
@ -333,7 +288,7 @@ president names. It will not include SheetJS library references!
In the last test, the generated source looked like the following snippet
```js title="out.js"
```js title="out.js (Expected output)"
(() => {
// pres.numbers
var data = [{ "Name": "Bill Clinton", "Index": 42 }, /* ... more data */];

@ -187,9 +187,16 @@ document.querySelector('#app').innerHTML = `<table>
## Complete Demo
:::note
:::note Tested Deployments
This demo was tested on 2023 September 07 with ViteJS version `4.4.9`.
This demo was tested in the following environments:
| ViteJS | Date |
|:---------|:-----------|
| `5.0.5` | 2023-12-04 |
| `4.5.0` | 2023-12-04 |
| `3.2.7` | 2023-12-04 |
| `2.9.16` | 2023-12-04 |
:::
@ -200,8 +207,15 @@ A Git repository with the completed site can be cloned[^7].
1) Create a new site with the `vue-ts` template and install the SheetJS package:
:::note pass
To force an older major version of ViteJS, change the `vite@5` to the desired
major version. For example, `npm create vite@3` will use ViteJS major version 3.
:::
<CodeBlock language="bash">{`\
npm create vite@latest sheetjs-vite -- --template vue-ts
npm create vite@5 sheetjs-vite -- --template vue-ts
cd sheetjs-vite
npm i
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}

@ -157,9 +157,9 @@ document.body.appendChild(elt);
## Webpack 5 Demo
:::note
:::note Tested Deployments
This demo was last tested on 2023 October 10 against Webpack 5.88.2
This demo was last tested on 2023 December 04 against Webpack 5.89.0
:::
@ -171,7 +171,7 @@ This demo was last tested on 2023 October 10 against Webpack 5.88.2
mkdir sheetjs-wp5
cd sheetjs-wp5
npm init -y
npm install webpack@5.88.2 webpack-cli@5.1.4 webpack-dev-server@4.15.1 --save
npm install webpack@5.89.0 webpack-cli@5.1.4 webpack-dev-server@4.15.1 --save
mkdir -p dist
mkdir -p src
mkdir -p data

@ -5,6 +5,8 @@ pagination_next: demos/mobile/index
---
import current from '/version.js';
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeBlock from '@theme/CodeBlock';
[Eleventy](https://www.11ty.dev/docs) is a telemetry-free static site generator.
@ -93,7 +95,7 @@ For example, [`pres.numbers`](https://sheetjs.com/pres.numbers) can be accessed
using the variable `pres` in a template:
```liquid title="index.njk"
<table><thead><th>Name</th><th>Index</th></thead>
<table><thead><tr><th>Name</th><th>Index</th></tr></thead>
<tbody>
{% for row in pres %}
<tr>
@ -108,9 +110,17 @@ using the variable `pres` in a template:
## Complete Example
:::note
:::note Tested Deployments
This demo was tested on 2023 October 08 using Eleventy `2.0.1`
This demo was tested in the following environments:
| Eleventy | Date |
|:----------------|:-----------|
| `2.0.1` | 2023-10-08 |
| `3.0.0-alpha.1` | 2023-12-04 |
At the time of writing, the `3.0.0-alpha.1` ("Alpha") release was only available
through the Git repository. Steps for `2.0.1` ("Stable") and Alpha are included.
:::
@ -126,10 +136,23 @@ npm init -y
2) Install Eleventy and SheetJS libraries:
<Tabs groupId="11ty">
<TabItem value="2" label="Stable">
<CodeBlock language="bash">{`\
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @11ty/eleventy`}
</CodeBlock>
</TabItem>
<TabItem value="3" label="Alpha">
<CodeBlock language="bash">{`\
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz 11ty/eleventy`}
</CodeBlock>
</TabItem>
</Tabs>
3) Make a new `_data` subdirectory in the project. Download the example file
[`pres.xlsx`](https://sheetjs.com/pres.xlsx) into `_data`:

@ -36,13 +36,13 @@ time and how to read files on the server in NextJS lifecycle methods.
NextJS collects telemetry by default. The `telemetry` subcommand can disable it:
```js
npx next@13.4.19 telemetry disable
npx next@13.5.6 telemetry disable
```
The setting can be verified by running
```js
npx next@13.4.19 telemetry status
npx next@13.5.6 telemetry status
```
:::
@ -61,17 +61,18 @@ module.exports = {
:::
:::note
:::note Tested Deployments
The following deployments were tested:
This demo was tested in the following environments:
| NextJS | NodeJS | Date |
|:----------|:----------|:-----------|
| ` 9.5.5` | `16.20.2` | 2023-08-20 |
| `10.2.3` | `16.20.2` | 2023-08-20 |
| `11.1.4` | `16.20.2` | 2023-08-20 |
| `12.3.4` | `18.17.1` | 2023-08-20 |
| `13.4.19` | `18.17.1` | 2023-08-20 |
| ` 9.5.5` | `16.20.2` | 2023-12-04 |
| `10.2.3` | `16.20.2` | 2023-12-04 |
| `11.1.4` | `16.20.2` | 2023-12-04 |
| `12.3.4` | `20.10.0` | 2023-12-04 |
| `13.5.6` | `20.10.0` | 2023-12-04 |
| `14.0.3` | `20.10.0` | 2023-12-04 |
:::
@ -189,9 +190,9 @@ imported from page scripts.
:::warning pass
[`import`](/docs/getting-started/installation/nodejs#esm-import) does not load
NodeJS native modules. The Installation section includes a note on dynamic
import of `fs` within lifecycle methods.
[The SheetJS ESM build](/docs/getting-started/installation/nodejs#esm-import)
does not load NodeJS native modules directly. The Installation section includes
a note on dynamic import of `fs` within lifecycle methods.
:::
@ -522,7 +523,7 @@ This demo showcases the following SheetJS + NextJS flows:
| `/sheets/[id]` | asset module | `getStaticPaths` | `sheet_to_html` |
| `/getServerSideProps` | lifecycle | `getServerSideProps` | `sheet_to_html` |
The commands in this demo use `next@13.4.19`. Other versions were tested by
The commands in this demo use `next@13.5.6`. Other versions were tested by
replacing the version number in the relevant commands.
:::
@ -545,13 +546,13 @@ When upgrading NextJS is not an option, NodeJS should be downgraded to v16.
0) Disable NextJS telemetry:
```js
npx next@13.4.19 telemetry disable
npx next@13.5.6 telemetry disable
```
Confirm it is disabled by running
```js
npx next@13.4.19 telemetry status
npx next@13.5.6 telemetry status
```
1) Set up folder structure. At the end, a `pages` folder with a `sheets`
@ -572,8 +573,19 @@ curl -LO https://docs.sheetjs.com/next/sheetjs.xlsx
3) Install dependencies:
:::note pass
The `next@13.5.6` dependency can be adjusted to pick a different version. For
example, NextJS `12.3.4` is installed with
<CodeBlock language="bash">{`\
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz next@13.4.19`}
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz next@12.3.4`}
</CodeBlock>
:::
<CodeBlock language="bash">{`\
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz next@13.5.6`}
</CodeBlock>
4) Download NextJS config scripts and place in the root folder:
@ -631,7 +643,7 @@ cd ../..
6) Test the deployment:
```bash
npx next@13.4.19
npx next
```
Open a web browser and access:
@ -648,7 +660,7 @@ The individual worksheets are available at
7) While the development server is running, open the `/getStaticProps` page and
open `sheetjs.xlsx` with a spreadsheet editor. In the editor, add a row to the
bottom of the "Indices" worksheet.
bottom of the "Indices" worksheet (set `A7` to "SheetJS Dev" and `B7` to `47`)
After saving the file, the website should refresh with the new row.
@ -657,7 +669,7 @@ After saving the file, the website should refresh with the new row.
8) Stop the server and run a production build:
```bash
npx next@13.4.19 build
npx next build
```
The final output will show a list of the routes and types:
@ -681,24 +693,71 @@ worksheets in the file. `/getServerSideProps` is server-rendered.
9) Try to build a static site:
<Tabs groupId="nextver">
<TabItem value="13" label="NextJS 9 - 13">
```bash
npx next@13.4.19 export
npx next export
```
:::note The static export will fail!
</TabItem>
<TabItem value="14" label="NextJS 14">
A static page cannot be generated at this point because `/getServerSideProps`
is still server-rendered.
:::warning NextJS breaking changes
**NextJS 14 removed the `export` subcommand!**
:::
Edit `next.config.js` and add the highlighted line:
```js title="next.config.js (add highlighted line)"
module.exports = {
// highlight-next-line
output: "export",
webpack: (config) => {
```
After adding the line, run the `build` command:
```bash
npx next build
```
</TabItem>
</Tabs>
This build will fail. A static page cannot be generated at this point because
`/getServerSideProps` is server-rendered.
### Static Site
10) Delete `pages/getServerSideProps.js` and rebuild:
<Tabs groupId="nextver">
<TabItem value="13" label="NextJS 9 - 13">
</TabItem>
<TabItem value="14" label="NextJS 14">
Edit `next.config.js` and comment the highlighted line:
```js title="next.config.js (comment highlighted line)"
module.exports = {
// highlight-next-line
// output: "export",
webpack: (config) => {
```
After editing `next.config.js`:
</TabItem>
</Tabs>
```bash
rm -f pages/getServerSideProps.js
npx next@13.4.19 build
npx next build
```
Inspecting the output, there should be no lines with the `λ` symbol:
@ -717,10 +776,34 @@ Route (pages) Size First Load JS
11) Generate the static site:
<Tabs groupId="nextver">
<TabItem value="13" label="NextJS 9 - 13">
```bash
npx next@13.4.19 export
npx next export
```
</TabItem>
<TabItem value="14" label="NextJS 14">
Edit `next.config.js` and restore the highlighted line:
```js title="next.config.js (restore highlighted line)"
module.exports = {
// highlight-next-line
output: "export",
webpack: (config) => {
```
After adding the line, run the `build` command:
```bash
npx next build
```
</TabItem>
</Tabs>
The static site will be written to the `out` subfolder
12) Serve the static site:

@ -7,6 +7,8 @@ pagination_next: demos/mobile/index
---
import current from '/version.js';
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeBlock from '@theme/CodeBlock';
[Nuxt Content](https://content.nuxtjs.org/) is a file-based CMS for NuxtJS,
@ -40,28 +42,65 @@ that process spreadsheets in the browser.
:::
:::note
:::note Tested Deployments
The following deployments were tested:
This demo was tested in the following environments:
| Nuxt Content | Nuxt | Date |
|:-------------|:---------|:-----------|
| `1.15.1` | `2.17.1` | 2023-08-16 |
| `2.7.2` | `3.6.5` | 2023-08-16 |
| `1.15.1` | `2.17.2` | 2023-12-04 |
| `2.9.0` | `3.8.2` | 2023-12-04 |
:::
:::caution Telemetry
:::warning Telemetry
Nuxt embeds telemetry. According to the developers, it is disabled by default.
To explicitly disable telemetry, the official documentation recommends:
Nuxt embeds telemetry. According to the docs, it can be disabled with:
```bash
npx nuxt telemetry disable
```
At the time the demo was last tested, this command did not work. Instead, a
option should be added in `nuxt.config.ts` or `nuxt.config.js` for Nuxt 3 sites:
**At the time the demo was last tested, this command did not work.**
Disabling telemetry requires a few steps:
1) Set the environment variable `NUXT_TELEMETRY_DISABLED` to `1`
<Tabs groupId="os">
<TabItem value="unix" label="Linux/MacOS">
Add the following line to `.profile`, `.bashrc` and `.zshrc`:
```bash
export NUXT_TELEMETRY_DISABLED=1
```
Close and restart the Terminal to load the changes.
</TabItem>
<TabItem value="win" label="Windows">
Type `env` in the search bar and select "Edit the system environment variables".
In the new window, click the "Environment Variables..." button.
In the new window, look for the "System variables" section and click "New..."
Set the "Variable name" to `NUXT_TELEMETRY_DISABLED` and the value to `1`.
Click "OK" in each window (3 windows) and restart your computer.
</TabItem>
</Tabs>
2) A global setting should be added to `.nuxtrc` in the user home directory:
```ini title="~/.nuxtrc"
telemetry.enabled=false
```
3) For Nuxt 3 sites, set the `telemetry` option in the Nuxt config file (either `nuxt.config.ts` or `nuxt.config.js`):
```js title="nuxt.config.js"
// ...
@ -74,12 +113,6 @@ export default defineNuxtConfig({
})
```
A global setting can be added to `.nuxtrc` in the user home directory:
```ini title=".nuxtrc"
telemetry.enabled=false
```
:::
## Nuxt Content v1
@ -342,7 +375,9 @@ not, click Refresh manually or open a new browser window.
![Nuxt Demo end of step 5](pathname:///nuxt/nuxt5.png)
6) To verify that live reload works, open `pres.xlsx` from the `content` folder
in Excel. Add a new row to the bottom and save the file:
with Excel or another spreadsheet editor.
Set cell `A7` to "SheetJS Dev" and set `B7` to `47`. Save the spreadsheet.
![Adding a new line to `pres.xlsx`](pathname:///nuxt/nuxl6.png)
@ -595,17 +630,23 @@ curl -O https://docs.sheetjs.com/nuxt/3/sheetmodule.ts
After creating the source files, the module must be added to `nuxt.config.ts`:
```ts title="nuxt.config.ts"
```ts title="nuxt.config.ts (add highlighted lines)"
// highlight-next-line
import SheetJSModule from './sheetmodule'
export default defineNuxtConfig({
// highlight-start
// @ts-ignore
telemetry: false,
// highlight-end
devtools: { enabled: true },
// highlight-start
modules: [
SheetJSModule,
'@nuxt/content'
],
content: {}
// highlight-end
});
```
@ -613,6 +654,7 @@ Restart the dev server by exiting the process (Control+C) and running:
```bash
npx -y nuxi clean
npx -y nuxi cleanup
npx -y nuxi typecheck
npx -y yarn run dev
```
@ -645,13 +687,16 @@ Restart the dev server by exiting the process (Control+C) and running:
```bash
npx -y nuxi clean
npx -y nuxi cleanup
npx -y yarn run dev
```
The browser should now display an HTML table.
6) To verify that hot loading works, open `pres.xlsx` from the `content` folder
in Excel. Add a new row to the bottom and save the file.
with Excel or another spreadsheet editor.
Set cell `A7` to "SheetJS Dev" and set `B7` to `47`. Save the spreadsheet.
The page should automatically refresh with the new content.

@ -45,9 +45,14 @@ flowchart LR
aoo --> |+page.svelte\ncomponent| html
```
:::note
:::note Tested Deployments
This demo was tested on 2023 October 24 using SvelteKit `1.27.0` and Svelte `4.2.2`
This demo was tested in the following environments:
| Svelte | Kit | Date |
|:----------------|:---------|:-----------|
| `4.2.8` | `1.27.6` | 2023-12-04 |
| `5.0.0-next.17` | `1.27.6` | 2023-12-04 |
:::
@ -160,7 +165,7 @@ Using standard Svelte patterns, HTML tables can be generated from the data:
</script>
<h1>Presidents</h1>
<table><thead><th>Name</th><th>Index</th></thead><tbody>
<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>
@ -187,6 +192,12 @@ When prompted:
- `Add type checking with TypeScript?` select `Yes, using JavaScript with JSDoc`
- `Select additional options` press Enter (do not select options)
:::note pass
To test the Svelte 5 beta, select "Try out Svelte 5 beta" before pressing Enter.
:::
2) Enter the project folder and install dependencies:
```bash
@ -208,18 +219,75 @@ curl -Lo data/pres.xlsx https://sheetjs.com/pres.xlsx
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
</CodeBlock>
5) Replace the contents of `vite.config.js` with the contents of the code block
named [`vite.config.js` in the "Loader" section](#loader)
5) Replace the contents of `vite.config.js` with the following:
6) Append the lines from [`src/app.d.ts` snippet in the "Types" section](#types)
to the `src/app.d.ts` file.
```js title="vite.config.js"
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';
import { readFileSync } from 'fs';
export default defineConfig({
assetsInclude: ['**/*.numbers', '**/*.xlsx'],
plugins: [sveltekit(), {
name: "sheet-base64",
transform(code, id) {
if(!id.match(/\.(numbers|xlsx)$/)) return;
var data = readFileSync(id, "base64");
return `export default '${data}'`;
}
}]
});
```
6) Append the following lines to `src/app.d.ts`:
```ts title="src/app.d.ts (add to end of file)"
declare global {
declare module '*.numbers' { const data: string; export default data; }
declare module '*.xlsx' { const data: string; export default data; }
}
```
7) Replace the contents of `src/routes/+page.server.js` with following:
```js title="src/routes/+page.server.js"
import b64 from "../../data/pres.xlsx";
import { read, utils } from "xlsx";
export const prerender = true;
/** @type {import('./$types').PageServerLoad} */
export async function load({ params }) {
const wb = read(b64);
/** @type {[string, any[]][]} */
const data = wb.SheetNames.map(n => [n, utils.sheet_to_json(wb.Sheets[n])]);
return Object.fromEntries(data);
}
```
7) Replace the contents of `src/routes/+page.server.ts` with the contents of the
code block named [`src/routes/+page.server.ts` in "Data Processing"](#data-processing).
If the file does not exist, create a new file.
8) Replace the contents of `src/routes/+page.svelte` with the contents of the
code block named [`src/routes/+page.svelte` in "Data Rendering"](#data-rendering)
8) Replace the contents of `src/routes/+page.svelte` with the following:
```html title="src/routes/+page.svelte"
<script>
/** @type {import('./$types').PageData} */
export let data;
/* `pres` will be the data from Sheet1 */
/** @type {Array<{Name: string, Index: number}>}*/
export let pres = data["Sheet1"];
</script>
<h1>Presidents</h1>
<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></table>
```
### Live Reload
@ -245,11 +313,18 @@ the file. After saving, the browser should automatically refresh with new data.
npm i --save @sveltejs/adapter-static
```
13) Edit `svelte.config.js` to use the new adapter:
13) Edit `svelte.config.js` to use the new adapter.
```diff title="svelte.config.js (diff)"
-import adapter from '@sveltejs/adapter-auto';
+import adapter from '@sveltejs/adapter-static';
The config should currently use `adapter-auto`:
```js title="svelte.config.js (default configuration)"
import adapter from '@sveltejs/adapter-auto';
```
Replace the module name with `@sveltejs/adapter-static`:
```js title="svelte.config.js (change dependency)"
import adapter from '@sveltejs/adapter-static';
```
14) Build the static site:
@ -270,7 +345,7 @@ View the page source and confirm that the raw HTML table includes the data.
Searching for `Bill Clinton` should reveal the following row:
```html
<tr><td>Bill Clinton</td> <td>42</td> </tr>
<tr><td>Bill Clinton</td><td>42</td></tr>
```
[^1]: See ["SvelteKit vs Svelte"](https://kit.svelte.dev/docs/introduction#sveltekit-vs-svelte) in the SvelteKit documentation.

@ -46,9 +46,22 @@ npx astro telemetry disable
:::
:::note
:::note Tested Deployments
This demo was last tested on 2023 October 24 using AstroJS `v3.3.4`
This demo was tested in the following environments:
| AstroJS | Date |
|:---------------|:-----------|
| `3.6.4` | 2023-12-04 |
| `4.0.0-beta.4` | 2023-12-04 |
:::
:::caution pass
AstroJS has introduced a number of breaking changes in minor releases.
**The demos worked as expected with the listed versions on the listed dates.**
:::
@ -197,6 +210,22 @@ npm create astro@latest -- --template starlight --yes ./sheetjs-astro
cd sheetjs-astro
```
:::note pass
To test the AstroJS 4 beta release, run the following command:
```bash
npm install --force @astrojs/starlight@^0.14.0 astro@4.0.0-beta.4
```
The version can be verified by running:
```bash
npx astro --version
```
:::
2) Fetch the example file [`pres.numbers`](https://sheetjs.com/pres.numbers):
```bash
@ -268,6 +297,18 @@ curl -o src/pages/index.astro https://docs.sheetjs.com/astrojs/index.astro
rm src/content/index.*
```
:::note pass
This command may show an error:
```
zsh: no matches found: src/content/index.*
```
This error can be ignored.
:::
8) Build the static site:
```bash
@ -292,8 +333,8 @@ contains the content from the file in an HTML table.
When this demo was first written, the `docs` template used `src/pages/index.astro`
During the most recent test, AstroJS removed the `docs` template and introduced
the `starlight` template. This template included `src/content/index.mdx`, which
In a later test, AstroJS removed the `docs` template and introduced the
`starlight` template. This template included `src/content/index.mdx`, which
takes priority over `src/pages/index.astro`.
To resolve this issue, as noted in step 7, remove any `index.*` files in the

@ -215,11 +215,14 @@ const wb = XLSX.read(ab);
:::note Tested Deployments
The Android demo was last tested on 2023 December 01 with RN `0.72.7`. The
simulator used Android 13 ("Tiramisu") API 33 on a Pixel 3.
This demo was tested in the following environments:
The iOS demo was last tested on 2023 December 01 with RN `0.72.7`. The
simulator used iOS 17.0.1 on an iPhone SE (3rd generation).
| OS | Type | Device | RN | Date |
|:-----------|:-----|:--------------------|:---------|:-----------|
| Android 34 | Sim | Pixel 3a | `0.72.7` | 2023-12-04 |
| iOS 17.0.1 | Sim | iPhone 15 Pro Max | `0.72.7` | 2023-12-04 |
| Android 29 | Real | NVIDIA Shield | `0.72.7` | 2023-12-04 |
| iOS 15.1 | Real | iPad Pro | `0.72.7` | 2023-12-04 |
:::
@ -328,6 +331,38 @@ tapping "Import data from a spreadsheet", verify that the app shows new data:
</td></tr></tbody></table>
**Android Device Testing**
10) Connect an Android device using a USB cable.
If the device asks to allow USB debugging, tap "Allow".
11) Close any Android / iOS emulators.
12) Build APK and run on device:
```bash
npx react-native run-android
```
**iOS Device Testing**
13) Close any Android / iOS emulators.
14) Enable developer code signing certificates[^7]
15) Install `ios-deploy` through Homebrew:
```bash
brew install ios-deploy
```
16) Run on device:
```bash
npx react-native run-ios
```
## Local Files
:::warning pass
@ -364,7 +399,7 @@ The following libraries have been tested:
#### `react-native-document-picker`
<details open><summary><b>Selecting a file</b> (click to show)</summary>
<details open><summary><b>Selecting a file</b> (click to hide)</summary>
The setting `copyTo: "cachesDirectory"` must be set:
@ -425,7 +460,7 @@ for the purposes of working with files.
The `ascii` type returns an array of numbers corresponding to the raw bytes.
A `Uint8Array` from the data is compatible with the `buffer` type.
<details open><summary><b>Reading and Writing snippets</b> (click to show)</summary>
<details open><summary><b>Reading and Writing snippets</b> (click to hide)</summary>
The snippets use `rn-fetch-blob`. To use `react-native-blob-util`, change the
`import` statements to load the module.
@ -487,7 +522,7 @@ const res = await writeFile(file, Array.from(wbout), 'ascii');
The `base64` encoding returns strings compatible with the `base64` type:
<details open><summary><b>Reading and Writing snippets</b> (click to show)</summary>
<details open><summary><b>Reading and Writing snippets</b> (click to hide)</summary>
_Reading Data_
@ -518,7 +553,7 @@ await FileSystem.writeFile(DDP + "sheetjs.xlsx", b64, "base64");
The `ascii` encoding returns binary strings compatible with the `binary` type:
<details open><summary><b>Reading and Writing snippets</b> (click to show)</summary>
<details open><summary><b>Reading and Writing snippets</b> (click to hide)</summary>
_Reading Data_
@ -605,7 +640,7 @@ simulator used iOS 17.0 on an iPhone 15 Pro Max.
There are many moving parts and pitfalls with React Native apps. It is strongly
recommended to follow the official React Native tutorials for iOS and Android
before approaching this demo.[^7] Details including Android Virtual Device
before approaching this demo.[^8] Details including Android Virtual Device
configuration are not covered here.
:::
@ -953,4 +988,5 @@ npx xlsx-cli /tmp/sheetjsw.xlsx
[^4]: See ["Array of Arrays Input" in "Utility Functions"](/docs/api/utilities/array#array-of-arrays-input)
[^5]: React-Native commit [`5b597b5`](https://github.com/facebook/react-native/commit/5b597b5ff94953accc635ed3090186baeecb3873) added the final piece required for `fetch` support. It landed in version `0.72.0-rc.1` and is available in official releases starting from `0.72.0`.
[^6]: When the demo was last tested, the Zulu11 distribution of Java 11 was installed through the macOS Brew package manager. [Direct downloads are available at `azul.com`](https://www.azul.com/downloads/?version=java-11-lts&package=jdk#zulu)
[^7]: Follow the ["React Native CLI Quickstart"](https://reactnative.dev/docs/environment-setup) for Android (and iOS, if applicable)
[^7]: See ["Running On Device"](https://reactnative.dev/docs/running-on-device) in the React Native documentation
[^8]: Follow the ["React Native CLI Quickstart"](https://reactnative.dev/docs/environment-setup) for Android (and iOS, if applicable)

@ -47,11 +47,24 @@ Angular and TypeScript is assumed.
:::
:::note Tested Deployments
This demo was tested in the following environments:
| OS | Type | Device | NS | Date |
|:-----------|:-----|:--------------------|:---------|:-----------|
| Android 34 | Sim | Pixel 3a | `8.6.1` | 2023-12-04 |
| iOS 17.0.1 | Sim | iPhone SE (3rd gen) | `8.6.1` | 2023-12-04 |
| Android 29 | Real | NVIDIA Shield | `8.6.1` | 2023-12-04 |
| iOS 15.1 | Real | iPad Pro | `8.6.1` | 2023-12-04 |
:::
:::warning Telemetry
Before starting this demo, manually disable telemetry.
NativeScript 8.6.0 split the telemetry into two parts: "usage" and "error". Both
NativeScript 8.6.1 split the telemetry into two parts: "usage" and "error". Both
must be disabled separately:
```bash
@ -174,19 +187,6 @@ const wb = read(ab);
## Complete Example
:::note
The project was last tested on 2023 October 12. NativeScript version
(as verified with `npx -p nativescript ns --version`) was `8.6.0`.
The iOS demo was last tested on 2023-10-12 with `@nativescript/ios`
version `8.6.1` on an emulated iPhone 15 Pro Max + iOS 17.0
The Android demo was last tested on 2023-10-12 with `@nativescript/android`
version `8.6.2` on an emulated Pixel 3 + Android 13 ("Tiramisu") API 33.
:::
### Platform Configuration
0) Disable telemetry:
@ -239,7 +239,7 @@ In the last macOS test, the following output was displayed:
<span {...g}></span> Javac is installed and is configured properly.{'\n'}
<span {...g}></span> The Java Development Kit (JDK) is installed and is configured properly.{'\n'}
<span {...g}></span> Getting NativeScript components versions information...{'\n'}
<span {...g}></span> Component nativescript has 8.6.0 version and is up to date.
<span {...g}></span> Component nativescript has 8.6.1 version and is up to date.
</pre>
</details>
@ -266,9 +266,9 @@ No issues were detected.{'\n'}
<span {...g}></span> Your current CocoaPods version is newer than 1.0.0.{'\n'}
<span {...g}></span> Python installed and configured correctly.{'\n'}
<span {...g}></span> The Python 'six' package is found.{'\n'}
<span {...g}></span> Xcode version 15.0.0 satisfies minimum required version 10.{'\n'}
<span {...g}></span> Xcode version 15.0.1 satisfies minimum required version 10.{'\n'}
<span {...g}></span> Getting NativeScript components versions information...{'\n'}
<span {...g}></span> Component nativescript has 8.6.0 version and is up to date.
<span {...g}></span> Component nativescript has 8.6.1 version and is up to date.
</pre>
</details>
@ -293,6 +293,14 @@ npx -p nativescript ns run android
Once the simulator launches and the test app is displayed, end the script by
selecting the terminal and entering the key sequence `CTRL + C`
:::note pass
If the emulator is not running, `nativescript` may fail with the message:
```
Emulator start failed with: No emulator image available for device identifier 'undefined'.
```
6) From the project folder, install the library:
<CodeBlock language="bash">{`\
@ -603,6 +611,37 @@ npx -p nativescript ns run android
The app should show Presidential data.
### Android Device
28) Connect an Android device using a USB cable.
If the device asks to allow USB debugging, tap "Allow".
29) Close any Android / iOS emulators.
30) Build APK and run on device:
```bash
npx -p nativescript ns run android
```
If the Android emulators are closed and an Android device is connected, the last
command will build an APK and install on the device.
### iOS Device
31) Connect an iOS device using a USB cable
32) Close any Android / iOS emulators.
33) Enable developer code signing certificates[^9]
34) Run on device:
```bash
npx -p nativescript ns run ios
```
[^1]: See [`read` in "Reading Files"](/docs/api/parse-options)
[^2]: See ["Workbook Object"](/docs/csf/book)
[^3]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
@ -610,4 +649,5 @@ The app should show Presidential data.
[^5]: See [`json_to_sheet` in "Utilities"](/docs/api/utilities/array#array-of-objects-input)
[^6]: See ["Workbook Helpers" in "Utilities"](/docs/api/utilities/wb) for details on `book_new` and `book_append_sheet`.
[^7]: See [`read` in "Reading Files"](/docs/api/parse-options)
[^8]: See ["Local setup"](https://docs.nativescript.org/setup/#local-setup) in the NativeScript documentation. For Windows and Linux, follow the "Android" instructions. For macOS, follow both the iOS and Android instructions.
[^8]: See ["Local setup"](https://docs.nativescript.org/setup/#local-setup) in the NativeScript documentation. For Windows and Linux, follow the "Android" instructions. For macOS, follow both the iOS and Android instructions.
[^9]: The [Flutter documentation](https://docs.flutter.dev/get-started/install/macos?tab=ios15#enable-developer-code-signing-certificates) covers the instructions in more detail. The correct workspace is `platforms/ios/SheetJSNS.xcodeproj/project.xcworkspace`

@ -37,6 +37,17 @@ The ["Demo"](#demo) creates an app that looks like the screenshots below:
</td></tr></tbody></table>
:::note Tested Deployments
This demo was tested in the following environments:
| OS | Type | Device | Quasar | Date |
|:-----------|:-----|:--------------------|:---------|:-----------|
| Android 34 | Sim | Pixel 3a | `2.14.1` | 2023-12-04 |
| iOS 17.0.1 | Sim | iPhone SE (3rd gen) | `2.14.1` | 2023-12-04 |
:::
## Integration Details
The [SheetJS NodeJS Module](/docs/getting-started/installation/nodejs) can be
@ -143,16 +154,6 @@ window.requestFileSystem(window.PERSISTENT, 0, function(fs) {
## Demo
:::note
The Android demo was last tested on 2023 September 18 with Quasar `2.12.7` on an
emulated Pixel 3 + Android 13 ("Tiramisu") API 33.
The iOS demo was last tested on 2023 September 18 with Quasar `2.12.7` on an
emulated iPhone SE (3rd generation) + iOS 16.4.
:::
The demo draws from the ViteJS example. Familiarity with VueJS and TypeScript
is assumed.

@ -45,6 +45,24 @@ The [CapacitorJS demo](/docs/demos/mobile/capacitor) covers CapacitorJS apps.
:::
:::note Tested Deployments
This demo was tested in the following environments:
| OS | Type | Device | Date |
|:-----------|:-----|:--------------------|:-----------|
| Android 34 | Sim | Pixel 3a | 2023-12-04 |
| iOS 17.0.1 | Sim | iPhone SE (3rd gen) | 2023-12-04 |
`ionic info` showed:
- Ionic: `@ionic/angular 7.5.7`, `@ionic/angular-toolkit 9.0.0`
- Cordova: `cordova-lib@12.0.1`, `android 12.0.1, ios 7.0.1`
The file integration uses `@awesome-cordova-plugins/file` version `6.4.0`.
:::
:::warning Telemetry
Before starting this demo, manually disable telemetry. On Linux and MacOS:
@ -173,23 +191,6 @@ this.file.writeFile(url, filename, blob, {replace: true});
## Demo
:::note
The project was last tested in 2023 September 10. `ionic info` showed:
- Ionic: `@ionic/angular 7.3.3`, `@ionic/angular-toolkit 9.0.0`
- Cordova: `cordova-lib@12.0.1`, `android 12.0.1, ios 7.0.1`
The file integration uses `@awesome-cordova-plugins/file` version `6.4.0`.
The iOS demo was last tested on 2023 September 10 on an emulated iPhone SE
(3rd generation) + iOS 16.4
The Android demo was last tested on 2023 September 10 on an emulated Pixel 3 +
Android 13 ("Tiramisu") API 33.
:::
The app in this demo will display data in a table.
On load, a [test file](https://sheetjs.com/pres.numbers) will be processed.
@ -230,6 +231,21 @@ If a prompt asks to confirm Cordova use, enter `Yes` to continue.
If a prompt asks about creating an Ionic account, enter `N` to opt out.
:::caution pass
Due to conflicts in the dependency tree, the command failed in the last test.
The fix is to force install all modules:
```bash
cd SheetJSIonic
npm i --force @angular/cli
npm i --force
cd ..
```
:::
4) Set up Cordova:
```bash
@ -379,6 +395,25 @@ native-run ios --app platforms/ios/build/Debug-iphonesimulator/SheetJSIonic.app
:::
:::caution pass
In some tests, the `emulate` command failed with:
```
Error: Unknown argument: platform
[ERROR] An error occurred while running subprocess ng.
ng run app:ionic-cordova-build --platform=ios exited with exit code 1.
```
The fix is to manually add `@ionic/cordova-builders`:
```bash
ng add @ionic/cordova-builders
```
:::
### Android
10) Enable file reading and writing in the Android app.

@ -28,6 +28,19 @@ The "Complete Example" creates an app that looks like the screenshots below:
</td></tr></tbody></table>
:::note Tested Deployments
This demo was tested in the following environments:
| OS | Type | Device | CapacitorJS + FS | Date |
|:-----------|:-----|:--------------------|:------------------|:-----------|
| Android 34 | Sim | Pixel 3a | `5.5.1` / `5.1.4` | 2023-12-04 |
| iOS 17.0.1 | Sim | iPhone 15 Pro Max | `5.5.1` / `5.1.4` | 2023-12-04 |
| Android 29 | Real | NVIDIA Shield | `5.5.1` / `5.1.4` | 2023-12-04 |
| iOS 15.1 | Real | iPad Pro | `5.5.1` / `5.1.4` | 2023-12-04 |
:::
:::warning Telemetry
Before starting this demo, manually disable telemetry. On Linux and MacOS:
@ -113,16 +126,6 @@ async function exportFile() {
## Demo
:::note
The Android demo was last tested on 2023 September 03 with CapacitorJS `5.3.0`
and filesystem `5.1.3` on an emulated Pixel 3 + Android 13 ("Tiramisu") API 33.
The iOS demo was last tested on 2023 September 03 with CapacitorJS `5.3.0`
and filesystem `5.1.3` on an emulated iPhone SE (3rd generation) + iOS 16.4.
:::
### Base Project
0) Follow the official "Environment Setup"[^1] instructions to set up Android
@ -175,6 +178,12 @@ npx cap init sheetjs-cap com.sheetjs.cap --web-dir=dist
npm run build
```
:::note
If prompted to create an Ionic account, type `N` and press Enter.
:::
5) Download [`src/App.svelte`](pathname:///cap/App.svelte) and replace:
```bash
@ -192,8 +201,8 @@ npx cap add android
7) Enable file reading and writing in the Android app.
The following lines must be added to `android/app/src/main/AndroidManifest.xml`
after the `Permissions` comment:
Add the highlighted lines to `android/app/src/main/AndroidManifest.xml` after
the `Permissions` comment:
```xml title="android/app/src/main/AndroidManifest.xml (add to file)"
<!-- Permissions -->
@ -259,6 +268,8 @@ npx cap sync
npx cap run ios
```
If prompted to select a target device, select "iPhone 15 Pro Max (simulator)".
13) Test the app
Open the app and observe that presidents are listed in the table.
@ -268,4 +279,61 @@ Touch "Export XLSX" and a popup will be displayed.
To see the generated file, switch to the "Files" app in the simulator and look
for `SheetJSCap.xlsx` in "On My iPhone" > "`sheetjs-cap`"
[^1]: See ["Environment Setup"](https://capacitorjs.com/docs/getting-started/environment-setup) in the CapacitorJS documentation.
### Android Device
14) Connect an Android device using a USB cable.
If the device asks to allow USB debugging, tap "Allow".
15) Close any Android / iOS emulators.
16) Build APK and run on device:
```bash
npm run build
npx cap sync
npx cap run android
```
If the Android emulators are closed and an Android device is connected, the last
command will build an APK and install on the device.
:::caution pass
For real devices running API level 29 or below, the following line must be added
to the `application` open tag in `android/app/src/main/AndroidManifest.xml`:
```xml title="android/app/src/main/AndroidManifest.xml (add highlighted attribute)"
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
// highlight-next-line
android:requestLegacyExternalStorage="true"
android:theme="@style/AppTheme">
```
:::
### iOS Device
17) Connect an iOS device using a USB cable
18) Close any Android / iOS emulators.
19) Enable developer code signing certificates[^2]
19) Run on device:
```bash
npm run build
npx cap sync
npx cap run ios
```
When prompted to select a target device, select the real device in the list.
[^1]: See ["Environment Setup"](https://capacitorjs.com/docs/getting-started/environment-setup) in the CapacitorJS documentation.
[^2]: The [Flutter documentation](https://docs.flutter.dev/get-started/install/macos?tab=ios15#enable-developer-code-signing-certificates) covers the instructions in more detail. The correct workspace is `ios/App/App.xcworkspace`

@ -37,6 +37,19 @@ The "Demo" creates an app that looks like the screenshots below:
</td></tr></tbody></table>
:::note Tested Deployments
This demo was tested in the following environments:
| OS | Type | Device | Dart | Flutter | Date |
|:-----------|:-----|:------------------|:--------|:---------|:-----------|
| Android 34 | Sim | Pixel 3a | `3.2.2` | `3.16.2` | 2023-12-04 |
| iOS 17.0.1 | Sim | iPhone 15 Pro Max | `3.2.2` | `3.16.2` | 2023-12-04 |
| Android 29 | Real | NVIDIA Shield | `3.2.2` | `3.16.2` | 2023-12-04 |
| iOS 15.1 | Real | iPad Pro | `3.2.2` | `3.16.2` | 2023-12-04 |
:::
:::warning Telemetry
Before starting this demo, manually disable telemetry. On MacOS:
@ -195,32 +208,51 @@ class SheetJSFlutterState extends State<SheetJSFlutter> {
## Demo
:::note
The Android demo was last tested on 2023 September 03 with Flutter `3.13.2`. The
simulator used Android 13 ("Tiramisu") API 33 on a Pixel 3.
The iOS demo was last tested on 2023 September 03 with Flutter `3.13.2`. The
simulator used iOS 16.4 on an iPhone SE (3rd generation).
Both tests used Dart 3.1.0 and Flutter JS plugin version `0.8.0`.
:::
0) Follow the official "Install" instructions for Flutter[^8].
Run `flutter doctor` and confirm the following items are checked:
```
[✓] Android toolchain - develop for Android devices (Android SDK version 33.0.0)
[✓] Xcode - develop for iOS and macOS (Xcode 14.2)
[✓] Android Studio (version 2022.1)
[✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
[✓] Xcode - develop for iOS and macOS (Xcode 15.0.1)
[✓] Android Studio (version 2022.3)
```
(the actual version numbers may differ)
<details open><summary><b>Installation Notes</b> (click to hide)</summary>
:::note pass
On first run, there may be a warning with "Android toolchain":
```
[!] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
! Some Android licenses not accepted. To resolve this, run: flutter doctor
--android-licenses
```
As stated, the fix is to run the command:
```bash
flutter doctor --android-licenses
```
:::
:::note pass
On first run, there may be a warning with "Xcode":
```
[!] Xcode - develop for iOS and macOS (Xcode 15.0.1)
✗ Unable to get list of installed Simulator runtimes.
```
Open "Settings" panel in Xcode. Under "Platforms", click "Get" next to "iOS".
:::
:::caution pass
In local testing, there were issues with the Android toolchain:
@ -234,7 +266,7 @@ back to `Android SDK Command-Line Tools (revision: 10.0)`
:::
:::caution pass
:::note pass
If Google Chrome is not installed, `flutter doctor` will show an issue:
@ -258,7 +290,7 @@ Run `flutter emulators` and check for both `ios` and `android` emulators:
```
apple_ios_simulator • iOS Simulator • Apple • ios
Pixel_3_API_33 • Pixel 3 API 33 • Google • android
Pixel_3a_API_34 • Pixel 3a API 34 • Google • android
```
1) Disable telemetry.
@ -297,17 +329,29 @@ List the available emulators with `flutter emulators`:
2 available emulators:
apple_ios_simulator • iOS Simulator • Apple • ios
Pixel_3_API_33 • Pixel 3 API 33 • Google • android
^^^^^^^^^^^^^^--- the first column is the name
Pixel_3a_API_34 • Pixel 3a API 34 • Google • android
^^^^^^^^^^^^^^^--- the first column is the name
```
The first column shows the name that should be passed to `emulator -avd`. In a
previous test, the name was `Pixel_3_API_33` and the launch command was:
previous test, the name was `Pixel_3a_API_34` and the launch command was:
```bash
emulator -avd Pixel_3a_API_34
```
% emulator -avd Pixel_3_API_33
:::note pass
On macOS, `~/Library/Android/sdk/emulator/` is the typical location for the
`emulator` binary. If it cannot be found, add the folder to `PATH`:
```bash
export PATH="$PATH":~/Library/Android/sdk/emulator
emulator -avd Pixel_3a_API_34
```
:::
</details>
4) While the Android emulator is open, start the application:
@ -444,6 +488,82 @@ flutter run
The app fetches <https://sheetjs.com/pres.numbers>, parses, converts data to an
array of arrays, and presents the data in a Flutter `Table` widget.
### Android Device
14) Connect an Android device using a USB cable.
If the device asks to allow USB debugging, tap "Allow".
15) Verify that `flutter` can find the device:
```bash
flutter devices
```
The list should include the device:
```
SheetJS (mobile) • 1234567890 • android-arm64 • Android 10 (API 29)
^^^^^^^--- the first column is the name
```
16) Build an APK:
```bash
flutter build apk --release
```
17) Install on the Android device:
```bash
flutter install
```
The script will ask for a device:
```
[1]: SheetJS (1234567890)
[2]: iPhone 15 Pro Max (12345678-9ABC-DEF0-1234-567890ABCDEF)
[3]: macOS (macos)
[4]: Chrome (chrome)
Please choose one (or "q" to quit):
```
Select the number corresponding to the device.
18) Launch the installed `sheetjs_flutter` app on the device
:::caution pass
The app may take 30 seconds to load the content.
**There are known bugs in the Dart HTTP client in Android 12[^9].**
:::
### iOS Device
19) Follow the official "Deploy to physical iOS devices" instructions[^10]
20) Connect the iOS device and verify that `flutter` can find the device:
```bash
flutter devices
```
The list should include the device:
```
SheetPad (mobile) • 00000000-0000000000000000 • ios • iOS 15.1 19B74
^^^^^^^^--- the first column is the name
```
21) Run the program on the device:
```bash
flutter run -d SheetPad
```
[^1]: <https://dart.dev/> is the official site for the Dart Programming Language.
[^2]: <https://flutter.dev/> is the official site for the Flutter Framework.
[^3]: [The `flutter_js` package](https://pub.dev/packages/flutter_js) is hosted on the Dart package repository.
@ -452,3 +572,5 @@ array of arrays, and presents the data in a Flutter `Table` widget.
[^6]: See [`read` in "Reading Files"](/docs/api/parse-options)
[^7]: See [`sheet_to_csv` in "CSV and Text"](/docs/api/utilities/csv#delimiter-separated-output)
[^8]: See [the Flutter Installation Instructions](https://docs.flutter.dev/get-started/install)
[^9]: For example, [Issue 836 in the `http` repository](https://github.com/dart-lang/http/issues/836) mentions that API calls may take 10+ seconds. This is an issue in Dart + Flutter.
[^10]: See ["Deploy to physical iOS devices"](https://docs.flutter.dev/get-started/install/macos?tab=ios15#deploy-to-physical-ios-devices) in the Flutter documentation

@ -858,7 +858,7 @@ highlighted lines:
npx react-native run-macos
```
:::note
:::note pass
If the app runs but no window is displayed, clear caches and try again:

@ -156,85 +156,4 @@ can be adapted to generate SQL statements for a variety of databases, including:
**MySQL / MariaDB**
The `mysql2` connector library was tested. The differences are shown below,
primarily stemming from the different quoting requirements and field types.
<details><summary><b>Differences</b> (click to show)</summary>
```js
// highlight-start
// define mapping between determined types and MySQL types
const PG = { "n": "REAL", "s": "TEXT", "b": "TINYINT" };
// highlight-end
function generate_sql(ws, wsname) {
// generate an array of objects from the data
const aoo = XLSX.utils.sheet_to_json(ws);
// types will map column headers to types, while hdr holds headers in order
const types = {}, hdr = [];
// loop across each row object
aoo.forEach(row =>
// Object.entries returns a row of [key, value] pairs. Loop across those
Object.entries(row).forEach(([k,v]) => {
// If this is first time seeing key, mark unknown and append header array
if(!types[k]) { types[k] = "?"; hdr.push(k); }
// skip null and undefined
if(v == null) return;
// check and resolve type
switch(typeof v) {
case "string": // strings are the broadest type
types[k] = "s"; break;
case "number": // if column is not string, number is the broadest type
if(types[k] != "s") types[k] = "n"; break;
case "boolean": // only mark boolean if column is unknown or boolean
if("?b".includes(types[k])) types[k] = "b"; break;
default: types[k] = "s"; break; // default to string type
}
})
);
// The final array consists of the CREATE TABLE query and a series of INSERTs
return [
// generate CREATE TABLE query and return batch
// highlight-next-line
`CREATE TABLE ${wsname} (${hdr.map(h =>
// highlight-next-line
`${h} ${PG[types[h]]}`
).join(", ")});`
].concat(aoo.map(row => { // generate INSERT query for each row
// entries will be an array of [key, value] pairs for the data in the row
const entries = Object.entries(row);
// fields will hold the column names and values will hold the values
const fields = [], values = [];
// check each key/value pair in the row
entries.forEach(([k,v]) => {
// skip null / undefined
if(v == null) return;
// highlight-next-line
fields.push(`${k}`);
// when the field type is numeric, `true` -> 1 and `false` -> 0
if(types[k] == "n") values.push(typeof v == "boolean" ? (v ? 1 : 0) : v);
// otherwise,
// highlight-next-line
else values.push(`"${v.toString().replaceAll('"', '""')}"`);
})
if(fields.length) return `INSERT INTO \`${wsname}\` (${fields.join(", ")}) VALUES (${values.join(", ")})`;
})).filter(x => x); // filter out skipped rows
}
```
</details>
The first property of a query result is an array of objects that plays nice
with `json_to_sheet`:
```js
const aoa = await connection.query(`SELECT * FROM DataTable`)[0];
const worksheet = XLSX.utils.json_to_sheet(aoa);
```
**[The exposition has been moved to a separate page.](/docs/demos/data/mariadb)**

@ -24,12 +24,6 @@ This demo uses SQLite and SheetJS to exchange data between spreadsheets and SQL
servers. We'll explore how to use save tables from a database to spreadsheets
and how to add data from spreadsheets into a database.
:::note
This demo was last tested on 2023 October 13
:::
:::info pass
This demo covers SQLite `.db` file processing.
@ -39,6 +33,19 @@ SQLite-compatible database built into Chromium and Google Chrome.
:::
:::note Tested Deployments
This demo was tested in the following environments:
| Platform | Connector Library | Date |
|:-----------------|:---------------------------|:-----------|
| Chromium 119 | `sql.js` (`1.8.0`) | 2023-12-04 |
| NodeJS `20.10.0` | `better-sqlite3` (`9.2.0`) | 2023-12-04 |
| BunJS `1.0.15` | (built-in) | 2023-12-04 |
| Deno `1.38.4` | `sqlite` (`3.8`) | 2023-12-04 |
:::
## Demo
The following examples show how to query for each table in an SQLite database,
@ -161,7 +168,7 @@ function SheetJSQLJS() { return (<button onClick={async() => {
const rowobj = stmt.getAsObject();
/* add to sheet */
XLSX.utils.sheet_add_json(ws, [rowobj], { header, origin: -1, skipHeader: true});
XLSX.utils.sheet_add_json(ws, [rowobj], { header, origin: -1, skipHeader: true });
}
if(ws) XLSX.utils.book_append_sheet(wb, ws, row.name);
}
@ -190,6 +197,8 @@ var aoo = db.prepare("SELECT * FROM 'Invoice' LIMIT 100000").all();
var ws = XLSX.utils.json_to_sheet(aoo, {dense: true});
```
#### NodeJS Demo
0) Build `chinook.db` from [the SQL statements](pathname:///sqlite/chinook.sql):
```bash
@ -200,7 +209,8 @@ sqlite3 chinook.db ".read chinook.sql"
1) Install the dependencies:
<CodeBlock language="bash">{`\
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz better-sqlite3@9.0.0`}
npm init -y
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz better-sqlite3@9.2.0`}
</CodeBlock>
2) Download [`SheetJSQLiteNode.mjs`](pathname:///sqlite/SheetJSQLiteNode.mjs):
@ -235,6 +245,8 @@ var aoo = db.prepare("SELECT * FROM 'Invoice' LIMIT 100000").all();
var ws = XLSX.utils.json_to_sheet(aoo, {dense: true});
```
#### BunJS Demo
0) Build `chinook.db` from [the SQL statements](pathname:///sqlite/chinook.sql):
```bash
@ -282,6 +294,8 @@ var data = [query.columns().map(x => x.name)].concat(aoa);
var ws = XLSX.utils.aoa_to_sheet(data, {dense: true});`}
</CodeBlock>
#### Deno Demo
0) Build `chinook.db` from [the SQL statements](pathname:///sqlite/chinook.sql):
```bash

@ -19,10 +19,14 @@ This demo uses KnexJS and SheetJS to exchange data between spreadsheets and SQL
servers. We'll explore how to use save tables from a database to spreadsheets
and how to add data from spreadsheets into a database.
:::note
:::note Tested Deployments
This demo was last tested on 2023 September 23 with Knex 2.5.1. The demo uses
the SQLite backend with the `better-sqlite3` connector.
This demo was tested in the following environments:
| Version | Database | Connector Module | Date |
|:--------|:---------|:-----------------|:-----------|
| `2.5.1` | SQLite | `better-sqlite` | 2023-12-04 |
| `3.0.1` | SQLite | `better-sqlite` | 2023-12-04 |
:::

@ -30,10 +30,14 @@ against SQL injection and other vulnerabilities.
:::
:::note
:::note Tested Deployments
This demo was last tested on 2023 October 30 with PostgreSQL 16.0.1. The demo
uses `pg` connector module version 8.11.3.
This demo was tested in the following environments:
| Postgres | Connector Library | Date |
|:---------|:------------------|:-----------|
| `16.0.1` | `pg` (`8.11.3`) | 2023-10-30 |
| `15.5` | `pg` (`8.11.3`) | 2023-12-04 |
:::
@ -372,18 +376,24 @@ correct host name and port number.
- If the server expects a different username and password, uncomment the `user`
and `password` lines and replace the values with the username and password.
11) Run the script:
11) Fetch the example file [`pres.numbers`](https://sheetjs.com/pres.numbers):
```bash
curl -L -O https://sheetjs.com/pres.numbers
```
12) Run the script:
```bash
node SheetJSPG.js
```
12) Verify the result:
13) Verify the result:
- `SheetJSPG.xlsx` can be opened in a spreadsheet app or tested in the terminal
- `SheetJSPGExport.xlsx` can be opened in a spreadsheet app or tested in the terminal
```bash
npx xlsx-cli SheetJSPG.xlsx
npx xlsx-cli SheetJSPGExport.xlsx
```
- The database server can be queried using the `psql` command line tool.

@ -0,0 +1,401 @@
---
title: Sheets with MariaDB and MySQL
sidebar_label: MariaDB / MySQL
pagination_prev: demos/desktop/index
pagination_next: demos/local/index
sidebar_custom_props:
sql: true
---
import current from '/version.js';
import CodeBlock from '@theme/CodeBlock';
[MariaDB](https://mariadb.com/) is an open source object-relational database
system compatible with MySQL.
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
data from spreadsheets.
This demo uses SheetJS to exchange data between spreadsheets and MariaDB
databases. We'll explore how to save tables from a database to spreadsheets and
how to add data from spreadsheets into a database.
:::caution pass
**It is strongly recommended to use MariaDB with a query builder or ORM.**
While it is possible to generate SQL statements directly, there are many subtle
details and pitfalls. Battle-tested solutions generally provide mitigations
against SQL injection and other vulnerabilities.
:::
:::note Tested Deployments
This demo was tested in the following environments:
| MariaDB | Connector Library | Date |
|:---------|:-------------------|:-----------|
| `11.2.2` | `mysql2` (`3.6.5`) | 2023-12-04 |
:::
## Integration Details
The [SheetJS NodeJS module](/docs/getting-started/installation/nodejs) can be
loaded in NodeJS scripts that connect to MariaDB and MySQL databases.
This demo uses the `mysql2` connector module[^1], but the same mechanics apply
to other MariaDB and MySQL libraries.
### Exporting Data
`Connection#execute` returns a Promise that resolves to a result array. The
first entry of the result is an array of objects.
The SheetJS `json_to_sheet` method[^2] can generate a worksheet object[^3] from
the array of objects:
```js
const mysql = require("mysql2/promise"), XLSX = require("xlsx");
const conn = await mysql.createConnection({
database: "SheetJSMariaDB",
/* ... other options ... */
});
const table_name = "Tabeller1"; // name of table
/* fetch all data from specified table */
const [rows, fields] = await conn.execute(`SELECT * FROM ${mysql.escapeId(table_name)}`);
/* generate a SheetJS worksheet object from the data */
const worksheet = XLSX.utils.json_to_sheet(rows);
```
A workbook object can be built from the worksheet using utility functions[^4].
The workbook can be exported using the SheetJS `writeFile` method[^5]:
```js
/* create a new workbook and add the worksheet */
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, worksheet, "Sheet1");
/* export workbook to XLSX */
XLSX.writeFile(wb, "SheetJSMariaDBExport.xlsx");
```
### Importing Data
The SheetJS `sheet_to_json` function[^6] takes a worksheet object and generates
an array of objects.
Queries must be manually generated from the objects. Assuming the field names
in the object match the column headers, a loop can generate `INSERT` queries.
:::warning pass
**MariaDB does not allow parameterized queries with variable column names**
```sql
INSERT INTO table_name (?) VALUES (?);
-- ---------------------^ variable column names are not valid
```
Queries are generated manually. To help prevent SQL injection vulnerabilities,
the undocumented `escapeId` method [^7] escapes identifiers and fields.
:::
```js
/* generate an array of arrays from the worksheet */
const aoo = XLSX.utils.sheet_to_json(ws);
const table_name = "Blatte1"; // name of table
/* loop through the data rows */
for(let row of aoo) {
/* generate INSERT column names and values */
const ent = Object.entries(row);
const Istr = ent.map(e => I(e[0])).join(", ");
const Vstr = ent.map(e => E(e[1])).join(", ");
/* execute INSERT statement */
await conn.execute(`INSERT INTO ${I(table_name)} (${Istr}) VALUES (${Vstr})`);
}
```
### Creating a Table
The array of objects can be scanned to determine column names and types. With
the names and types, a `CREATE TABLE` query can be written.
<details><summary><b>Implementation Details</b> (click to show)</summary>
The `aoo_to_mariadb_table` function:
- scans each row object to determine column names and types
- drops and creates a new table with the determined column names and types
- loads the entire dataset into the new table
```js
/* create table and load data given an array of objects and a mysql2 connection */
async function aoo_to_mariadb_table(conn, aoo, table_name) {
/* define types that can be converted (e.g. boolean can be stored in float) */
const T_FLOAT = ["DOUBLE", "BOOLEAN"];
const T_BOOL = ["BOOLEAN"];
/* types is a map from column headers to Knex schema column type */
const types = {};
/* names is an ordered list of the column header names */
const names = [];
/* loop across each row object */
aoo.forEach(row =>
/* Object.entries returns a row of [key, value] pairs */
Object.entries(row).forEach(([k,v]) => {
/* If this is first occurrence, mark unknown and append header to names */
if(!types[k]) { types[k] = ""; names.push(k); }
/* skip null and undefined values */
if(v == null) return;
/* check and resolve type */
switch(typeof v) {
/* change type if it is empty or can be stored in a float */
case "number": if(!types[k] || T_FLOAT.includes(types[k])) types[k] = "DOUBLE"; break;
/* change type if it is empty or can be stored in a boolean */
case "boolean": if(!types[k] || T_BOOL.includes(types[k])) types[k] = "BOOLEAN"; break;
/* no other type can hold strings */
case "string": types[k] = "TEXT"; break;
default: types[k] = "TEXT"; break;
}
})
);
const I = (id) => mysql.escapeId(id), E = (d) => mysql.escape(d);
/* Delete table if it exists in the DB */
await conn.execute(`DROP TABLE IF EXISTS ${I(table_name)};`);
/* Create table */
{
const Istr = Object.entries(types).map(e => `${I(e[0])} ${e[1]}`).join(", ");
await conn.execute(`CREATE TABLE ${I(table_name)} (${Istr});`);
}
/* Insert each row */
for(let row of aoo) {
const ent = Object.entries(row);
const Istr = ent.map(e => I(e[0])).join(", ");
const Vstr = ent.map(e => E(e[1])).join(", ");
await conn.execute(`INSERT INTO ${I(table_name)} (${Istr}) VALUES (${Vstr})`);
}
return conn;
}
```
</details>
## Complete Example
0) Install and start the MariaDB server.
<details><summary><b>Installation Notes</b> (click to show)</summary>
On macOS, install the `mariadb` formula with Homebrew:
```bash
brew install mariadb
```
The last few lines of the installer explain how to start the database:
```text
Or, if you don't want/need a background service you can just run:
// highlight-next-line
/usr/local/opt/mariadb/bin/mysqld_safe --datadir\=/usr/local/var/mysql
```
Run the command to start a local database instance.
</details>
1) Drop any existing database with the name `SheetJSMariaDB`:
```bash
mysql -e 'drop database if exists SheetJSMariaDB;'
```
:::info pass
If the server is running elsewhere, or if the username is different from the
current user, command-line flags can override the defaults.
| Option | Explanation
|:--------------|:--------------------------|
| `-h HOSTNAME` | Name of the server |
| `-P PORT` | specifies the port number |
| `-U USERNAME` | specifies the username |
| `-p PASSWORD` | specifies the password |
:::
2) Create an empty `SheetJSMariaDB` database:
```bash
mysql -e 'create database SheetJSMariaDB;'
```
### Connector Test
3) Create a project folder:
```bash
mkdir sheetjs-mariadb
cd sheetjs-mariadb
npm init -y
```
4) Install the `mysql2` connector module:
```bash
npm i --save mysql2@3.6.5
```
5) Save the following example codeblock to `MariaDBTest.js`:
```js title="MariaDBTest.js"
const mysql = require("mysql2/promise");
(async() => {
const conn = await mysql.createConnection({
database:"SheetJSMariaDB",
// highlight-start
host: "127.0.0.1", // localhost
port: 3306,
user: "sheetjs",
//password: ""
// highlight-end
});
const [rows, fields] = await conn.execute('SELECT ? as message', ['Hello world!']);
console.log(rows[0].message); // Hello world!
await conn.end();
})();
```
6) Edit the new `MariaDBTest.js` script and modify the highlighted lines from
the codeblock to reflect the database deployment settings.
- Set `user` to the username (it is almost certainly not `"sheetjs"`)
- If the server is not running on your computer, set `host` and `port` to the
correct host name and port number.
- If the server expects a password, uncomment the `password` line and replace
the value with the password.
7) Run the script:
```bash
node MariaDBTest.js
```
It should print `Hello world!`
:::caution pass
If the output is not `Hello world!` or if there is an error, please report the
issue to the `mysql2` connector project for further diagnosis[^8]
:::
### Add SheetJS
8) Install dependencies:
<CodeBlock language="bash">{`\
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
</CodeBlock>
9) Download [`SheetJSMariaDB.js`](pathname:///mariadb/SheetJSMariaDB.js):
```bash
curl -LO https://docs.sheetjs.com/mariadb/SheetJSMariaDB.js
```
This script will:
- read and parse the test file `pres.numbers`
- create a connection to the `SheetJSMariaDB` database on a local MariaDB server
- load data from the first worksheet into a table with name `Presidents`
- disconnect and reconnect to the database
- dump data from the table `Presidents`
- export the dataset to `SheetJSMariaDB.xlsx`
10) Edit the `SheetJSMariaDB.js` script.
The script defines an `opts` object:
```js title="SheetJSMariaDB.js (configuration lines)"
const XLSX = require("xlsx");
const opts = {
database:"SheetJSMariaDB",
// highlight-start
host: "127.0.0.1", // localhost
port: 3306,
user: "sheetjs",
//password: ""
// highlight-end
};
```
Modify the highlighted lines to reflect the database deployment settings.
- Set `user` to the username (it is almost certainly not `"sheetjs"`)
- If the server is not running on your computer, set `host` and `port` to the
correct host name and port number.
- If the server expects a password, uncomment the `password` line and replace
the value with the password.
11) Fetch the example file [`pres.numbers`](https://sheetjs.com/pres.numbers):
```bash
curl -L -O https://sheetjs.com/pres.numbers
```
12) Run the script:
```bash
node SheetJSMariaDB.js
```
13) Verify the result:
- `SheetJSMariaDBExport.xlsx` can be opened in a spreadsheet app or tested in the terminal
```bash
npx xlsx-cli SheetJSMariaDBExport.xlsx
```
- The database server can be queried using the `mysql` command line tool.
If the server is running locally, the command will be:
```bash
mysql -D SheetJSMariaDB -e 'SELECT * FROM `Presidents`;'
```
[^1]: See [the `mysql2` repository](https://github.com/sidorares/node-mysql2) for more info.
[^2]: See [`json_to_sheet` in "Utilities"](/docs/api/utilities/array#array-of-objects-input)
[^3]: See ["Sheet Objects"](/docs/csf/sheet) in "SheetJS Data Model" for more details.
[^4]: See ["Workbook Helpers" in "Utilities"](/docs/api/utilities/wb) for details on `book_new` and `book_append_sheet`.
[^5]: See [`writeFile` in "Writing Files"](/docs/api/write-options)
[^6]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
[^7]: The `mysql2` connector library `escapeId` method is not mentioned in the documentation but is present in the TypeScript definitions.
[^8]: The official [issue tracker](https://github.com/sidorares/node-mysql2/issues) is hosted on GitHub

@ -1,5 +1,6 @@
---
title: AlaSQL
title: Sheets with AlaSQL
sidebar_label: AlaSQL
pagination_prev: demos/desktop/index
pagination_next: demos/local/index
sidebar_custom_props:
@ -29,8 +30,8 @@ This demo was tested in the following environments:
| Environment | AlaSQL | Date |
|:--------------------|:-------|:----------:|
| NodeJS | 3.1.0 | 2023-10-26 |
| Standalone (Chrome) | 3.0.0 | 2023-11-27 |
| NodeJS | 3.1.0 | 2023-12-04 |
| Standalone (Chrome) | 3.1.0 | 2023-12-04 |
:::
@ -187,7 +188,7 @@ The `XLSX` "into" target calls `XLSX.writeFile` under the hood:
(async() => {
const data = [
{ Name: "Bill Clinton", Index: 42 },
{ Name: "Someone Else", Index: 47 }
{ Name: "SheetJS Dev", Index: 47 }
];
await alasql.promise(`SELECT * INTO XLSX("PresMod5.xlsx") FROM ?`, [data]);
/* PresMod5.xlsx will be created */
@ -249,7 +250,7 @@ const { promise: alasql } = require("alasql");
(async() => {
const data = [
{ Name: "Bill Clinton", Index: 42 },
{ Name: "Someone Else", Index: 47 }
{ Name: "SheetJS Dev", Index: 47 }
];
await alasql(`SELECT * INTO XLSX("PresMod5.xlsx") FROM ?`, [data]);
/* PresMod5.xlsx will be created */
@ -302,7 +303,7 @@ const { promise: alasql } = require("alasql");
console.log(data);
/* write data from JS to spreadsheet */
data.push({Name: "Someone Else", Index: 47});
data.push({ Name: "SheetJS Dev", Index: 47 });
await alasql(`SELECT * INTO XLSX("SheetJSAlaSQL1.xlsx") FROM ?`, [data]);
})();
```
@ -330,5 +331,5 @@ Name,Index
Bill Clinton,42
GeorgeW Bush,43
Barack Obama,44
Someone Else,47
SheetJS Dev,47
```

@ -1,5 +1,6 @@
---
title: MongoDB
title: Sheets with MongoDB
sidebar_label: MongoDB
pagination_prev: demos/desktop/index
pagination_next: demos/local/index
sidebar_custom_props:
@ -19,10 +20,14 @@ This demo uses SheetJS to exchange data between spreadsheets and MongoDB. We'll
explore how to use save tables from a MongoDB collection to spreadsheets and how
to add data from spreadsheets into a collection.
:::note
:::note Tested Deployments
This demo was last tested on 2023 October 12 with MongoDB CE 7.0.2, MongoDB
connector module 5.7.0 and NodeJS 20.8.0
This demo was tested in the following environments:
| MongoDB CE | Connector Library | Date |
|:-----------|:--------------------|:-----------|
| `6.0.10` | `mongodb` (`5.7.0`) | 2023-12-04 |
| `7.0.2` | `mongodb` (`5.7.0`) | 2023-12-04 |
:::

@ -21,6 +21,17 @@ This demo defines a schema for storing Redis databases in spreadsheets. We'll
explore how to use SheetJS and Redis NodeJS connector modules to pull data
from XLSX files to a Redis database and to serialize a database to a workbook.
:::note Tested Deployments
This demo was tested in the following environments:
| Redis | Connector Module | Date |
|:--------|--------------------|:----------:|
| `6.2.9` | `redis` (`4.6.11`) | 2023-12-04 |
| `7.2.3` | `redis` (`4.6.11`) | 2023-12-04 |
:::
#### Overview
Redis has 5 core data types: "String", List", "Set", "Sorted Set", and "Hash".
@ -208,13 +219,6 @@ const aoa = [ ["Hash"], [key] ].concat(Object.entries(values));
## Complete Example
:::note Tested Deployments
This demo was last tested on 2023 November 18 with Redis 7.2.3, Redis connector
module 4.6.10 and NodeJS 20.9.0.
:::
:::warning pass
The most recent version of the `redis` node module does not work with most

@ -23,10 +23,14 @@ arrays of objects to mesh with both libraries.
The ["Complete Example"](#complete-example) section imbues the official "Todos"
demo with the ability to export the list to XLSX workbooks.
:::note
:::note Tested Deployments
This demo was last tested on 2023 September 04 against PouchDB 8.0.1 standalone
browser script in Chrome 116.
This demo was tested in the following environments:
| PouchDB | Date |
|:--------|:----------:|
| `7.3.1` | 2023-12-04 |
| `8.0.1` | 2023-12-04 |
:::

@ -181,7 +181,7 @@ Deno.writeFileSync(out_file, new TextEncoder().encode(csv));`}
:::note Tested Deployments
This was last tested by SheetJS users on 2023 September 24 using the GitHub UI.
This was last tested by SheetJS users on 2023 December 04.
:::
@ -190,7 +190,6 @@ This was last tested by SheetJS users on 2023 September 24 using the GitHub UI.
<https://github.com/SheetJS/flat-sheet> is an example from a previous test. The
Flat Viewer URL for the repo is <https://flatgithub.com/SheetJS/flat-sheet/>
:::
### Create Project

@ -336,13 +336,29 @@ first worksheet name, and the contents of the first sheet as CSV rows.
### CLI Test
:::note
:::note Tested Deployments
This demo was last tested on 2023 October 11 against QuickJS commit `2788d71`.
This demo was tested in the following environments:
| Git Commit | Date |
|:-----------|:-----------|
| `03cc5ec` | 2023-12-01 |
| `2788d71` | 2023-10-11 |
When the demo was tested, commit `03cc5ec` corresponded to the latest commit.
:::
0) Ensure `qjs` command line utility is installed
0) Build the `qjs` command line utility from source:
```bash
git clone https://github.com/bellard/quickjs
cd quickjs
git checkout 03cc5ec
make
cd ..
cp quickjs/qjs .
```
1) Download the SheetJS Standalone script and the test file. Save both files in
the project directory:
@ -366,10 +382,11 @@ curl -LO https://docs.sheetjs.com/quickjs/SheetJSQuick.js
3) Test the program:
```bash
qjs SheetJSQuick.js
./qjs SheetJSQuick.js
```
If successful, the script will generate `SheetJSQuick.xlsx`.
If successful, the script will print CSV rows and generate `SheetJSQuick.xlsx`.
The generated file can be opened in Excel or another spreadsheet editor.
[^1]: See ["Runtime and Contexts"](https://bellard.org/quickjs/quickjs.html#Runtime-and-contexts) in the QuickJS documentation

@ -405,7 +405,7 @@ formula `=CONTAR(A1:C3;B4:D6)` is equivalent to the SheetJS formula string
[JSON Translation table](https://oss.sheetjs.com/notes/fmla/table.json).
<details open><summary><b>Function Name Translator</b> (click to show)</summary>
<details open><summary><b>Function Name Translator</b> (click to hide)</summary>
```jsx live
/* The live editor requires this function wrapper */

@ -306,7 +306,7 @@ sudo apt-get install curl git mercurial subversion
Other Linux distributions may use other package managers.
<details open><summary><b>Steam Deck</b> (click to show)</summary>
<details open><summary><b>Steam Deck</b> (click to hide)</summary>
Desktop Mode on the Steam Deck uses `pacman`. It also requires a few steps.

@ -1,4 +1,4 @@
<table><thead><th>Name</th><th>Index</th></thead>
<table><thead><tr><th>Name</th><th>Index</th></tr></thead>
<tbody>
{% for row in pres %}
<tr>

@ -0,0 +1,47 @@
import * as esbuild from 'esbuild'
import * as XLSX from 'xlsx';
import * as fs from 'fs';
XLSX.set_fs(fs);
/* plugin */
let sheetjsPlugin = {
name: 'sheetjs',
setup(build) {
/* match NUMBERS, XLSX, XLS, and XLSB files */
const EXTS = /.(numbers|xlsx|xls|xlsb)$/;
/* this method will be called once for each referenced file */
build.onLoad({ filter: EXTS }, (args) => {
/* parse file from filesystem */
const wb = XLSX.readFile(args.path);
/* get first worksheet */
const ws = wb.Sheets[wb.SheetNames[0]];
/* workaround for JSON limitation */
Date.prototype.toJSON2 = Date.prototype.toJSON;
Date.prototype.toJSON = function() { return {d:this.toISOString()}; };
/* generate row objects */
const data = XLSX.utils.sheet_to_json(ws);
/* generate final module code */
const res = JSON.stringify(data);
Date.prototype.toJSON = Date.prototype.toJSON2;
const contents = `const data = ${res};
data.forEach(row => {
Object.keys(row).forEach(k => {
if(row[k]?.d) row[k] = new Date(row[k].d);
})
});
export default data;`
return { contents, loader: 'js' };
});
},
};
await esbuild.build({
entryPoints: ['app.js'],
bundle: true,
outfile: 'out.js',
plugins: [sheetjsPlugin],
});

@ -0,0 +1,103 @@
const mysql = require("mysql2/promise");
const XLSX = require("xlsx");
const opts = {
database:"SheetJSMariaDB",
host: "127.0.0.1", // localhost
port: 3306,
user: "sheetjs",
//password: ""
};
/* create table and load data given an array of objects and a mysql2 connection */
async function aoo_to_mariadb_table(conn, aoo, table_name) {
/* define types that can be converted (e.g. boolean can be stored in float) */
const T_FLOAT = ["DOUBLE", "BOOLEAN"];
const T_BOOL = ["BOOLEAN"];
/* types is a map from column headers to Knex schema column type */
const types = {};
/* names is an ordered list of the column header names */
const names = [];
/* loop across each row object */
aoo.forEach(row =>
/* Object.entries returns a row of [key, value] pairs */
Object.entries(row).forEach(([k,v]) => {
/* If this is first occurrence, mark unknown and append header to names */
if(!types[k]) { types[k] = ""; names.push(k); }
/* skip null and undefined values */
if(v == null) return;
/* check and resolve type */
switch(typeof v) {
/* change type if it is empty or can be stored in a float */
case "number": if(!types[k] || T_FLOAT.includes(types[k])) types[k] = "DOUBLE"; break;
/* change type if it is empty or can be stored in a boolean */
case "boolean": if(!types[k] || T_BOOL.includes(types[k])) types[k] = "BOOLEAN"; break;
/* no other type can hold strings */
case "string": types[k] = "TEXT"; break;
default: types[k] = "TEXT"; break;
}
})
);
const I = (id) => mysql.escapeId(id), E = (d) => mysql.escape(d);
/* Delete table if it exists in the DB */
await conn.execute(`DROP TABLE IF EXISTS ${I(table_name)};`);
/* Create table */
{
const Istr = Object.entries(types).map(e => `${I(e[0])} ${e[1]}`).join(", ");
await conn.execute(`CREATE TABLE ${I(table_name)} (${Istr});`);
}
/* Insert each row */
for(let row of aoo) {
const ent = Object.entries(row);
const Istr = ent.map(e => I(e[0])).join(", ");
const Vstr = ent.map(e => E(e[1])).join(", ");
await conn.execute(`INSERT INTO ${I(table_name)} (${Istr}) VALUES (${Vstr})`);
}
return conn;
}
(async() => {
/* read file and get first worksheet */
const oldwb = XLSX.readFile("pres.numbers");
const oldws = oldwb.Sheets[oldwb.SheetNames[0]];
/* import data to mariadb */
let conn = await mysql.createConnection(opts);
try {
/* generate array of objects from worksheet */
const aoo = XLSX.utils.sheet_to_json(oldws);
/* create table and load data */
await aoo_to_mariadb_table(conn, aoo, "Presidents");
} finally {
/* disconnect */
await conn.end();
}
/* export data to xlsx */
conn = await mysql.createConnection(opts);
try {
/* fetch all data from specified table */
const [ rows ] = await conn.execute(`SELECT * FROM ${mysql.escapeId("Presidents")}`);
/* export to file */
const newws = XLSX.utils.json_to_sheet(rows);
const newwb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(newwb, newws, "Export");
XLSX.writeFile(newwb, "SheetJSMariaDBExport.xlsx");
} finally {
/* disconnect */
await conn.end();
}
})();

@ -16,6 +16,9 @@ rh.close();
/* parse file */
var wb = XLSX.read(ab);
/* print CSV rows from first sheet */
console.log(XLSX.utils.sheet_to_csv(wb.Sheets[wb.SheetNames[0]]));
/* write XLSX */
var out = XLSX.write(wb, {bookType: "xlsx", type: "array"});