| 
									
										
										
										
											2023-05-25 01:36:15 +00:00
										 |  |  | --- | 
					
						
							|  |  |  | title: 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'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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-09-24 03:59:48 +00:00
										 |  |  | ["Bundlers" demo](/docs/demos/frontend/bundler#webpack) includes an example of | 
					
						
							|  |  |  | importing the SheetJS library in a browser script. | 
					
						
							| 
									
										
										
										
											2023-05-25 01:36:15 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | ::: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The [NodeJS Module](/docs/getting-started/installation/nodejs) can be imported | 
					
						
							|  |  |  | from Webpack loader scripts. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ## 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 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. That function receives | 
					
						
							|  |  |  | the file contents as a Buffer, which can be parsed with `XLSX.read`. Typically | 
					
						
							|  |  |  | this script is CommonJS so the `require` form should be used. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The loader in this demo will parse the first worksheet: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ```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 = "<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
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | :::note | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | This demo was last tested on 2023 May 24 against Webpack 5.84.0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ::: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ### Initial Setup
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 0) Create a new skeleton project: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ```bash | 
					
						
							|  |  |  | mkdir sheetjs-wp5 | 
					
						
							|  |  |  | cd sheetjs-wp5 | 
					
						
							|  |  |  | npm init -y | 
					
						
							|  |  |  | npm install webpack@5.84.0 webpack-cli@5.1.1 webpack-dev-server@4.15.0 --save | 
					
						
							|  |  |  | 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: [ | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         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 <https://sheetjs.com/pres.xlsx> 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: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | <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 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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! |