mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-25 15:15:19 +00:00
fix(nuxt): allow granularly overriding pages in layers (#23134)
This commit is contained in:
parent
aa2c5c3df4
commit
15e6dfb560
@ -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
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -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>
|
||||||
|
5
test/fixtures/basic/extends/node_modules/foo/pages/override/index.vue
generated
vendored
Normal file
5
test/fixtures/basic/extends/node_modules/foo/pages/override/index.vue
generated
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
This child page should not be overridden by bar
|
||||||
|
</div>
|
||||||
|
</template>
|
Loading…
Reference in New Issue
Block a user