From 15e6dfb5606befd9972f8aab29653fc59bf2ebe1 Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Tue, 12 Sep 2023 10:46:35 +0100 Subject: [PATCH] fix(nuxt): allow granularly overriding pages in layers (#23134) --- packages/nuxt/src/pages/utils.ts | 31 ++++++++++------- packages/nuxt/test/pages.test.ts | 34 +++++++++++-------- test/basic.test.ts | 1 + .../basic/extends/bar/pages/override.vue | 1 + .../node_modules/foo/pages/override/index.vue | 5 +++ 5 files changed, 44 insertions(+), 28 deletions(-) create mode 100644 test/fixtures/basic/extends/node_modules/foo/pages/override/index.vue diff --git a/packages/nuxt/src/pages/utils.ts b/packages/nuxt/src/pages/utils.ts index 0195ae6e0e..f745c30309 100644 --- a/packages/nuxt/src/pages/utils.ts +++ b/packages/nuxt/src/pages/utils.ts @@ -33,6 +33,11 @@ interface SegmentToken { value: string } +interface ScannedFile { + relativePath: string + absolutePath: string +} + export async function resolvePagesRoutes (): Promise { const nuxt = useNuxt() @@ -40,30 +45,30 @@ export async function resolvePagesRoutes (): Promise { layer => resolve(layer.config.srcDir, layer.config.dir?.pages || 'pages') ) - const allRoutes = (await Promise.all( - pagesDirs.map(async (dir) => { - 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() + const scannedFiles: ScannedFile[] = [] + for (const dir of pagesDirs) { + const files = await resolveFiles(dir, `**/*{${nuxt.options.extensions.join(',')}}`) + 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): Promise { +export async function generateRoutesFromFiles (files: ScannedFile[], shouldExtractBuildMeta = false, vfs?: Record): Promise { 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 diff --git a/packages/nuxt/test/pages.test.ts b/packages/nuxt/test/pages.test.ts index 70f0b07c24..3a62a4b236 100644 --- a/packages/nuxt/test/pages.test.ts +++ b/packages/nuxt/test/pages.test.ts @@ -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) } diff --git a/test/basic.test.ts b/test/basic.test.ts index b048d1fe7f..8118262645 100644 --- a/test/basic.test.ts +++ b/test/basic.test.ts @@ -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') }) }) diff --git a/test/fixtures/basic/extends/bar/pages/override.vue b/test/fixtures/basic/extends/bar/pages/override.vue index d104ece7bf..0892321fdb 100644 --- a/test/fixtures/basic/extends/bar/pages/override.vue +++ b/test/fixtures/basic/extends/bar/pages/override.vue @@ -10,5 +10,6 @@ definePageMeta({
Extended page from bar
Middleware | override: {{ $route.meta.override }}
+ diff --git a/test/fixtures/basic/extends/node_modules/foo/pages/override/index.vue b/test/fixtures/basic/extends/node_modules/foo/pages/override/index.vue new file mode 100644 index 0000000000..442e45b5f5 --- /dev/null +++ b/test/fixtures/basic/extends/node_modules/foo/pages/override/index.vue @@ -0,0 +1,5 @@ +