fix(nuxt): deep clone extracted page meta (#30717)

This commit is contained in:
xjccc 2025-01-24 22:40:08 +08:00 committed by Daniel Roe
parent 09d8db5f2f
commit cb7f30a1ea
No known key found for this signature in database
GPG Key ID: 3714AB03996F442B
3 changed files with 54 additions and 3 deletions

View File

@ -11,6 +11,7 @@ import { transform } from 'esbuild'
import type { Property } from 'estree'
import type { NuxtPage } from 'nuxt/schema'
import { klona } from 'klona'
import { parseAndWalk, withLocations } from '../core/utils/parse'
import { getLoader, uniqueBy } from '../core/utils'
import { logger, toArray } from '../utils'
@ -215,7 +216,7 @@ export async function getRouteMeta (contents: string, absolutePath: string, extr
}
if (absolutePath in metaCache && metaCache[absolutePath]) {
return metaCache[absolutePath]
return klona(metaCache[absolutePath])
}
const loader = getLoader(absolutePath)
@ -314,7 +315,7 @@ export async function getRouteMeta (contents: string, absolutePath: string, extr
}
metaCache[absolutePath] = extractedMeta
return extractedMeta
return klona(extractedMeta)
}
const COLON_RE = /:/g

View File

@ -1,6 +1,7 @@
import { describe, expect, it } from 'vitest'
import { type MockedFunction, describe, expect, it, vi } from 'vitest'
import { compileScript, parse } from '@vue/compiler-sfc'
import * as Parser from 'acorn'
import { klona } from 'klona'
import { transform as esbuildTransform } from 'esbuild'
import { PageMetaPlugin } from '../src/pages/plugins/page-meta'
import { getRouteMeta, normalizeRoutes } from '../src/pages/utils'
@ -8,6 +9,8 @@ import type { NuxtPage } from '../schema'
const filePath = '/app/pages/index.vue'
vi.mock('klona', { spy: true })
describe('page metadata', () => {
it('should not extract metadata from empty files', async () => {
expect(await getRouteMeta('', filePath)).toEqual({})
@ -67,11 +70,20 @@ definePageMeta({ name: 'bar' })
})
it('should use and invalidate cache', async () => {
const _klona = klona as unknown as MockedFunction<typeof klona>
_klona.mockImplementation(obj => obj)
const fileContents = `<script setup>definePageMeta({ foo: 'bar' })</script>`
const meta = await getRouteMeta(fileContents, filePath)
expect(meta === await getRouteMeta(fileContents, filePath)).toBeTruthy()
expect(meta === await getRouteMeta(fileContents, '/app/pages/other.vue')).toBeFalsy()
expect(meta === await getRouteMeta('<template><div>Hi</div></template>' + fileContents, filePath)).toBeFalsy()
_klona.mockReset()
})
it('should not share state between page metadata', async () => {
const fileContents = `<script setup>definePageMeta({ foo: 'bar' })</script>`
const meta = await getRouteMeta(fileContents, filePath)
expect(meta === await getRouteMeta(fileContents, filePath)).toBeFalsy()
})
it('should extract serialisable metadata', async () => {

View File

@ -836,3 +836,41 @@ describe('pages:pathToNitroGlob', () => {
expect(pathToNitroGlob(path)).to.equal(expected)
})
})
describe('page:extends', () => {
const DYNAMIC_META_KEY = '__nuxt_dynamic_meta_key' as const
it('should preserve distinct metadata for multiple routes referencing the same file', async () => {
const files: NuxtPage[] = [
{ path: 'home', file: `pages/index.vue` },
{ path: 'home1', file: `pages/index.vue`, meta: { test: true } },
{ path: 'home2', file: `pages/index.vue`, meta: { snap: true } },
]
const vfs = Object.fromEntries(
files.map(file => [file.file, `
<script setup lang="ts">
definePageMeta({
hello: 'world'
})
</script>
`]),
) as Record<string, string>
await augmentPages(files, vfs)
expect(files).toEqual([
{
path: 'home',
file: `pages/index.vue`,
meta: { [DYNAMIC_META_KEY]: new Set(['meta']) },
},
{
path: 'home1',
file: `pages/index.vue`,
meta: { [DYNAMIC_META_KEY]: new Set(['meta']), test: true },
},
{
path: 'home2',
file: `pages/index.vue`,
meta: { [DYNAMIC_META_KEY]: new Set(['meta']), snap: true },
},
])
})
})