forked from sheetjs/docs.sheetjs.com
		
	
		
			
	
	
		
			305 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
		
		
			
		
	
	
			305 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
|  | --- | ||
|  | 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'; | ||
|  | 
 | ||
|  | :::note | ||
|  | 
 | ||
|  | This demo covers static asset imports. For processing files in the browser, the | ||
|  | ["Bundlers" demo](/docs/demos/frontend/bundler#webpack) includes an example. | ||
|  | 
 | ||
|  | ::: | ||
|  | 
 | ||
|  | 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! |