| 
									
										
										
										
											2022-08-13 22:01:26 +00:00
										 |  |  |  | --- | 
					
						
							|  |  |  |  | sidebar_position: 20 | 
					
						
							|  |  |  |  | title: Content and Site Generation | 
					
						
							|  |  |  |  | --- | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | With the advent of server-side frameworks and content management systems, it is | 
					
						
							|  |  |  |  | possible to build sites whose source of truth is a spreadsheet!  This demo | 
					
						
							|  |  |  |  | explores a number of approaches. | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | ## GatsbyJS
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | [`gatsby-transformer-excel`](https://www.gatsbyjs.com/plugins/gatsby-transformer-excel/) | 
					
						
							|  |  |  |  | generates nodes for each data row of each worksheet. The official documentation | 
					
						
							|  |  |  |  | includes examples and more detailed usage instructions. | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | :::note | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | `gatsby-transformer-excel` is maintained by the Gatsby core team and all bugs | 
					
						
							|  |  |  |  | should be directed to the main Gatsby project.  If it is determined to be a bug | 
					
						
							|  |  |  |  | in the parsing logic, issues should then be raised with the SheetJS project. | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | ::: | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | ## NuxtJS
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | `@nuxt/content` is a file-based CMS for Nuxt, enabling static-site generation | 
					
						
							|  |  |  |  | and on-demand server rendering powered by spreadsheets. | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | #### nuxt.config.js configuration
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | Through an override in `nuxt.config.js`, Nuxt Content will use custom parsers. | 
					
						
							|  |  |  |  | Differences from a stock `create-nuxt-app` config are shown below: | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | ```js | 
					
						
							|  |  |  |  | // highlight-start | 
					
						
							|  |  |  |  | import { readFile, utils } from 'xlsx'; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // This will be called when the files change | 
					
						
							|  |  |  |  | const parseSheet = (file, { path }) => { | 
					
						
							|  |  |  |  |   // `path` is a path that can be read with `XLSX.readFile` | 
					
						
							|  |  |  |  |   const wb = readFile(path); | 
					
						
							|  |  |  |  |   const o = wb.SheetNames.map(name => ({ name, data: utils.sheet_to_json(wb.Sheets[name])})); | 
					
						
							|  |  |  |  |   return { data: o }; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | // highlight-end | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | export default { | 
					
						
							|  |  |  |  | // ... | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // highlight-start | 
					
						
							|  |  |  |  |   // content.extendParser allows us to hook into the parsing step | 
					
						
							|  |  |  |  |   content: { | 
					
						
							|  |  |  |  |     extendParser: { | 
					
						
							|  |  |  |  |       // the keys are the extensions that will be matched.  The "." is required | 
					
						
							|  |  |  |  |       ".numbers": parseSheet, | 
					
						
							|  |  |  |  |       ".xlsx": parseSheet, | 
					
						
							|  |  |  |  |       ".xls": parseSheet, | 
					
						
							|  |  |  |  |       // can add other extensions like ".fods" as desired | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |   }, | 
					
						
							|  |  |  |  | // highlight-end | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // ... | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | ``` | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | #### Template Use
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | When a spreadsheet is placed in the `content` folder, Nuxt will find it.  The | 
					
						
							|  |  |  |  | data can be referenced in a view with `asyncData`.  The name should not include | 
					
						
							|  |  |  |  | the extension, so `"sheetjs.numbers"` would be referenced as `"sheetjs"`: | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | ```js | 
					
						
							|  |  |  |  |   async asyncData ({$content}) { | 
					
						
							|  |  |  |  |     return { | 
					
						
							|  |  |  |  |       // $content('sheetjs') will match files with extensions in nuxt.config.js | 
					
						
							|  |  |  |  |       data: await $content('sheetjs').fetch() | 
					
						
							|  |  |  |  |     }; | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | ``` | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | In the template, `data.data` is an array of objects.  Each object has a `name` | 
					
						
							|  |  |  |  | property for the worksheet name and a `data` array of row objects.  This maps | 
					
						
							|  |  |  |  | neatly with nested `v-for`: | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | ```xml | 
					
						
							|  |  |  |  |   <!-- loop over the worksheets --> | 
					
						
							|  |  |  |  |   <div v-for="item in data.data" v-bind:key="item.name"> | 
					
						
							|  |  |  |  |     <table> | 
					
						
							|  |  |  |  |       <!-- loop over the rows of each worksheet --> | 
					
						
							|  |  |  |  |       <tr v-for="row in item.data" v-bind:key="row.Index"> | 
					
						
							|  |  |  |  |         <!-- here `row` is a row object generated from sheet_to_json --> | 
					
						
							|  |  |  |  |         <td>{{ row.Name }}</td> | 
					
						
							|  |  |  |  |         <td>{{ row.Index }}</td> | 
					
						
							|  |  |  |  |       </tr> | 
					
						
							|  |  |  |  |     </table> | 
					
						
							|  |  |  |  |   </div> | 
					
						
							|  |  |  |  | ``` | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | ### Nuxt Content Demo
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | <details open><summary><b>Complete Example</b> (click to show)</summary> | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | :::note | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | This was tested against `create-nuxt-app v4.0.0` on 2022 August 13. | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | ::: | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 1) Create a stock app: | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | ```bash | 
					
						
							|  |  |  |  | npx create-nuxt-app SheetJSNuxt | 
					
						
							|  |  |  |  | ``` | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | When prompted, enter the following options: | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-14 08:10:18 +00:00
										 |  |  |  | - `Project name`: press Enter (use default SheetJSNuxt) | 
					
						
							|  |  |  |  | - `Programming language`: press Down Arrow (`TypeScript` selected) then Enter | 
					
						
							|  |  |  |  | - `Package manager`: select `Npm` and press Enter | 
					
						
							|  |  |  |  | - `UI framework`: select `None` and press Enter | 
					
						
							|  |  |  |  | - `Nuxt.js modules`: scroll to `Content`, select with Space, then press Enter | 
					
						
							|  |  |  |  | - `Linting tools`: press Enter (do not select any Linting tools) | 
					
						
							|  |  |  |  | - `Testing framework`: select `None` and press Enter | 
					
						
							|  |  |  |  | - `Rendering mode`: select `Universal (SSR / SSG)` and press Enter | 
					
						
							|  |  |  |  | - `Deployment target`: select `Static (Static/Jamstack hosting)` and press Enter | 
					
						
							|  |  |  |  | - `Development tools`: press Enter (do not select any Development tools) | 
					
						
							|  |  |  |  | - `What is your GitHub username?`: press Enter | 
					
						
							| 
									
										
										
										
											2022-08-13 22:01:26 +00:00
										 |  |  |  | - `Version control system`: select `None` | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | The project will be configured and modules will be installed. | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 2) Install the SheetJS library and start the dev server: | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | ```bash | 
					
						
							|  |  |  |  | cd SheetJSNuxt | 
					
						
							|  |  |  |  | npm i --save https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz | 
					
						
							|  |  |  |  | npm run dev | 
					
						
							|  |  |  |  | ``` | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | When the build finishes, the terminal will display a URL like: | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | ``` | 
					
						
							|  |  |  |  | ℹ Listening on: http://localhost:64688/                                                            05:41:11 | 
					
						
							|  |  |  |  | No issues found.                                                                                   05:41:11 | 
					
						
							|  |  |  |  | ``` | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | The dev server is listening on that URL.  Open the link in a web browser. | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 3) Download <https://sheetjs.com/pres.xlsx> and move to the `content` folder. | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 4) Modify `nuxt.config.js` as described [earlier](#nuxtconfigjs-configuration) | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 5) Replace `pages/index.vue` with the following: | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | ```html | 
					
						
							|  |  |  |  | <!-- sheetjs (C) 2013-present  SheetJS -- http://sheetjs.com --> | 
					
						
							|  |  |  |  | <template><div> | 
					
						
							|  |  |  |  |   <div v-for="item in data.data" v-bind:key="item.name"> | 
					
						
							|  |  |  |  |     <h2>{{ item.name }}</h2> | 
					
						
							|  |  |  |  |     <table><thead><tr><th>Name</th><th>Index</th></tr></thead><tbody> | 
					
						
							|  |  |  |  |       <tr v-for="row in item.data" v-bind:key="row.Index"> | 
					
						
							|  |  |  |  |         <td>{{ row.Name }}</td> | 
					
						
							|  |  |  |  |         <td>{{ row.Index }}</td> | 
					
						
							|  |  |  |  |       </tr> | 
					
						
							|  |  |  |  |     </tbody></table> | 
					
						
							|  |  |  |  |   </div> | 
					
						
							|  |  |  |  | </div></template> | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | <script> | 
					
						
							|  |  |  |  | export default { | 
					
						
							|  |  |  |  |   async asyncData ({$content}) { | 
					
						
							|  |  |  |  |     return { | 
					
						
							|  |  |  |  |       data: await $content('pres').fetch() | 
					
						
							|  |  |  |  |     }; | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | </script> | 
					
						
							|  |  |  |  | ``` | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | The browser should refresh to show the contents of the spreadsheet.  If it does | 
					
						
							| 
									
										
										
										
											2022-08-14 08:10:18 +00:00
										 |  |  |  | not, click Refresh manually or open a new browser window. | 
					
						
							| 
									
										
										
										
											2022-08-13 22:01:26 +00:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |  | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 6) To verify that hot loading works, open `pres.xlsx` from the `content` folder | 
					
						
							|  |  |  |  | in Excel.  Add a new row to the bottom and save the file: | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |  | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | The dev server terminal should show a line like: | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | ``` | 
					
						
							|  |  |  |  | ℹ Updated ./content/pres.xlsx                                       @nuxt/content 05:43:37 | 
					
						
							|  |  |  |  | ``` | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | The page should automatically refresh with the new content: | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |  | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-14 08:10:18 +00:00
										 |  |  |  | 7) Stop the dev server (press `CTRL+C` in the terminal window) and run | 
					
						
							| 
									
										
										
										
											2022-08-13 22:01:26 +00:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | ```bash | 
					
						
							|  |  |  |  | npm run generate | 
					
						
							|  |  |  |  | ``` | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | This will create a static site in the `dist` folder, which can be served with: | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | ```bash | 
					
						
							|  |  |  |  | npx http-server dist | 
					
						
							|  |  |  |  | ``` | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | Accessing the page http://localhost:8080 will show the page contents. Verifying | 
					
						
							|  |  |  |  | the static nature is trivial: make another change in Excel and save.  The page | 
					
						
							|  |  |  |  | will not change. | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | </details> |