Skip to content

Commit

Permalink
Refactor metadata og and twitter title to be always presented (#52320)
Browse files Browse the repository at this point in the history
Follow up for #52196 

The og title should be always resolved as `AbsoluteTemplate`. Move all the title resolving inside the og metadata resolving, so that it's aligned with type. And we don't have to check title everywhere, when og/twitter metadata is resolved, `title` is always presented as a property with `AbsoluteTemplate` type

Closes NEXT-1399
  • Loading branch information
huozhi committed Jul 7, 2023
1 parent a3d5a85 commit 89bccd0
Show file tree
Hide file tree
Showing 7 changed files with 82 additions and 84 deletions.
4 changes: 2 additions & 2 deletions packages/next/src/lib/metadata/metadata.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export async function MetadataTree({
searchParams: { [key: string]: any }
getDynamicParamFromSegment: GetDynamicParamFromSegment
}) {
const options = {
const metadataContext = {
pathname,
}
const resolvedMetadata = await resolveMetadata({
Expand All @@ -41,7 +41,7 @@ export async function MetadataTree({
searchParams,
getDynamicParamFromSegment,
})
const metadata = await accumulateMetadata(resolvedMetadata, options)
const metadata = await accumulateMetadata(resolvedMetadata, metadataContext)

const elements = MetaFilter([
BasicMetadata({ metadata }),
Expand Down
89 changes: 48 additions & 41 deletions packages/next/src/lib/metadata/resolve-metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import type { GetDynamicParamFromSegment } from '../../server/app-render/app-ren
import type { Twitter } from './types/twitter-types'
import type { OpenGraph } from './types/opengraph-types'
import type { ComponentsType } from '../../build/webpack/loaders/next-app-loader'
import type { MetadataAccumulationOptions } from './types/resolvers'
import type { MetadataContext } from './types/resolvers'
import { createDefaultMetadata } from './default-metadata'
import { resolveOpenGraph, resolveTwitter } from './resolvers/resolve-opengraph'
import { resolveTitle } from './resolvers/resolve-title'
Expand Down Expand Up @@ -44,10 +44,17 @@ export type MetadataItems = [
StaticMetadata
][]

type TitleTemplates = {
title: string | null
twitter: string | null
openGraph: string | null
}

function mergeStaticMetadata(
metadata: ResolvedMetadata,
staticFilesMetadata: StaticMetadata,
{ pathname }: MetadataAccumulationOptions
metadataContext: MetadataContext,
titleTemplates: TitleTemplates
) {
if (!staticFilesMetadata) return
const { icon, apple, openGraph, twitter, manifest } = staticFilesMetadata
Expand All @@ -60,7 +67,8 @@ function mergeStaticMetadata(
if (twitter) {
const resolvedTwitter = resolveTwitter(
{ ...metadata.twitter, images: twitter } as Twitter,
metadata.metadataBase
metadata.metadataBase,
titleTemplates.twitter
)
metadata.twitter = resolvedTwitter
}
Expand All @@ -69,7 +77,8 @@ function mergeStaticMetadata(
const resolvedOpenGraph = resolveOpenGraph(
{ ...metadata.openGraph, images: openGraph } as OpenGraph,
metadata.metadataBase,
{ pathname }
metadataContext,
titleTemplates.openGraph
)
metadata.openGraph = resolvedOpenGraph
}
Expand All @@ -86,17 +95,13 @@ function merge({
source,
staticFilesMetadata,
titleTemplates,
options,
metadataContext,
}: {
target: ResolvedMetadata
source: Metadata | null
staticFilesMetadata: StaticMetadata
titleTemplates: {
title: string | null
twitter: string | null
openGraph: string | null
}
options: MetadataAccumulationOptions
titleTemplates: TitleTemplates
metadataContext: MetadataContext
}) {
// If there's override metadata, prefer it otherwise fallback to the default metadata.
const metadataBase =
Expand All @@ -115,32 +120,25 @@ function merge({
target.alternates = resolveAlternates(
source.alternates,
metadataBase,
options
metadataContext
)
break
}
case 'openGraph': {
target.openGraph = resolveOpenGraph(
source.openGraph,
metadataBase,
options
metadataContext,
titleTemplates.openGraph
)
if (target.openGraph) {
target.openGraph.title = resolveTitle(
target.openGraph.title,
titleTemplates.openGraph
)
}
break
}
case 'twitter': {
target.twitter = resolveTwitter(source.twitter, metadataBase)
if (target.twitter) {
target.twitter.title = resolveTitle(
target.twitter.title,
titleTemplates.twitter
)
}
target.twitter = resolveTwitter(
source.twitter,
metadataBase,
titleTemplates.twitter
)
break
}
case 'verification':
Expand Down Expand Up @@ -180,7 +178,11 @@ function merge({
break
}
case 'itunes': {
target[key] = resolveItunes(source.itunes, metadataBase, options)
target[key] = resolveItunes(
source.itunes,
metadataBase,
metadataContext
)
break
}
// directly assign fields that fallback to null
Expand Down Expand Up @@ -208,7 +210,12 @@ function merge({
break
}
}
mergeStaticMetadata(target, staticFilesMetadata, options)
mergeStaticMetadata(
target,
staticFilesMetadata,
metadataContext,
titleTemplates
)
}

async function getDefinedMetadata(
Expand Down Expand Up @@ -368,15 +375,18 @@ export async function resolveMetadata({
}

const commonOgKeys = ['title', 'description', 'images'] as const
function postProcessMetadata(metadata: ResolvedMetadata): ResolvedMetadata {
function postProcessMetadata(
metadata: ResolvedMetadata,
titleTemplates: TitleTemplates
): ResolvedMetadata {
const { openGraph, twitter } = metadata
if (openGraph) {
let autoFillProps: Partial<{
[Key in (typeof commonOgKeys)[number]]: NonNullable<
ResolvedMetadata['openGraph']
>[Key]
}> = {}
const hasTwTitle = twitter?.title?.absolute
const hasTwTitle = twitter?.title.absolute
const hasTwDescription = twitter?.description
const hasTwImages = twitter?.images
if (!hasTwTitle) autoFillProps.title = openGraph.title
Expand All @@ -386,7 +396,8 @@ function postProcessMetadata(metadata: ResolvedMetadata): ResolvedMetadata {
if (Object.keys(autoFillProps).length > 0) {
const partialTwitter = resolveTwitter(
autoFillProps,
metadata.metadataBase
metadata.metadataBase,
titleTemplates.twitter
)
if (metadata.twitter) {
metadata.twitter = Object.assign({}, metadata.twitter, {
Expand All @@ -406,17 +417,13 @@ function postProcessMetadata(metadata: ResolvedMetadata): ResolvedMetadata {

export async function accumulateMetadata(
metadataItems: MetadataItems,
options: MetadataAccumulationOptions
metadataContext: MetadataContext
): Promise<ResolvedMetadata> {
const resolvedMetadata = createDefaultMetadata()
const resolvers: ((value: ResolvedMetadata) => void)[] = []
const generateMetadataResults: (Metadata | Promise<Metadata>)[] = []

let titleTemplates: {
title: string | null
twitter: string | null
openGraph: string | null
} = {
let titleTemplates: TitleTemplates = {
title: null,
twitter: null,
openGraph: null,
Expand Down Expand Up @@ -472,7 +479,7 @@ export async function accumulateMetadata(
}

merge({
options,
metadataContext,
target: resolvedMetadata,
source: metadata,
staticFilesMetadata,
Expand All @@ -484,11 +491,11 @@ export async function accumulateMetadata(
if (i < metadataItems.length - 2) {
titleTemplates = {
title: resolvedMetadata.title?.template || null,
openGraph: resolvedMetadata.openGraph?.title?.template || null,
twitter: resolvedMetadata.twitter?.title?.template || null,
openGraph: resolvedMetadata.openGraph?.title.template || null,
twitter: resolvedMetadata.twitter?.title.template || null,
}
}
}

return postProcessMetadata(resolvedMetadata)
return postProcessMetadata(resolvedMetadata, titleTemplates)
}
12 changes: 6 additions & 6 deletions packages/next/src/lib/metadata/resolvers/resolve-basics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import type { Metadata, ResolvedMetadata } from '../types/metadata-interface'
import type { ResolvedVerification } from '../types/metadata-types'
import type {
FieldResolver,
FieldResolverWithMetadataBase,
MetadataAccumulationOptions,
FieldResolverExtraArgs,
MetadataContext,
} from '../types/resolvers'
import type { Viewport } from '../types/extra-types'
import { resolveAsArrayOrUndefined } from '../generate/utils'
Expand Down Expand Up @@ -114,9 +114,9 @@ function resolveCanonicalUrl(
}
}

export const resolveAlternates: FieldResolverWithMetadataBase<
export const resolveAlternates: FieldResolverExtraArgs<
'alternates',
MetadataAccumulationOptions
[ResolvedMetadata['metadataBase'], MetadataContext]
> = (alternates, metadataBase, { pathname }) => {
if (!alternates) return null

Expand Down Expand Up @@ -252,9 +252,9 @@ export const resolveAppLinks: FieldResolver<'appLinks'> = (appLinks) => {
return appLinks as ResolvedMetadata['appLinks']
}

export const resolveItunes: FieldResolverWithMetadataBase<
export const resolveItunes: FieldResolverExtraArgs<
'itunes',
MetadataAccumulationOptions
[ResolvedMetadata['metadataBase'], MetadataContext]
> = (itunes, metadataBase, { pathname }) => {
if (!itunes) return null
return {
Expand Down
40 changes: 20 additions & 20 deletions packages/next/src/lib/metadata/resolvers/resolve-opengraph.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import type { Metadata, ResolvedMetadata } from '../types/metadata-interface'
import type { ResolvedMetadata } from '../types/metadata-interface'
import type {
OpenGraphType,
OpenGraph,
ResolvedOpenGraph,
} from '../types/opengraph-types'
import type {
FieldResolverWithMetadataBase,
MetadataAccumulationOptions,
FieldResolverExtraArgs,
MetadataContext,
} from '../types/resolvers'
import type { ResolvedTwitterMetadata, Twitter } from '../types/twitter-types'
import { resolveAsArrayOrUndefined } from '../generate/utils'
Expand All @@ -16,6 +16,7 @@ import {
resolveUrl,
resolveAbsoluteUrlWithPathname,
} from './resolve-url'
import { resolveTitle } from './resolve-title'

const OgTypeFields = {
article: ['authors', 'tags'],
Expand Down Expand Up @@ -92,19 +93,13 @@ function getFieldsByOgType(ogType: OpenGraphType | undefined) {
}
}

export const resolveOpenGraph: FieldResolverWithMetadataBase<
export const resolveOpenGraph: FieldResolverExtraArgs<
'openGraph',
MetadataAccumulationOptions
> = (
openGraph: Metadata['openGraph'],
metadataBase: ResolvedMetadata['metadataBase'],
{ pathname }
) => {
[ResolvedMetadata['metadataBase'], MetadataContext, string | null]
> = (openGraph, metadataBase, { pathname }, titleTemplate) => {
if (!openGraph) return null

const resolved = { ...openGraph } as ResolvedOpenGraph

function assignProps(og: OpenGraph) {
function resolveProps(target: ResolvedOpenGraph, og: OpenGraph) {
const ogType = og && 'type' in og ? og.type : undefined
const keys = getFieldsByOgType(ogType)
for (const k of keys) {
Expand All @@ -114,16 +109,20 @@ export const resolveOpenGraph: FieldResolverWithMetadataBase<
if (value) {
const arrayValue = resolveAsArrayOrUndefined(value)
/// TODO: improve typing inferring
;(resolved as any)[key] = arrayValue
;(target as any)[key] = arrayValue
}
}
}

const imageMetadataBase = getSocialImageFallbackMetadataBase(metadataBase)
resolved.images = resolveImages(og.images, imageMetadataBase)
target.images = resolveImages(og.images, imageMetadataBase)
}

assignProps(openGraph)
const resolved = {
...openGraph,
title: resolveTitle(openGraph.title, titleTemplate),
} as ResolvedOpenGraph
resolveProps(resolved, openGraph)

resolved.url = openGraph.url
? resolveAbsoluteUrlWithPathname(openGraph.url, metadataBase, pathname)
Expand All @@ -140,14 +139,15 @@ const TwitterBasicInfoKeys = [
'description',
] as const

export const resolveTwitter: FieldResolverWithMetadataBase<'twitter'> = (
twitter,
metadataBase
) => {
export const resolveTwitter: FieldResolverExtraArgs<
'twitter',
[ResolvedMetadata['metadataBase'], string | null]
> = (twitter, metadataBase, titleTemplate) => {
if (!twitter) return null
const resolved = {
...twitter,
card: 'card' in twitter ? twitter.card : 'summary',
title: resolveTitle(twitter.title, titleTemplate),
} as ResolvedTwitterMetadata
for (const infoKey of TwitterBasicInfoKeys) {
resolved[infoKey] = twitter[infoKey] || null
Expand Down
2 changes: 1 addition & 1 deletion packages/next/src/lib/metadata/types/opengraph-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ export type ResolvedOpenGraph =

type ResolvedOpenGraphMetadata = {
determiner?: 'a' | 'an' | 'the' | 'auto' | ''
title?: AbsoluteTemplateString
title: AbsoluteTemplateString
description?: string
emails?: Array<string>
phoneNumbers?: Array<string>
Expand Down
17 changes: 4 additions & 13 deletions packages/next/src/lib/metadata/types/resolvers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,11 @@ import { Metadata, ResolvedMetadata } from './metadata-interface'
export type FieldResolver<Key extends keyof Metadata> = (
T: Metadata[Key]
) => ResolvedMetadata[Key]
export type FieldResolverWithMetadataBase<
export type FieldResolverExtraArgs<
Key extends keyof Metadata,
Options = undefined
> = Options extends undefined
? (
T: Metadata[Key],
metadataBase: ResolvedMetadata['metadataBase']
) => ResolvedMetadata[Key]
: (
T: Metadata[Key],
metadataBase: ResolvedMetadata['metadataBase'],
options: Options
) => ResolvedMetadata[Key]
ExtraArgs extends unknown[] = any[]
> = (T: Metadata[Key], ...args: ExtraArgs) => ResolvedMetadata[Key]

export type MetadataAccumulationOptions = {
export type MetadataContext = {
pathname: string
}
2 changes: 1 addition & 1 deletion packages/next/src/lib/metadata/types/twitter-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ type ResolvedTwitterSummary = {
creator: string | null
creatorId: string | null
description: string | null
title?: AbsoluteTemplateString
title: AbsoluteTemplateString
images?: Array<ResolvedTwitterImage>
}
type ResolvedTwitterPlayer = ResolvedTwitterSummary & {
Expand Down

0 comments on commit 89bccd0

Please sign in to comment.