diff --git a/packages/next/src/lib/metadata/resolve-metadata.ts b/packages/next/src/lib/metadata/resolve-metadata.ts index b15c81428fa61..ece6f8b8096fa 100644 --- a/packages/next/src/lib/metadata/resolve-metadata.ts +++ b/packages/next/src/lib/metadata/resolve-metadata.ts @@ -52,7 +52,7 @@ function mergeStaticMetadata( card: 'summary_large_image', images: twitter, }, - null + metadata.metadataBase ) metadata.twitter = { ...metadata.twitter, ...resolvedTwitter! } } @@ -62,7 +62,7 @@ function mergeStaticMetadata( { images: opengraph, }, - null + metadata.metadataBase ) metadata.openGraph = { ...metadata.openGraph, ...resolvedOg! } } @@ -81,7 +81,7 @@ function merge( openGraph: string | null } ) { - const metadataBase = source?.metadataBase || null + const metadataBase = source?.metadataBase || target.metadataBase for (const key_ in source) { const key = key_ as keyof Metadata diff --git a/packages/next/src/lib/metadata/resolvers/resolve-opengraph.ts b/packages/next/src/lib/metadata/resolvers/resolve-opengraph.ts index 81619bec09698..961fbfcaa4d90 100644 --- a/packages/next/src/lib/metadata/resolvers/resolve-opengraph.ts +++ b/packages/next/src/lib/metadata/resolvers/resolve-opengraph.ts @@ -5,7 +5,7 @@ import type { ResolvedOpenGraph, } from '../types/opengraph-types' import type { FieldResolverWithMetadataBase } from '../types/resolvers' -import type { ResolvedTwitterMetadata } from '../types/twitter-types' +import type { ResolvedTwitterMetadata, Twitter } from '../types/twitter-types' import { resolveAsArrayOrUndefined } from '../generate/utils' import { isStringOrURL, resolveUrl } from './resolve-url' @@ -25,6 +25,34 @@ const OgTypFields = { ], } as const +function resolveImages( + images: Twitter['images'], + metadataBase: ResolvedMetadata['metadataBase'] +): NonNullable['images'] +function resolveImages( + images: OpenGraph['images'], + metadataBase: ResolvedMetadata['metadataBase'] +): NonNullable['images'] +function resolveImages( + images: OpenGraph['images'] | Twitter['images'], + metadataBase: ResolvedMetadata['metadataBase'] +): + | NonNullable['images'] + | NonNullable['images'] { + const resolvedImages = resolveAsArrayOrUndefined(images) + resolvedImages?.forEach((item, index, array) => { + if (isStringOrURL(item)) { + array[index] = { + url: metadataBase ? resolveUrl(item, metadataBase)! : item, + } + } else { + // Update image descriptor url + item.url = metadataBase ? resolveUrl(item.url, metadataBase)! : item.url + } + }) + return resolvedImages +} + function getFieldsByOgType(ogType: OpenGraphType | undefined) { switch (ogType) { case 'article': @@ -68,7 +96,8 @@ export function resolveOpenGraph( } } } - resolved.images = resolveAsArrayOrUndefined(og.images) + + resolved.images = resolveImages(og.images, metadataBase) } assignProps(openGraph) @@ -97,18 +126,8 @@ export const resolveTwitter: FieldResolverWithMetadataBase<'twitter'> = ( for (const infoKey of TwitterBasicInfoKeys) { resolved[infoKey] = twitter[infoKey] || null } - resolved.images = resolveAsArrayOrUndefined(twitter.images)?.map((item) => { - if (isStringOrURL(item)) - return { - url: metadataBase ? resolveUrl(item, metadataBase) : item, - } - else { - return { - url: metadataBase ? resolveUrl(item.url, metadataBase) : item.url, - alt: item.alt, - } - } - }) + resolved.images = resolveImages(twitter.images, metadataBase) + if ('card' in twitter) { resolved.card = twitter.card switch (twitter.card) { diff --git a/packages/next/src/lib/metadata/resolvers/resolve-url.test.ts b/packages/next/src/lib/metadata/resolvers/resolve-url.test.ts index 54703f87b8b36..919aefe75994a 100644 --- a/packages/next/src/lib/metadata/resolvers/resolve-url.test.ts +++ b/packages/next/src/lib/metadata/resolvers/resolve-url.test.ts @@ -9,7 +9,7 @@ describe('metadata: resolveUrl', () => { }) it('should error when metadataBase is not provided but url is not valid URL', () => { - expect(() => resolveUrl('/abc', null)).toThrow('missing metadataBase') + expect(() => resolveUrl('/abc', null)).toThrow() }) it('should return url itself when metadataBase is null or url is valid URL', () => { diff --git a/packages/next/src/lib/metadata/resolvers/resolve-url.ts b/packages/next/src/lib/metadata/resolvers/resolve-url.ts index d09e4dd5529f7..0f1e8f0f68b19 100644 --- a/packages/next/src/lib/metadata/resolvers/resolve-url.ts +++ b/packages/next/src/lib/metadata/resolvers/resolve-url.ts @@ -18,7 +18,10 @@ function resolveUrl( return parsedUrl } catch (_) {} - if (!metadataBase) throw new Error('missing metadataBase') + if (!metadataBase) + throw new Error( + `metadata.metadataBase needs to be provided for resolving absolute URLs: ${url}` + ) // Handle relative or absolute paths const basePath = metadataBase.pathname || '/' diff --git a/packages/next/src/lib/metadata/types/twitter-types.ts b/packages/next/src/lib/metadata/types/twitter-types.ts index 15dbf23f6f4f6..6bd44173bb21d 100644 --- a/packages/next/src/lib/metadata/types/twitter-types.ts +++ b/packages/next/src/lib/metadata/types/twitter-types.ts @@ -64,8 +64,12 @@ type TwitterPlayerDescriptor = { } type ResolvedTwitterImage = { - url: null | URL | string + url: string | URL alt?: string + secureUrl?: string | URL + type?: string + width?: string | number + height?: string | number } type ResolvedTwitterSummary = { site: string | null diff --git a/test/e2e/app-dir/metadata/app/opengraph/layout.tsx b/test/e2e/app-dir/metadata/app/opengraph/layout.tsx new file mode 100644 index 0000000000000..91357392209bc --- /dev/null +++ b/test/e2e/app-dir/metadata/app/opengraph/layout.tsx @@ -0,0 +1,7 @@ +export default function Layout({ children }) { + return children +} + +export const metadata = { + metadataBase: new URL('https://example.com/'), +} diff --git a/test/e2e/app-dir/metadata/metadata.test.ts b/test/e2e/app-dir/metadata/metadata.test.ts index c6c37ace9b4ba..a168ce32828b4 100644 --- a/test/e2e/app-dir/metadata/metadata.test.ts +++ b/test/e2e/app-dir/metadata/metadata.test.ts @@ -450,7 +450,7 @@ createNextDescribe( it('should pick up opengraph-image and twitter-image as static metadata files', async () => { const $ = await next.render$('/opengraph/static') expect($('[property="og:image:url"]').attr('content')).toMatch( - /_next\/static\/media\/metadata\/opengraph-image.\w+.png/ + /https:\/\/example.com\/_next\/static\/media\/metadata\/opengraph-image.\w+.png/ ) expect($('[property="og:image:type"]').attr('content')).toBe( 'image/png' @@ -459,7 +459,7 @@ createNextDescribe( expect($('[property="og:image:height"]').attr('content')).toBe('114') expect($('[name="twitter:image"]').attr('content')).toMatch( - /_next\/static\/media\/metadata\/twitter-image.\w+.png/ + /https:\/\/example.com\/_next\/static\/media\/metadata\/twitter-image.\w+.png/ ) expect($('[name="twitter:card"]').attr('content')).toBe( 'summary_large_image'