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)
})
})