Skip to content

Commit

Permalink
Override file based images with social images property (#52416)
Browse files Browse the repository at this point in the history
Metadata API should provide a way to override the filebased metadata
images. As usually for child routes, if there's new social images or
icons are provided, the ones from parent routes should be overridden /
skipped.

The `metadata` object export or `generateMetadata` should be able to do
that. Sometimes users still add other og info (besides images) to
metadata export (both object and `generateMetadata`).
I think we should check if they really have returned images property,
then decide to override.

- For the same level of routes:
- If there's no `openGraph.images` in the returned value, merge with
file based images
- If there's any `openGraph.images` in the returned value, ignore file
based ones

- For child level of routes:
Always override the parent level, ignoring parent level file based
images unless they use `generateMetadata` to merge from
`resolvingParentMetadata` value, then the parent level's file based ones
will present there


Closes NEXT-1418
  • Loading branch information
huozhi committed Jul 10, 2023
1 parent 2ef8938 commit 85033e3
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 19 deletions.
62 changes: 62 additions & 0 deletions packages/next/src/lib/metadata/resolve-metadata.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,68 @@ describe('accumulateMetadata', () => {
},
})
})

it('should override openGraph or twitter images when current layer specifies social images properties', async () => {
const metadataItems1: MetadataItems = [
[
{
openGraph: {
images: 'https://test.com/og.png',
},
twitter: {
images: 'https://test.com/twitter.png',
},
},
// has static metadata files
{
icon: undefined,
apple: undefined,
twitter: ['/filebased/twitter.png'],
openGraph: ['/filebased/og.png'],
manifest: undefined,
},
],
]
const metadata1 = await accumulateMetadata(metadataItems1)
expect(metadata1).toMatchObject({
openGraph: {
images: [{ url: new URL('https://test.com/og.png') }],
},
twitter: {
images: [{ url: new URL('https://test.com/twitter.png ') }],
},
})

const metadataItems2: MetadataItems = [
[
function gM2() {
return {
openGraph: {
images: undefined,
},
// twitter is not specified, supposed to merged with openGraph but images should not be picked up
}
},
// has static metadata files
{
icon: undefined,
apple: undefined,
twitter: undefined,
openGraph: ['/filebased/og.png'],
manifest: undefined,
},
],
]
const metadata2 = await accumulateMetadata(metadataItems2)
expect(metadata2).toMatchObject({
openGraph: {
images: undefined,
},
twitter: {
images: undefined,
},
})
})
})

describe('themeColor', () => {
Expand Down
42 changes: 26 additions & 16 deletions packages/next/src/lib/metadata/resolve-metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,54 +51,61 @@ type TitleTemplates = {
}

function mergeStaticMetadata(
metadata: ResolvedMetadata,
source: Metadata | null,
target: ResolvedMetadata,
staticFilesMetadata: StaticMetadata,
metadataContext: MetadataContext,
titleTemplates: TitleTemplates
) {
if (!staticFilesMetadata) return
const { icon, apple, openGraph, twitter, manifest } = staticFilesMetadata
if (icon || apple) {
metadata.icons = {
// file based metadata is specified and current level metadata icons is not specified
if (
(icon && !source?.icons && !source?.icons?.hasOwnProperty('icon')) ||
(apple && !source?.icons?.hasOwnProperty('apple'))
) {
target.icons = {
icon: icon || [],
apple: apple || [],
}
}
if (twitter) {
// file based metadata is specified and current level metadata twitter.images is not specified
if (twitter && !source?.twitter?.hasOwnProperty('images')) {
const resolvedTwitter = resolveTwitter(
{ ...metadata.twitter, images: twitter } as Twitter,
metadata.metadataBase,
{ ...target.twitter, images: twitter } as Twitter,
target.metadataBase,
titleTemplates.twitter
)
metadata.twitter = resolvedTwitter
target.twitter = resolvedTwitter
}

if (openGraph) {
// file based metadata is specified and current level metadata openGraph.images is not specified
if (openGraph && !source?.openGraph?.hasOwnProperty('images')) {
const resolvedOpenGraph = resolveOpenGraph(
{ ...metadata.openGraph, images: openGraph } as OpenGraph,
metadata.metadataBase,
{ ...target.openGraph, images: openGraph } as OpenGraph,
target.metadataBase,
metadataContext,
titleTemplates.openGraph
)
metadata.openGraph = resolvedOpenGraph
target.openGraph = resolvedOpenGraph
}
if (manifest) {
metadata.manifest = manifest
target.manifest = manifest
}

return metadata
return target
}

// Merge the source metadata into the resolved target metadata.
function merge({
target,
source,
target,
staticFilesMetadata,
titleTemplates,
metadataContext,
}: {
target: ResolvedMetadata
source: Metadata | null
target: ResolvedMetadata
staticFilesMetadata: StaticMetadata
titleTemplates: TitleTemplates
metadataContext: MetadataContext
Expand Down Expand Up @@ -211,6 +218,7 @@ function merge({
}
}
mergeStaticMetadata(
source,
target,
staticFilesMetadata,
metadataContext,
Expand Down Expand Up @@ -388,7 +396,9 @@ function postProcessMetadata(
}> = {}
const hasTwTitle = twitter?.title.absolute
const hasTwDescription = twitter?.description
const hasTwImages = twitter?.images
const hasTwImages = Boolean(
twitter?.hasOwnProperty('images') && twitter.images
)
if (!hasTwTitle) autoFillProps.title = openGraph.title
if (!hasTwDescription) autoFillProps.description = openGraph.description
if (!hasTwImages) autoFillProps.images = openGraph.images
Expand Down
15 changes: 15 additions & 0 deletions test/e2e/app-dir/metadata/app/opengraph/static/override/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export default function Page() {
return 'opengraph-static-override'
}

export const metadata = {
icons: ['https://custom-icon-1.png'],
openGraph: {
title: 'no-og-image',
images: undefined,
},
twitter: {
title: 'no-tw-image',
images: null,
},
}
25 changes: 22 additions & 3 deletions test/e2e/app-dir/metadata/metadata.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ createNextDescribe(
tag: string,
queryKey: string,
domAttributeField: string,
expected: Record<string, string | string[]>
expected: Record<string, string | string[] | undefined>
) => {
const res = {}
for (const key of Object.keys(expected)) {
Expand Down Expand Up @@ -569,8 +569,27 @@ createNextDescribe(
})

// favicon shouldn't be overridden
const $icon = $('link[rel="icon"]')
expect($icon.attr('href')).toBe('/favicon.ico')
expect($('link[rel="icon"]').attr('href')).toBe('/favicon.ico')
})

it('should override file based images when opengraph-image and twitter-image specify images property', async () => {
const $ = await next.render$('/opengraph/static/override')

const match = createMultiHtmlMatcher($)
await match('meta', 'property', 'content', {
'og:title': 'no-og-image',
'og:image': undefined,
})

await match('meta', 'name', 'content', {
'twitter:image': undefined,
'twitter:title': 'no-tw-image',
})

// icon should be overridden
expect($('link[rel="icon"]').attr('href')).toBe(
'https://custom-icon-1.png'
)
})
})

Expand Down

0 comments on commit 85033e3

Please sign in to comment.