| 
									
										
										
										
											2023-01-15 03:36:13 +00:00
										 |  |  |  | --- | 
					
						
							|  |  |  |  | title: NuxtJS | 
					
						
							| 
									
										
										
										
											2023-02-28 11:40:44 +00:00
										 |  |  |  | pagination_prev: demos/net/index | 
					
						
							|  |  |  |  | pagination_next: demos/mobile/index | 
					
						
							| 
									
										
										
										
											2023-01-15 03:36:13 +00:00
										 |  |  |  | --- | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-27 09:12:19 +00:00
										 |  |  |  | import current from '/version.js'; | 
					
						
							| 
									
										
										
										
											2023-05-01 01:27:02 +00:00
										 |  |  |  | import CodeBlock from '@theme/CodeBlock'; | 
					
						
							| 
									
										
										
										
											2023-04-27 09:12:19 +00:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-15 03:36:13 +00:00
										 |  |  |  | `@nuxt/content` is a file-based CMS for Nuxt, enabling static-site generation | 
					
						
							|  |  |  |  | and on-demand server rendering powered by spreadsheets. | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-02 02:17:56 +00:00
										 |  |  |  | The [NodeJS module](/docs/getting-started/installation/nodejs) can be imported | 
					
						
							|  |  |  |  | from Content v1 "parsers" and Content v2 "transformers". | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-20 02:25:16 +00:00
										 |  |  |  | :::note | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-01 01:27:02 +00:00
										 |  |  |  | The following deployments were tested: | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | | Nuxt Content | Nuxt     | Date       | | 
					
						
							|  |  |  |  | |:-------------|:---------|:-----------| | 
					
						
							| 
									
										
										
										
											2023-06-02 02:17:56 +00:00
										 |  |  |  | | `1.15.1`     | `2.16.3` | 2023-06-01 | | 
					
						
							| 
									
										
										
										
											2023-05-01 01:27:02 +00:00
										 |  |  |  | | `2.3.0`      | `3.0.0`  | 2023-01-19 | | 
					
						
							| 
									
										
										
										
											2023-06-02 02:17:56 +00:00
										 |  |  |  | | `2.6.0`      | `3.5.2`  | 2023-06-01 | | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | ::: | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | :::caution Telemetry | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | Nuxt embeds telemetry. According to the developers, it is disabled by default. | 
					
						
							|  |  |  |  | To explicitly disable telemetry, the official documentation recommends: | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | ```bash | 
					
						
							|  |  |  |  | npx nuxt telemetry disable | 
					
						
							|  |  |  |  | ``` | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | At the time the demo was last tested, this command did not work.  Instead, a | 
					
						
							|  |  |  |  | option should be added in `nuxt.config.ts` or `nuxt.config.js` for Nuxt 3 sites: | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | ```js | 
					
						
							|  |  |  |  | // ... | 
					
						
							|  |  |  |  | // highlight-start | 
					
						
							|  |  |  |  | export default defineNuxtConfig({ | 
					
						
							|  |  |  |  |   // @ts-ignore | 
					
						
							|  |  |  |  |   telemetry: false, | 
					
						
							|  |  |  |  | // highlight-end | 
					
						
							|  |  |  |  |   // ... | 
					
						
							|  |  |  |  | }) | 
					
						
							|  |  |  |  | ``` | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | A global setting can be added to `.nuxtrc` in the user home directory: | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | ```ini title=".nuxtrc" | 
					
						
							|  |  |  |  | telemetry.enabled=false | 
					
						
							|  |  |  |  | ``` | 
					
						
							| 
									
										
										
										
											2023-01-20 02:25:16 +00:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | ::: | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-01 01:27:02 +00:00
										 |  |  |  | ## Nuxt Content v1
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | Nuxt Content v1 is designed to work with Nuxt v2. | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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 --> |nuxt.config.js\ncustom parser| buffer | 
					
						
							|  |  |  |  |   buffer --> |nuxt.config.js\ncustom parser| aoo | 
					
						
							|  |  |  |  |   aoo --> |index.vue\ntemplate| html | 
					
						
							|  |  |  |  | ``` | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-20 02:25:16 +00:00
										 |  |  |  | ### Configuration
 | 
					
						
							| 
									
										
										
										
											2023-01-15 03:36:13 +00:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 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 title="nuxt.config.js" | 
					
						
							|  |  |  |  | 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 }; | 
					
						
							| 
									
										
										
										
											2023-06-02 02:17:56 +00:00
										 |  |  |  | }; | 
					
						
							| 
									
										
										
										
											2023-01-15 03:36:13 +00:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | export default { | 
					
						
							|  |  |  |  | // ... | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   // 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 | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |   }, | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // ... | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | ``` | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-20 02:25:16 +00:00
										 |  |  |  | ### Template Use
 | 
					
						
							| 
									
										
										
										
											2023-01-15 03:36:13 +00:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 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> | 
					
						
							|  |  |  |  | ``` | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-20 02:25:16 +00:00
										 |  |  |  | ### Nuxt Content Demo
 | 
					
						
							| 
									
										
										
										
											2023-01-15 03:36:13 +00:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-02 02:17:56 +00:00
										 |  |  |  | :::caution | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | When the demo was last tested, parts of the Nuxt dependency tree did not support | 
					
						
							|  |  |  |  | NodeJS version 20.  The creation step will show warnings like | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | ``` | 
					
						
							|  |  |  |  | npm WARN EBADENGINE Unsupported engine { | 
					
						
							|  |  |  |  | npm WARN EBADENGINE   package: '@nuxt/types@2.16.3', | 
					
						
							|  |  |  |  | npm WARN EBADENGINE   required: { node: '^14.18.0 || ^16.10.0 || ^17.0.0 || ... | 
					
						
							|  |  |  |  | npm WARN EBADENGINE   current: { node: 'v20.2.0', npm: '9.6.6' } | 
					
						
							|  |  |  |  | npm WARN EBADENGINE } | 
					
						
							|  |  |  |  | ``` | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | The recommended solution is to switch to Node 18. | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | ::: | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-15 03:36:13 +00:00
										 |  |  |  | 1) Create a stock app: | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | ```bash | 
					
						
							| 
									
										
										
										
											2023-06-02 02:17:56 +00:00
										 |  |  |  | npx create-nuxt-app@4.0.0 sheetjs-nuxt | 
					
						
							| 
									
										
										
										
											2023-01-15 03:36:13 +00:00
										 |  |  |  | ``` | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | When prompted, enter the following options: | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-02 02:17:56 +00:00
										 |  |  |  | - `Project name`: press Enter (use default `sheetjs-nuxt`) | 
					
						
							| 
									
										
										
										
											2023-01-15 03:36:13 +00:00
										 |  |  |  | - `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 | 
					
						
							|  |  |  |  | - `Version control system`: select `None` | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | The project will be configured and modules will be installed. | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 2) Install the SheetJS library and start the server: | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-01 01:27:02 +00:00
										 |  |  |  | <CodeBlock language="bash">{`\ | 
					
						
							| 
									
										
										
										
											2023-06-02 02:17:56 +00:00
										 |  |  |  | cd sheetjs-nuxt | 
					
						
							| 
									
										
										
										
											2023-05-01 01:27:02 +00:00
										 |  |  |  | npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz | 
					
						
							|  |  |  |  | npm run dev`} | 
					
						
							|  |  |  |  | </CodeBlock> | 
					
						
							| 
									
										
										
										
											2023-01-15 03:36:13 +00:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | When the build finishes, the terminal will display a URL like: | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | ``` | 
					
						
							|  |  |  |  | ℹ Listening on: http://localhost:64688/ | 
					
						
							|  |  |  |  | ``` | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | The 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. | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | ```bash | 
					
						
							|  |  |  |  | curl -L -o content/pres.xlsx https://sheetjs.com/pres.xlsx | 
					
						
							|  |  |  |  | ``` | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 4) Modify `nuxt.config.js` as follows: | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | - Add the following to the top of the script: | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | ```js | 
					
						
							|  |  |  |  | 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 }; | 
					
						
							| 
									
										
										
										
											2023-06-02 02:17:56 +00:00
										 |  |  |  | }; | 
					
						
							| 
									
										
										
										
											2023-01-15 03:36:13 +00:00
										 |  |  |  | ``` | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | - Look for the exported object.  There should be a `content` property: | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | ```js | 
					
						
							|  |  |  |  |   // Content module configuration: https://go.nuxtjs.dev/config-content | 
					
						
							|  |  |  |  |   content: {}, | 
					
						
							|  |  |  |  | ``` | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | Replace the property with the following definition: | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | ```js | 
					
						
							|  |  |  |  |   // 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 | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |   }, | 
					
						
							|  |  |  |  | ``` | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | (If the property is missing, add it to the end of the exported object) | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 5) Replace `pages/index.vue` with the following: | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | ```html | 
					
						
							|  |  |  |  | <!-- sheetjs (C) 2013-present  SheetJS -- https://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 | 
					
						
							|  |  |  |  | not, click Refresh manually or open a new browser window. | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |  | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 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 server terminal window should show a line like: | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | ``` | 
					
						
							|  |  |  |  | ℹ Updated ./content/pres.xlsx                                       @nuxt/content 05:43:37 | 
					
						
							|  |  |  |  | ``` | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | The page should automatically refresh with the new content: | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |  | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 7) Stop the server (press `CTRL+C` in the terminal window) and run | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | ```bash | 
					
						
							|  |  |  |  | npm run generate | 
					
						
							|  |  |  |  | ``` | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | This will create a static site in the `dist` folder, which can be served with: | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | ```bash | 
					
						
							|  |  |  |  | npx http-server dist | 
					
						
							|  |  |  |  | ``` | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-03 03:40:40 +00:00
										 |  |  |  | Accessing the page `http://localhost:8080` will show the page contents. Verifying | 
					
						
							| 
									
										
										
										
											2023-01-15 03:36:13 +00:00
										 |  |  |  | the static nature is trivial: make another change in Excel and save.  The page | 
					
						
							|  |  |  |  | will not change. | 
					
						
							| 
									
										
										
										
											2023-01-20 02:25:16 +00:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | ## Nuxt Content v2
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-01 01:27:02 +00:00
										 |  |  |  | Nuxt Content v2 is designed to work with Nuxt v3. | 
					
						
							| 
									
										
										
										
											2023-01-20 02:25:16 +00:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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 --> |custom module\ntransformer| buffer | 
					
						
							|  |  |  |  |   buffer --> |custom module\ntransformer| aoo | 
					
						
							|  |  |  |  |   aoo --> |index.vue\nContentRenderer| html | 
					
						
							|  |  |  |  | ``` | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-20 02:25:16 +00:00
										 |  |  |  | ### Overview
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | Nuxt Content `v2` supports custom transformers for controlling data.  Although | 
					
						
							|  |  |  |  | the library hard-codes UTF-8 interpretations, the `_id` field currently uses | 
					
						
							|  |  |  |  | the pattern `content:` followed by the filename (if files are placed in the | 
					
						
							|  |  |  |  | `content` folder directly).  This enables a transformer to re-read the file: | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-02 02:17:56 +00:00
										 |  |  |  | ```ts title="Transformer" | 
					
						
							|  |  |  |  | // @ts-ignore | 
					
						
							| 
									
										
										
										
											2023-01-20 02:25:16 +00:00
										 |  |  |  | import { defineTransformer } from "@nuxt/content/transformers/utils"; | 
					
						
							|  |  |  |  | import { read, utils } from "xlsx"; | 
					
						
							|  |  |  |  | import { readFileSync } from "node:fs"; | 
					
						
							|  |  |  |  | import { resolve } from 'node:path'; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | export default defineTransformer({ | 
					
						
							|  |  |  |  |   name: 'sheetformer', | 
					
						
							|  |  |  |  |   extensions: ['.xlsx'], | 
					
						
							|  |  |  |  |   parse (_id: string, rawContent: string) { | 
					
						
							| 
									
										
										
										
											2023-06-02 02:17:56 +00:00
										 |  |  |  |     // highlight-start | 
					
						
							|  |  |  |  |     /* read the underlying file */ | 
					
						
							|  |  |  |  |     const buf = readFileSync(resolve("./content/" + _id.slice(8))); | 
					
						
							|  |  |  |  |     /* parse */ | 
					
						
							|  |  |  |  |     const wb = read(buf); | 
					
						
							|  |  |  |  |     // highlight-end | 
					
						
							|  |  |  |  |     /* generate JS objects for each worksheet */ | 
					
						
							| 
									
										
										
										
											2023-01-20 02:25:16 +00:00
										 |  |  |  |     const body = wb.SheetNames.map(name => ({ name, data: utils.sheet_to_json(wb.Sheets[name])})); | 
					
						
							|  |  |  |  |     return { _id, body }; | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | }); | 
					
						
							|  |  |  |  | ``` | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | Pages can pull data using `useAsyncData`: | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-02 02:17:56 +00:00
										 |  |  |  | ```html title="Page" | 
					
						
							| 
									
										
										
										
											2023-01-20 02:25:16 +00:00
										 |  |  |  | <script setup> | 
					
						
							|  |  |  |  | const key = "pres"; // matches pres.xlsx | 
					
						
							|  |  |  |  | const {data} = await useAsyncData('x', ()=>queryContent(`/${key}`).findOne()); | 
					
						
							|  |  |  |  | // data.body is the output from the transformer and can be used in the template | 
					
						
							|  |  |  |  | </script> | 
					
						
							|  |  |  |  | ``` | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | Pages should use `ContentRenderer` to reference the data: | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-02 02:17:56 +00:00
										 |  |  |  | ```html title="Page" | 
					
						
							| 
									
										
										
										
											2023-01-20 02:25:16 +00:00
										 |  |  |  | <template><ContentRenderer :value="data"> | 
					
						
							|  |  |  |  |   <!-- data.body is the array defined in the transformer --> | 
					
						
							|  |  |  |  |   <div v-for="item in data.body" v-bind:key="item.name"> | 
					
						
							|  |  |  |  |     <!-- each item has a "name" string for worsheet name --> | 
					
						
							|  |  |  |  |     <h2>{{ item.name }}</h2> | 
					
						
							|  |  |  |  |     <!-- each item has a "body" array of data rows --> | 
					
						
							|  |  |  |  |     <table><thead><tr><th>Name</th><th>Index</th></tr></thead><tbody> | 
					
						
							|  |  |  |  |       <tr v-for="row in item.data" v-bind:key="row.Index"> | 
					
						
							|  |  |  |  |         <!-- Assuming the sheet uses the columns "Name" and "Index" --> | 
					
						
							|  |  |  |  |         <td>{{ row.Name }}</td> | 
					
						
							|  |  |  |  |         <td>{{ row.Index }}</td> | 
					
						
							|  |  |  |  |       </tr> | 
					
						
							|  |  |  |  |     </tbody></table> | 
					
						
							|  |  |  |  |   </div> | 
					
						
							|  |  |  |  | </ContentRenderer></template> | 
					
						
							|  |  |  |  | ``` | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | ### Nuxt Content 2 Demo
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-02 02:17:56 +00:00
										 |  |  |  | :::caution | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | When the demo was last tested, parts of the Nuxt dependency tree did not support | 
					
						
							|  |  |  |  | NodeJS version 20. If the `yarn install` step fails with a message like | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | ``` | 
					
						
							|  |  |  |  | error @nuxt/kit@3.4.1: The engine "node" is incompatible with this module. | 
					
						
							|  |  |  |  | ``` | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | The recommended solution is to switch to Node 18. | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | ::: | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-20 02:25:16 +00:00
										 |  |  |  | 1) Create a stock app and install dependencies: | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | ```bash | 
					
						
							| 
									
										
										
										
											2023-06-02 02:17:56 +00:00
										 |  |  |  | npx -y nuxi init -t content sheetjs-nc2 | 
					
						
							| 
									
										
										
										
											2023-01-20 02:25:16 +00:00
										 |  |  |  | cd sheetjs-nc2 | 
					
						
							| 
									
										
										
										
											2023-06-02 02:17:56 +00:00
										 |  |  |  | npx -y yarn install | 
					
						
							|  |  |  |  | npx -y yarn add --dev @types/node | 
					
						
							| 
									
										
										
										
											2023-01-20 02:25:16 +00:00
										 |  |  |  | ``` | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 2) Install the SheetJS library and start the server: | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-01 01:27:02 +00:00
										 |  |  |  | <CodeBlock language="bash">{`\ | 
					
						
							| 
									
										
										
										
											2023-06-02 02:17:56 +00:00
										 |  |  |  | npx -y yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz | 
					
						
							|  |  |  |  | npx -y yarn dev`} | 
					
						
							| 
									
										
										
										
											2023-05-01 01:27:02 +00:00
										 |  |  |  | </CodeBlock> | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-20 02:25:16 +00:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | When the build finishes, the terminal will display a URL like: | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | ``` | 
					
						
							|  |  |  |  |   > Local:    http://localhost:3000/
 | 
					
						
							| 
									
										
										
										
											2023-05-01 01:27:02 +00:00
										 |  |  |  | ``` | 
					
						
							| 
									
										
										
										
											2023-01-20 02:25:16 +00:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | The 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. | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | ```bash | 
					
						
							|  |  |  |  | curl -L -o content/pres.xlsx https://sheetjs.com/pres.xlsx | 
					
						
							|  |  |  |  | ``` | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 4) Create the transformer. | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | Two files must be written: | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | - `sheetformer.ts` (the raw transformer module): | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | ```ts title="sheetformer.ts" | 
					
						
							| 
									
										
										
										
											2023-01-22 04:23:58 +00:00
										 |  |  |  | // @ts-ignore | 
					
						
							| 
									
										
										
										
											2023-01-20 02:25:16 +00:00
										 |  |  |  | import { defineTransformer } from "@nuxt/content/transformers/utils"; | 
					
						
							|  |  |  |  | import { read, utils } from "xlsx"; | 
					
						
							|  |  |  |  | import { readFileSync } from "node:fs"; | 
					
						
							|  |  |  |  | import { resolve } from 'node:path'; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | export default defineTransformer({ | 
					
						
							|  |  |  |  |   name: 'sheetformer', | 
					
						
							|  |  |  |  |   extensions: ['.xlsx'], | 
					
						
							|  |  |  |  |   parse (_id: string, rawContent: string) { | 
					
						
							|  |  |  |  |     const wb = read(readFileSync(resolve("./content/" + _id.slice(8)))); | 
					
						
							|  |  |  |  |     const body = wb.SheetNames.map(name => ({ name, data: utils.sheet_to_json(wb.Sheets[name])})); | 
					
						
							|  |  |  |  |     return { _id, body }; | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | }); | 
					
						
							|  |  |  |  | ``` | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-19 20:03:23 +00:00
										 |  |  |  | - `sheetmodule.ts` (the Nuxt configuration module): | 
					
						
							| 
									
										
										
										
											2023-01-20 02:25:16 +00:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | ```ts title="sheetmodule.ts" | 
					
						
							|  |  |  |  | import { resolve } from 'path' | 
					
						
							|  |  |  |  | import { defineNuxtModule } from '@nuxt/kit' | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | export default defineNuxtModule({ | 
					
						
							|  |  |  |  |   setup (_options, nuxt) { | 
					
						
							|  |  |  |  |     nuxt.options.nitro.externals = nuxt.options.nitro.externals || {} | 
					
						
							|  |  |  |  |     nuxt.options.nitro.externals.inline = nuxt.options.nitro.externals.inline || [] | 
					
						
							|  |  |  |  |     nuxt.options.nitro.externals.inline.push(resolve('./sheetmodule')) | 
					
						
							|  |  |  |  |     // @ts-ignore | 
					
						
							|  |  |  |  |     nuxt.hook('content:context', (contentContext) => { | 
					
						
							|  |  |  |  |       contentContext.transformers.push(resolve('./sheetformer.ts')) | 
					
						
							|  |  |  |  |     }) | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | }) | 
					
						
							|  |  |  |  | ``` | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | After creating the source files, the module must be added to `nuxt.config.ts`: | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | ```ts title="nuxt.config.ts" | 
					
						
							|  |  |  |  | import SheetJSModule from './sheetmodule' | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | export default defineNuxtConfig({ | 
					
						
							| 
									
										
										
										
											2023-06-02 02:17:56 +00:00
										 |  |  |  |   // @ts-ignore | 
					
						
							|  |  |  |  |   telemetry: false, | 
					
						
							| 
									
										
										
										
											2023-01-20 02:25:16 +00:00
										 |  |  |  |   modules: [ | 
					
						
							|  |  |  |  |     SheetJSModule, | 
					
						
							|  |  |  |  |     '@nuxt/content' | 
					
						
							|  |  |  |  |   ], | 
					
						
							|  |  |  |  |   content: {} | 
					
						
							|  |  |  |  | }) | 
					
						
							|  |  |  |  | ``` | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | Restart the dev server by exiting the process (Control+C) and running: | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | ```bash | 
					
						
							| 
									
										
										
										
											2023-06-02 02:17:56 +00:00
										 |  |  |  | npx -y nuxi clean | 
					
						
							|  |  |  |  | npx -y nuxi typecheck | 
					
						
							|  |  |  |  | npx -y yarn run dev | 
					
						
							| 
									
										
										
										
											2023-01-20 02:25:16 +00:00
										 |  |  |  | ``` | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-03 03:40:40 +00:00
										 |  |  |  | Loading `http://localhost:3000/pres` should show some JSON data: | 
					
						
							| 
									
										
										
										
											2023-01-20 02:25:16 +00:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | ```json | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   // ... | 
					
						
							|  |  |  |  |   "data": { | 
					
						
							|  |  |  |  |     "_path": "/pres", | 
					
						
							|  |  |  |  |     // ... | 
					
						
							|  |  |  |  |     "_id": "content:pres.xlsx", | 
					
						
							|  |  |  |  |     "body": [ | 
					
						
							|  |  |  |  |       { | 
					
						
							|  |  |  |  |         "name": "Sheet1", // <-- sheet name | 
					
						
							|  |  |  |  |         "data": [ // <-- array of data objects | 
					
						
							|  |  |  |  |           { | 
					
						
							|  |  |  |  |             "Name": "Bill Clinton", | 
					
						
							|  |  |  |  |             "Index": 42 | 
					
						
							|  |  |  |  |           }, | 
					
						
							|  |  |  |  | ``` | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 5) Create a page.  Save the following content to `pages/pres.vue`: | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-22 04:23:58 +00:00
										 |  |  |  | ```html title="pages/pres.vue" | 
					
						
							| 
									
										
										
										
											2023-01-20 02:25:16 +00:00
										 |  |  |  | <script setup> | 
					
						
							|  |  |  |  | const {data} = await useAsyncData('s5s', () => queryContent('/pres').findOne()); | 
					
						
							|  |  |  |  | </script> | 
					
						
							|  |  |  |  | <template><ContentRenderer :value="data"> | 
					
						
							|  |  |  |  |   <div v-for="item in data.body" 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> | 
					
						
							|  |  |  |  | </ContentRenderer></template> | 
					
						
							|  |  |  |  | ``` | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | Restart the dev server by exiting the process (Control+C) and running: | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | ```bash | 
					
						
							| 
									
										
										
										
											2023-06-02 02:17:56 +00:00
										 |  |  |  | npx -y nuxi clean | 
					
						
							|  |  |  |  | npx -y yarn run dev | 
					
						
							| 
									
										
										
										
											2023-01-20 02:25:16 +00:00
										 |  |  |  | ``` | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | The browser should now display an HTML table. | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 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 page should automatically refresh with the new content. | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 7) Stop the server (press `CTRL+C` in the terminal window) and run | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | ```bash | 
					
						
							| 
									
										
										
										
											2023-06-02 02:17:56 +00:00
										 |  |  |  | npx -y yarn run generate | 
					
						
							| 
									
										
										
										
											2023-01-20 02:25:16 +00:00
										 |  |  |  | ``` | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | This will create a static site in `.output/public`, which can be served with: | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | ```bash | 
					
						
							| 
									
										
										
										
											2023-06-02 02:17:56 +00:00
										 |  |  |  | npx -y http-server .output/public | 
					
						
							| 
									
										
										
										
											2023-01-20 02:25:16 +00:00
										 |  |  |  | ``` | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-03 03:40:40 +00:00
										 |  |  |  | Accessing `http://localhost:8080/pres` will show the page contents. Verifying | 
					
						
							| 
									
										
										
										
											2023-01-20 02:25:16 +00:00
										 |  |  |  | the static nature is trivial: make another change in Excel and save.  The page | 
					
						
							|  |  |  |  | will not change. |