2023-05-25 01:36:15 +00:00
|
|
|
---
|
2023-10-11 02:18:57 +00:00
|
|
|
title: Packing Sheets with Webpack
|
|
|
|
sidebar_label: Webpack
|
2023-05-25 01:36:15 +00:00
|
|
|
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';
|
|
|
|
|
2023-10-11 02:18:57 +00:00
|
|
|
[Webpack](https://webpack.js.org/) 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 Webpack 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 Webpack 5
|
|
|
|
Asset Plugin and generate data for use in webpages.
|
|
|
|
|
|
|
|
The ["Webpack 5 Demo"](#webpack-5-demo) creates a complete website powered by a
|
|
|
|
XLSX spreadsheet.
|
|
|
|
|
2023-09-24 03:59:48 +00:00
|
|
|
:::info pass
|
2023-05-25 01:36:15 +00:00
|
|
|
|
|
|
|
This demo covers static asset imports. For processing files in the browser, the
|
2023-10-18 02:07:06 +00:00
|
|
|
["Bundlers" demo](/docs/demos/frontend/bundler/webpack) includes an example of
|
2023-09-24 03:59:48 +00:00
|
|
|
importing the SheetJS library in a browser script.
|
2023-05-25 01:36:15 +00:00
|
|
|
|
|
|
|
:::
|
|
|
|
|
|
|
|
## Webpack 5 Asset Module
|
|
|
|
|
|
|
|
Webpack 5 supports asset modules. With a special option, the loader will receive
|
|
|
|
NodeJS Buffers that can be parsed. The dev server will even watch the files and
|
|
|
|
reload the page in development mode!
|
|
|
|
|
2023-10-11 02:18:57 +00:00
|
|
|
The [SheetJS NodeJS module](/docs/getting-started/installation/nodejs) can be
|
|
|
|
imported from Webpack loader scripts.
|
|
|
|
|
2023-05-25 01:36:15 +00:00
|
|
|
The following diagram depicts the workbook waltz:
|
|
|
|
|
|
|
|
```mermaid
|
|
|
|
flowchart LR
|
|
|
|
file[(workbook\nfile)]
|
|
|
|
subgraph SheetJS operations
|
|
|
|
buffer(NodeJS\nBuffer)
|
|
|
|
aoo(array of\nobjects)
|
|
|
|
end
|
|
|
|
html{{HTML\nTABLE}}
|
|
|
|
file --> |webpack.config.js\ncustom rule| buffer
|
|
|
|
buffer --> |sheetjs-loader.js\ncustom plugin| aoo
|
|
|
|
aoo --> |src/index.js\nfrontend code| html
|
|
|
|
```
|
|
|
|
|
|
|
|
### Webpack Config
|
|
|
|
|
2024-04-08 03:55:10 +00:00
|
|
|
The Webpack configuration is normally saved to `webpack.config.js`.
|
2023-05-25 01:36:15 +00:00
|
|
|
|
2024-04-08 03:55:10 +00:00
|
|
|
#### Required Settings
|
|
|
|
|
|
|
|
`module.rules` is an array of rule objects that controls module synthesis.[^2]
|
|
|
|
For the SheetJS Webpack integration, the following properties are required:
|
|
|
|
|
|
|
|
- `test` describes whether the rule is relevant. If the property is a regular
|
|
|
|
expression, Webpack will test the filename against the `test` property.
|
|
|
|
|
|
|
|
- `use` lists the loaders that will process files matching the `test`. The
|
|
|
|
loaders are specified using the `loader` property of the loader object.
|
|
|
|
|
|
|
|
The following example instructs Webpack to use the `sheetjs-loader.js` script
|
|
|
|
when the file name ends in `.numbers` or `.xls` or `.xlsx` or `.xlsb`:
|
|
|
|
|
|
|
|
```js title="webpack.config.js (define loader)"
|
2023-05-25 01:36:15 +00:00
|
|
|
// ...
|
|
|
|
module.exports = {
|
|
|
|
// ...
|
|
|
|
module: {
|
|
|
|
rules: [
|
|
|
|
// highlight-start
|
|
|
|
{
|
|
|
|
/* `test` matches file extensions */
|
2024-04-08 03:55:10 +00:00
|
|
|
test: /\.(numbers|xls|xlsx|xlsb)$/,
|
2023-05-25 01:36:15 +00:00
|
|
|
/* use the loader script */
|
|
|
|
use: [ { loader: './sheetjs-loader' } ]
|
|
|
|
}
|
|
|
|
// highlight-end
|
|
|
|
]
|
|
|
|
}
|
|
|
|
};
|
|
|
|
```
|
|
|
|
|
2024-04-08 03:55:10 +00:00
|
|
|
#### Recommended Settings
|
2023-05-25 01:36:15 +00:00
|
|
|
|
2024-04-08 03:55:10 +00:00
|
|
|
It is strongly recommended to enable other Webpack features:
|
2023-05-25 01:36:15 +00:00
|
|
|
|
2024-04-08 03:55:10 +00:00
|
|
|
- `resolve.alias` defines path aliases. If data files are stored in one folder,
|
|
|
|
an alias ensures that each page can reference the files using the same name[^3].
|
2023-05-25 01:36:15 +00:00
|
|
|
|
2024-04-08 03:55:10 +00:00
|
|
|
- `devServer.hot` enables "hot module replacement"[^4], ensuring that pages will
|
|
|
|
refresh in development mode when spreadsheets are saved.
|
|
|
|
|
|
|
|
The following example instructs Webpack to treat `~` as the root of the project
|
|
|
|
(so `~/data/pres.xlsx` refers to `pres.xlsx` in the data folder) and to enable
|
|
|
|
live reloading:
|
|
|
|
|
|
|
|
```js title="webpack.config.js (other recommended settings)"
|
2023-05-25 01:36:15 +00:00
|
|
|
// ...
|
|
|
|
module.exports = {
|
|
|
|
// ...
|
|
|
|
// highlight-start
|
|
|
|
resolve: {
|
|
|
|
alias: {
|
|
|
|
/* `~` root of the project */
|
|
|
|
"~": __dirname
|
|
|
|
}
|
|
|
|
},
|
|
|
|
// highlight-end
|
2024-04-08 03:55:10 +00:00
|
|
|
// ...
|
|
|
|
// highlight-start
|
|
|
|
/* enable live reloading in development mode */
|
|
|
|
devServer: { static: './dist', hot: true }
|
|
|
|
// highlight-end
|
2023-05-25 01:36:15 +00:00
|
|
|
};
|
|
|
|
```
|
|
|
|
|
|
|
|
### SheetJS Loader
|
|
|
|
|
2024-04-08 03:55:10 +00:00
|
|
|
The SheetJS loader script must be saved to the script referenced in the Webpack
|
|
|
|
configuration (`sheetjs-loader.js`).
|
|
|
|
|
|
|
|
As with [ViteJS](/docs/demos/static/vitejs), Webpack will interpret data as
|
|
|
|
UTF-8 strings. This corrupts binary formats including XLSX and XLS. To suppress
|
|
|
|
this behavior and instruct Webpack to pass a NodeJS `Buffer` object, the loader
|
|
|
|
script must export a `raw` property that is set to `true`[^5].
|
2023-05-25 01:36:15 +00:00
|
|
|
|
2023-10-11 02:18:57 +00:00
|
|
|
The base export is expected to be the loader function. The loader receives the
|
2024-04-08 03:55:10 +00:00
|
|
|
file bytes as a Buffer, which can be parsed with the SheetJS `read` method[^6].
|
|
|
|
`read` returns a SheetJS workbook object[^7].
|
2023-05-25 01:36:15 +00:00
|
|
|
|
2023-10-11 02:18:57 +00:00
|
|
|
The loader in this demo will parse the workbook, pull the first worksheet, and
|
2024-04-08 03:55:10 +00:00
|
|
|
generate an array of row objects using the `sheet_to_json` method[^8]:
|
2023-05-25 01:36:15 +00:00
|
|
|
|
2024-04-08 03:55:10 +00:00
|
|
|
```js title="sheetjs-loader.js (Webpack loader)"
|
2023-05-25 01:36:15 +00:00
|
|
|
const XLSX = require("xlsx");
|
|
|
|
|
|
|
|
function loader(content) {
|
|
|
|
/* since `loader.raw` is true, `content` is a Buffer */
|
|
|
|
const wb = XLSX.read(content);
|
|
|
|
/* pull data from first worksheet */
|
|
|
|
var data = XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]);
|
|
|
|
return `export default JSON.parse('${JSON.stringify(data)}')`;
|
|
|
|
}
|
2024-04-08 03:55:10 +00:00
|
|
|
|
2023-05-25 01:36:15 +00:00
|
|
|
/* ensure the function receives a Buffer */
|
|
|
|
loader.raw = true;
|
2024-04-08 03:55:10 +00:00
|
|
|
|
|
|
|
/* export the loader */
|
2023-05-25 01:36:15 +00:00
|
|
|
module.exports = loader;
|
|
|
|
```
|
|
|
|
|
|
|
|
### Asset Imports
|
|
|
|
|
|
|
|
Spreadsheets can be imported using the plugin. Assuming `pres.xlsx` is stored
|
|
|
|
in the `data` subfolder, `~/data/pres.xlsx` can be imported from any script:
|
|
|
|
|
2024-04-08 03:55:10 +00:00
|
|
|
```js title="src/index.js (main script)"
|
2023-05-25 01:36:15 +00:00
|
|
|
import data from '~/data/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);
|
|
|
|
```
|
|
|
|
|
|
|
|
## Webpack 5 Demo
|
|
|
|
|
2023-12-05 03:46:54 +00:00
|
|
|
:::note Tested Deployments
|
2023-05-25 01:36:15 +00:00
|
|
|
|
2024-04-08 03:55:10 +00:00
|
|
|
This demo was last tested on 2024 April 06 against Webpack 5.91.0
|
2023-05-25 01:36:15 +00:00
|
|
|
|
|
|
|
:::
|
|
|
|
|
|
|
|
### Initial Setup
|
|
|
|
|
|
|
|
0) Create a new skeleton project:
|
|
|
|
|
|
|
|
```bash
|
|
|
|
mkdir sheetjs-wp5
|
|
|
|
cd sheetjs-wp5
|
|
|
|
npm init -y
|
2024-04-08 03:55:10 +00:00
|
|
|
npm install webpack@5.91.0 webpack-cli@5.1.4 webpack-dev-server@5.0.4 --save
|
2023-05-25 01:36:15 +00:00
|
|
|
mkdir -p dist
|
|
|
|
mkdir -p src
|
|
|
|
mkdir -p data
|
|
|
|
```
|
|
|
|
|
|
|
|
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 `dist/index.html`:
|
|
|
|
|
|
|
|
```html title="dist/index.html"
|
|
|
|
<!DOCTYPE html>
|
|
|
|
<html>
|
|
|
|
<head>
|
|
|
|
<title>SheetJS + Webpack 5</title>
|
|
|
|
</head>
|
|
|
|
<body>
|
|
|
|
<script src="main.js"></script>
|
|
|
|
</body>
|
|
|
|
</html>
|
|
|
|
```
|
|
|
|
|
|
|
|
3) Save the following to `src/index.js`:
|
|
|
|
|
|
|
|
```js title="src/index.js"
|
|
|
|
import data 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);
|
|
|
|
```
|
|
|
|
|
|
|
|
4) Save the following to `webpack.config.js`:
|
|
|
|
|
|
|
|
```js title="webpack.config.js"
|
|
|
|
const path = require('path');
|
|
|
|
|
|
|
|
module.exports = {
|
|
|
|
entry: './src/index.js',
|
|
|
|
output: {
|
|
|
|
filename: 'main.js',
|
|
|
|
path: path.resolve(__dirname, 'dist'),
|
|
|
|
},
|
|
|
|
devServer: {
|
|
|
|
static: './dist',
|
|
|
|
hot: true,
|
|
|
|
},
|
|
|
|
resolve: {
|
|
|
|
alias: {
|
|
|
|
"~": __dirname
|
|
|
|
}
|
|
|
|
},
|
|
|
|
module: {
|
|
|
|
rules: [
|
|
|
|
{
|
2024-04-08 03:55:10 +00:00
|
|
|
test: /\.(numbers|xls|xlsx|xlsb)$/,
|
2023-05-25 01:36:15 +00:00
|
|
|
use: [ { loader: './sheetjs-loader' } ]
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
};
|
|
|
|
```
|
|
|
|
|
|
|
|
5) Save the following to `sheetjs-loader.js`:
|
|
|
|
|
|
|
|
```js title="sheetjs-loader.js"
|
|
|
|
const XLSX = require("xlsx");
|
|
|
|
|
|
|
|
function loader(content) {
|
|
|
|
/* since `loader.raw` is true, `content` is a Buffer */
|
|
|
|
const wb = XLSX.read(content);
|
|
|
|
/* pull data from first worksheet */
|
|
|
|
var data = XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]);
|
|
|
|
return `export default JSON.parse('${JSON.stringify(data)}')`;
|
|
|
|
}
|
|
|
|
/* ensure the function receives a Buffer */
|
|
|
|
loader.raw = true;
|
|
|
|
module.exports = loader;
|
|
|
|
```
|
|
|
|
|
2024-04-08 04:47:04 +00:00
|
|
|
6) Download https://sheetjs.com/pres.xlsx and save to the `data` folder:
|
2023-05-25 01:36:15 +00:00
|
|
|
|
|
|
|
```bash
|
|
|
|
curl -L -o data/pres.xlsx https://sheetjs.com/pres.xlsx
|
|
|
|
```
|
|
|
|
|
|
|
|
### Live Reload Test
|
|
|
|
|
|
|
|
7) Open the test file `data/pres.xlsx` in a spreadsheet editor like Excel.
|
|
|
|
|
|
|
|
8) Start the development server:
|
|
|
|
|
|
|
|
```bash
|
|
|
|
npx webpack serve --mode=development
|
|
|
|
```
|
|
|
|
|
|
|
|
The terminal will print URLs for the development server:
|
|
|
|
|
|
|
|
```
|
|
|
|
<i> [webpack-dev-server] Project is running at:
|
|
|
|
<i> [webpack-dev-server] Loopback: http://localhost:8080/
|
|
|
|
```
|
|
|
|
|
|
|
|
9) Open the `Loopback` address (`http://localhost:8080`) in a web browser.
|
|
|
|
|
|
|
|
It should display a table of Presidents with "Name" and "Index" columns
|
|
|
|
|
2024-04-08 03:55:10 +00:00
|
|
|
10) Add a new row to the spreadsheet (set `A7` to "SheetJS Dev" and `B7` to 47)
|
|
|
|
and save the file.
|
2023-05-25 01:36:15 +00:00
|
|
|
|
2024-04-08 03:55:10 +00:00
|
|
|
After saving the file, the page should automatically refresh with the new data.
|
2023-05-25 01:36:15 +00:00
|
|
|
|
|
|
|
### Static Site Test
|
|
|
|
|
|
|
|
11) Stop Webpack and build the site:
|
|
|
|
|
|
|
|
```bash
|
|
|
|
npx webpack --mode=production
|
|
|
|
```
|
|
|
|
|
|
|
|
The final site will be placed in the `dist` folder.
|
|
|
|
|
|
|
|
12) Start a local web server to host the `dist` folder:
|
|
|
|
|
|
|
|
```bash
|
|
|
|
npx http-server dist
|
|
|
|
```
|
|
|
|
|
|
|
|
The command will print a list of URLs.
|
|
|
|
|
|
|
|
13) 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 page is independent of the spreadsheet, make some changes to
|
|
|
|
the file and save. The page will not automatically update.
|
|
|
|
|
|
|
|
To verify that the data was added to the page, append `main.js` to the URL
|
|
|
|
(`http://localhost:8080/main.js`) and view the source. The source will include
|
|
|
|
president names. It will not include SheetJS library references!
|
2023-10-11 02:18:57 +00:00
|
|
|
|
|
|
|
[^1]: See ["Plugins"](https://webpack.js.org/concepts/plugins/) in the Webpack documentation.
|
2024-04-08 03:55:10 +00:00
|
|
|
[^2]: See [`module.rules`](https://webpack.js.org/configuration/module/#modulerules) in the Webpack documentation.
|
|
|
|
[^3]: See [`resolve.alias`](https://webpack.js.org/configuration/resolve/#resolvealias) in the Webpack documentation.
|
|
|
|
[^4]: See ["Hot Module Replacement"](https://webpack.js.org/concepts/hot-module-replacement/) in the Webpack documentation.
|
|
|
|
[^5]: See ["Raw" Loader](https://webpack.js.org/api/loaders/#raw-loader) in the Webpack documentation.
|
|
|
|
[^6]: See [`read` in "Reading Files"](/docs/api/parse-options)
|
|
|
|
[^7]: See ["Workbook Object"](/docs/csf/book)
|
|
|
|
[^8]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
|