fix(nuxt): allow granularly overriding pages in layers (#23134)

This commit is contained in:
Daniel Roe 2023-09-12 10:46:35 +01:00 committed by GitHub
parent aa2c5c3df4
commit 15e6dfb560
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 44 additions and 28 deletions

View File

@ -33,6 +33,11 @@ interface SegmentToken {
value: string value: string
} }
interface ScannedFile {
relativePath: string
absolutePath: string
}
export async function resolvePagesRoutes (): Promise<NuxtPage[]> { export async function resolvePagesRoutes (): Promise<NuxtPage[]> {
const nuxt = useNuxt() const nuxt = useNuxt()
@ -40,30 +45,30 @@ export async function resolvePagesRoutes (): Promise<NuxtPage[]> {
layer => resolve(layer.config.srcDir, layer.config.dir?.pages || 'pages') layer => resolve(layer.config.srcDir, layer.config.dir?.pages || 'pages')
) )
const allRoutes = (await Promise.all( const scannedFiles: ScannedFile[] = []
pagesDirs.map(async (dir) => { for (const dir of pagesDirs) {
const files = await resolveFiles(dir, `**/*{${nuxt.options.extensions.join(',')}}`) const files = await resolveFiles(dir, `**/*{${nuxt.options.extensions.join(',')}}`)
// Sort to make sure parent are listed first scannedFiles.push(...files.map(file => ({ relativePath: relative(dir, file), absolutePath: file })))
files.sort() }
return generateRoutesFromFiles(files, dir, nuxt.options.experimental.typedPages, nuxt.vfs) scannedFiles.sort((a, b) => a.relativePath.localeCompare(b.relativePath))
})
)).flat() const allRoutes = await generateRoutesFromFiles(scannedFiles, nuxt.options.experimental.typedPages, nuxt.vfs)
return uniqueBy(allRoutes, 'path') return uniqueBy(allRoutes, 'path')
} }
export async function generateRoutesFromFiles (files: string[], pagesDir: string, shouldExtractBuildMeta = false, vfs?: Record<string, string>): Promise<NuxtPage[]> { export async function generateRoutesFromFiles (files: ScannedFile[], shouldExtractBuildMeta = false, vfs?: Record<string, string>): Promise<NuxtPage[]> {
const routes: NuxtPage[] = [] const routes: NuxtPage[] = []
for (const file of files) { for (const file of files) {
const segments = relative(pagesDir, file) const segments = file.relativePath
.replace(new RegExp(`${escapeRE(extname(file))}$`), '') .replace(new RegExp(`${escapeRE(extname(file.relativePath))}$`), '')
.split('/') .split('/')
const route: NuxtPage = { const route: NuxtPage = {
name: '', name: '',
path: '', path: '',
file, file: file.absolutePath,
children: [] children: []
} }
@ -94,7 +99,7 @@ export async function generateRoutesFromFiles (files: string[], pagesDir: string
} }
if (shouldExtractBuildMeta && vfs) { if (shouldExtractBuildMeta && vfs) {
const fileContent = file in vfs ? vfs[file] : fs.readFileSync(resolve(pagesDir, file), 'utf-8') const fileContent = file.absolutePath in vfs ? vfs[file.absolutePath] : fs.readFileSync(file.absolutePath, 'utf-8')
const overrideRouteName = await getRouteName(fileContent) const overrideRouteName = await getRouteName(fileContent)
if (overrideRouteName) { if (overrideRouteName) {
route.name = overrideRouteName route.name = overrideRouteName

View File

@ -5,6 +5,7 @@ import { generateRouteKey } from '../src/pages/runtime/utils'
describe('pages:generateRoutesFromFiles', () => { describe('pages:generateRoutesFromFiles', () => {
const pagesDir = 'pages' const pagesDir = 'pages'
const layerDir = 'layer/pages'
const tests: Array<{ const tests: Array<{
description: string description: string
files: Array<{ path: string; template?: string; }> files: Array<{ path: string; template?: string; }>
@ -176,7 +177,7 @@ describe('pages:generateRoutesFromFiles', () => {
{ {
children: [], children: [],
name: 'slug', name: 'slug',
file: 'pages/[slug].vue', file: `${pagesDir}/[slug].vue`,
path: '/:slug()' path: '/:slug()'
}, },
{ {
@ -189,7 +190,7 @@ describe('pages:generateRoutesFromFiles', () => {
children: [] children: []
} }
], ],
file: 'pages/[[foo]]', file: `${pagesDir}/[[foo]]`,
path: '/:foo?' path: '/:foo?'
}, },
{ {
@ -220,7 +221,7 @@ describe('pages:generateRoutesFromFiles', () => {
{ {
children: [], children: [],
name: 'bar', name: 'bar',
file: 'pages/[bar]/index.vue', file: `${pagesDir}/[bar]/index.vue`,
path: '/:bar()' path: '/:bar()'
}, },
{ {
@ -378,11 +379,11 @@ describe('pages:generateRoutesFromFiles', () => {
description: 'should correctly merge nested routes', description: 'should correctly merge nested routes',
files: [ files: [
{ path: `${pagesDir}/param.vue` }, { path: `${pagesDir}/param.vue` },
{ path: `${pagesDir}/param/index.vue` }, { path: `${layerDir}/param/index.vue` },
{ path: `${pagesDir}/param/index/index.vue` }, { path: `${pagesDir}/param/index/index.vue` },
{ path: `${pagesDir}/param/index/sibling.vue` }, { path: `${layerDir}/param/index/sibling.vue` },
{ path: `${pagesDir}/wrapper-expose/other.vue` }, { path: `${pagesDir}/wrapper-expose/other.vue` },
{ path: `${pagesDir}/wrapper-expose/other/index.vue` }, { path: `${layerDir}/wrapper-expose/other/index.vue` },
{ path: `${pagesDir}/wrapper-expose/other/sibling.vue` }, { path: `${pagesDir}/wrapper-expose/other/sibling.vue` },
{ path: `${pagesDir}/param/sibling.vue` } { path: `${pagesDir}/param/sibling.vue` }
], ],
@ -393,46 +394,46 @@ describe('pages:generateRoutesFromFiles', () => {
children: [ children: [
{ {
children: [], children: [],
file: 'pages/param/index/index.vue', file: `${pagesDir}/param/index/index.vue`,
name: 'param-index', name: 'param-index',
path: '' path: ''
}, },
{ {
children: [], children: [],
file: 'pages/param/index/sibling.vue', file: `${layerDir}/param/index/sibling.vue`,
name: 'param-index-sibling', name: 'param-index-sibling',
path: 'sibling' path: 'sibling'
} }
], ],
file: 'pages/param/index.vue', file: `${layerDir}/param/index.vue`,
path: '' path: ''
}, },
{ {
children: [], children: [],
file: 'pages/param/sibling.vue', file: `${pagesDir}/param/sibling.vue`,
name: 'param-sibling', name: 'param-sibling',
path: 'sibling' path: 'sibling'
} }
], ],
file: 'pages/param.vue', file: `${pagesDir}/param.vue`,
path: '/param' path: '/param'
}, },
{ {
children: [ children: [
{ {
children: [], children: [],
file: 'pages/wrapper-expose/other/index.vue', file: `${layerDir}/wrapper-expose/other/index.vue`,
name: 'wrapper-expose-other', name: 'wrapper-expose-other',
path: '' path: ''
}, },
{ {
children: [], children: [],
file: 'pages/wrapper-expose/other/sibling.vue', file: `${pagesDir}/wrapper-expose/other/sibling.vue`,
name: 'wrapper-expose-other-sibling', name: 'wrapper-expose-other-sibling',
path: 'sibling' path: 'sibling'
} }
], ],
file: 'pages/wrapper-expose/other.vue', file: `${pagesDir}/wrapper-expose/other.vue`,
path: '/wrapper-expose/other' path: '/wrapper-expose/other'
} }
] ]
@ -447,7 +448,10 @@ describe('pages:generateRoutesFromFiles', () => {
let result let result
try { try {
result = await generateRoutesFromFiles(test.files.map(file => file.path), pagesDir, true, vfs) result = await generateRoutesFromFiles(test.files.map(file => ({
absolutePath: file.path,
relativePath: file.path.replace(/^(pages|layer\/pages)\//, '')
})), true, vfs)
} catch (error: any) { } catch (error: any) {
expect(error.message).toEqual(test.error) expect(error.message).toEqual(test.error)
} }

View File

@ -981,6 +981,7 @@ describe('extends support', () => {
const html = await $fetch('/override') const html = await $fetch('/override')
expect(html).toContain('Extended layout from bar') expect(html).toContain('Extended layout from bar')
expect(html).toContain('Extended page from bar') expect(html).toContain('Extended page from bar')
expect(html).toContain('This child page should not be overridden by bar')
}) })
}) })

View File

@ -10,5 +10,6 @@ definePageMeta({
<div>Extended page from bar</div> <div>Extended page from bar</div>
<div>Middleware | override: {{ $route.meta.override }}</div> <div>Middleware | override: {{ $route.meta.override }}</div>
<ExtendsOverride /> <ExtendsOverride />
<NuxtPage />
</div> </div>
</template> </template>

View File

@ -0,0 +1,5 @@
<template>
<div>
This child page should not be overridden by bar
</div>
</template>