diff --git a/docz/docs/03-demos/03-desktop/06-reactnative.md b/docz/docs/03-demos/03-desktop/06-reactnative.md
index 4a97731..47313c0 100644
--- a/docz/docs/03-demos/03-desktop/06-reactnative.md
+++ b/docz/docs/03-demos/03-desktop/06-reactnative.md
@@ -214,10 +214,9 @@ been tested against both application types.
:::caution
-NodeJS `v16` is required. There are OS-specific tools for downgrading:
-
-- [`nvm-windows`](https://github.com/coreybutler/nvm-windows/releases) Windows
-- [`n`](https://github.com/tj/n/) Linux, MacOS, WSL, etc.
+At the time of testing, NodeJS `v16` was required. A tool like
+[`nvm-windows`](https://github.com/coreybutler/nvm-windows/releases) should be
+used to switch the NodeJS version.
:::
@@ -436,10 +435,8 @@ This demo was tested against `v0.64.30` on 2023 January 04 in MacOS 12.4
:::caution
-NodeJS `v16` is required. There are OS-specific tools for downgrading:
-
-- [`nvm-windows`](https://github.com/coreybutler/nvm-windows/releases) Windows
-- [`n`](https://github.com/tj/n/) Linux, MacOS, WSL, etc.
+At the time of testing, NodeJS `v16` was required. A tool like
+[`n`](https://github.com/tj/n/) should be used to switch the NodeJS version.
:::
diff --git a/docz/docs/03-demos/04-grid.md b/docz/docs/03-demos/05-grid.md
similarity index 100%
rename from docz/docs/03-demos/04-grid.md
rename to docz/docs/03-demos/05-grid.md
diff --git a/docz/docs/03-demos/05-database.md b/docz/docs/03-demos/06-database.md
similarity index 100%
rename from docz/docs/03-demos/05-database.md
rename to docz/docs/03-demos/06-database.md
diff --git a/docz/docs/03-demos/11-static/09-nuxtjs.md b/docz/docs/03-demos/11-static/09-nuxtjs.md
index 0c5684f..b65e57e 100644
--- a/docz/docs/03-demos/11-static/09-nuxtjs.md
+++ b/docz/docs/03-demos/11-static/09-nuxtjs.md
@@ -4,17 +4,9 @@ pagination_prev: demos/extensions/index
pagination_next: demos/gsheet
---
-### NuxtJS
-
`@nuxt/content` is a file-based CMS for Nuxt, enabling static-site generation
and on-demand server rendering powered by spreadsheets.
-:::note
-
-This demo was tested on 2022 November 18 against Nuxt Content `v1.15.1`.
-
-:::
-
:::warning
Nuxt Content `v2` (NuxtJS `v3`) employs a different architecture from `v1`.
@@ -25,7 +17,15 @@ until the issues are resolved.
:::
-## Configuration
+## Nuxt Content v1
+
+:::note
+
+This demo was tested on 2022 November 18 against Nuxt Content `v1.15.1`.
+
+:::
+
+### 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:
@@ -59,7 +59,7 @@ export default {
}
```
-## Template Use
+### 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
@@ -92,7 +92,7 @@ neatly with nested `v-for`:
```
-## Nuxt Content Demo
+### Nuxt Content Demo
:::note
@@ -248,3 +248,235 @@ 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.
+
+## Nuxt Content v2
+
+:::note
+
+This demo was tested on 2023 January 19 against Nuxt Content `v2.3.0`.
+
+:::
+
+### 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:
+
+```ts
+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 };
+ }
+});
+```
+
+Pages can pull data using `useAsyncData`:
+
+```html
+
+```
+
+Pages should use `ContentRenderer` to reference the data:
+
+```html
+
+
+
+
+
{{ item.name }}
+
+
Name | Index |
+
+
+ {{ row.Name }} |
+ {{ row.Index }} |
+
+
+
+
+```
+
+### Nuxt Content 2 Demo
+
+:::note
+
+This demo was tested on 2023 January 19 against Nuxt Content `v2.3.0`.
+
+The generated project used Nuxt `v3.0.0`.
+
+:::
+
+1) Create a stock app and install dependencies:
+
+```bash
+npx nuxi init -t content sheetjs-nc2
+cd sheetjs-nc2
+npx yarn install
+```
+
+2) Install the SheetJS library and start the server:
+
+```bash
+npx yarn add https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz
+npx yarn dev
+```
+
+When the build finishes, the terminal will display a URL like:
+
+```
+ > Local: http://localhost:3000/
+ ```
+
+The server is listening on that URL. Open the link in a web browser.
+
+3) Download 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"
+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 };
+ }
+});
+```
+
+- `sheetmodule.ts` (the nuxt configuration module):
+
+```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({
+ modules: [
+ SheetJSModule,
+ '@nuxt/content'
+ ],
+ content: {}
+})
+```
+
+Restart the dev server by exiting the process (Control+C) and running:
+
+```bash
+rm -rf .nuxt/content-cache ## forcefully clear cache
+npx yarn run dev
+```
+
+Loading http://localhost:3000/pres should show some JSON data:
+
+```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`:
+
+```html
+
+
+
+
{{ item.name }}
+
Name | Index |
+
+ {{ row.Name }} |
+ {{ row.Index }} |
+
+
+
+
+```
+
+Restart the dev server by exiting the process (Control+C) and running:
+
+```bash
+rm -rf .nuxt/content-cache ## forcefully clear cache
+npx yarn run dev
+```
+
+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
+npx yarn run generate
+```
+
+This will create a static site in `.output/public`, which can be served with:
+
+```bash
+npx http-server .output/public
+```
+
+Accessing http://localhost:8080/pres will show the page contents. Verifying
+the static nature is trivial: make another change in Excel and save. The page
+will not change.