fix(nuxt): extract route rules/page meta in 2+ script blocks (#28625)

This commit is contained in:
Daniel Roe 2024-08-21 12:38:18 +01:00 committed by GitHub
parent 161a1f10ee
commit 2ccdaa14cb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 144 additions and 103 deletions

View File

@ -18,10 +18,12 @@ export async function extractRouteRules (code: string): Promise<NitroRouteConfig
}
if (!ROUTE_RULE_RE.test(code)) { return null }
const script = extractScriptContent(code)
code = script?.code || code
let rule: NitroRouteConfig | null = null
const contents = extractScriptContent(code)
for (const script of contents) {
if (rule) { break }
code = script?.code || code
const js = await transform(code, { loader: script?.loader || 'ts' })
walk(parse(js.code, {
@ -42,6 +44,7 @@ export async function extractRouteRules (code: string): Promise<NitroRouteConfig
}
},
})
}
ruleCache[code] = rule
return rule

View File

@ -168,21 +168,23 @@ export async function augmentPages (routes: NuxtPage[], vfs: Record<string, stri
return augmentedPages
}
const SFC_SCRIPT_RE = /<script(?<attrs>[^>]*)>(?<content>[\s\S]*?)<\/script[^>]*>/i
const SFC_SCRIPT_RE = /<script(?<attrs>[^>]*)>(?<content>[\s\S]*?)<\/script[^>]*>/gi
export function extractScriptContent (html: string) {
const groups = html.match(SFC_SCRIPT_RE)?.groups || {}
if (groups.content) {
return {
loader: groups.attrs.includes('tsx') ? 'tsx' : 'ts',
code: groups.content.trim(),
} as const
const contents: Array<{ loader: 'tsx' | 'ts', code: string }> = []
for (const match of html.matchAll(SFC_SCRIPT_RE)) {
if (match?.groups?.content) {
contents.push({
loader: match.groups.attrs.includes('tsx') ? 'tsx' : 'ts',
code: match.groups.content.trim(),
})
}
}
return null
return contents
}
const PAGE_META_RE = /definePageMeta\([\s\S]*?\)/
const extractionKeys = ['name', 'path', 'alias', 'redirect'] as const
const DYNAMIC_META_KEY = '__nuxt_dynamic_meta_key' as const
const pageContentsCache: Record<string, string> = {}
@ -197,15 +199,17 @@ export async function getRouteMeta (contents: string, absolutePath: string): Pro
if (absolutePath in metaCache) { return metaCache[absolutePath] }
const loader = getLoader(absolutePath)
const script = !loader ? null : loader === 'vue' ? extractScriptContent(contents) : { code: contents, loader }
if (!script) {
const scriptBlocks = !loader ? null : loader === 'vue' ? extractScriptContent(contents) : [{ code: contents, loader }]
if (!scriptBlocks) {
metaCache[absolutePath] = {}
return {}
}
const extractedMeta = {} as Partial<Record<keyof NuxtPage, any>>
for (const script of scriptBlocks) {
if (!PAGE_META_RE.test(script.code)) {
metaCache[absolutePath] = {}
return {}
continue
}
const js = await transform(script.code, { loader: script.loader })
@ -215,8 +219,6 @@ export async function getRouteMeta (contents: string, absolutePath: string): Pro
ranges: true,
}) as unknown as Program
const extractedMeta = {} as Partial<Record<keyof NuxtPage, any>>
const extractionKeys = ['name', 'path', 'alias', 'redirect'] as const
const dynamicProperties = new Set<keyof NuxtPage>()
let foundMeta = false
@ -291,6 +293,7 @@ export async function getRouteMeta (contents: string, absolutePath: string): Pro
}
},
})
}
metaCache[absolutePath] = extractedMeta
return extractedMeta

View File

@ -58,6 +58,41 @@ describe('page metadata', () => {
`)
})
it('should extract serialisable metadata from files with multiple blocks', async () => {
const meta = await getRouteMeta(`
<script lang="ts">
export default {
name: 'thing'
}
</script>
<script setup>
definePageMeta({
name: 'some-custom-name',
path: '/some-custom-path',
validate: () => true,
middleware: [
function () {},
],
otherValue: {
foo: 'bar',
},
})
</script>
`, filePath)
expect(meta).toMatchInlineSnapshot(`
{
"meta": {
"__nuxt_dynamic_meta_key": Set {
"meta",
},
},
"name": "some-custom-name",
"path": "/some-custom-path",
}
`)
})
it('should extract serialisable metadata in options api', async () => {
const meta = await getRouteMeta(`
<script>