diff --git a/packages/next/build/index.ts b/packages/next/build/index.ts index 685c7356b5bb43d..4e03e996ae47e7f 100644 --- a/packages/next/build/index.ts +++ b/packages/next/build/index.ts @@ -493,7 +493,7 @@ export default async function build( .traceAsyncFn(() => recursiveReadDir( appDir, - new RegExp(`page\\.(?:${config.pageExtensions.join('|')})$`) + new RegExp(`^page\\.(?:${config.pageExtensions.join('|')})$`) ) ) } diff --git a/packages/next/server/dev/next-dev-server.ts b/packages/next/server/dev/next-dev-server.ts index 0a1f86351132e59..389f283c367fa90 100644 --- a/packages/next/server/dev/next-dev-server.ts +++ b/packages/next/server/dev/next-dev-server.ts @@ -405,7 +405,7 @@ export default class DevServer extends Server { }) if (isAppPath) { - if (!isLayoutsLeafPage(fileName)) { + if (!isLayoutsLeafPage(fileName, this.nextConfig.pageExtensions)) { continue } diff --git a/packages/next/server/lib/find-page-file.ts b/packages/next/server/lib/find-page-file.ts index 53d2a86dd35e86d..0b573f475166a2d 100644 --- a/packages/next/server/lib/find-page-file.ts +++ b/packages/next/server/lib/find-page-file.ts @@ -64,9 +64,9 @@ export async function findPageFile( } // Determine if the file is leaf node page file under layouts, -// The filename should start with 'page', it can be either shared, -// client, or server components with allowed page file extension. -// e.g. page.js, page.server.js, page.client.tsx, etc. -export function isLayoutsLeafPage(filePath: string) { - return /[\\/]?page\.((server|client)\.?)?[jt]sx?$/.test(filePath) +// The filename should start with 'page' and end with one of the allowed extensions +export function isLayoutsLeafPage(filePath: string, pageExtensions: string[]) { + return new RegExp(`(^page|/page)\\.(?:${pageExtensions.join('|')})$`).test( + filePath + ) } diff --git a/test/e2e/app-dir/app/app/catch-all/[...slug]/not-a-page.js b/test/e2e/app-dir/app/app/catch-all/[...slug]/not-a-page.js new file mode 100644 index 000000000000000..b49da2a9eee6c65 --- /dev/null +++ b/test/e2e/app-dir/app/app/catch-all/[...slug]/not-a-page.js @@ -0,0 +1,3 @@ +export default function NotAPage() { + return

Not a page

+} diff --git a/test/e2e/app-dir/app/app/catch-all/[...slug]/page.js b/test/e2e/app-dir/app/app/catch-all/[...slug]/page.js index d5361f48d544757..55ba1e455951799 100644 --- a/test/e2e/app-dir/app/app/catch-all/[...slug]/page.js +++ b/test/e2e/app-dir/app/app/catch-all/[...slug]/page.js @@ -1,4 +1,5 @@ import Widget from './components/widget' +import NotAPage from './not-a-page' export default function Page({ params }) { return ( @@ -7,6 +8,7 @@ export default function Page({ params }) { hello from /catch-all/{params.slug.join('/')} + ) } diff --git a/test/e2e/app-dir/index.test.ts b/test/e2e/app-dir/index.test.ts index 14a1da1c295dafd..6af1d4603ae2978 100644 --- a/test/e2e/app-dir/index.test.ts +++ b/test/e2e/app-dir/index.test.ts @@ -726,6 +726,7 @@ describe('app dir', () => { const html = await renderViaHTTP(next.url, `/catch-all/${route}`) const $ = cheerio.load(html) expect($('#text').attr('data-params')).toBe(route) + expect($('#not-a-page').text()).toBe('Not a page') // Components under catch-all should not be treated as route that errors during build. // They should be rendered properly when imported in page route. diff --git a/test/unit/find-page-file.test.ts b/test/unit/find-page-file.test.ts index b59853a41017d34..d49fb4347bd978d 100644 --- a/test/unit/find-page-file.test.ts +++ b/test/unit/find-page-file.test.ts @@ -46,16 +46,22 @@ describe('findPageFile', () => { }) describe('isLayoutsLeafPage', () => { + const pageExtensions = ['tsx', 'ts', 'jsx', 'js'] it('should determine either server or client component page file as leaf node page', () => { - expect(isLayoutsLeafPage('page.js')).toBe(true) - expect(isLayoutsLeafPage('./page.server.js')).toBe(true) - expect(isLayoutsLeafPage('./page.server.jsx')).toBe(true) - expect(isLayoutsLeafPage('./page.client.ts')).toBe(true) - expect(isLayoutsLeafPage('./page.client.tsx')).toBe(true) + expect(isLayoutsLeafPage('page.js', pageExtensions)).toBe(true) + expect(isLayoutsLeafPage('./page.js', pageExtensions)).toBe(true) + expect(isLayoutsLeafPage('./page.jsx', pageExtensions)).toBe(true) + expect(isLayoutsLeafPage('/page.ts', pageExtensions)).toBe(true) + expect(isLayoutsLeafPage('/path/page.tsx', pageExtensions)).toBe(true) }) it('should determine other files under layout routes as non leaf node', () => { - expect(isLayoutsLeafPage('./page.component.jsx')).toBe(false) - expect(isLayoutsLeafPage('layout.js')).toBe(false) + expect(isLayoutsLeafPage('./not-a-page.js', pageExtensions)).toBe(false) + expect(isLayoutsLeafPage('not-a-page.js', pageExtensions)).toBe(false) + expect(isLayoutsLeafPage('./page.component.jsx', pageExtensions)).toBe( + false + ) + expect(isLayoutsLeafPage('layout.js', pageExtensions)).toBe(false) + expect(isLayoutsLeafPage('page', pageExtensions)).toBe(false) }) })