diff --git a/docs/2.guide/3.going-further/1.experimental-features.md b/docs/2.guide/3.going-further/1.experimental-features.md
index 40d8c5bebf..35280ba7ea 100644
--- a/docs/2.guide/3.going-further/1.experimental-features.md
+++ b/docs/2.guide/3.going-further/1.experimental-features.md
@@ -395,6 +395,35 @@ In addition, any changes to files within `srcDir` will trigger a rebuild of the
A maximum of 10 cache tarballs are kept.
::
+## extraPageMetaExtractionKeys
+
+The `definePageMeta()` macro is a useful way to collect build-time meta about pages. Nuxt itself provides a set list of supported keys which is used to power some of the internal features such as redirects, page aliases and custom paths.
+
+This option allows passing additional keys to extract from the page metadata when using `scanPageMeta`.
+
+```vue
+
+```
+
+```ts
+export default defineNuxtConfig({
+ experimental: {
+ extraPageMetaExtractionKeys: ['foo'],
+ },
+ hooks: {
+ 'pages:resolved' (ctx) {
+ // ✅ foo is available
+ },
+ },
+})
+```
+
+This allows modules to access additional metadata from the page metadata in the build context. If you are using this within a module, it's recommended also to [augment the `NuxtPage` types with your keys](/docs/guide/directory-structure/pages#typing-custom-metadata).
+
## normalizeComponentNames
Ensure that auto-generated Vue component names match the full component name
diff --git a/packages/nuxt/src/pages/utils.ts b/packages/nuxt/src/pages/utils.ts
index 27aa91c398..0b68904979 100644
--- a/packages/nuxt/src/pages/utils.ts
+++ b/packages/nuxt/src/pages/utils.ts
@@ -71,13 +71,14 @@ export async function resolvePagesRoutes (): Promise {
return pages
}
+ const augmentCtx = { extraExtractionKeys: nuxt.options.experimental.extraPageMetaExtractionKeys }
if (shouldAugment === 'after-resolve') {
await nuxt.callHook('pages:extend', pages)
- await augmentPages(pages, nuxt.vfs)
+ await augmentPages(pages, nuxt.vfs, augmentCtx)
} else {
- const augmentedPages = await augmentPages(pages, nuxt.vfs)
+ const augmentedPages = await augmentPages(pages, nuxt.vfs, augmentCtx)
await nuxt.callHook('pages:extend', pages)
- await augmentPages(pages, nuxt.vfs, { pagesToSkip: augmentedPages })
+ await augmentPages(pages, nuxt.vfs, { pagesToSkip: augmentedPages, ...augmentCtx })
augmentedPages?.clear()
}
@@ -158,13 +159,15 @@ export function generateRoutesFromFiles (files: ScannedFile[], options: Generate
interface AugmentPagesContext {
pagesToSkip?: Set
augmentedPages?: Set
+ extraExtractionKeys?: string[]
}
+
export async function augmentPages (routes: NuxtPage[], vfs: Record, ctx: AugmentPagesContext = {}) {
ctx.augmentedPages ??= new Set()
for (const route of routes) {
if (route.file && !ctx.pagesToSkip?.has(route.file)) {
const fileContent = route.file in vfs ? vfs[route.file]! : fs.readFileSync(await resolvePath(route.file), 'utf-8')
- const routeMeta = await getRouteMeta(fileContent, route.file)
+ const routeMeta = await getRouteMeta(fileContent, route.file, ctx.extraExtractionKeys)
if (route.meta) {
routeMeta.meta = { ...routeMeta.meta, ...route.meta }
}
@@ -196,12 +199,12 @@ export function extractScriptContent (html: string) {
}
const PAGE_META_RE = /definePageMeta\([\s\S]*?\)/
-const extractionKeys = ['name', 'path', 'props', 'alias', 'redirect'] as const
+const defaultExtractionKeys = ['name', 'path', 'props', 'alias', 'redirect'] as const
const DYNAMIC_META_KEY = '__nuxt_dynamic_meta_key' as const
const pageContentsCache: Record = {}
const metaCache: Record>> = {}
-export async function getRouteMeta (contents: string, absolutePath: string): Promise>> {
+export async function getRouteMeta (contents: string, absolutePath: string, extraExtractionKeys: string[] = []): Promise>> {
// set/update pageContentsCache, invalidate metaCache on cache mismatch
if (!(absolutePath in pageContentsCache) || pageContentsCache[absolutePath] !== contents) {
pageContentsCache[absolutePath] = contents
@@ -221,6 +224,8 @@ export async function getRouteMeta (contents: string, absolutePath: string): Pro
const extractedMeta = {} as Partial>
+ const extractionKeys = new Set([...defaultExtractionKeys, ...extraExtractionKeys as Array])
+
for (const script of scriptBlocks) {
if (!PAGE_META_RE.test(script.code)) {
continue
@@ -295,7 +300,7 @@ export async function getRouteMeta (contents: string, absolutePath: string): Pro
continue
}
const name = property.key.type === 'Identifier' ? property.key.name : String(property.value)
- if (!(extractionKeys as unknown as string[]).includes(name)) {
+ if (!extractionKeys.has(name as keyof NuxtPage)) {
dynamicProperties.add('meta')
break
}
diff --git a/packages/nuxt/test/page-metadata.test.ts b/packages/nuxt/test/page-metadata.test.ts
index 6206e07c08..cdd299c415 100644
--- a/packages/nuxt/test/page-metadata.test.ts
+++ b/packages/nuxt/test/page-metadata.test.ts
@@ -142,6 +142,24 @@ describe('page metadata', () => {
}
`)
})
+
+ it('should extract configured extra meta', async () => {
+ const meta = await getRouteMeta(`
+
+ `, filePath, ['bar', 'foo'])
+
+ expect(meta).toMatchInlineSnapshot(`
+ {
+ "bar": true,
+ "foo": "bar",
+ }
+ `)
+ })
})
describe('normalizeRoutes', () => {
diff --git a/packages/schema/src/config/experimental.ts b/packages/schema/src/config/experimental.ts
index 00ae9e2e0c..506ddb6481 100644
--- a/packages/schema/src/config/experimental.ts
+++ b/packages/schema/src/config/experimental.ts
@@ -308,6 +308,16 @@ export default defineUntypedSchema({
},
},
+ /**
+ * Configure additional keys to extract from the page metadata when using `scanPageMeta`.
+ *
+ * This allows modules to access additional metadata from the page metadata. It's recommended
+ * to augment the NuxtPage types with your keys.
+ *
+ * @type {string[]}
+ */
+ extraPageMetaExtractionKeys: [],
+
/**
* Automatically share payload _data_ between pages that are prerendered. This can result in a significant
* performance improvement when prerendering sites that use `useAsyncData` or `useFetch` and fetch the same