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

View File

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

View File

@ -981,6 +981,7 @@ describe('extends support', () => {
const html = await $fetch('/override')
expect(html).toContain('Extended layout 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>Middleware | override: {{ $route.meta.override }}</div>
<ExtendsOverride />
<NuxtPage />
</div>
</template>

View File

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