From 6bc0bb8d399c08b6896312f8491b5c27eab17023 Mon Sep 17 00:00:00 2001 From: Jiachi Liu Date: Thu, 6 Jul 2023 13:19:37 +0200 Subject: [PATCH 1/2] Refactor metadata og and twitter title to be always presented --- packages/next/src/lib/metadata/metadata.tsx | 4 +- .../next/src/lib/metadata/resolve-metadata.ts | 89 ++++++++++--------- .../lib/metadata/resolvers/resolve-basics.ts | 12 +-- .../metadata/resolvers/resolve-opengraph.ts | 38 ++++---- .../src/lib/metadata/types/opengraph-types.ts | 2 +- .../next/src/lib/metadata/types/resolvers.ts | 17 +--- .../src/lib/metadata/types/twitter-types.ts | 2 +- 7 files changed, 81 insertions(+), 83 deletions(-) diff --git a/packages/next/src/lib/metadata/metadata.tsx b/packages/next/src/lib/metadata/metadata.tsx index 2f086e24e9427..3ac86f0f74db8 100644 --- a/packages/next/src/lib/metadata/metadata.tsx +++ b/packages/next/src/lib/metadata/metadata.tsx @@ -31,7 +31,7 @@ export async function MetadataTree({ searchParams: { [key: string]: any } getDynamicParamFromSegment: GetDynamicParamFromSegment }) { - const options = { + const metadataContext = { pathname, } const resolvedMetadata = await resolveMetadata({ @@ -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 }), diff --git a/packages/next/src/lib/metadata/resolve-metadata.ts b/packages/next/src/lib/metadata/resolve-metadata.ts index b438c4cb019a9..6b9044b41fb3f 100644 --- a/packages/next/src/lib/metadata/resolve-metadata.ts +++ b/packages/next/src/lib/metadata/resolve-metadata.ts @@ -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' @@ -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 @@ -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 } @@ -69,7 +77,8 @@ function mergeStaticMetadata( const resolvedOpenGraph = resolveOpenGraph( { ...metadata.openGraph, images: openGraph } as OpenGraph, metadata.metadataBase, - { pathname } + metadataContext, + titleTemplates.openGraph ) metadata.openGraph = resolvedOpenGraph } @@ -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 = @@ -115,7 +120,7 @@ function merge({ target.alternates = resolveAlternates( source.alternates, metadataBase, - options + metadataContext ) break } @@ -123,24 +128,17 @@ function merge({ 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': @@ -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 @@ -208,7 +210,12 @@ function merge({ break } } - mergeStaticMetadata(target, staticFilesMetadata, options) + mergeStaticMetadata( + target, + staticFilesMetadata, + metadataContext, + titleTemplates + ) } async function getDefinedMetadata( @@ -368,7 +375,10 @@ 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<{ @@ -376,7 +386,7 @@ function postProcessMetadata(metadata: ResolvedMetadata): ResolvedMetadata { 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 @@ -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, { @@ -406,17 +417,13 @@ function postProcessMetadata(metadata: ResolvedMetadata): ResolvedMetadata { export async function accumulateMetadata( metadataItems: MetadataItems, - options: MetadataAccumulationOptions + metadataContext: MetadataContext ): Promise { const resolvedMetadata = createDefaultMetadata() const resolvers: ((value: ResolvedMetadata) => void)[] = [] const generateMetadataResults: (Metadata | Promise)[] = [] - let titleTemplates: { - title: string | null - twitter: string | null - openGraph: string | null - } = { + let titleTemplates: TitleTemplates = { title: null, twitter: null, openGraph: null, @@ -472,7 +479,7 @@ export async function accumulateMetadata( } merge({ - options, + metadataContext, target: resolvedMetadata, source: metadata, staticFilesMetadata, @@ -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) } diff --git a/packages/next/src/lib/metadata/resolvers/resolve-basics.ts b/packages/next/src/lib/metadata/resolvers/resolve-basics.ts index 2d3b42550cdc2..b10954e027567 100644 --- a/packages/next/src/lib/metadata/resolvers/resolve-basics.ts +++ b/packages/next/src/lib/metadata/resolvers/resolve-basics.ts @@ -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' @@ -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 @@ -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 { diff --git a/packages/next/src/lib/metadata/resolvers/resolve-opengraph.ts b/packages/next/src/lib/metadata/resolvers/resolve-opengraph.ts index d3edddabe5246..0b3077d1f0849 100644 --- a/packages/next/src/lib/metadata/resolvers/resolve-opengraph.ts +++ b/packages/next/src/lib/metadata/resolvers/resolve-opengraph.ts @@ -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' @@ -16,6 +16,7 @@ import { resolveUrl, resolveAbsoluteUrlWithPathname, } from './resolve-url' +import { resolveTitle } from './resolve-title' const OgTypeFields = { article: ['authors', 'tags'], @@ -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) { @@ -114,7 +109,7 @@ export const resolveOpenGraph: FieldResolverWithMetadataBase< if (value) { const arrayValue = resolveAsArrayOrUndefined(value) /// TODO: improve typing inferring - ;(resolved as any)[key] = arrayValue + ;(target as any)[key] = arrayValue } } } @@ -123,7 +118,11 @@ export const resolveOpenGraph: FieldResolverWithMetadataBase< resolved.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) @@ -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 diff --git a/packages/next/src/lib/metadata/types/opengraph-types.ts b/packages/next/src/lib/metadata/types/opengraph-types.ts index 2946e0d68a288..156719ab2a808 100644 --- a/packages/next/src/lib/metadata/types/opengraph-types.ts +++ b/packages/next/src/lib/metadata/types/opengraph-types.ts @@ -163,7 +163,7 @@ export type ResolvedOpenGraph = type ResolvedOpenGraphMetadata = { determiner?: 'a' | 'an' | 'the' | 'auto' | '' - title?: AbsoluteTemplateString + title: AbsoluteTemplateString description?: string emails?: Array phoneNumbers?: Array diff --git a/packages/next/src/lib/metadata/types/resolvers.ts b/packages/next/src/lib/metadata/types/resolvers.ts index d7e55f7473237..f7feab5115863 100644 --- a/packages/next/src/lib/metadata/types/resolvers.ts +++ b/packages/next/src/lib/metadata/types/resolvers.ts @@ -3,20 +3,11 @@ import { Metadata, ResolvedMetadata } from './metadata-interface' export type FieldResolver = ( 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 } diff --git a/packages/next/src/lib/metadata/types/twitter-types.ts b/packages/next/src/lib/metadata/types/twitter-types.ts index 2595f7e1891c0..6bd44173bb21d 100644 --- a/packages/next/src/lib/metadata/types/twitter-types.ts +++ b/packages/next/src/lib/metadata/types/twitter-types.ts @@ -77,7 +77,7 @@ type ResolvedTwitterSummary = { creator: string | null creatorId: string | null description: string | null - title?: AbsoluteTemplateString + title: AbsoluteTemplateString images?: Array } type ResolvedTwitterPlayer = ResolvedTwitterSummary & { From fc48ee3ceec34c9efeb536a726976ecd794f7faa Mon Sep 17 00:00:00 2001 From: Jiachi Liu Date: Thu, 6 Jul 2023 13:56:28 +0200 Subject: [PATCH 2/2] fix --- packages/next/src/lib/metadata/resolvers/resolve-opengraph.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/next/src/lib/metadata/resolvers/resolve-opengraph.ts b/packages/next/src/lib/metadata/resolvers/resolve-opengraph.ts index 0b3077d1f0849..60cd4b0200272 100644 --- a/packages/next/src/lib/metadata/resolvers/resolve-opengraph.ts +++ b/packages/next/src/lib/metadata/resolvers/resolve-opengraph.ts @@ -115,7 +115,7 @@ export const resolveOpenGraph: FieldResolverExtraArgs< } const imageMetadataBase = getSocialImageFallbackMetadataBase(metadataBase) - resolved.images = resolveImages(og.images, imageMetadataBase) + target.images = resolveImages(og.images, imageMetadataBase) } const resolved = {