Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prefer to use deployment url for metadata routes on production #48556

Merged
merged 3 commits into from
Apr 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 1 addition & 6 deletions packages/next/src/lib/metadata/default-metadata.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
import type { ResolvedMetadata } from './types/metadata-interface'

export function createDefaultMetadata(): ResolvedMetadata {
const defaultMetadataBase =
process.env.NODE_ENV === 'production' && process.env.VERCEL_URL
? new URL(`https://${process.env.VERCEL_URL}`)
: null

return {
viewport: 'width=device-width, initial-scale=1',
metadataBase: defaultMetadataBase,
metadataBase: null,

// Other values are all null
title: null,
Expand Down
12 changes: 9 additions & 3 deletions packages/next/src/lib/metadata/resolvers/resolve-opengraph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ import type {
import type { FieldResolverWithMetadataBase } from '../types/resolvers'
import type { ResolvedTwitterMetadata, Twitter } from '../types/twitter-types'
import { resolveAsArrayOrUndefined } from '../generate/utils'
import { isStringOrURL, resolveUrl } from './resolve-url'
import {
getFallbackMetadataBaseIfPresent,
isStringOrURL,
resolveUrl,
} from './resolve-url'

const OgTypeFields = {
article: ['authors', 'tags'],
Expand Down Expand Up @@ -97,7 +101,8 @@ export const resolveOpenGraph: FieldResolverWithMetadataBase<'openGraph'> = (
}
}

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

assignProps(openGraph)
Expand Down Expand Up @@ -127,7 +132,8 @@ export const resolveTwitter: FieldResolverWithMetadataBase<'twitter'> = (
for (const infoKey of TwitterBasicInfoKeys) {
resolved[infoKey] = twitter[infoKey] || null
}
resolved.images = resolveImages(twitter.images, metadataBase)
const imageMetadataBase = getFallbackMetadataBaseIfPresent(metadataBase)
resolved.images = resolveImages(twitter.images, imageMetadataBase)

if ('card' in resolved) {
switch (resolved.card) {
Expand Down
21 changes: 20 additions & 1 deletion packages/next/src/lib/metadata/resolvers/resolve-url.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,25 @@ function isStringOrURL(icon: any): icon is string | URL {
return typeof icon === 'string' || icon instanceof URL
}

function createLocalMetadataBase() {
return new URL(`http://localhost:${process.env.PORT || 3000}`)
}

// For deployment url for metadata routes, prefer to use the deployment url if possible
// as these routes are unique to the deployments url.
export function getFallbackMetadataBaseIfPresent(
metadataBase: URL | null
): URL | null {
const defaultMetadataBase = createLocalMetadataBase()
return process.env.NODE_ENV === 'production' &&
process.env.VERCEL_URL &&
process.env.VERCEL_ENV === 'preview'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about production deployments? If I have metadataBase missing, it will be VERCEL_URL on preview deployments BUT http://localhost:3000 on production deployments? 😨

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For prod case we should pick metadataBase but fallback to VERCEL_URL 🤔 going to fix it in another PR

? new URL(`https://${process.env.VERCEL_URL}`)
: process.env.NODE_ENV === 'development'
? defaultMetadataBase
: metadataBase || defaultMetadataBase
}

function resolveUrl(url: null | undefined, metadataBase: URL | null): null
function resolveUrl(url: string | URL, metadataBase: URL | null): URL
function resolveUrl(
Expand All @@ -24,7 +43,7 @@ function resolveUrl(
} catch (_) {}

if (!metadataBase) {
metadataBase = new URL(`http://localhost:${process.env.PORT || 3000}`)
metadataBase = createLocalMetadataBase()
}

// Handle relative or absolute paths
Expand Down
7 changes: 4 additions & 3 deletions test/e2e/app-dir/metadata-dynamic-routes/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@ export default function Layout({ children }) {
}

export const metadata = {
metadataBase: process.env.VERCEL_URL
? new URL(`https://${process.env.VERCEL_URL}`)
: new URL(`http://localhost:${process.env.PORT || 3000}`),
metadataBase: new URL('https://mydomain.com'),
title: 'Next.js App',
description: 'This is a Next.js App',
twitter: {
cardType: 'summary_large_image',
title: 'Twitter - Next.js App',
description: 'Twitter - This is a Next.js App',
},
alternates: {
canonical: './',
},
}
31 changes: 20 additions & 11 deletions test/e2e/app-dir/metadata-dynamic-routes/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,12 @@ createNextDescribe(
expect(resTwitter.status).toBe(200)
})

it('should pick configured metadataBase instead of deployment url for canonical url', async () => {
const $ = await next.render$('/')
const canonicalUrl = $('link[rel="canonical"]').attr('href')
expect(canonicalUrl).toBe('https://mydomain.com/')
})

it('should inject dynamic metadata properly to head', async () => {
const $ = await next.render$('/')
const $icon = $('link[rel="icon"]')
Expand Down Expand Up @@ -269,21 +275,24 @@ createNextDescribe(
expect(twitterTitle).toBe('Twitter - Next.js App')
expect(twitterDescription).toBe('Twitter - This is a Next.js App')

// Should prefer to pick up deployment url for metadata routes
let ogImageUrlPattern
let twitterImageUrlPattern
if (isNextDeploy) {
// absolute urls
expect(ogImageUrl).toMatch(
/https:\/\/\w+.vercel.app\/opengraph-image\?/
)
expect(twitterImageUrl).toMatch(
/https:\/\/\w+.vercel.app\/twitter-image\?/
)
ogImageUrlPattern = /https:\/\/\w+.vercel.app\/opengraph-image\?/
twitterImageUrlPattern = /https:\/\/\w+.vercel.app\/twitter-image\?/
} else if (isNextStart) {
// configured metadataBase for next start
ogImageUrlPattern = /https:\/\/mydomain.com\/opengraph-image\?/
twitterImageUrlPattern = /https:\/\/mydomain.com\/twitter-image\?/
} else {
// absolute urls
expect(ogImageUrl).toMatch(/http:\/\/localhost:\d+\/opengraph-image\?/)
expect(twitterImageUrl).toMatch(
/http:\/\/localhost:\d+\/twitter-image\?/
)
// localhost for dev
ogImageUrlPattern = /http:\/\/localhost:\d+\/opengraph-image\?/
twitterImageUrlPattern = /http:\/\/localhost:\d+\/twitter-image\?/
}
expect(ogImageUrl).toMatch(ogImageUrlPattern)
expect(twitterImageUrl).toMatch(twitterImageUrlPattern)
expect(ogImageUrl).toMatch(hashRegex)
expect(twitterImageUrl).toMatch(hashRegex)

Expand Down
14 changes: 10 additions & 4 deletions test/e2e/app-dir/metadata/metadata.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -487,13 +487,19 @@ createNextDescribe(
'og:image:height': '114',
'og:image:type': 'image/png',
'og:image:alt': 'A alt txt for og',
'og:image':
'https://example.com/opengraph/static/opengraph-image.png?b76e8f0282c93c8e',
'og:image': isNextDev
? expect.stringMatching(
/http:\/\/localhost:\d+\/opengraph\/static\/opengraph-image.png\?b76e8f0282c93c8e/
)
: 'https://example.com/opengraph/static/opengraph-image.png?b76e8f0282c93c8e',
})

await match('meta', 'name', 'content', {
'twitter:image':
'https://example.com/opengraph/static/twitter-image.png?b76e8f0282c93c8e',
'twitter:image': isNextDev
? expect.stringMatching(
/http:\/\/localhost:\d+\/opengraph\/static\/twitter-image.png\?b76e8f0282c93c8e/
)
: 'https://example.com/opengraph/static/twitter-image.png?b76e8f0282c93c8e',
'twitter:image:alt': 'A alt txt for twitter',
'twitter:card': 'summary_large_image',
})
Expand Down