--- title: Packing Sheets with Webpack sidebar_label: Webpack 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'; [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. :::info pass This demo covers static asset imports. For processing files in the browser, the ["Bundlers" demo](/docs/demos/frontend/bundler/webpack) includes an example of importing the SheetJS library in a browser script. ::: ## 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! The [SheetJS NodeJS module](/docs/getting-started/installation/nodejs) can be imported from Webpack loader scripts. 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 A special rule should be added to `module.rules`: ```js title="webpack.config.js" // ... module.exports = { // ... module: { rules: [ // highlight-start { /* `test` matches file extensions */ test: /\.(numbers|xls|xlsx|xlsb)/, /* use the loader script */ use: [ { loader: './sheetjs-loader' } ] } // highlight-end ] } }; ``` Hot Module Replacement enables reloading when files are updated: ```js title="webpack.config.js" // ... module.exports = { // ... // highlight-start devServer: { static: './dist', hot: true, } // highlight-end }; ``` It is strongly recommended to add an alias to simplify imports: ```js title="webpack.config.js" // ... module.exports = { // ... // highlight-start resolve: { alias: { /* `~` root of the project */ "~": __dirname } }, // highlight-end }; ``` ### SheetJS Loader The SheetJS loader script must export a `raw` property that is set to `true`. The base export is expected to be the loader function. The loader receives the file bytes as a Buffer, which can be parsed with the SheetJS `read` method[^2]. `read` returns 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]: ```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; ``` ### 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: ```js title="src/index.js" import data from '~/data/pres.xlsx'; /* `data` is an array of objects from data/pres.xlsx */ const elt = document.createElement('div'); elt.innerHTML = "" + data.map((row) => ``).join("") + "
NameIndex
${row.Name} ${row.Index}
"; document.body.appendChild(elt); ``` ## Webpack 5 Demo :::note This demo was last tested on 2023 October 10 against Webpack 5.88.2 ::: ### Initial Setup 0) Create a new skeleton project: ```bash mkdir sheetjs-wp5 cd sheetjs-wp5 npm init -y npm install webpack@5.88.2 webpack-cli@5.1.4 webpack-dev-server@4.15.1 --save mkdir -p dist mkdir -p src mkdir -p data ``` 1) Install the SheetJS NodeJS module: {`\ npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`} 2) Save the following to `dist/index.html`: ```html title="dist/index.html" SheetJS + Webpack 5 ``` 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 = "" + data.map((row) => ``).join("") + "
NameIndex
${row.Name} ${row.Index}
"; 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: [ { test: /\.(numbers|xls|xlsx|xlsb)/, 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; ``` 6) Download and save to the `data` folder: ```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: ``` [webpack-dev-server] Project is running at: [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 10) Add a new row to the spreadsheet and save the file. Upon saving, the page should refresh with the new data. ### 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! [^1]: See ["Plugins"](https://webpack.js.org/concepts/plugins/) in the Webpack documentation. [^2]: See [`read` 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)