diff --git a/docz/docs/02-getting-started/02-examples/02-export.md b/docz/docs/02-getting-started/02-examples/02-export.md
index 46a16cd..4d594db 100644
--- a/docz/docs/02-getting-started/02-examples/02-export.md
+++ b/docz/docs/02-getting-started/02-examples/02-export.md
@@ -469,14 +469,16 @@ The result is an array of "simple" objects with no nesting:
## Create a Workbook
-With the cleaned dataset, `XLSX.utils.json_to_sheet`[^3] generates a worksheet:
+The [`json_to_sheet`](/docs/api/utilities/array#array-of-objects-input) method
+can generate a SheetJS worksheet from the cleaned dataset:
```js
const worksheet = XLSX.utils.json_to_sheet(rows);
```
-`XLSX.utils.book_new`[^4] creates a new workbook and `XLSX.utils.book_append_sheet`[^5]
-appends a worksheet to the workbook. The new worksheet will be called "Dates":
+[`XLSX.utils.book_new`](/docs/api/utilities/wb) creates a new workbook and
+[`XLSX.utils.book_append_sheet`](/docs/api/utilities/wb) appends a worksheet to
+the workbook. The new worksheet will be called "Dates":
```js
const workbook = XLSX.utils.book_new();
@@ -503,10 +505,12 @@ cell styling and frozen rows.
Changing Header Names (click to show)
By default, `json_to_sheet` creates a worksheet with a header row. In this case,
-the headers come from the JS object keys: "name" and "birthday".
+the headers come from the JS object keys: "name" and "birthday". The headers are
+written to the first row.
-The headers are in cells `A1` and `B1`. `XLSX.utils.sheet_add_aoa`[^6] can write
-text values to the existing worksheet starting at cell `A1`:
+[`XLSX.utils.sheet_add_aoa`](/docs/api/utilities/array#array-of-arrays-input)
+can overwrite data in the worksheet. The following line will set `A1` to "Name"
+and set `B1` to "Birthday":
```js
XLSX.utils.sheet_add_aoa(worksheet, [["Name", "Birthday"]], { origin: "A1" });
@@ -518,7 +522,7 @@ XLSX.utils.sheet_add_aoa(worksheet, [["Name", "Birthday"]], { origin: "A1" });
Changing Column Widths (click to show)
Some of the names are longer than the default column width. Column widths are
-set by setting the `"!cols"` worksheet property.[^7]
+stored in the [`"!cols"` worksheet property](/docs/csf/features/colprops).
The following line sets the width of column A to approximately 10 characters:
@@ -541,9 +545,9 @@ After cleanup, the generated workbook looks like the screenshot below:
## Export a File
-`XLSX.writeFile`[^8] creates a spreadsheet file and tries to write it to the
-system. In the browser, it will try to prompt the user to download the file. In
-NodeJS, it will write to the local directory.
+[`XLSX.writeFile`](/docs/api/write-options) creates a spreadsheet file and tries
+to write it to the system. In the browser, it will try to prompt the user to
+download the file. In NodeJS, it will write to the local directory.
```js
XLSX.writeFile(workbook, "Presidents.xlsx", { compression: true });
@@ -1170,9 +1174,3 @@ see a preview of the data. The Numbers app can open the file.
dedicated the content to the public domain. When this demo was last tested,
[^2]: See ["The Executive Branch"](https://github.com/unitedstates/congress-legislators#the-executive-branch)
in the dataset documentation.
-[^3]: See [`json_to_sheet` in "Utilities"](/docs/api/utilities/array#array-of-objects-input)
-[^4]: See [`book_new` in "Utilities"](/docs/api/utilities/wb)
-[^5]: See [`book_append_sheet` in "Utilities"](/docs/api/utilities/wb)
-[^6]: See [`sheet_add_aoa` in "Utilities"](/docs/api/utilities/array#array-of-arrays-input)
-[^7]: See ["Column Properties"](/docs/csf/features/colprops)
-[^8]: See [`writeFile` in "Writing Files"](/docs/api/write-options)
\ No newline at end of file
diff --git a/docz/docs/02-getting-started/02-examples/04-import.md b/docz/docs/02-getting-started/02-examples/04-import.md
index 83850fb..2a1e5a3 100644
--- a/docz/docs/02-getting-started/02-examples/04-import.md
+++ b/docz/docs/02-getting-started/02-examples/04-import.md
@@ -148,14 +148,15 @@ The file data is stored in an `ArrayBuffer`.
## Parse File
-With the file data in hand, `XLSX.read`[^2] parses the workbook:
+With the file data in hand, [`XLSX.read`](/docs/api/parse-options) parses the
+file and generates a SheetJS workbook object:
```js
const workbook = XLSX.read(file);
```
-The `workbook` object follows the "Common Spreadsheet Format"[^3], an in-memory
-format for representing workbooks, worksheets, cells, and spreadsheet features.
+The `workbook` object follows the ["Common Spreadsheet Format"](/docs/csf/), an
+in-memory schema for workbooks, worksheets, cells, and spreadsheet features.
## Explore Dataset
@@ -170,8 +171,8 @@ To determine how to process the data, it is best to inspect the file first.
### List Sheet Names
-As explained in the "Workbook Object"[^4] section, the `SheetNames` property is
-a ordered list of the sheet names in the workbook.
+As explained in the ["Workbook Object"](/docs/csf/book) page, the `SheetNames`
+property is a list of the sheet names in the workbook.
The following live code block displays an ordered list of the sheet names:
@@ -195,21 +196,25 @@ function SheetJSheetNames() {
### Inspect Worksheet Data
-The `Sheets` property of the workbook object[^5] is an object whose keys are
-sheet names and whose values are sheet objects. For example, the first worksheet
-is pulled by indexing `SheetNames` and using the name to index `Sheets`:
+The [`Sheets` property of the workbook object](/docs/csf/book) is an object
+whose keys are sheet names and whose values are sheet objects.
+
+For example, the first worksheet can be pulled by indexing `SheetNames` and
+using the name to index `Sheets`:
```js
var first_sheet = workbook.Sheets[workbook.SheetNames[0]];
```
-The actual worksheet object can be inspected directly[^6], but it is strongly
-recommended to use utility functions to present JS-friendly data structures.
+The [worksheet object](/docs/csf/sheet) can be inspected directly, but it is
+strongly recommended to use utility functions to extract relevant data.
### Preview HTML
-The `sheet_to_html` utility function[^7] generates an HTML table from worksheet
-objects. The following live example shows the first 20 rows of data in a table:
+The [`sheet_to_html` function](/docs/api/utilities/html#html-table-output)
+generates an HTML table from worksheet objects.
+
+The following live example shows the first 20 rows of data in a table:
Live example (click to show)
@@ -254,7 +259,8 @@ The key points from looking at the table are:
### Extract Raw Data
-`XLSX.utils.sheet_to_json`[^8] generates arrays of data from worksheet objects.
+The [`sheet_to_json` function](/docs/api/utilities/array#array-output) generates
+arrays of data from worksheet objects.
For a complex layout like this, it is easiest to generate an "array of arrays"
where each row is an array of cell values. The screenshot shows rows 5-8:
@@ -277,7 +283,7 @@ Row 7 includes the data for FY2007:
```
`XLSX.utils.sheet_to_json` will generate an array of arrays if the option
-`header: 1` is specified[^9]:
+[`header: 1`](/docs/api/utilities/array#array-output) is specified:
```js
const worksheet = workbook.Sheets[workbook.SheetNames[0]];
@@ -332,7 +338,9 @@ function SheetJSAoAHoles() {
-The worksheet `!merges` property[^10] includes every merge range in the sheet.
+The [worksheet `!merges` property](/docs/csf/features/merges) includes every
+merge range in the sheet.
+
It is possible to loop through every merge block and fill cells, but in this
case it is easier to post-process the raw data:
@@ -539,7 +547,7 @@ Looking at the headers:

The desired data is in column `I`. The column index can be calculated using
-`XLSX.utils.decode_col`[^11].
+the [`decode_col` utility function](/docs/csf/general#column-names).
Column Index calculation (click to show)
@@ -617,7 +625,7 @@ At this point, `objects` is an array of objects.
### ReactJS
The live demos in this example use ReactJS. In ReactJS, arrays of objects are
-best presented in simple HTML tables[^12]:
+best presented in [simple tables](/docs/demos/frontend/react#array-of-objects):
```jsx
@@ -1053,7 +1061,7 @@ Press `a` to run on Android.
:::info Device Testing
-The demo also runs on real Android devices! After enabling USB debugging[^13],
+The demo also runs on real Android devices! After enabling USB debugging[^2],
the Android device can be connected to the computer with a USB cable.
:::
@@ -1088,15 +1096,4 @@ When the app is loaded, the data will be displayed in rows.
[^1]: The dataset URL has changed many times over the years. The current location for the CC0-licensed dataset can be found by [searching for "National Student Loan Data System" on `data.gov`](https://catalog.data.gov/dataset/?q=national+student+loan+data+system&publisher=Office+of+Federal+Student+Aid+%28FSA%29&organization=ed-gov). `PortfolioSummary.xls` is the file name within the dataset.
-[^2]: See [`read` in "Reading Files"](/docs/api/parse-options)
-[^3]: See ["SheetJS Data Model"](/docs/csf/)
-[^4]: See ["Workbook Object"](/docs/csf/book)
-[^5]: See ["Workbook Object"](/docs/csf/book)
-[^6]: See ["Sheet Objects"](/docs/csf/sheet)
-[^7]: See [`sheet_to_html` in "Utilities"](/docs/api/utilities/html#html-table-output)
-[^8]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
-[^9]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
-[^10]: See ["Merged Cells" in "SheetJS Data Model"](/docs/csf/features/merges)
-[^11]: See ["Column Names" in "Addresses and Ranges"](/docs/csf/general#column-names)
-[^12]: See ["Array of Objects" in "ReactJS"](/docs/demos/frontend/react#array-of-objects)
-[^13]: See ["Running on Device"](https://reactnative.dev/docs/running-on-device) in the React Native documentation for more details.
\ No newline at end of file
+[^2]: See ["Running on Device"](https://reactnative.dev/docs/running-on-device) in the React Native documentation for more details.
\ No newline at end of file
diff --git a/docz/docs/03-demos/03-net/01-network/index.md b/docz/docs/03-demos/03-net/01-network/index.md
index 7d68920..72614eb 100644
--- a/docz/docs/03-demos/03-net/01-network/index.md
+++ b/docz/docs/03-demos/03-net/01-network/index.md
@@ -98,11 +98,40 @@ Each browser demo was tested in the following environments:
### XMLHttpRequest
-For downloading data, the `arraybuffer` response type generates an `ArrayBuffer`
-that can be viewed as an `Uint8Array` and fed to the SheetJS `read` method. For
-legacy browsers, the option `type: "array"` should be specified:
+`XMLHttpRequest` is a browser API for performing network requests. Files can be
+downloaded from external sources in `GET` and `POST` requests.
+
+The `responseType` property controls how `XMLHttpRequest` processes data. The
+SheetJS [`read`](/docs/api/parse-options/) method accepts a `type` option that
+specifies the data representation. SheetJS and `XMLHttpRequest` options must be
+set in tandem.
+
+In modern browsers, when the response type is `"arraybuffer"`, `XMLHttpRequest`
+will generate an `ArrayBuffer` of raw data. The SheetJS `read` method directly
+processes `ArrayBuffer` objects.
+
+```mermaid
+---
+title: XMLHttpRequest + SheetJS in modern browsers
+---
+flowchart LR
+ file[(workbook\nfile)]
+ ab[(file bytes\nArrayBuffer)]
+ wb(((SheetJS\nWorkbook)))
+ file --> |XMLHttpRequest\nresponseType="arraybuffer"| ab
+ ab --> |SheetJS read\n\n|wb
+```
+
+The following example fetches a file, generates an HTML `TABLE` from the first
+sheet using the [`sheet_to_html`](/docs/api/utilities/html#html-table-output)
+method, and inserts the `TABLE` in a `DIV` container:
+
+```html title="Download and parse files with XMLHttpRequest and SheetJS"
+
+
```
@@ -135,7 +172,7 @@ function SheetJSXHRDL() {
req.responseType = "arraybuffer";
req.onload = e => {
/* Parse file */
- const wb = XLSX.read(new Uint8Array(req.response));
+ const wb = XLSX.read(req.response);
const ws = wb.Sheets[wb.SheetNames[0]];
/* Generate HTML */
@@ -150,6 +187,35 @@ function SheetJSXHRDL() {
+In browsers that do not support `ArrayBuffer`, including Internet Explorer and
+older versions of Firefox, `XMLHttpRequest` will return an array of unsigned
+bytes. In this case, the `read` method requires the option `type: "array"`:
+
+```mermaid
+---
+title: XMLHttpRequest + SheetJS in browsers lacking ArrayBuffer support
+---
+flowchart LR
+ file[(workbook\nfile)]
+ ab[(file bytes\nArray)]
+ wb(((SheetJS\nWorkbook)))
+ file --> |XMLHttpRequest\nresponseType="arraybuffer"| ab
+ ab --> |SheetJS read\ntype="array"|wb
+```
+
+```js title="Download and parse files in legacy browsers (snippet)"
+/* set up an async GET request */
+var req = new XMLHttpRequest();
+req.open("GET", url, true);
+req.responseType = "arraybuffer";
+
+req.onload = function(e) {
+ /* parse the data when it is received */
+ var workbook = XLSX.read(req.response, {type: "array"});
+ /* DO SOMETHING WITH workbook HERE */
+};
+req.send();
+```
### fetch
diff --git a/docz/docs/03-demos/23-data/14-knex.md b/docz/docs/03-demos/23-data/14-knex.md
index c8d72b2..09823f7 100644
--- a/docz/docs/03-demos/23-data/14-knex.md
+++ b/docz/docs/03-demos/23-data/14-knex.md
@@ -42,8 +42,8 @@ loaded in NodeJS scripts that use KnexJS.
The KnexJS `select` method[^1] creates a `SELECT` query. The return value is a
Promise that resolves to an array of objects.
-The SheetJS `json_to_sheet` method[^2] can generate a worksheet object[^3] from
-the array of objects:
+The SheetJS [`json_to_sheet`](/docs/api/utilities/array#array-of-objects-input)
+method can generate a [worksheet object](/docs/csf/sheet) from the array:
```js
const table_name = "Tabeller1"; // name of table
@@ -55,8 +55,9 @@ const aoo = await knex.select("*").from(table_name);
const worksheet = XLSX.utils.json_to_sheet(aoo);
```
-A workbook object can be built from the worksheet using utility functions[^4].
-The workbook can be exported using the SheetJS `writeFile` method[^5]:
+Using [`book_new` and `book_append_sheet`](/docs/api/utilities/wb), a workbook
+object can be created. This workbook is typically exported to the filesystem
+with the [`writeFile`](/docs/api/write-options) method:
```js
/* create a new workbook and add the worksheet */
@@ -69,10 +70,10 @@ XLSX.writeFile(wb, "SheetJSKnexJSExport.xlsx");
### Importing Data
-The SheetJS `sheet_to_json` function[^6] takes a worksheet object and generates
-an array of objects.
+The SheetJS [`sheet_to_json` function](/docs/api/utilities/array#array-output)
+accepts a worksheet object and generates an array of objects.
-The KnexJS `insert` method[^7] creates `INSERT` queries. The return value is a
+The KnexJS `insert` method[^2] creates `INSERT` queries. The return value is a
Promise that resolves when the query is executed:
```js
@@ -87,8 +88,8 @@ await knex.insert(aoo).into(table_name);
### Creating a Table
-The KnexJS Schema Builder supports creating tables with `createTable`[^8] and
-dropping tables with `dropTableIfExists`[^9].
+The KnexJS Schema Builder supports creating tables with `createTable`[^3] and
+dropping tables with `dropTableIfExists`[^4].
The array of objects can be scanned to determine column names and types.
@@ -272,11 +273,6 @@ Older versions of KnexJS do not support the `better-sqlite3` module. The
:::
[^1]: See [`select`](https://knexjs.org/guide/query-builder.html#select) in the KnexJS query builder documentation.
-[^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]: See [`insert`](https://knexjs.org/guide/query-builder.html#insert) in the KnexJS query builder documentation.
-[^8]: See [`createTable`](https://knexjs.org/guide/schema-builder.html#createtable) in the KnexJS Schema Builder documentation.
-[^9]: See [`dropTableIfExists`](https://knexjs.org/guide/schema-builder.html#droptableifexists) in the KnexJS Schema Builder documentation.
+[^2]: See [`insert`](https://knexjs.org/guide/query-builder.html#insert) in the KnexJS query builder documentation.
+[^3]: See [`createTable`](https://knexjs.org/guide/schema-builder.html#createtable) in the KnexJS Schema Builder documentation.
+[^4]: See [`dropTableIfExists`](https://knexjs.org/guide/schema-builder.html#droptableifexists) in the KnexJS Schema Builder documentation.
diff --git a/docz/docs/03-demos/23-data/16-postgresql.md b/docz/docs/03-demos/23-data/16-postgresql.md
index dd742c3..f9d5602 100644
--- a/docz/docs/03-demos/23-data/16-postgresql.md
+++ b/docz/docs/03-demos/23-data/16-postgresql.md
@@ -58,8 +58,8 @@ other PostgreSQL libraries.
`Client#query` returns a Promise that resolves to a result set. The `rows`
property 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:
+The SheetJS [`json_to_sheet`](/docs/api/utilities/array#array-of-objects-input)
+method can generate a [worksheet object](/docs/csf/sheet) from the array:
```js
const table_name = "Tabeller1"; // name of table
@@ -71,8 +71,8 @@ const res = await client.query(`SELECT * FROM ${table_name}`);
const worksheet = XLSX.utils.json_to_sheet(res.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]:
+[Utility functions](/docs/api/utilities/wb) can build a SheetJS workbook object.
+The workbook can be exported using the [`writeFile`](/docs/api/write-options):
```js
/* create a new workbook and add the worksheet */
@@ -85,8 +85,8 @@ XLSX.writeFile(wb, "SheetJSPGExport.xlsx");
### Importing Data
-The SheetJS `sheet_to_json` function[^6] takes a worksheet object and generates
-an array of objects.
+The SheetJS [`sheet_to_json` function](/docs/api/utilities/array#array-output)
+accepts 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.
@@ -101,7 +101,7 @@ INSERT INTO table_name (?) VALUES (?);
```
Queries are generated manually. To help prevent SQL injection vulnerabilities,
-the `pg-format`[^7] module escapes identifiers and fields.
+the `pg-format`[^2] module escapes identifiers and fields.
:::
@@ -308,7 +308,7 @@ sudo -u postgres createuser -P $USER
sudo -u postgres psql -c "ALTER USER $USER WITH SUPERUSER;"
```
-If running the optional user creation steps above, a PostgreSQL password will be required. [^8]
+If running the optional user creation steps above, a PostgreSQL password will be required. [^3]
Run the command to start a local database instance.
@@ -538,10 +538,5 @@ psql SheetJSPG -c 'SELECT * FROM "Presidents";'
[^1]: See [the official `pg` website](https://node-postgres.com/) 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 [`pg-format`](https://npm.im/pg-format) package is available on the public NPM registry. Even though the project is marked as deprecated, the official [`pg` website still recommends `pg-format`](https://node-postgres.com/features/queries#parameterized-query:~:text=use%20pg%2Dformat%20package%20for%20handling%20escaping)
-[^8]: PostgreSQL on Linux uses [SCRAM authentication by default, which requires a password](https://www.postgresql.org/docs/current/auth-password.html)
\ No newline at end of file
+[^2]: The [`pg-format`](https://npm.im/pg-format) package is available on the public NPM registry. Even though the project is marked as deprecated, the official [`pg` website still recommends `pg-format`](https://node-postgres.com/features/queries#parameterized-query:~:text=use%20pg%2Dformat%20package%20for%20handling%20escaping)
+[^3]: PostgreSQL on Linux uses [SCRAM authentication by default, which requires a password](https://www.postgresql.org/docs/current/auth-password.html)
\ No newline at end of file
diff --git a/docz/docs/03-demos/23-data/17-mariadb.md b/docz/docs/03-demos/23-data/17-mariadb.md
index 6b4ddf4..0916b79 100644
--- a/docz/docs/03-demos/23-data/17-mariadb.md
+++ b/docz/docs/03-demos/23-data/17-mariadb.md
@@ -53,8 +53,8 @@ to other MariaDB and MySQL libraries.
`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:
+The SheetJS [`json_to_sheet`](/docs/api/utilities/array#array-of-objects-input)
+method can generate a [worksheet object](/docs/csf/sheet) from the array:
```js
const mysql = require("mysql2/promise"), XLSX = require("xlsx");
@@ -72,8 +72,9 @@ const [rows, fields] = await conn.execute(`SELECT * FROM ${mysql.escapeId(table_
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]:
+Using [`book_new` and `book_append_sheet`](/docs/api/utilities/wb), a workbook
+object can be created. This workbook is typically exported to the filesystem
+with the [`writeFile`](/docs/api/write-options) method:
```js
/* create a new workbook and add the worksheet */
@@ -86,8 +87,8 @@ XLSX.writeFile(wb, "SheetJSMariaDBExport.xlsx");
### Importing Data
-The SheetJS `sheet_to_json` function[^6] takes a worksheet object and generates
-an array of objects.
+The SheetJS [`sheet_to_json` function](/docs/api/utilities/array#array-output)
+accepts 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.
@@ -102,7 +103,7 @@ INSERT INTO table_name (?) VALUES (?);
```
Queries are generated manually. To help prevent SQL injection vulnerabilities,
-the undocumented `escapeId` method [^7] escapes identifiers and fields.
+the undocumented `escapeId` method [^2] escapes identifiers and fields.
:::
@@ -408,9 +409,4 @@ The output should be consistent with the following table:
```
[^1]: See [the official `mysql2` website](https://sidorares.github.io/node-mysql2/docs) 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.
+[^2]: The `mysql2` connector library `escapeId` method is not mentioned in the documentation but is present in the TypeScript definitions.
diff --git a/docz/docs/03-demos/23-data/25-mongodb.md b/docz/docs/03-demos/23-data/25-mongodb.md
index 3183c91..c1b88ba 100644
--- a/docz/docs/03-demos/23-data/25-mongodb.md
+++ b/docz/docs/03-demos/23-data/25-mongodb.md
@@ -45,8 +45,10 @@ a row in the table.
#### Importing Data
Data stored in an array of objects can be added to MongoDB Collections using
-`Collection#insertMany`[^1]. The SheetJS `sheet_to_json` method[^2] can generate
-data from worksheets:
+`Collection#insertMany`[^1].
+
+The SheetJS [`sheet_to_json` method](/docs/api/utilities/array#array-output) can
+generate data from worksheets:
```js
/* import data from a worksheet to a collection */
@@ -54,15 +56,15 @@ const aoo = XLSX.utils.sheet_to_json(ws);
await collection.insertMany(aoo, {ordered: true});
```
-Typically worksheet objects are extracted from workbook objects[^3] generated
-from the SheetJS `read` or `readFile` methods[^4].
+Typically worksheets are extracted from [workbook objects](/docs/csf/book) that
+are created by SheetJS [`read` or `readFile`](/docs/api/parse-options) methods.
#### Exporting Data
-`Collection#find`[^5] can pull an array of objects from a MongoDB Collection.
+`Collection#find`[^2] can pull an array of objects from a MongoDB Collection.
-The SheetJS `json_to_sheet` method[^6] can take the result and generate a
-worksheet object.
+The SheetJS [`json_to_sheet`](/docs/api/utilities/array#array-of-objects-input)
+method can take the result and generate a worksheet object.
:::info pass
@@ -79,13 +81,13 @@ const aoo = await collection.find({}, {projection:{_id:0}}).toArray();
const ws = utils.json_to_sheet(aoo);
```
-Using `book_new` and `book_append_sheet`[^7], a workbook object can be created.
-This workbook is typically exported to the filesystem with `writeFile`[^8].
+Using [`book_new` and `book_append_sheet`](/docs/api/utilities/wb), a workbook
+object can be created. This workbook is typically exported to the filesystem
+with the [`writeFile`](/docs/api/write-options) method.
## Complete Example
-0) Install a MongoDB-compatible server. Options include MongoDB CE[^9] and
-FerretDB[^10]
+0) Install a MongoDB-compatible server (MongoDB CE[^3] or FerretDB[^4]).
1) Start a server on `localhost` (follow official instructions).
@@ -247,12 +249,6 @@ There should be no errors in the terminal. The script will generate the file
`SheetJSMongoCRUD.xlsx`. That file can be opened in a spreadsheet editor.
[^1]: See [`insertMany`](https://mongodb.github.io/node-mongodb-native/5.7/classes/Collection.html#insertMany) in the MongoDB documentation.
-[^2]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
-[^3]: See ["Workbook Object"](/docs/csf/book)
-[^4]: See [`read` and `readFile` in "Reading Files"](/docs/api/parse-options)
-[^5]: See [`find`](https://mongodb.github.io/node-mongodb-native/5.7/classes/Collection.html#find) in the MongoDB documentation.
-[^6]: See [`json_to_sheet` in "Utilities"](/docs/api/utilities/array#array-of-objects-input)
-[^7]: See ["Workbook Helpers" in "Utilities"](/docs/api/utilities/wb) for details on `book_new` and `book_append_sheet`.
-[^8]: See [`writeFile` in "Writing Files"](/docs/api/write-options)
-[^9]: See ["Install MongoDB Community Edition"](https://www.mongodb.com/docs/manual/administration/install-community/#std-label-install-mdb-community-edition) in the MongoDB documentation.
-[^10]: See ["SQLite Setup with Docker Compose"](https://docs.ferretdb.io/quickstart-guide/docker/#sqlite-setup-with-docker-compose) in the FerretDB documentation.
\ No newline at end of file
+[^2]: See [`find`](https://mongodb.github.io/node-mongodb-native/5.7/classes/Collection.html#find) in the MongoDB documentation.
+[^3]: See ["Install MongoDB Community Edition"](https://www.mongodb.com/docs/manual/administration/install-community/#std-label-install-mdb-community-edition) in the MongoDB documentation.
+[^4]: See ["SQLite Setup with Docker Compose"](https://docs.ferretdb.io/quickstart-guide/docker/#sqlite-setup-with-docker-compose) in the FerretDB documentation.
\ No newline at end of file
diff --git a/docz/docs/03-demos/23-data/29-pouchdb.md b/docz/docs/03-demos/23-data/29-pouchdb.md
index ef94827..07a3122 100644
--- a/docz/docs/03-demos/23-data/29-pouchdb.md
+++ b/docz/docs/03-demos/23-data/29-pouchdb.md
@@ -29,13 +29,13 @@ This demo was tested in the following environments:
| PouchDB | Date |
|:--------|:----------:|
-| `9.0.0` | 2025-01-19 |
-| `8.0.1` | 2025-01-19 |
-| `7.3.1` | 2025-01-19 |
-| `6.4.3` | 2025-01-19 |
-| `5.4.5` | 2025-01-19 |
-| `4.0.3` | 2025-01-19 |
-| `3.6.0` | 2025-01-19 |
+| `9.0.0` | 2026-01-08 |
+| `8.0.1` | 2026-01-08 |
+| `7.3.1` | 2026-01-08 |
+| `6.4.3` | 2026-01-08 |
+| `5.4.5` | 2026-01-08 |
+| `4.0.3` | 2026-01-08 |
+| `3.6.0` | 2026-01-08 |
:::
@@ -53,20 +53,18 @@ The `PouchDB` constructor returns a `Database` object.
#### Importing Data
`Database#bulkDocs`[^2] is the standard approach for bulk data import. The method
-accepts "arrays of objects" that can be generated through the SheetJS
-`sheet_to_json`[^3] method.
+accepts "arrays of objects" that can be generated using the SheetJS
+[`sheet_to_json`](/docs/api/utilities/array#array-output) method.
If rows do not include the `_id` parameter, the database will automatically
assign an ID per row. It is strongly recommended to generate the `_id` directly.
-This method starts from a SheetJS workbook object[^4] and uses data from the
-first sheet. `read` and `readFile`[^5] can generate workbook objects from files.
-
-```js
-async function push_first_sheet_to_pouchdb(db, wb, _id_) {
- /* get first worksheet */
- const ws = wb.Sheets[wb.SheetNames[0]];
+This method starts from a SheetJS [worksheet object](/docs/csf/sheet) and uses
+data from the first sheet. [`read` and `readFile`](/docs/api/parse-options) can
+generate workbook objects from files.
+```js title="Extract data from a SheetJS worksheet and push to PouchDB"
+async function push_sheet_to_pouchdb(db, ws, _id_) {
/* generate array of objects */
const aoo = XLSX.utils.sheet_to_json(ws);
@@ -86,14 +84,15 @@ Existing data can be erased with `Database#destroy`.
#### Exporting Data
-`Database#allDocs`[^6] is the standard approach for bulk data export. Generated
+`Database#allDocs`[^3] is the standard approach for bulk data export. Generated
row objects have additional `_id` and `_rev` keys that should be removed.
-After removing the PouchDB internal fields, the SheetJS `json_to_sheet`[^7]
-method can generate a worksheet. Other utility functions[^8] can construct a
-workbook. The workbook can be exported with the SheetJS `writeFile`[^9] method:
+The SheetJS [`json_to_sheet`](/docs/api/utilities/array#array-of-objects-input)
+method can generate a worksheet. [API functions](/docs/api/utilities/wb) can
+generate a workbook object. The [`writeFile`](/docs/api/write-options) method
+will attempt to download the generated workbook:
-```js
+```js title="Extract data from PouchDB and export to XLSX using SheetJS"
function export_pouchdb_to_xlsx(db) {
/* fetch all rows, including the underlying data */
db.allDocs({include_docs: true}, function(err, doc) {
@@ -171,8 +170,8 @@ cd getting-started-todo-master
- `}
-
+
+`}
3) Near the end of `index.html`, look for a script tag referencing a CDN:
@@ -180,7 +179,7 @@ cd getting-started-todo-master
```
-Upgrade PouchDB by changing the `src` attribute to the production build[^10]:
+Upgrade PouchDB by changing the `src` attribute to the production build[^4]:
```html title="index.html (replace line)"
@@ -255,11 +254,5 @@ export named "SheetJSPouch.xlsx"
[^1]: See ["Setting up PouchDB"](https://pouchdb.com/guides/setup-pouchdb.html) in the PouchDB documentation.
[^2]: See ["Create/update a batch of documents"](https://pouchdb.com/api.html#batch_create) in the PouchDB API documentation
-[^3]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
-[^4]: See ["SheetJS Data Model"](/docs/csf)
-[^5]: See [`read` in "Reading Files"](/docs/api/parse-options)
-[^6]: See ["Fetch a batch of documents"](https://pouchdb.com/api.html#batch_fetch) in the PouchDB API documentation
-[^7]: See [`json_to_sheet` in "Utilities"](/docs/api/utilities/array#array-of-objects-input)
-[^8]: See ["Workbook Helpers" in "Utilities"](/docs/api/utilities/wb) for details on `book_new` and `book_append_sheet`.
-[^9]: See [`writeFile` in "Writing Files"](/docs/api/write-options)
-[^10]: The ["Quick Start" section of "Download"](https://pouchdb.com/download.html#file) in the PouchDB website describes the recommended CDN for PouchDB scripts.
\ No newline at end of file
+[^3]: See ["Fetch a batch of documents"](https://pouchdb.com/api.html#batch_fetch) in the PouchDB API documentation
+[^4]: The ["Quick Start" section of "Download"](https://pouchdb.com/download.html#file) in the PouchDB website describes the recommended CDN for PouchDB scripts.
\ No newline at end of file
diff --git a/docz/docs/03-demos/30-cloud/19-deno.md b/docz/docs/03-demos/30-cloud/19-deno.md
index 139d761..fc95b67 100644
--- a/docz/docs/03-demos/30-cloud/19-deno.md
+++ b/docz/docs/03-demos/30-cloud/19-deno.md
@@ -8,7 +8,7 @@ pagination_next: demos/extensions/index
import current from '/version.js';
import CodeBlock from '@theme/CodeBlock';
-[Deno Deploy](https://dash.deno.com/) offers distributed "Serverless Functions"
+[Deno Deploy](https://deno.com/deploy) offers distributed "Serverless Functions"
powered by Deno.
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
@@ -22,7 +22,7 @@ types of spreadsheets to HTML tables and CSV rows.
:::caution pass
-When the demo was last tested, Deno Deploy required a GitHub account.
+When the demo was last tested, Deno Deploy required a GitHub or Google account.
:::
@@ -82,9 +82,9 @@ class SheetJSResource extends Drash.Resource {
## Demo
-0) Create a new GitHub account or sign into an existing account.
+0) Create a new Github or Google account, or sign into an existing account.
-1) Open the [main Deno Deploy portal](https://dash.deno.com/) in a browser.
+1) Open the [main Deno Deploy portal](https://console.deno.com/) in a browser.
2) If the account never signed into Deno Deploy, click "Continue with Github".
diff --git a/docz/docs/03-demos/42-engines/01-duktape.md b/docz/docs/03-demos/42-engines/01-duktape.md
index 1193bed..34a1788 100644
--- a/docz/docs/03-demos/42-engines/01-duktape.md
+++ b/docz/docs/03-demos/42-engines/01-duktape.md
@@ -1115,7 +1115,7 @@ This demo was tested in the following deployments:
| `darwin-x64` | `2.2.1` | 2025-03-31 |
| `darwin-arm` | `2.2.1` | 2025-03-31 |
| `win11-x64` | `2.2.1` | 2025-04-17 |
-| `linux-x64` | `2.2.1` | 2025-04-18 |
+| `linux-x64` | `2.2.1` | 2026-01-08 |
| `linux-arm` | `2.2.1` | 2025-04-18 |
:::
diff --git a/docz/docs/03-demos/42-engines/02-v8.md b/docz/docs/03-demos/42-engines/02-v8.md
index 58d520d..90d9e44 100644
--- a/docz/docs/03-demos/42-engines/02-v8.md
+++ b/docz/docs/03-demos/42-engines/02-v8.md
@@ -184,7 +184,7 @@ cd /usr/local/lib
:::note pass
-If this step throws a permission error, run the following commands:
+If this step throws an error, run the following commands to fix permissions:
```bash
sudo mkdir -p /usr/local/lib
@@ -1078,18 +1078,14 @@ may not work on every platform.
The `v8` crate[^6] provides binary builds and straightforward bindings. The Rust
code is similar to the C++ code.
-Pulling data from an `ArrayBuffer` back into Rust involves an unsafe operation:
+Pulling data from an `ArrayBuffer` back into Rust involves an unsafe conversion
+from a raw pointer and byte length to `Vec`:
-```rust
-/* assuming JS code returns an ArrayBuffer, copy result to a Vec */
-fn eval_code_ab(scope: &mut v8::HandleScope, code: &str) -> Vec {
- let source = v8::String::new(scope, code).unwrap();
- let script = v8::Script::compile(scope, source, None).unwrap();
- let result: v8::Local = script.run(scope).unwrap().try_into().unwrap();
- /* In C++, `Data` returns a pointer. Collecting data into Vec is unsafe */
+```rust title="Pull ArrayBuffer data from V8 to Rust vector of bytes"
+fn pull_ab_to_vecu8(ab: v8::Local) -> Vec {
unsafe { return std::slice::from_raw_parts_mut(
- result.data().unwrap().cast::().as_ptr(),
- result.byte_length()
+ ab.data().unwrap().cast::().as_ptr(),
+ ab.byte_length()
).to_vec(); }
}
```
@@ -1103,7 +1099,7 @@ This demo was last tested in the following deployments:
| `darwin-x64` | `136.0.0` | 2025-04-21 |
| `darwin-arm` | `134.3.0` | 2025-02-13 |
| `win11-x64` | `137.1.0` | 2025-05-11 |
-| `linux-x64` | `137.2.0` | 2025-06-16 |
+| `linux-x64` | `142.2.0` | 2026-01-08 |
| `linux-arm` | `134.4.0` | 2025-02-15 |
:::
@@ -1177,13 +1173,12 @@ curl.exe -L -o src/main.rs https://docs.sheetjs.com/v8/main.rs
:::info pass
-There was a breaking change in version `0.102.0` affecting `v8::Context::new`.
-When targeting older versions of the crate, remove the second argument:
+There were multiple breaking changes in the `v8` crate. This example was tested
+against version `142.2.0`.
-```rust title="src/main.rs"
- let context = v8::Context::new(handle_scope); // v8 <= 0.101.0
- //let context = v8::Context::new(handle_scope, Default::default()); // v8 >= 0.102.0
-```
+Older versions of this demo script were designed for older `v8` versions. They
+are available in the source repo for the documentation. Please reach out to the
+[SheetJS chat](https://sheetjs.com/chat) for more details.
:::
diff --git a/docz/docs/03-demos/42-engines/21-boa.md b/docz/docs/03-demos/42-engines/21-boa.md
index a6c0093..fda5d92 100644
--- a/docz/docs/03-demos/42-engines/21-boa.md
+++ b/docz/docs/03-demos/42-engines/21-boa.md
@@ -82,7 +82,8 @@ Boa supports `ArrayBuffer` natively. This snippet reads data from a file into
```rust
/* read file */
let data: Vec = std::fs::read("pres.xlsx").unwrap();
- let array: boa_engine::object::builtins::JsArrayBuffer = boa_engine::object::builtins::JsArrayBuffer::from_byte_block(file, context).unwrap();
+ let aligned = boa_engine::object::builtins::AlignedVec::from_iter(0, file.into_iter());
+ let array: boa_engine::object::builtins::JsArrayBuffer = boa_engine::object::builtins::JsArrayBuffer::from_byte_block(aligned, context).unwrap();
let attrs = boa_engine::property::Attribute::WRITABLE | boa_engine::property::Attribute::ENUMERABLE | boa_engine::property::Attribute::CONFIGURABLE;
let _ = context.register_global_property(boa_engine::js_string!("buf"), array, attrs);
@@ -108,7 +109,7 @@ This demo was tested in the following deployments:
| `darwin-arm` | `0.20.0` | 2025-09-03 |
| `win11-x64` | `0.20.0` | 2025-04-28 |
| `win11-arm` | `0.20.0` | 2025-02-23 |
-| `linux-x64` | `0.20.0` | 2025-04-21 |
+| `linux-x64` | `0.21.0` | 2026-01-08 |
| `linux-arm` | `0.20.0` | 2025-02-15 |
:::
diff --git a/docz/docs/03-demos/index.md b/docz/docs/03-demos/index.md
index 35cbfaa..53e4b9a 100644
--- a/docz/docs/03-demos/index.md
+++ b/docz/docs/03-demos/index.md
@@ -8,6 +8,23 @@ hide_table_of_contents: true
Demos include complete examples and short discussions. For features that can
run in the web browser, demos will include interactive examples.
+:::info pass
+
+**These demos were tested or verified by SheetJS teammates!**
+
+Many demos were borne from requests by [SheetJS Pro](https://sheetjs.com/pro)
+customers and written based on experience from interactive debugging sessions.
+
+When possible, SheetJS teammates test demos on local infrastructure. Some cloud
+service tests were verified in screen-share sessions.
+
+The web ecosystem moves fast and break things. Demos that worked a week ago may
+not work today if a dependency makes a breaking change. Please leave a note in
+the [issue tracker](https://git.sheetjs.com/sheetjs/docs.sheetjs.com/issues) if
+a demo does not currently work.
+
+:::
+
:::tip pass
If a demo for a library or framework is not included here, please leave a note
diff --git a/docz/docs/08-api/07-utilities/03-html.md b/docz/docs/08-api/07-utilities/03-html.md
index aee6c74..ea4a34a 100644
--- a/docz/docs/08-api/07-utilities/03-html.md
+++ b/docz/docs/08-api/07-utilities/03-html.md
@@ -26,7 +26,7 @@ HTML format and HTML table utilities.
var html = XLSX.utils.sheet_to_html(ws, opts);
```
-The `sheet_to_html` method generates HTML strings from SheetJS worksheets[^1].
+The `sheet_to_html` method generates HTML strings from [SheetJS worksheet objects](/docs/csf/sheet).
The following options are supported:
@@ -513,5 +513,3 @@ browser. ["Browser Automation"](/docs/demos/net/headless) covers some browsers.
Some ecosystems provide DOM-like frameworks that are compatible with SheetJS.
Examples are included in the ["Synthetic DOM"](/docs/demos/net/dom) demo
-
-[^1]: See ["Worksheet Object" in "SheetJS Data Model"](/docs/csf/sheet) for more details.
diff --git a/docz/docs/12-constellation/21-dta.md b/docz/docs/12-constellation/21-dta.md
index 998e5e1..e8cc6e0 100644
--- a/docz/docs/12-constellation/21-dta.md
+++ b/docz/docs/12-constellation/21-dta.md
@@ -48,7 +48,7 @@ Alias variables (supported in DTA versions 120-121) are not processed.
This demo fetches a [sample DTA file](pathname:///dta/pres.dta), parses the data
using the SheetJS DTA Codec and displays the data in an HTML table using the
-`sheet_to_html` method[^1].
+[`sheet_to_html` utility function](/docs/api/utilities/html#html-table-output):
:::tip pass
@@ -97,5 +97,3 @@ function SheetJSDTA() {
> );
}
```
-
-[^1]: See [`sheet_to_html` in "Utilities"](/docs/api/utilities/html#html-table-output)
diff --git a/docz/static/boa/main.rs b/docz/static/boa/main.rs
index 3cd3b37..3fe5e2a 100644
--- a/docz/static/boa/main.rs
+++ b/docz/static/boa/main.rs
@@ -31,9 +31,10 @@ fn main() {
let path: String = iter.nth(1).expect("must specify a file name");
let file: Vec = std::fs::read(path.clone()).unwrap();
-
+
/* push data to boa */
- let array: boa_engine::object::builtins::JsArrayBuffer = boa_engine::object::builtins::JsArrayBuffer::from_byte_block(file, context).unwrap();
+ let aligned = boa_engine::object::builtins::AlignedVec::from_iter(0, file.into_iter());
+ let array: boa_engine::object::builtins::JsArrayBuffer = boa_engine::object::builtins::JsArrayBuffer::from_byte_block(aligned, context).unwrap();
let attrs = boa_engine::property::Attribute::WRITABLE | boa_engine::property::Attribute::ENUMERABLE | boa_engine::property::Attribute::CONFIGURABLE;
let _ = context.register_global_property(boa_engine::js_string!("buf"), array, attrs);
}
diff --git a/docz/static/v8/main.rs b/docz/static/v8/main.rs
index cd831cd..cdaaf1b 100644
--- a/docz/static/v8/main.rs
+++ b/docz/static/v8/main.rs
@@ -1,6 +1,6 @@
/*! sheetjs (C) SheetJS -- https://sheetjs.com */
/* run code, get result as a Rust String */
-fn eval_code(scope: &mut v8::HandleScope, code: &str) -> std::string::String {
+fn eval_code(scope: &mut v8::ContextScope, code: &str) -> std::string::String {
let source = v8::String::new(scope, code).unwrap();
let script = v8::Script::compile(scope, source, None).unwrap();
let result = script.run(scope).unwrap();
@@ -8,7 +8,7 @@ fn eval_code(scope: &mut v8::HandleScope, code: &str) -> std::string::String {
}
/* assuming JS code returns an ArrayBuffer, copy result to a Vec */
-fn eval_code_ab(scope: &mut v8::HandleScope, code: &str) -> Vec {
+fn eval_code_ab(scope: &mut v8::ContextScope, code: &str) -> Vec {
let source = v8::String::new(scope, code).unwrap();
let script = v8::Script::compile(scope, source, None).unwrap();
let result: v8::Local = script.run(scope).unwrap().try_into().unwrap();
@@ -26,20 +26,19 @@ fn main() {
v8::V8::initialize();
let isolate = &mut v8::Isolate::new(Default::default());
- let handle_scope = &mut v8::HandleScope::new(isolate);
- //let context = v8::Context::new(handle_scope); // v8 <= 0.101.0
- let context = v8::Context::new(handle_scope, Default::default()); // v8 >= 0.102.0
- let context_scope = &mut v8::ContextScope::new(handle_scope, context);
+ v8::scope!(let handle_scope, isolate);
+ let context = v8::Context::new(handle_scope, Default::default());
+ let scope = &mut v8::ContextScope::new(handle_scope, context);
/* load library */
{
let script = std::fs::read_to_string("./xlsx.full.min.js").expect("Error reading xlsx.full.min.js");
- let _result = eval_code(context_scope, &script);
+ let _result = eval_code(scope, &script);
}
/* get version string */
{
- let result = eval_code(context_scope, "XLSX.version");
+ let result = eval_code(scope, "XLSX.version");
println!("SheetJS library version {}", result);
}
@@ -48,26 +47,26 @@ fn main() {
let data: Vec = std::fs::read(path.clone()).unwrap();
let back: v8::UniqueRef = v8::ArrayBuffer::new_backing_store_from_vec(data);
let shared = back.make_shared();
- let ab: v8::Local = v8::ArrayBuffer::with_backing_store(context_scope, &shared);
- let key = v8::String::new(context_scope, "buf").unwrap();
- context.global(context_scope).set(context_scope, key.into(), ab.into());
+ let ab: v8::Local = v8::ArrayBuffer::with_backing_store(scope, &shared);
+ let key = v8::String::new(scope, "buf").unwrap();
+ context.global(scope).set(scope, key.into(), ab.into());
println!("Loaded file {}", path);
}
/* parse workbook and assign to global `wb` property */
{
- let _result = eval_code(context_scope, "void (globalThis.wb = XLSX.read(buf))");
+ let _result = eval_code(scope, "void (globalThis.wb = XLSX.read(buf))");
}
/* print CSV of first worksheet */
{
- let result = eval_code(context_scope, "XLSX.utils.sheet_to_csv(wb.Sheets[wb.SheetNames[0]])");
+ let result = eval_code(scope, "XLSX.utils.sheet_to_csv(wb.Sheets[wb.SheetNames[0]])");
println!("{}", result);
}
/* write sheetjsw.xlsb */
{
- let result = eval_code_ab(context_scope, "XLSX.write(wb, {type:'array', bookType:'xlsb'})");
+ let result = eval_code_ab(scope, "XLSX.write(wb, {type:'array', bookType:'xlsb'})");
std::fs::write("sheetjsw.xlsb", result).unwrap();
}
}
\ No newline at end of file
diff --git a/tests/data/pouchdb.sh b/tests/data/pouchdb.sh
new file mode 100755
index 0000000..841ce6f
--- /dev/null
+++ b/tests/data/pouchdb.sh
@@ -0,0 +1,91 @@
+#!/bin/bash
+# https://docs.sheetjs.com/docs/demos/data/pouchdb
+cd /tmp
+rm -rf sheetjs-pouch
+mkdir sheetjs-pouch
+cd sheetjs-pouch
+
+# official url is https://github.com/nickcolley/getting-started-todo/archive/master.zip
+curl -LO https://docs.sheetjs.com/pouchdb/master.zip
+
+unzip master.zip
+cd getting-started-todo-master
+
+awk 'NR>1{print p} {p = $0}' js/app.js > export_code.js
+cat >> export_code.js << 'EOF'
+ document.getElementById("xport").addEventListener("click", function() {
+ console.log("clicked");
+ db.allDocs({include_docs: true, descending: true}, function(err, doc) {
+ const aoo = doc.rows.map(r => {
+ const { _id, _rev, ...rest } = r.doc;
+ return rest;
+ });
+ const ws = XLSX.utils.json_to_sheet(aoo);
+ const wb = XLSX.utils.book_new();
+ XLSX.utils.book_append_sheet(wb, ws, "Sheet1");
+ XLSX.writeFile(wb, "SheetJSPouch.xlsx");
+ });
+ });
+})();
+EOF
+cp export_code.js js/app.js
+
+sed -i.bak 's||