esbuild-plugin
This commit is contained in:
parent
3ab0c1ab67
commit
aeb932e1d0
@ -25,7 +25,7 @@ bundle with ESBuild for browser use.
|
||||
- ["NodeJS"](#nodejs) explores how to import SheetJS libraries in a script and
|
||||
bundle with ESBuild for NodeJS use.
|
||||
|
||||
:::info pass
|
||||
:::note pass
|
||||
|
||||
This demo focuses on integration details with the ESBuild bundler.
|
||||
|
||||
@ -34,6 +34,13 @@ The tutorial covers SheetJS library usage.
|
||||
|
||||
:::
|
||||
|
||||
:::info pass
|
||||
|
||||
The [ESBuild section of the Content demo](/docs/demos/static/esbuild) covers
|
||||
loaders. They are ideal for static sites pulling data from sheets at build time.
|
||||
|
||||
:::
|
||||
|
||||
:::note
|
||||
|
||||
This demo was last tested on 2023 October 19 against esbuild `0.19.5`
|
||||
|
151
docz/docs/03-demos/01-frontend/19-bundler/09-browserify.md
Normal file
151
docz/docs/03-demos/01-frontend/19-bundler/09-browserify.md
Normal file
@ -0,0 +1,151 @@
|
||||
---
|
||||
title: Bundling Sheets with Browserify
|
||||
sidebar_label: Browserify
|
||||
pagination_prev: demos/index
|
||||
pagination_next: demos/grid/index
|
||||
sidebar_position: 9
|
||||
---
|
||||
|
||||
import current from '/version.js';
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
import CodeBlock from '@theme/CodeBlock';
|
||||
|
||||
[Browserify](https://browserify.org/) is a module bundler.
|
||||
|
||||
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
|
||||
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
|
||||
|
||||
This demo was last tested on 2023 October 21 against Browserify `17.0.0`
|
||||
|
||||
:::
|
||||
|
||||
## Integration Details
|
||||
|
||||
[The "Frameworks" section](/docs/getting-started/installation/frameworks) covers
|
||||
installation with Yarn and other package managers.
|
||||
|
||||
After installing the SheetJS module in a Browserify project, `require`
|
||||
expressions can load relevant parts of the library.
|
||||
|
||||
```js
|
||||
var XLSX = require("xlsx");
|
||||
// ... use XLSX ...
|
||||
```
|
||||
|
||||
Browserify can also process `require` expressions in Web Worker scripts.
|
||||
|
||||
## Complete Example
|
||||
|
||||
0) Initialize a new project:
|
||||
|
||||
```bash
|
||||
mkdir sheetjs-browserify
|
||||
cd sheetjs-browserify
|
||||
npm init -y
|
||||
```
|
||||
|
||||
1) Install the tarball using a package manager:
|
||||
|
||||
<Tabs groupId="pm">
|
||||
<TabItem value="npm" label="npm">
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
|
||||
</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="pnpm" label="pnpm">
|
||||
<CodeBlock language="bash">{`\
|
||||
pnpm install https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
|
||||
</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="yarn" label="Yarn" default>
|
||||
<CodeBlock language="bash">{`\
|
||||
yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
|
||||
</CodeBlock>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
2) Save the following to `index.js`:
|
||||
|
||||
```js title="index.js"
|
||||
// highlight-next-line
|
||||
const { utils, version, writeFileXLSX } = require('xlsx');
|
||||
|
||||
document.getElementById("xport").addEventListener("click", function() {
|
||||
/* fetch JSON data and parse */
|
||||
var url = "https://sheetjs.com/data/executive.json";
|
||||
fetch(url).then(function(res) { return res.json(); }).then(function(raw_data) {
|
||||
|
||||
/* filter for the Presidents */
|
||||
var prez = raw_data.filter(function(row) { return row.terms.some(function(term) { return term.type === "prez"; }); });
|
||||
|
||||
/* sort by first presidential term */
|
||||
prez.forEach(function(row) {
|
||||
row.start = row.terms.find(function(term) {
|
||||
return term.type === "prez";
|
||||
}).start
|
||||
});
|
||||
prez.sort(function(l,r) { return l.start.localeCompare(r.start); });
|
||||
|
||||
/* flatten objects */
|
||||
var rows = prez.map(function(row) { return {
|
||||
name: row.name.first + " " + row.name.last,
|
||||
birthday: row.bio.birthday
|
||||
}; });
|
||||
|
||||
/* generate worksheet and workbook */
|
||||
var worksheet = utils.json_to_sheet(rows);
|
||||
var workbook = utils.book_new();
|
||||
utils.book_append_sheet(workbook, worksheet, "Dates");
|
||||
|
||||
/* fix headers */
|
||||
utils.sheet_add_aoa(worksheet, [["Name", "Birthday"]], { origin: "A1" });
|
||||
|
||||
/* calculate column width */
|
||||
var max_width = rows.reduce(function(w, r) { return Math.max(w, r.name.length); }, 10);
|
||||
worksheet["!cols"] = [ { wch: max_width } ];
|
||||
|
||||
/* create an XLSX file and try to save to Presidents.xlsx */
|
||||
writeFileXLSX(workbook, "Presidents.xlsx");
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
3) Bundle the scripts:
|
||||
|
||||
```bash
|
||||
npx browserify app.js > browserify.js
|
||||
```
|
||||
|
||||
4) Spin up a local web server:
|
||||
|
||||
```bash
|
||||
npx http-server
|
||||
```
|
||||
|
||||
5) Create a small HTML page that loads the script. Save to `index.html`:
|
||||
|
||||
```html title="index.html"
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head></head>
|
||||
<body>
|
||||
<h1>SheetJS Presidents Demo</h1>
|
||||
<button id="xport">Click here to export</button>
|
||||
<script src="./index.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
6) Start a local HTTP server and go to `http://localhost:8080/`
|
||||
|
||||
```bash
|
||||
npx http-server .
|
||||
```
|
||||
|
||||
Click on "Click here to export" to generate a file.
|
186
docz/docs/03-demos/01-frontend/19-bundler/21-swcpack.md
Normal file
186
docz/docs/03-demos/01-frontend/19-bundler/21-swcpack.md
Normal file
@ -0,0 +1,186 @@
|
||||
---
|
||||
title: Bundling Sheets with SWC
|
||||
sidebar_label: SWC spack
|
||||
pagination_prev: demos/index
|
||||
pagination_next: demos/grid/index
|
||||
sidebar_position: 21
|
||||
---
|
||||
|
||||
import current from '/version.js';
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
import CodeBlock from '@theme/CodeBlock';
|
||||
|
||||
SWC[^1] is a JS toolchain. SWC provides `spack` (formally called "swcpack") for
|
||||
bundling scripts.
|
||||
|
||||
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
|
||||
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
|
||||
|
||||
This demo was last tested on 2023 October 20 against SWC 1.2.246
|
||||
|
||||
:::
|
||||
|
||||
## Integration Details
|
||||
|
||||
[The "Frameworks" section](/docs/getting-started/installation/frameworks) covers
|
||||
installation with Yarn and other package managers.
|
||||
|
||||
After installing the SheetJS module in a SWC `spack` project, `import`
|
||||
statements can load relevant parts of the library.
|
||||
|
||||
Projects that import data will use methods such as `read`[^2] to parse workbooks
|
||||
and `sheet_to_json`[^3] to generate usable data from files. As `sheet_to_json`
|
||||
is part of the `utils` object, the required import is:
|
||||
|
||||
```js
|
||||
import { read, utils } from 'xlsx';
|
||||
```
|
||||
|
||||
Projects that export data will use methods such as `json_to_sheet`[^4] to
|
||||
generate worksheets and `writeFile`[^5] to export files. As `json_to_sheet` is
|
||||
part of the `utils` object, the required import is:
|
||||
|
||||
```js
|
||||
import { utils, writeFile } from 'xlsx';
|
||||
```
|
||||
|
||||
:::warning pass
|
||||
|
||||
When this demo was tested against the latest version, `spack` crashed:
|
||||
|
||||
```
|
||||
thread '<unnamed>' panicked at 'cannot access a scoped thread local variable without calling `set` first',
|
||||
```
|
||||
|
||||
Until the bug is fixed, it is strongly recommended to use `@swc/core@1.2.246`.
|
||||
|
||||
:::
|
||||
|
||||
## Complete Example
|
||||
|
||||
0) Initialize a new project:
|
||||
|
||||
```bash
|
||||
mkdir sheetjs-spack
|
||||
cd sheetjs-spack
|
||||
npm init -y
|
||||
```
|
||||
|
||||
1) Install the dependencies using a package manager:
|
||||
|
||||
<Tabs groupId="pm">
|
||||
<TabItem value="npm" label="npm">
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`} regenerator-runtime @swc/cli @swc/core@1.2.246
|
||||
</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="pnpm" label="pnpm">
|
||||
<CodeBlock language="bash">{`\
|
||||
pnpm install https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`} regenerator-runtime @swc/cli @swc/core@1.2.246
|
||||
</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="yarn" label="Yarn" default>
|
||||
<CodeBlock language="bash">{`\
|
||||
yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`} regenerator-runtime @swc/cli @swc/core@1.2.246
|
||||
</CodeBlock>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
:::note pass
|
||||
|
||||
The `regenerator-runtime` dependency is used for transpiling `fetch` and is not
|
||||
required if the interface code does not use `fetch` or Promises.
|
||||
|
||||
:::
|
||||
|
||||
2) Save the following to `index.js`:
|
||||
|
||||
```js title="index.js"
|
||||
import { utils, version, writeFileXLSX } from 'xlsx';
|
||||
|
||||
document.getElementById("xport").addEventListener("click", async() => {
|
||||
/* fetch JSON data and parse */
|
||||
const url = "https://sheetjs.com/data/executive.json";
|
||||
const raw_data = await (await fetch(url)).json();
|
||||
|
||||
/* filter for the Presidents */
|
||||
const prez = raw_data.filter(row => row.terms.some(term => term.type === "prez"));
|
||||
|
||||
/* flatten objects */
|
||||
const rows = prez.map(row => ({
|
||||
name: row.name.first + " " + row.name.last,
|
||||
birthday: row.bio.birthday
|
||||
}));
|
||||
|
||||
/* generate worksheet and workbook */
|
||||
const worksheet = utils.json_to_sheet(rows);
|
||||
const workbook = utils.book_new();
|
||||
utils.book_append_sheet(workbook, worksheet, "Dates");
|
||||
|
||||
/* fix headers */
|
||||
utils.sheet_add_aoa(worksheet, [["Name", "Birthday"]], { origin: "A1" });
|
||||
|
||||
/* calculate column width */
|
||||
const max_width = rows.reduce((w, r) => Math.max(w, r.name.length), 10);
|
||||
worksheet["!cols"] = [ { wch: max_width } ];
|
||||
|
||||
/* create an XLSX file and try to save to Presidents.xlsx */
|
||||
writeFileXLSX(workbook, "Presidents.xlsx");
|
||||
});
|
||||
```
|
||||
|
||||
3) Create an `spack.config.js` config file:
|
||||
|
||||
```js title="spack.config.js"
|
||||
module.exports = ({
|
||||
entry: {
|
||||
'web': __dirname + '/index.js',
|
||||
},
|
||||
output: {
|
||||
path: __dirname + '/lib'
|
||||
},
|
||||
module: {},
|
||||
});
|
||||
```
|
||||
|
||||
4) Build for production:
|
||||
|
||||
```bash
|
||||
npx spack
|
||||
```
|
||||
|
||||
This command will create the script `lib/web.js`
|
||||
|
||||
5) Create a small HTML page that loads the generated script:
|
||||
|
||||
```html title="index.html"
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head></head>
|
||||
<body>
|
||||
<h1>SheetJS Presidents Demo</h1>
|
||||
<button id="xport">Click here to export</button>
|
||||
<script src="lib/web.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
6) Start a local HTTP server, then go to `http://localhost:8080/`
|
||||
|
||||
```bash
|
||||
npx http-server
|
||||
```
|
||||
|
||||
Click on "Click here to export" to generate a file.
|
||||
|
||||
[^1]: See ["Bundling Configuration"](https://swc.rs/docs/configuration/bundling) in the SWC documentation for more details.
|
||||
[^2]: See [`read` in "Reading Files"](/docs/api/parse-options)
|
||||
[^3]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
|
||||
[^4]: See [`json_to_sheet` in "Utilities"](/docs/api/utilities/array#array-of-objects-input)
|
||||
[^5]: See [`writeFile` in "Writing Files"](/docs/api/write-options)
|
@ -35,84 +35,9 @@ The following tools are covered in separate pages:
|
||||
</li>);
|
||||
})}</ul>
|
||||
|
||||
## Browserify
|
||||
|
||||
`browserify` is compatible with the library and should "just work" with the
|
||||
`require` form in a main page or in a web worker:
|
||||
|
||||
```js
|
||||
var XLSX = require("xlsx");
|
||||
// ... use XLSX ...
|
||||
```
|
||||
|
||||
[After installing the NodeJS module](/docs/getting-started/installation/nodejs),
|
||||
bundling is straightforward:
|
||||
|
||||
```bash
|
||||
browserify app.js > browserify.js
|
||||
uglifyjs browserify.js > browserify.min.js
|
||||
```
|
||||
|
||||
Web Worker scripts can be bundled using the same approach.
|
||||
|
||||
<details><summary><b>Complete Example</b> (click to show)</summary>
|
||||
|
||||
:::note
|
||||
|
||||
This demo was last tested on 2023 May 07 against Browserify `17.0.0`
|
||||
|
||||
:::
|
||||
|
||||
1) Install the tarball using a package manager:
|
||||
|
||||
<Tabs groupId="pm">
|
||||
<TabItem value="npm" label="npm">
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
|
||||
</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="pnpm" label="pnpm">
|
||||
<CodeBlock language="bash">{`\
|
||||
pnpm install https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
|
||||
</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="yarn" label="Yarn" default>
|
||||
<CodeBlock language="bash">{`\
|
||||
yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
|
||||
</CodeBlock>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
2) Download the following files:
|
||||
|
||||
- [`app.js`](pathname:///browserify/app.js)
|
||||
- [`index.html`](pathname:///browserify/index.html)
|
||||
- [`xlsxworker.js`](pathname:///browserify/xlsxworker.js)
|
||||
|
||||
```bash
|
||||
curl -LO https://docs.sheetjs.com/browserify/app.js
|
||||
curl -LO https://docs.sheetjs.com/browserify/index.html
|
||||
curl -LO https://docs.sheetjs.com/browserify/xlsxworker.js
|
||||
```
|
||||
|
||||
3) Bundle the scripts:
|
||||
|
||||
```bash
|
||||
npx browserify app.js > browserify.js
|
||||
npx browserify xlsxworker.js > worker.js
|
||||
```
|
||||
|
||||
4) Spin up a local web server:
|
||||
|
||||
```bash
|
||||
npx http-server
|
||||
```
|
||||
|
||||
5) Access the site `http://localhost:8080/` and use the file input element to
|
||||
select a spreadsheet.
|
||||
|
||||
</details>
|
||||
#### Browserify
|
||||
|
||||
**[The exposition has been moved to a separate page.](/docs/demos/frontend/bundler/browserify)**
|
||||
|
||||
## Bun
|
||||
|
||||
@ -312,7 +237,7 @@ click the "Click to Export!" button to generate a file.
|
||||
|
||||
</details>
|
||||
|
||||
## RequireJS
|
||||
#### RequireJS
|
||||
|
||||
**[The exposition has been moved to a separate page.](/docs/demos/frontend/bundler/requirejs)**
|
||||
|
||||
@ -405,7 +330,6 @@ npx rollup index.js --plugin @rollup/plugin-node-resolve --file bundle.js --form
|
||||
</html>
|
||||
```
|
||||
|
||||
|
||||
5) Start a local HTTP server, then go to `http://localhost:8080/`
|
||||
|
||||
```bash
|
||||
@ -521,135 +445,9 @@ Click on "Click here to export" to generate a file.
|
||||
|
||||
</details>
|
||||
|
||||
## SWC
|
||||
#### SWC
|
||||
|
||||
SWC provides `spack` for bundling scripts.
|
||||
|
||||
:::warning pass
|
||||
|
||||
When this demo was last tested, there was a bug affecting 1.2.247 and 1.3 . It
|
||||
is strongly recommended to use `@swc/core@1.2.245` until the bug is fixed.
|
||||
|
||||
:::
|
||||
|
||||
<details><summary><b>Complete Example</b> (click to show)</summary>
|
||||
|
||||
:::note
|
||||
|
||||
This demo was last tested on 2023 May 07 against SWC 1.2.246
|
||||
|
||||
:::
|
||||
|
||||
1) Install the dependencies using a package manager:
|
||||
|
||||
<Tabs groupId="pm">
|
||||
<TabItem value="npm" label="npm">
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`} regenerator-runtime @swc/cli @swc/core@1.2.246
|
||||
</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="pnpm" label="pnpm">
|
||||
<CodeBlock language="bash">{`\
|
||||
pnpm install https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`} regenerator-runtime @swc/cli @swc/core@1.2.246
|
||||
</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="yarn" label="Yarn" default>
|
||||
<CodeBlock language="bash">{`\
|
||||
yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`} regenerator-runtime @swc/cli @swc/core@1.2.246
|
||||
</CodeBlock>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
:::note pass
|
||||
|
||||
The `regenerator-runtime` dependency is used for transpiling `fetch` and is not
|
||||
required if the interface code does not use `fetch` or Promises.
|
||||
|
||||
:::
|
||||
|
||||
2) Save the following to `index.js`:
|
||||
|
||||
```js title="index.js"
|
||||
import { utils, version, writeFileXLSX } from 'xlsx';
|
||||
|
||||
document.getElementById("xport").addEventListener("click", async() => {
|
||||
/* fetch JSON data and parse */
|
||||
const url = "https://sheetjs.com/data/executive.json";
|
||||
const raw_data = await (await fetch(url)).json();
|
||||
|
||||
/* filter for the Presidents */
|
||||
const prez = raw_data.filter(row => row.terms.some(term => term.type === "prez"));
|
||||
|
||||
/* flatten objects */
|
||||
const rows = prez.map(row => ({
|
||||
name: row.name.first + " " + row.name.last,
|
||||
birthday: row.bio.birthday
|
||||
}));
|
||||
|
||||
/* generate worksheet and workbook */
|
||||
const worksheet = utils.json_to_sheet(rows);
|
||||
const workbook = utils.book_new();
|
||||
utils.book_append_sheet(workbook, worksheet, "Dates");
|
||||
|
||||
/* fix headers */
|
||||
utils.sheet_add_aoa(worksheet, [["Name", "Birthday"]], { origin: "A1" });
|
||||
|
||||
/* calculate column width */
|
||||
const max_width = rows.reduce((w, r) => Math.max(w, r.name.length), 10);
|
||||
worksheet["!cols"] = [ { wch: max_width } ];
|
||||
|
||||
/* create an XLSX file and try to save to Presidents.xlsx */
|
||||
writeFileXLSX(workbook, "Presidents.xlsx");
|
||||
});
|
||||
```
|
||||
|
||||
3) Create an `spack.config.js` config file:
|
||||
|
||||
```js title="spack.config.js"
|
||||
const { config } = require('@swc/core/spack');
|
||||
|
||||
module.exports = ({
|
||||
entry: {
|
||||
'web': __dirname + '/index.js',
|
||||
},
|
||||
output: {
|
||||
path: __dirname + '/lib'
|
||||
},
|
||||
module: {},
|
||||
});
|
||||
```
|
||||
|
||||
4) Build for production:
|
||||
|
||||
```bash
|
||||
npx spack
|
||||
```
|
||||
|
||||
This command will create the script `lib/web.js`
|
||||
|
||||
5) Create a small HTML page that loads the generated script:
|
||||
|
||||
```html title="index.html"
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head></head>
|
||||
<body>
|
||||
<h1>SheetJS Presidents Demo</h1>
|
||||
<button id="xport">Click here to export</button>
|
||||
<script src="lib/web.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
6) Start a local HTTP server, then go to `http://localhost:8080/`
|
||||
|
||||
```bash
|
||||
npx http-server
|
||||
```
|
||||
|
||||
Click on "Click here to export" to generate a file.
|
||||
|
||||
</details>
|
||||
**[The exposition has been moved to a separate page.](/docs/demos/frontend/bundler/swcpack)**
|
||||
|
||||
#### SystemJS
|
||||
|
||||
|
361
docz/docs/03-demos/04-static/04-esbuild.md
Normal file
361
docz/docs/03-demos/04-static/04-esbuild.md
Normal file
@ -0,0 +1,361 @@
|
||||
---
|
||||
title: Building Sheets with ESBuild
|
||||
sidebar_label: ESBuild
|
||||
pagination_prev: demos/net/index
|
||||
pagination_next: demos/mobile/index
|
||||
sidebar_custom_props:
|
||||
type: bundler
|
||||
---
|
||||
|
||||
import current from '/version.js';
|
||||
import CodeBlock from '@theme/CodeBlock';
|
||||
|
||||
[ESBuild](https://esbuild.github.io/) is a modern build tool for generating
|
||||
static sites. It has a robust JavaScript-powered plugin system[^1]
|
||||
|
||||
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
|
||||
data from spreadsheets.
|
||||
|
||||
This demo uses ESBuild and SheetJS to pull data from a spreadsheet and display
|
||||
the content in an HTML table. We'll explore how to load SheetJS in a ESBuild
|
||||
loader and generate data for use in webpages.
|
||||
|
||||
The ["Demo"](#demo) creates a complete website powered by a XLSX spreadsheet.
|
||||
|
||||
:::info pass
|
||||
|
||||
This demo covers static asset imports. For processing files in the browser, the
|
||||
["Bundlers" demo](/docs/demos/frontend/bundler/esbuild) includes an example of
|
||||
importing the SheetJS library in a browser script.
|
||||
|
||||
:::
|
||||
|
||||
## ESBuild Loader
|
||||
|
||||
ESBuild supports custom loader plugins. The loader receives an absolute path to
|
||||
the spreadsheet on the filesystem.
|
||||
|
||||
The [SheetJS NodeJS module](/docs/getting-started/installation/nodejs) can be
|
||||
imported from ESBuild loader plugins.
|
||||
|
||||
:::info pass
|
||||
|
||||
ESBuild loader plugins use ECMAScript Modules. The plugin ultimately receives
|
||||
raw paths to files. [`fs`](/docs/getting-started/installation/nodejs#esm-import)
|
||||
must be manually imported:
|
||||
|
||||
```js
|
||||
import * as XLSX from 'xlsx';
|
||||
|
||||
/* load 'fs' for readFile and writeFile support */
|
||||
import * as fs from 'fs';
|
||||
XLSX.set_fs(fs);
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
The following diagram depicts the workbook waltz:
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph ESBuild Custom Plugin in build.mjs
|
||||
file[(workbook\nfile)]
|
||||
wb(((SheetJS\nWorkbook)))
|
||||
aoo(array of\nobjects)
|
||||
end
|
||||
html{{HTML\nTABLE}}
|
||||
file --> |`readFile`\n\n| wb
|
||||
wb --> |`sheet_to_json`\n\n| aoo
|
||||
aoo --> |app.js\nfrontend code| html
|
||||
```
|
||||
|
||||
### ESBuild Config
|
||||
|
||||
Plugins can be referenced in the `plugins` array of the build config object:
|
||||
|
||||
```js title="build.mjs (structure)"
|
||||
import * as esbuild from 'esbuild'
|
||||
|
||||
// highlight-next-line
|
||||
let sheetjsPlugin = {
|
||||
name: 'sheetjs',
|
||||
setup(build) {
|
||||
// ...
|
||||
}
|
||||
};
|
||||
|
||||
await esbuild.build({
|
||||
entryPoints: ['app.js'],
|
||||
bundle: true,
|
||||
outfile: 'out.js',
|
||||
// highlight-next-line
|
||||
plugins: [sheetjsPlugin],
|
||||
})
|
||||
```
|
||||
|
||||
### Registering File Extensions
|
||||
|
||||
The `setup` method receives the build options. Handlers for custom files should
|
||||
be added using `build.onLoad`.
|
||||
|
||||
The first argument to `onLoad` is a configuration object. The `filter` property
|
||||
is expected to be a regular expression. The following regular expression matches
|
||||
NUMBERS, XLSX, XLS, and XLSB files:
|
||||
|
||||
```js
|
||||
const EXTS = /.(numbers|xlsx|xls|xlsb)$/;
|
||||
```
|
||||
|
||||
The second argument to `onLoad` is a callback that receives an arguments object.
|
||||
The `path` property of the object is the absolute path to the file.
|
||||
|
||||
```js
|
||||
setup(build) {
|
||||
build.onLoad({ filter: EXTS }, (args) => {
|
||||
const path = args.path;
|
||||
// ...
|
||||
});
|
||||
},
|
||||
```
|
||||
|
||||
### SheetJS Operations
|
||||
|
||||
The SheetJS `readFile` method[^2] will directly read the file on the filesystem.
|
||||
The return value is a SheetJS workbook object[^3].
|
||||
|
||||
The loader in this demo will parse the workbook, pull the first worksheet, and
|
||||
generate an array of row objects using the `sheet_to_json` method[^4].
|
||||
|
||||
:::caution pass
|
||||
|
||||
JSON does not natively support Dates! `JSON.stringify` will generate strings.
|
||||
|
||||
Through a clever workaround, it is possible to encode dates separately and
|
||||
recover the Date objects in the generated code module.
|
||||
|
||||
:::
|
||||
|
||||
```js
|
||||
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' };
|
||||
});
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
### Asset Imports
|
||||
|
||||
Spreadsheets can be imported using the plugin. Assuming `pres.xlsx` is stored
|
||||
in the same folder as the script, `./pres.xlsx` will be a data module:
|
||||
|
||||
```js title="src/index.js"
|
||||
import data from './pres.xlsx';
|
||||
/* `data` is an array of objects from data/pres.xlsx */
|
||||
|
||||
const elt = document.createElement('div');
|
||||
elt.innerHTML = "<table><tr><th>Name</th><th>Index</th></tr>" +
|
||||
data.map((row) => `<tr>
|
||||
<td>${row.Name}</td>
|
||||
<td>${row.Index}</td>
|
||||
</tr>`).join("") +
|
||||
"</table>";
|
||||
document.body.appendChild(elt);
|
||||
```
|
||||
|
||||
## Demo
|
||||
|
||||
:::note
|
||||
|
||||
This demo was last tested on 2023 October 21 against ESBuild 0.19.5
|
||||
|
||||
:::
|
||||
|
||||
### Initial Setup
|
||||
|
||||
0) Create a new skeleton project:
|
||||
|
||||
```bash
|
||||
mkdir sheetjs-esb
|
||||
cd sheetjs-esb
|
||||
npm init -y
|
||||
npm i --save esbuild@0.19.5
|
||||
```
|
||||
|
||||
1) Install the SheetJS NodeJS module:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
|
||||
</CodeBlock>
|
||||
|
||||
2) Save the following to `index.html`:
|
||||
|
||||
```html title="index.html"
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>SheetJS + ESBuild</title>
|
||||
</head>
|
||||
<body>
|
||||
<script src="out.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
3) Save the following to `app.js`:
|
||||
|
||||
```js title="app.js"
|
||||
import data from './pres.numbers'
|
||||
const elt = document.createElement('div');
|
||||
elt.innerHTML = "<table><tr><th>Name</th><th>Index</th></tr>" +
|
||||
data.map((row) => `<tr>
|
||||
<td>${row.Name}</td>
|
||||
<td>${row.Index}</td>
|
||||
</tr>`).join("") +
|
||||
"</table>";
|
||||
document.body.appendChild(elt);
|
||||
```
|
||||
|
||||
4) Save the following to `build.mjs`:
|
||||
|
||||
```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],
|
||||
});
|
||||
```
|
||||
|
||||
5) Download <https://sheetjs.com/pres.numbers> and save to the project folder:
|
||||
|
||||
```bash
|
||||
curl -LO https://sheetjs.com/pres.numbers
|
||||
```
|
||||
|
||||
### Static Site Test
|
||||
|
||||
6) Build the site:
|
||||
|
||||
```bash
|
||||
node build.mjs
|
||||
```
|
||||
|
||||
The final script will be saved to `out.js`
|
||||
|
||||
7) Start a local web server to host the project folder:
|
||||
|
||||
```bash
|
||||
npx http-server .
|
||||
```
|
||||
|
||||
The command will print a list of URLs.
|
||||
|
||||
8) Open one of the URLs printed in the previous step (`http://localhost:8080`)
|
||||
and confirm that the same data is displayed.
|
||||
|
||||
To verify that the data was added to the page, append `out.js` to the URL
|
||||
(`http://localhost:8080/out.js`) and view the source. The source will include
|
||||
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"
|
||||
(() => {
|
||||
// pres.numbers
|
||||
var data = [{ "Name": "Bill Clinton", "Index": 42 }, /* ... more data */];
|
||||
data.forEach((row) => {
|
||||
Object.keys(row).forEach((k) => {
|
||||
if (row[k]?.d)
|
||||
row[k] = new Date(row[k].d);
|
||||
});
|
||||
});
|
||||
var pres_default = data;
|
||||
|
||||
// app.js
|
||||
var elt = document.createElement("div");
|
||||
elt.innerHTML = "<table><tr><th>Name</th><th>Index</th></tr>" + pres_default.map((row) => `<tr>
|
||||
<td>${row.Name}</td>
|
||||
<td>${row.Index}</td>
|
||||
</tr>`).join("") + "</table>";
|
||||
document.body.appendChild(elt);
|
||||
})();
|
||||
```
|
||||
|
||||
[^1]: See ["Plugins"](https://esbuild.github.io/plugins/) in the ESBuild documentation.
|
||||
[^2]: See [`readFile` in "Reading Files"](/docs/api/parse-options)
|
||||
[^3]: See ["Workbook Object"](/docs/csf/book)
|
||||
[^4]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
|
@ -125,7 +125,7 @@ This demo was tested in the following deployments:
|
||||
| V8 Version | Platform | OS Version | Compiler | Date |
|
||||
|:--------------|:-------------|:--------------|:-----------------|:-----------|
|
||||
| `11.8.82` | `darwin-x64` | macOS 13.5.1 | `clang 14.0.3` | 2023-08-26 |
|
||||
| `11.8.82` | `darwin-arm` | macOS 13.5.1 | `clang 14.0.3` | 2023-08-26 |
|
||||
| `12.0.175` | `darwin-arm` | macOS 14.0 | `clang 15.0.0` | 2023-10-20 |
|
||||
| `11.8.82` | `win10-x64` | Windows 10 | `CL 19.37.32822` | 2023-08-26 |
|
||||
| `12.0.72` | `linux-x64` | HoloOS 3.4.11 | `gcc 12.2.0` | 2023-10-11 |
|
||||
| `11.8.82` | `linux-arm` | Debian 11 | `gcc 10.2.1` | 2023-09-26 |
|
||||
|
@ -115,7 +115,7 @@ This demo was tested in the following deployments:
|
||||
| Architecture | Date |
|
||||
|:-------------|:-----------|
|
||||
| `darwin-x64` | 2023-08-31 |
|
||||
| `darwin-arm` | 2023-07-05 |
|
||||
| `darwin-arm` | 2023-10-20 |
|
||||
| `win10-x64` | 2023-08-31 |
|
||||
| `win11-arm` | 2023-09-26 |
|
||||
| `linux-x64` | 2023-10-11 |
|
||||
|
@ -134,6 +134,8 @@ This demo was tested in the following deployments:
|
||||
| `darwin-x64` | `a588e49` | 2023-09-22 |
|
||||
| `linux-x64` | `a588e49` | 2023-10-11 |
|
||||
|
||||
:::
|
||||
|
||||
:::note pass
|
||||
|
||||
While applications should link against the official libraries, the standalone tool
|
||||
|
Loading…
Reference in New Issue
Block a user