new-dataset-url

This commit is contained in:
SheetJS 2023-10-08 21:13:21 -04:00
parent 990f42934b
commit 5d87422b97
8 changed files with 634 additions and 77 deletions

@ -1064,7 +1064,7 @@ When the app is loaded, the data will be displayed in rows.
</TabItem>
</Tabs>
[^1]: <https://catalog.data.gov/dataset/national-student-loan-data-system-aa85f> is the current location for the CC0-licensed dataset. `PortfolioSummary.xls` is the file name.
[^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)

@ -159,12 +159,19 @@ import { read, utils } from 'xlsx';
/* Fetch and update the state once */
useEffect(() => { (async() => {
/* Download file */
const f = await (await fetch("https://sheetjs.com/pres.xlsx")).arrayBuffer();
/* Download from https://sheetjs.com/pres.numbers */
const f = await fetch("https://sheetjs.com/pres.numbers");
const ab = await f.arrayBuffer();
// highlight-start
const wb = read(f); // parse the array buffer
/* parse */
const wb = read(ab);
/* generate array of objects from first worksheet */
const ws = wb.Sheets[wb.SheetNames[0]]; // get the first worksheet
const data = utils.sheet_to_json(ws); // generate objects
/* update state */
setPres(data); // update state
// highlight-end
})(); }, []);
@ -179,12 +186,19 @@ import { read, utils } from 'xlsx';
/* Fetch and update the state once */
useEffect(() => { (async() => {
/* Download file */
const f = await (await fetch("https://sheetjs.com/pres.xlsx")).arrayBuffer();
/* Download from https://sheetjs.com/pres.numbers */
const f = await fetch("https://sheetjs.com/pres.numbers");
const ab = await f.arrayBuffer();
// highlight-start
const wb = read(f); // parse the array buffer
/* parse */
const wb = read(ab);
/* generate array of presidents from the first worksheet */
const ws = wb.Sheets[wb.SheetNames[0]]; // get the first worksheet
const data: President[] = utils.sheet_to_json<President>(ws); // generate objects
/* update state */
setPres(data); // update state
// highlight-end
})(); }, []);
@ -305,6 +319,57 @@ export default function SheetJSReactAoO() {
<details open><summary><b>How to run the example</b> (click to show)</summary>
<Tabs groupId="starter">
<TabItem name="vite" value="ViteJS">
:::note
This demo was last tested on 2023 October 08 with ViteJS 4.4.1 and React 18.2.0
:::
1) Create a new site:
```bash
npm create vite@latest sheetjs-react -- --template react
```
2) Install the SheetJS dependency and start the dev server:
<CodeBlock language="bash">{`\
cd sheetjs-react
npm i
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz
npm run dev`}
</CodeBlock>
3) Open a web browser and access the displayed URL (`http://localhost:5173`)
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`.
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.
</TabItem>
<TabItem name="CRA" value="create-react-app">
:::note
This demo was last run on 2023 July 03 using `create-react-app@5.0.1` and
@ -334,8 +399,25 @@ npm start`}
The page will refresh and show a table with an Export button. Click the button
and the page will attempt to download `SheetJSReactAoA.xlsx`.
5) Build the site with `npm run build`, then test with `npx http-server build`.
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 `build` folder.
6) Start a local web server:
```bash
npx http-server build
```
Access the displayed URL (typically `http://localhost:8080`) with a web browser
and test the page.
</TabItem>
</Tabs>
</details>
@ -394,6 +476,57 @@ export default function SheetJSReactHTML() {
<details open><summary><b>How to run the example</b> (click to show)</summary>
<Tabs groupId="starter">
<TabItem name="vite" value="ViteJS">
:::note
This demo was last tested on 2023 October 08 with ViteJS 4.4.1 and React 18.2.0
:::
1) Create a new site:
```bash
npm create vite@latest sheetjs-react -- --template react
```
2) Install the SheetJS dependency and start the dev server:
<CodeBlock language="bash">{`\
cd sheetjs-react
npm i
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz
npm run dev`}
</CodeBlock>
3) Open a web browser and access the displayed URL (`http://localhost:5173`)
4) Replace `src/App.jsx` with the `src/SheetJSReactHTML.js` example.
The page will refresh and show a table with an Export button. Click the button
and the page will attempt to download `SheetJSReactHTML.xlsx`.
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.
</TabItem>
<TabItem name="CRA" value="create-react-app">
:::note
This demo was last run on 2023 July 03 using `create-react-app@5.0.1` and
@ -423,8 +556,25 @@ npm start`}
The page will refresh and show a table with an Export button. Click the button
and the page will attempt to download `SheetJSReactHTML.xlsx`.
5) Build the site with `npm run build`, then test with `npx http-server build`.
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 `build` folder.
6) Start a local web server:
```bash
npx http-server build
```
Access the displayed URL (typically `http://localhost:8080`) with a web browser
and test the page.
</TabItem>
</Tabs>
</details>

@ -54,27 +54,17 @@ depends on the application.
Typically, some users will create a spreadsheet with source data that should be
loaded into the site. This sheet will have known columns.
For example, our [presidents sheet](https://sheetjs.com/pres.xlsx) has "Name" and "Index" columns:
#### State
The example [presidents sheet](https://sheetjs.com/pres.xlsx) has one header row
with "Name" and "Index" columns. The natural JS representation is an object for
each row, using the values in the first rows as keys:
<table><thead><tr><th>Spreadsheet</th><th>State</th></tr></thead><tbody><tr><td>
![`pres.xlsx` data](pathname:///pres.png)
This naturally maps to an array of typed objects, as in the TS example below:
```ts
import { read, utils } from 'xlsx';
interface President {
Name: string;
Index: number;
}
const f = await (await fetch("https://sheetjs.com/pres.xlsx")).arrayBuffer();
const wb = read(f);
const data = utils.sheet_to_json<President>(wb.Sheets[wb.SheetNames[0]]);
console.log(data);
```
`data` will be an array of objects:
</td><td>
```js
[
@ -86,8 +76,234 @@ console.log(data);
]
```
A component will typically map over the data. The following example generates
a TABLE with a row for each President:
</td></tr></tbody></table>
Using the VueJS Composition API, the `ref`[^1] function creates state objects:
<Tabs groupId="lang">
<TabItem name="JS" value="JavaScript">
```html
<script setup>
import { ref } from "vue";
/* the component state is an array of objects */
const pres = ref([]);
</script>
```
</TabItem>
<TabItem name="TS" value="TypeScript" default>
```html
<script setup lang="ts">
import { ref } from "vue";
/* the component state is an array of objects */
const pres = ref<any[]>([]);
</script>
```
When the spreadsheet header row is known ahead of time, row typing is possible:
```html
<script setup lang="ts">
import { ref } from "vue";
interface President {
Name: string;
Index: number;
}
/* the component state is an array of presidents */
const pres = ref<President[]>([]);
</script>
```
:::caution pass
The types are informative. They do not enforce that worksheets include the named
columns. A runtime data validation library should be used to verify the dataset.
When the file header is not known in advance, `any` should be used.
:::
</TabItem>
</Tabs>
#### Updating State
The SheetJS [`read`](/docs/api/parse-options) and [`sheet_to_json`](/docs/api/utilities/array#array-output)
functions simplify state updates. They are best used in the function bodies of
lifecycle hooks including `onMounted`[^2].
The `onMounted` hook can download and update state when a person loads the site:
```mermaid
flowchart LR
url[(Remote\nFile)]
ab[(Data\nArrayBuffer)]
wb(SheetJS\nWorkbook)
ws(SheetJS\nWorksheet)
aoo(array of\nobjects)
state((component\nstate))
url --> |fetch\n\n| ab
ab --> |read\n\n| wb
wb --> |wb.Sheets\nselect sheet| ws
ws --> |sheet_to_json\n\n| aoo
aoo --> |setPres\nfrom `setState`| state
```
<Tabs groupId="lang">
<TabItem name="JS" value="JavaScript">
```html
<script setup>
import { ref, onMounted } from "vue";
import { read, utils } from 'xlsx';
/* the component state is an array of objects */
const pres = ref([]);
/* Fetch and update the state once */
onMounted(async() => {
/* Download from https://sheetjs.com/pres.numbers */
const f = await fetch("https://sheetjs.com/pres.numbers");
const ab = await f.arrayBuffer();
// highlight-start
/* parse */
const wb = read(ab);
/* generate array of objects from first worksheet */
const ws = wb.Sheets[wb.SheetNames[0]]; // get the first worksheet
const data = utils.sheet_to_json(ws); // generate objects
/* update state */
pres.value = data;
// highlight-end
});
</script>
```
</TabItem>
<TabItem name="TS" value="TypeScript" default>
```html
<script setup lang="ts">
import { ref, onMounted } from "vue";
import { read, utils } from 'xlsx';
interface President {
Name: string;
Index: number;
}
/* the component state is an array of presidents */
const pres = ref<President[]>([]);
/* Fetch and update the state once */
onMounted(async() => {
/* Download from https://sheetjs.com/pres.numbers */
const f = await fetch("https://sheetjs.com/pres.numbers");
const ab = await f.arrayBuffer();
// highlight-start
/* parse */
const wb = read(ab);
/* generate array of presidents from the first worksheet */
const ws = wb.Sheets[wb.SheetNames[0]]; // get the first worksheet
const data: President[] = utils.sheet_to_json<President>(ws); // generate objects
/* update state */
pres.value = data;
// highlight-end
});
</script>
```
</TabItem>
</Tabs>
#### Rendering Data
A component will typically map over the data with `v-for`[^3]. The following
example generates a TABLE with a row for each President:
```html title="Example SFC for displaying arrays of objects"
<script setup>
import { ref } from "vue";
const rows = ref([]);
</script>
<template>
<table>
<!-- The `thead` section includes the table header row -->
<thead><th>Name</th><th>Index</th></thead>
<!-- The `tbody` section includes the data rows -->
<tbody>
<!-- generate row (TR) for each president -->
<!-- highlight-start-->
<tr v-for="(row, idx) in rows" :key="idx">
<td>{{ row.Name }}</td>
<td>{{ row.Index }}</td>
</tr>
<!-- highlight-end-->
</tbody>
</table>
</template>
```
#### Exporting Data
The [`writeFile`](/docs/api/write-options) and [`json_to_sheet`](/docs/api/utilities/array#array-of-objects-input)
functions simplify exporting data. They are best used in the function bodies of
`v-on` event handlers like `@click`[^4].
A callback can generate a local file when a user clicks a button:
```mermaid
flowchart LR
state((component\nstate))
ws(SheetJS\nWorksheet)
wb(SheetJS\nWorkbook)
file[(XLSX\nexport)]
state --> |json_to_sheet\n\n| ws
ws --> |book_new\nbook_append_sheet| wb
wb --> |writeFile\n\n| file
```
```html
<script setup>
import { ref } from "vue";
import { utils, writeFileXLSX } from 'xlsx';
const pres = ref([]);
/* get state data and export to XLSX */
function exportFile() {
/* generate worksheet from state */
// highlight-next-line
const ws = utils.json_to_sheet(pres.value);
/* create workbook and append worksheet */
const wb = utils.book_new();
utils.book_append_sheet(wb, ws, "Data");
/* export to XLSX */
writeFileXLSX(wb, "SheetJSVueAoO.xlsx");
}
</script>
<template>
<button @click="exportFile">Export XLSX</button>
</template>
```
#### Complete Component
This complete component example fetches a test file and displays the contents in
a HTML table. When the export button is clicked, a callback will export a file:
```html title="src/SheetJSVueAoO.vue"
<script setup>
@ -157,8 +373,22 @@ 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
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>
@ -168,9 +398,14 @@ The main disadvantage of the Array of Objects approach is the specific nature
of the columns. For more general use, passing around an Array of Arrays works.
However, this does not handle merge cells well!
The `sheet_to_html` function generates HTML that is aware of merges and other
worksheet features. VueJS `v-html` attribute allows assignment of `innerHTML`
attribute, effectively inserting the code into the page:
The [`sheet_to_html`](/docs/api/utilities/html#html-table-output) function
generates HTML that is aware of merges and other worksheet features. VueJS
`v-html`[^5] attribute allows code to set the `innerHTML` attribute, effectively
inserting the code into the page.
In this example, the component attaches a `ref` to the `DIV` container. During
export, the first `TABLE` child element can be parsed with [`table_to_book`](/docs/api/utilities/html#html-table-input) to
generate a workbook object.
```html title="src/SheetJSVueHTML.vue"
<script setup>
@ -233,8 +468,40 @@ The page will refresh and show a table with an Export button. Click the button
and the page will attempt to download `SheetJSVueHTML.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.
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>
@ -284,3 +551,9 @@ The entire demo is designed to run in Internet Explorer and does not reflect
modern design patterns.
:::
[^1]: See [`ref()`](https://vuejs.org/api/reactivity-core.html#ref) in the VueJS documentation.
[^2]: See [`onMounted()`](https://vuejs.org/api/composition-api-lifecycle.html#onmounted) in the VueJS documentation.
[^3]: See [`v-for`](https://vuejs.org/api/built-in-directives.html#v-for) in the VueJS documentation.
[^4]: See [`v-on`](https://vuejs.org/api/built-in-directives.html#v-on) in the VueJS documentation.
[^5]: See [`v-html`](https://vuejs.org/api/built-in-directives.html#v-html) in the VueJS documentation.

@ -1,5 +1,7 @@
---
title: GatsbyJS
title: Spreadsheets in GatsbyJS Sites
sidebar_label: GatsbyJS
description: Make static websites from spreadsheets using GatsbyJS. Seamlessly integrate data into your website using SheetJS. Generate websites from data in Excel spreadsheets.
pagination_prev: demos/net/index
pagination_next: demos/mobile/index
sidebar_custom_props:
@ -7,21 +9,27 @@ sidebar_custom_props:
---
import current from '/version.js';
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeBlock from '@theme/CodeBlock';
[GatsbyJS](https://www.gatsbyjs.com/) is a framework for creating websites. It
uses React components for page templates and GraphQL for loading data.
GatsbyJS is a framework for creating websites. It uses React components for page
templates and GraphQL for loading data.
[`gatsby-transformer-excel`](https://www.gatsbyjs.com/plugins/gatsby-transformer-excel/)
is a transformer that generates GraphQL nodes for each row of each worksheet.
The plugin is officially supported by the Gatsby team. The plugin documentation
includes examples and more detailed usage instructions.
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
data from spreadsheets.
:::note pass
This demo uses GatsbyJS and SheetJS (through the `gatsby-transformer-excel`[^1]
transformer) to pull data from a spreadsheet and display the content in a page.
The ["Complete Example"](#complete-example) section includes a complete website
powered by an XLSX spreadsheet.
:::info pass
`gatsby-transformer-excel` is maintained by the Gatsby core team and all bugs
should be directed to the main Gatsby project. If it is determined to be a bug
in the parsing logic, issues should then be raised with the SheetJS project.
in the parsing logic, issues should then be raised with the SheetJS team.
:::
@ -30,7 +38,7 @@ in the parsing logic, issues should then be raised with the SheetJS project.
`gatsby-transformer-excel` uses an older version of the library. It can be
overridden through a `package.json` override in the latest versions of NodeJS:
<CodeBlock language="json">{`\
<CodeBlock language="json" title="package.json (add highlighted lines)">{`\
{
"overrides": {
"xlsx": "https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz"
@ -40,11 +48,93 @@ overridden through a `package.json` override in the latest versions of NodeJS:
:::
## GraphQL details
:::warning Telemetry
`gatsby-transformer-excel` generates nodes for each data row of each worksheet.
Under the hood, it uses [`sheet_to_json`](/docs/api/utilities#array-output)
to generate row objects using the headers in the first row as keys.
GatsbyJS collects telemetry by default. The `telemetry` subcommand can disable it:
```js
npx gatsby telemetry --disable
```
:::
## Integration Details
```mermaid
flowchart LR
file[(workbook\nfile)]
subgraph SheetJS operations
filenode[File\nNode]
datanode[Data\nNodes]
end
aoo(array of\nobjects)
html{{HTML\nTABLE}}
file --> |Source\nPlugin| filenode
filenode --> |Transformer\nPlugin| datanode
datanode --> |GraphQL\nQuery| aoo
aoo --> |React\nJSX| html
```
In the GatsbyJS data system, source plugins read from data sources and generate
nodes represent raw data. Transformer plugins transform these nodes into other
nodes that represent processed data for use in pages.
This example uses `gatsby-source-filesystem`[^2] to read files from the
filesystem and `gatsby-transformer-excel` transformer to perform the transform.
### Installation
The [SheetJS NodeJS module](/docs/getting-started/installation/nodejs) will be
referenced by `gatsby-transformer-excel`.
Before installing, to ensure that the transformer uses the latest version of the
library, the `overrides` section must be added to `package.json`:
<CodeBlock language="json" title="package.json (add highlighted lines)">{`\
{
// highlight-start
"overrides": {
"xlsx": "https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz"
}
// highlight-end
}`}
</CodeBlock>
`gatsby-transformer-excel` and `gatsby-source-filesystem` should be installed
after installing SheetJS modules:
<Tabs groupId="pm">
<TabItem value="npm" label="npm">
<CodeBlock language="bash">{`\
npx gatsby telemetry --disable
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz
npm i --save gatsby-transformer-excel gatsby-source-filesystem`}
</CodeBlock>
</TabItem>
<TabItem value="pnpm" label="pnpm">
<CodeBlock language="bash">{`\
npx gatsby telemetry --disable
pnpm install https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz
pnpm install gatsby-transformer-excel gatsby-source-filesystem`}
</CodeBlock>
</TabItem>
<TabItem value="yarn" label="Yarn" default>
<CodeBlock language="bash">{`\
npx gatsby telemetry --disable
yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz
yarn add gatsby-transformer-excel gatsby-source-filesystem`}
</CodeBlock>
</TabItem>
</Tabs>
### GraphQL details
Under the hood, `gatsby-transformer-excel` uses the SheetJS `read`[^3] method to
parse the workbook into a SheetJS workbook[^4]. Each worksheet is extracted from
the workbook. The `sheet_to_json` method[^5] generates row objects using the
headers in the first row as keys.
Consider the following worksheet:
![pres.xlsx](pathname:///pres.png)
@ -78,18 +168,28 @@ The following query pulls the `Name` and `Index` fields from each row:
}
```
## GatsbyJS Demo
## Complete Example
:::note
This demo was tested on 2023 September 13 against `create-gatsby@3.12.0`. The
generated project used `gatsby@5.12.4` and `react@18.2.0`.
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`.
:::
### Project setup
1) Run `npm init gatsby -- -y sheetjs-gatsby` to create the template site.
0) Disable GatsbyJS telemetry:
```bash
npx gatsby telemetry --disable
```
1) Create a template site:
```bash
npm init gatsby -- -y sheetjs-gatsby
```
2) Follow the on-screen instructions for starting the local development server:
@ -157,7 +257,7 @@ 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 at `http://localhost:8000/___graphql`
There is an editor in the left pane. Paste the following query into the editor:
@ -203,8 +303,8 @@ const PageComponent = ({data}) => {
export default PageComponent;
```
After saving the file, access `http://localhost:8000/pres`. The displayed JSON
is the data that the component receives:
After saving the file, access `http://localhost:8000/pres` in the browser. The
displayed JSON is the data that the component receives:
```js
{
@ -301,3 +401,9 @@ There will be a HTML row:
```html title="public/pres/index.html"
<tr><td>SheetJS Dev</td><td>47</td></tr>
```
[^1]: The package is available as [`gatsby-transformer-excel` on the public NPM registry](https://www.npmjs.com/package/gatsby-transformer-excel). It is also listed on the [GatsbyJS plugin library](https://www.gatsbyjs.com/plugins/gatsby-transformer-excel/).
[^2]: See [the `gatsby-source-filesystem` plugin](https://www.gatsbyjs.com/plugins/gatsby-source-filesystem/) in the GatsbyJS documentation
[^3]: See [`read` in "Reading Files"](/docs/api/parse-options)
[^4]: See ["Workbook Object"](/docs/csf/book) for more details on the SheetJS workbook object.
[^5]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)

@ -25,14 +25,15 @@ plugin and compare a few different data loading strategies.
The ["Complete Demo"](#complete-demo) section creates a complete website powered
by a XLSX spreadsheet.
:::tip pass
:::info pass
This demo covers use cases where data is available at build time. This flow is
suitable for end of week or end of month (EOM) reports published in HTML tables
suitable for end of week or end of month (EOM) reports published in HTML tables.
For processing user-submitted files in the browser, the
["Bundlers" demo](/docs/demos/frontend/bundler#vite) shows client-side bundling
of the SheetJS library.
of the SheetJS library. The ["ReactJS" demo](/docs/demos/frontend/react) shows
example sites using ViteJS with the ReactJS starter.
:::

@ -7,10 +7,15 @@ pagination_next: demos/mobile/index
import current from '/version.js';
import CodeBlock from '@theme/CodeBlock';
Eleventy is a static site generator.
[Eleventy](https://www.11ty.dev/docs) is a telemetry-free static site generator.
The data pipeline can be augmented with custom data file parsers.
The [NodeJS module](/docs/getting-started/installation/nodejs) can be loaded in
`.eleventy.js` and used in custom data file format parsers.
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
data from spreadsheets.
This demo uses Eleventy and SheetJS to pull data from a spreadsheet and display
the content in a page. We'll explore how to load SheetJS libraries in a custom
data file format parser and generate arrays of objects for use in pages.
The following diagram depicts the workbook waltz:
@ -27,19 +32,33 @@ flowchart LR
aoo --> |index.njk\ntemplate| html
```
:::tip No Telemetry
The author has publicly stated that Eleventy does not embed any telemetry or
data collection.[^1]
:::
## Integration Details
The [SheetJS NodeJS module](/docs/getting-started/installation/nodejs) can be
loaded in `.eleventy.js` and used in custom data file format parsers.
### Data File Parser
Custom data file parsers must be registered in `.eleventy.js`
`addDataExtension` accepts a list of file extensions and a parser object.
The Eleventy config `addDataExtension` method[^2] accepts a list of file
extensions and a parser configuration object.
The parser object must include the options `read: true` and `encoding: null` .
Eleventy will read files and pass raw `Buffer` objects to the parser callback.
The `parser` callback can parse the data with `XLSX.read`. In this demo, the
parser will generate an array of row objects using `XLSX.utils.sheet_to_json`:
The `parser` callback can parse the raw `Buffer` data with the SheetJS `read`
method[^3]. The method returns a workbook object[^4].
In this example, the parser will use the SheetJS `sheet_to_json` method[^5] to
generate an array of objects from the data in the first worksheet:
```js title=".eleventy.js"
const XLSX = require("xlsx");
@ -91,7 +110,7 @@ using the variable `pres` in a template:
:::note
This demo was tested on 2023 May 07 using Eleventy `2.0.1`
This demo was tested on 2023 October 08 using Eleventy `2.0.1`
:::
@ -179,3 +198,9 @@ npx http-server _site
Open a web browser and access the displayed URL ( `http://localhost:8080` ).
View the page source and confirm that no JS was added to the page. It only
contains the content from the file in an HTML table.
[^1]: See [the "Telemetry" section](https://www.zachleat.com/web/site-generator-review/#telemetry) of the site generator review from the author of Eleventy. When this page was last checked, the author proudly asserted that Eleventy had "No known Telemetry or data collection".
[^2]: See ["Custom Data File Formats"](https://www.11ty.dev/docs/data-custom/) in the Eleventy documentation.
[^3]: See [`read` in "Reading Files"](/docs/api/parse-options)
[^4]: See ["Workbook Object"](/docs/csf/book) for more details on the SheetJS workbook object.
[^5]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)

@ -37,13 +37,11 @@ the browser refreshes to show the new content.
:::note Recommendation
It is strongly recommended to use a framework that provides an official plugin
for working with SheetJS.
It is strongly recommended to use a telemetry-free framework that provides an
official plugin for working with SheetJS.
Lume is a great choice for lightweight sites.
GatsbyJS is excellent for teams well-versed in the ReactJS framework.
:::
### Official

@ -145,10 +145,6 @@ If the file does not exist, servers will send a 404 response that may include a
friendly HTML page. Without checking the response code, the integration will try
to read the 404 page and fail since the HTML typically has no TABLE elements.
When building a project with CRA or other templates, spreadsheets must be placed
in the `public` folder. That folder is served by the dev server and copied in
the build process.
Integration code should defend against network issues by checking status code.
For example, when using `fetch`:
@ -161,6 +157,14 @@ async function fetch_workbook_and_error_on_404(url) {
}
```
:::note pass
When building a project with `create-react-app` or other templates, spreadsheets
must be placed in the `public` folder. That folder is typically served by the
dev server and copied to the production site in the build process.
:::
#### Cloudflare Worker "Error: Script startup exceeded CPU time limit."
This may show up in projects with many dependencies. The official workaround is