feat(nuxt): experimental extraPageMetaExtractionKeys (#30015)

This commit is contained in:
Harlan Wilton 2024-11-28 03:57:15 +11:00 committed by GitHub
parent 0f3e1e9442
commit 16ef9be903
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 69 additions and 7 deletions

View File

@ -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
<script lang="ts" setup>
definePageMeta({
foo: 'bar'
})
</script>
```
```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

View File

@ -71,13 +71,14 @@ export async function resolvePagesRoutes (): Promise<NuxtPage[]> {
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<string>
augmentedPages?: Set<string>
extraExtractionKeys?: string[]
}
export async function augmentPages (routes: NuxtPage[], vfs: Record<string, string>, 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<string, string> = {}
const metaCache: Record<string, Partial<Record<keyof NuxtPage, any>>> = {}
export async function getRouteMeta (contents: string, absolutePath: string): Promise<Partial<Record<keyof NuxtPage, any>>> {
export async function getRouteMeta (contents: string, absolutePath: string, extraExtractionKeys: string[] = []): Promise<Partial<Record<keyof NuxtPage, any>>> {
// 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<Record<keyof NuxtPage, any>>
const extractionKeys = new Set<keyof NuxtPage>([...defaultExtractionKeys, ...extraExtractionKeys as Array<keyof NuxtPage>])
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
}

View File

@ -142,6 +142,24 @@ describe('page metadata', () => {
}
`)
})
it('should extract configured extra meta', async () => {
const meta = await getRouteMeta(`
<script setup>
definePageMeta({
foo: 'bar',
bar: true,
})
</script>
`, filePath, ['bar', 'foo'])
expect(meta).toMatchInlineSnapshot(`
{
"bar": true,
"foo": "bar",
}
`)
})
})
describe('normalizeRoutes', () => {

View File

@ -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