diff --git a/packages/next/src/lib/metadata/resolve-metadata.ts b/packages/next/src/lib/metadata/resolve-metadata.ts index 94b692ca8fefc..25e337908040d 100644 --- a/packages/next/src/lib/metadata/resolve-metadata.ts +++ b/packages/next/src/lib/metadata/resolve-metadata.ts @@ -51,7 +51,7 @@ import { import { resolveIcons } from './resolvers/resolve-icons' import { getTracer } from '../../server/lib/trace/tracer' import { ResolveMetadataSpan } from '../../server/lib/trace/constants' -import { PAGE_SEGMENT_KEY } from '../../shared/lib/segment' +import { DEFAULT_SEGMENT_KEY, PAGE_SEGMENT_KEY } from '../../shared/lib/segment' import * as Log from '../../build/output/log' import type { WorkStore } from '../../server/app-render/work-async-storage.external' import type { @@ -443,6 +443,7 @@ async function collectMetadata({ const hasErrorConventionComponent = Boolean( errorConvention && tree[2][errorConvention] ) + if (errorConvention) { mod = await getComponentTypeModule(tree, 'layout') modType = errorConvention @@ -550,6 +551,15 @@ async function resolveMetadataItemsImpl( } } + const pageKey = tree[0] + + // If it's default.js, ignore the metadata + // webpack loader: pageKey of the default.js is DEFAULT_SEGMENT_KEY ('__DEFAULT__') + // turbopack loader: pageKey of the parallel route @bar is 'page$' + const isDefaultPage = pageKey === DEFAULT_SEGMENT_KEY || pageKey === 'page$' + if (isDefaultPage) { + return metadataItems + } await collectMetadata({ tree, metadataItems, diff --git a/test/e2e/app-dir/metadata-parallel-routes/app/layout.tsx b/test/e2e/app-dir/metadata-parallel-routes/app/layout.tsx new file mode 100644 index 0000000000000..621aea648e1a0 --- /dev/null +++ b/test/e2e/app-dir/metadata-parallel-routes/app/layout.tsx @@ -0,0 +1,19 @@ +import Link from 'next/link' +import React from 'react' + +export default function Root({ children }: { children: React.ReactNode }) { + return ( + + +
+ to nested +
+
+ to nested subroute +
+

Root Layout

+
{children}
+ + + ) +} diff --git a/test/e2e/app-dir/metadata-parallel-routes/app/nested/@bar/default.tsx b/test/e2e/app-dir/metadata-parallel-routes/app/nested/@bar/default.tsx new file mode 100644 index 0000000000000..014c2d9b53246 --- /dev/null +++ b/test/e2e/app-dir/metadata-parallel-routes/app/nested/@bar/default.tsx @@ -0,0 +1,11 @@ +export default function Default() { + console.log( + '@bar default page', + typeof window !== 'undefined' ? 'client' : 'server' + ) + return '@bar default' +} + +export const metadata = { + title: '@bar - page', +} diff --git a/test/e2e/app-dir/metadata-parallel-routes/app/nested/@bar/layout.tsx b/test/e2e/app-dir/metadata-parallel-routes/app/nested/@bar/layout.tsx new file mode 100644 index 0000000000000..2e09ca74cc17d --- /dev/null +++ b/test/e2e/app-dir/metadata-parallel-routes/app/nested/@bar/layout.tsx @@ -0,0 +1,8 @@ +export default function Layout({ children }) { + return ( +
+

@bar Layout

+
{children}
+
+ ) +} diff --git a/test/e2e/app-dir/metadata-parallel-routes/app/nested/@bar/page.tsx b/test/e2e/app-dir/metadata-parallel-routes/app/nested/@bar/page.tsx new file mode 100644 index 0000000000000..dd9ee3b726d00 --- /dev/null +++ b/test/e2e/app-dir/metadata-parallel-routes/app/nested/@bar/page.tsx @@ -0,0 +1,7 @@ +export default function Page() { + return
Bar Slot
+} + +export const metadata = { + title: '@bar - page', +} diff --git a/test/e2e/app-dir/metadata-parallel-routes/app/nested/@bar/subroute/page.tsx b/test/e2e/app-dir/metadata-parallel-routes/app/nested/@bar/subroute/page.tsx new file mode 100644 index 0000000000000..d351cc540fa7e --- /dev/null +++ b/test/e2e/app-dir/metadata-parallel-routes/app/nested/@bar/subroute/page.tsx @@ -0,0 +1,7 @@ +export default function Page() { + return
Subroute
+} + +export const metadata = { + title: 'subroute - page', +} diff --git a/test/e2e/app-dir/metadata-parallel-routes/app/nested/@foo/default.tsx b/test/e2e/app-dir/metadata-parallel-routes/app/nested/@foo/default.tsx new file mode 100644 index 0000000000000..7adcff2087d8c --- /dev/null +++ b/test/e2e/app-dir/metadata-parallel-routes/app/nested/@foo/default.tsx @@ -0,0 +1,11 @@ +export default function Default() { + console.log( + 'foo default default page', + typeof window !== 'undefined' ? 'client' : 'server' + ) + return '@foo default' +} + +export const metadata = { + title: '@foo - default', +} diff --git a/test/e2e/app-dir/metadata-parallel-routes/app/nested/@foo/layout.tsx b/test/e2e/app-dir/metadata-parallel-routes/app/nested/@foo/layout.tsx new file mode 100644 index 0000000000000..ab0a56dffff47 --- /dev/null +++ b/test/e2e/app-dir/metadata-parallel-routes/app/nested/@foo/layout.tsx @@ -0,0 +1,8 @@ +export default function Layout({ children }) { + return ( +
+

@foo Layout

+
{children}
+
+ ) +} diff --git a/test/e2e/app-dir/metadata-parallel-routes/app/nested/@foo/page.tsx b/test/e2e/app-dir/metadata-parallel-routes/app/nested/@foo/page.tsx new file mode 100644 index 0000000000000..07028d5f5da9c --- /dev/null +++ b/test/e2e/app-dir/metadata-parallel-routes/app/nested/@foo/page.tsx @@ -0,0 +1,7 @@ +export default function Page() { + return
Foo Slot
+} + +export const metadata = { + title: '@foo - page', +} diff --git a/test/e2e/app-dir/metadata-parallel-routes/app/nested/default.tsx b/test/e2e/app-dir/metadata-parallel-routes/app/nested/default.tsx new file mode 100644 index 0000000000000..bef9e8b7075ad --- /dev/null +++ b/test/e2e/app-dir/metadata-parallel-routes/app/nested/default.tsx @@ -0,0 +1,11 @@ +export default function Page() { + console.log( + 'nested default page', + typeof window !== 'undefined' ? 'client' : 'server' + ) + return 'nested default page' +} + +export const metadata = { + title: 'nested - default', +} diff --git a/test/e2e/app-dir/metadata-parallel-routes/app/nested/layout.tsx b/test/e2e/app-dir/metadata-parallel-routes/app/nested/layout.tsx new file mode 100644 index 0000000000000..467f9ca246a53 --- /dev/null +++ b/test/e2e/app-dir/metadata-parallel-routes/app/nested/layout.tsx @@ -0,0 +1,10 @@ +export default function Layout({ children, bar, foo }) { + return ( +
+

Nested Layout

+
{children}
+
{foo}
+
{bar}
+
+ ) +} diff --git a/test/e2e/app-dir/metadata-parallel-routes/app/nested/page.tsx b/test/e2e/app-dir/metadata-parallel-routes/app/nested/page.tsx new file mode 100644 index 0000000000000..5c6924d018e43 --- /dev/null +++ b/test/e2e/app-dir/metadata-parallel-routes/app/nested/page.tsx @@ -0,0 +1,7 @@ +export default function Page() { + return
Hello from Nested
+} + +export const metadata = { + title: 'nested - page', +} diff --git a/test/e2e/app-dir/metadata-parallel-routes/app/page.tsx b/test/e2e/app-dir/metadata-parallel-routes/app/page.tsx new file mode 100644 index 0000000000000..c9f9a60006193 --- /dev/null +++ b/test/e2e/app-dir/metadata-parallel-routes/app/page.tsx @@ -0,0 +1,3 @@ +export default async function Home() { + return
Hello World
+} diff --git a/test/e2e/app-dir/metadata-parallel-routes/metadata-parallel-routes.test.ts b/test/e2e/app-dir/metadata-parallel-routes/metadata-parallel-routes.test.ts new file mode 100644 index 0000000000000..2f35e83e25c81 --- /dev/null +++ b/test/e2e/app-dir/metadata-parallel-routes/metadata-parallel-routes.test.ts @@ -0,0 +1,22 @@ +import { nextTestSetup } from 'e2e-utils' + +describe('metadata-parallel-routes', () => { + const { next } = nextTestSetup({ + files: __dirname, + }) + + it('should ignore metadata from default.js in normal route', async () => { + const $ = await next.render$('/') + // When there's no title, parallel routes should not affect it + expect($('title').length).toBe(0) + + // When there's defined title, parallel routes should not affect it + const $nested = await next.render$('/nested') + expect($nested('title').text()).toBe('nested - page') + }) + + it('should ignore metadata from default.js in parallel routes', async () => { + const $ = await next.render$('/nested/subroute') + expect($('title').text()).toBe('subroute - page') + }) +}) diff --git a/test/e2e/app-dir/metadata-parallel-routes/next.config.js b/test/e2e/app-dir/metadata-parallel-routes/next.config.js new file mode 100644 index 0000000000000..807126e4cf0bf --- /dev/null +++ b/test/e2e/app-dir/metadata-parallel-routes/next.config.js @@ -0,0 +1,6 @@ +/** + * @type {import('next').NextConfig} + */ +const nextConfig = {} + +module.exports = nextConfig diff --git a/test/e2e/app-dir/parallel-routes-layouts/app/nested/@bar/page.tsx b/test/e2e/app-dir/parallel-routes-layouts/app/nested/@bar/page.tsx index 86d9ad310e688..24edb5a8595ec 100644 --- a/test/e2e/app-dir/parallel-routes-layouts/app/nested/@bar/page.tsx +++ b/test/e2e/app-dir/parallel-routes-layouts/app/nested/@bar/page.tsx @@ -1,3 +1,7 @@ export default function Page() { return
Bar Slot
} + +export const metadata = { + title: 'nested - page', +} diff --git a/test/e2e/app-dir/parallel-routes-layouts/app/nested/@bar/subroute/page.tsx b/test/e2e/app-dir/parallel-routes-layouts/app/nested/@bar/subroute/page.tsx index 83be0d5a350c7..d351cc540fa7e 100644 --- a/test/e2e/app-dir/parallel-routes-layouts/app/nested/@bar/subroute/page.tsx +++ b/test/e2e/app-dir/parallel-routes-layouts/app/nested/@bar/subroute/page.tsx @@ -1,3 +1,7 @@ export default function Page() { return
Subroute
} + +export const metadata = { + title: 'subroute - page', +}