From 41e2613aca1614dd74c4d7a318ad18bb18b036ac Mon Sep 17 00:00:00 2001 From: Jiachi Liu Date: Mon, 30 Jan 2023 21:49:30 +0100 Subject: [PATCH] Update robots meta and add verification rendering (#45409) Follow up for: #45237 * Adding more fields for robots meta, most related to googlebot * Add `me` and `yandex` field and render for verification field --- .../next/src/lib/metadata/generate/basic.tsx | 34 +++++++++-- .../next/src/lib/metadata/generate/meta.tsx | 1 + packages/next/src/lib/metadata/metadata.tsx | 2 + .../next/src/lib/metadata/resolve-metadata.ts | 57 +++++++++++++------ .../src/lib/metadata/types/metadata-types.ts | 11 ++++ test/e2e/app-dir/metadata/app/robots/page.tsx | 4 ++ .../metadata/app/verification/page.tsx | 14 +++++ test/e2e/app-dir/metadata/metadata.test.ts | 19 ++++++- 8 files changed, 119 insertions(+), 23 deletions(-) create mode 100644 test/e2e/app-dir/metadata/app/verification/page.tsx diff --git a/packages/next/src/lib/metadata/generate/basic.tsx b/packages/next/src/lib/metadata/generate/basic.tsx index b64e6a5e74..d2b799ccc8 100644 --- a/packages/next/src/lib/metadata/generate/basic.tsx +++ b/packages/next/src/lib/metadata/generate/basic.tsx @@ -1,7 +1,7 @@ import type { ResolvedMetadata } from '../types/metadata-interface' import React from 'react' -import { Meta } from './meta' +import { Meta, MultiMeta } from './meta' export function BasicMetadata({ metadata }: { metadata: ResolvedMetadata }) { return ( @@ -100,9 +100,7 @@ export function AppleWebAppMeta({ {capable ? ( ) : null} - {title ? ( - - ) : null} + {startupImage ? startupImage.map((image, index) => ( ) } + +export function VerificationMeta({ + verification, +}: { + verification: ResolvedMetadata['verification'] +}) { + if (!verification) return null + + return ( + <> + + + + + {verification.other + ? Object.entries(verification.other).map(([key, value], index) => ( + + )) + : null} + + ) +} diff --git a/packages/next/src/lib/metadata/generate/meta.tsx b/packages/next/src/lib/metadata/generate/meta.tsx index 90a6fb00f3..a0e6a12677 100644 --- a/packages/next/src/lib/metadata/generate/meta.tsx +++ b/packages/next/src/lib/metadata/generate/meta.tsx @@ -91,6 +91,7 @@ export function MultiMeta({ } else { return ( + diff --git a/packages/next/src/lib/metadata/resolve-metadata.ts b/packages/next/src/lib/metadata/resolve-metadata.ts index 3163435efc..e3f77f4664 100644 --- a/packages/next/src/lib/metadata/resolve-metadata.ts +++ b/packages/next/src/lib/metadata/resolve-metadata.ts @@ -10,6 +10,7 @@ import type { Icon, IconDescriptor, Icons, + ResolvedVerification, } from './types/metadata-types' import { createDefaultMetadata } from './default-metadata' import { resolveOpenGraph } from './resolve-opengraph' @@ -76,22 +77,26 @@ const resolveViewport: FieldResolver<'viewport'> = (viewport) => { return resolved } +const VerificationKeys = ['google', 'yahoo', 'yandex', 'me', 'other'] as const const resolveVerification: FieldResolver<'verification'> = (verification) => { - const google = resolveAsArrayOrUndefined(verification?.google) - const yahoo = resolveAsArrayOrUndefined(verification?.yahoo) - let other: Record | undefined - if (verification?.other) { - other = {} - for (const key in verification.other) { - const value = resolveAsArrayOrUndefined(verification.other[key]) - if (value) other[key] = value + if (!verification) return null + const res = {} as ResolvedVerification + + for (const key of VerificationKeys) { + const value = verification[key] + if (value) { + if (key === 'other') { + res.other = {} + for (const otherKey in verification.other) { + const otherValue = resolveAsArrayOrUndefined( + verification.other[otherKey] + ) + if (otherValue) res.other[otherKey] = otherValue + } + } else res[key] = resolveAsArrayOrUndefined(value) as (string | number)[] } } - return { - google, - yahoo, - other, - } + return res } function isStringOrURL(icon: any): icon is string | URL { @@ -203,23 +208,39 @@ const resolveAppLinks: FieldResolver<'appLinks'> = (appLinks) => { return appLinks as ResolvedMetadata['appLinks'] } +const robotsKeys = [ + 'noarchive', + 'nosnippet', + 'noimageindex', + 'nocache', + 'notranslate', + 'indexifembedded', + 'nositelinkssearchbox', + 'unavailable_after', + 'max-video-preview', + 'max-image-preview', + 'max-snippet', +] as const const resolveRobotsValue: (robots: Metadata['robots']) => string | null = ( robots ) => { if (!robots) return null if (typeof robots === 'string') return robots - const values = [] + const values: string[] = [] if (robots.index) values.push('index') else if (typeof robots.index === 'boolean') values.push('noindex') if (robots.follow) values.push('follow') else if (typeof robots.follow === 'boolean') values.push('nofollow') - if (robots.noarchive) values.push('noarchive') - if (robots.nosnippet) values.push('nosnippet') - if (robots.noimageindex) values.push('noimageindex') - if (robots.nocache) values.push('nocache') + + for (const key of robotsKeys) { + const value = robots[key] + if (typeof value !== 'undefined' && value !== false) { + values.push(typeof value === 'boolean' ? key : `${key}:${value}`) + } + } return values.join(', ') } diff --git a/packages/next/src/lib/metadata/types/metadata-types.ts b/packages/next/src/lib/metadata/types/metadata-types.ts index b098018f0a..71fc3f2f72 100644 --- a/packages/next/src/lib/metadata/types/metadata-types.ts +++ b/packages/next/src/lib/metadata/types/metadata-types.ts @@ -59,6 +59,13 @@ type RobotsInfo = { nosnippet?: boolean noimageindex?: boolean nocache?: boolean + notranslate?: boolean + indexifembedded?: boolean + nositelinkssearchbox?: boolean + unavailable_after?: string + 'max-video-preview'?: number | string + 'max-image-preview'?: 'none' | 'standard' | 'large' + 'max-snippet'?: number } export type Robots = RobotsInfo & { // if you want to specify an alternate robots just for google @@ -93,6 +100,8 @@ export type Icons = { export type Verification = { google?: null | string | number | (string | number)[] yahoo?: null | string | number | (string | number)[] + yandex?: null | string | number | (string | number)[] + me?: null | string | number | (string | number)[] // if you ad-hoc additional verification other?: { [name: string]: string | number | (string | number)[] @@ -102,6 +111,8 @@ export type Verification = { export type ResolvedVerification = { google?: null | (string | number)[] yahoo?: null | (string | number)[] + yandex?: null | (string | number)[] + me?: null | (string | number)[] other?: { [name: string]: (string | number)[] } diff --git a/test/e2e/app-dir/metadata/app/robots/page.tsx b/test/e2e/app-dir/metadata/app/robots/page.tsx index 14170a7322..00ccf80b85 100644 --- a/test/e2e/app-dir/metadata/app/robots/page.tsx +++ b/test/e2e/app-dir/metadata/app/robots/page.tsx @@ -12,6 +12,10 @@ export const metadata = { index: true, follow: false, noimageindex: true, + + 'max-video-preview': 'standard', + 'max-image-preview': -1, + 'max-snippet': -1, }, }, } diff --git a/test/e2e/app-dir/metadata/app/verification/page.tsx b/test/e2e/app-dir/metadata/app/verification/page.tsx new file mode 100644 index 0000000000..3a39a2e97f --- /dev/null +++ b/test/e2e/app-dir/metadata/app/verification/page.tsx @@ -0,0 +1,14 @@ +export default function page() { + return 'verification' +} + +export const metadata = { + verification: { + google: 'google', + yandex: 'yandex', + yahoo: 'yahoo', + other: { + me: ['my-email', 'my-link'], + }, + }, +} diff --git a/test/e2e/app-dir/metadata/metadata.test.ts b/test/e2e/app-dir/metadata/metadata.test.ts index 716f6e6ef7..1106fdfe73 100644 --- a/test/e2e/app-dir/metadata/metadata.test.ts +++ b/test/e2e/app-dir/metadata/metadata.test.ts @@ -235,10 +235,27 @@ createNextDescribe( await checkMetaNameContentPair( browser, 'googlebot', - 'index, nofollow, noimageindex' + 'index, nofollow, noimageindex, max-video-preview:standard, max-image-preview:-1, max-snippet:-1' ) }) + it('should support verification tags', async () => { + const browser = await next.browser('/verification') + + await checkMetaNameContentPair( + browser, + 'google-site-verification', + 'google' + ) + await checkMetaNameContentPair(browser, 'y_key', 'yahoo') + await checkMetaNameContentPair( + browser, + 'yandex-verification', + 'yandex' + ) + await checkMetaNameContentPair(browser, 'me', ['my-email', 'my-link']) + }) + it('should support appLinks tags', async () => { const browser = await next.browser('/app-links') await checkMetaPropertyContentPair(