Skip to content

Commit

Permalink
Backport metadata fixes (#62663)
Browse files Browse the repository at this point in the history
Backport metadata fixes 
#61898
#62109
#62615

Closes NEXT-2641
  • Loading branch information
huozhi committed Feb 28, 2024
1 parent 4002f4b commit a6946b6
Show file tree
Hide file tree
Showing 30 changed files with 249 additions and 93 deletions.
3 changes: 2 additions & 1 deletion packages/next/src/build/webpack/loaders/next-app-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,9 @@ async function createAppRouteCode({

resolvedPagePath = `next-metadata-route-loader?${stringify({
page,
filePath: resolvedPagePath,
isDynamic: isDynamic ? '1' : '0',
})}!${resolvedPagePath}${`?${WEBPACK_RESOURCE_QUERIES.metadataRoute}`}`
})}!?${WEBPACK_RESOURCE_QUERIES.metadataRoute}`
}

const pathname = new AppPathnameNormalizer().normalize(page)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const cacheHeader = {

type MetadataRouteLoaderOptions = {
page: string
filePath: string
isDynamic: '1' | '0'
}

Expand All @@ -46,7 +47,6 @@ function getContentType(resourcePath: string) {
return 'text/plain'
}

// Strip metadata resource query string from `import.meta.url` to make sure the fs.readFileSync get the right path.
async function getStaticAssetRouteCode(
resourcePath: string,
fileBaseName: string
Expand All @@ -58,6 +58,7 @@ async function getStaticAssetRouteCode(
? cacheHeader.none
: cacheHeader.longCache
const code = `\
/* static asset route */
import { NextResponse } from 'next/server'
const contentType = ${JSON.stringify(getContentType(resourcePath))}
Expand All @@ -82,6 +83,7 @@ export const dynamic = 'force-static'

function getDynamicTextRouteCode(resourcePath: string) {
return `\
/* dynamic asset route */
import { NextResponse } from 'next/server'
import handler from ${JSON.stringify(resourcePath)}
import { resolveRouteData } from 'next/dist/build/webpack/loaders/metadata/resolve-route-data'
Expand All @@ -108,6 +110,7 @@ export async function GET() {
// <metadata-image>/[id]/route.js
function getDynamicImageRouteCode(resourcePath: string) {
return `\
/* dynamic image route */
import { NextResponse } from 'next/server'
import * as userland from ${JSON.stringify(resourcePath)}
Expand Down Expand Up @@ -159,6 +162,7 @@ async function getDynamicSiteMapRouteCode(
page.includes('[__metadata_id__]')
) {
staticGenerationCode = `\
/* dynamic sitemap route */
export async function generateStaticParams() {
const sitemaps = generateSitemaps ? await generateSitemaps() : []
const params = []
Expand Down Expand Up @@ -224,26 +228,25 @@ ${staticGenerationCode}
`
return code
}
// `import.meta.url` is the resource name of the current module.

// When it's static route, it could be favicon.ico, sitemap.xml, robots.txt etc.
// TODO-METADATA: improve the cache control strategy
const nextMetadataRouterLoader: webpack.LoaderDefinitionFunction<MetadataRouteLoaderOptions> =
async function () {
const { resourcePath } = this
const { page, isDynamic } = this.getOptions()
const { name: fileBaseName } = getFilenameAndExtension(resourcePath)
const { page, isDynamic, filePath } = this.getOptions()
const { name: fileBaseName } = getFilenameAndExtension(filePath)

let code = ''
if (isDynamic === '1') {
if (fileBaseName === 'robots' || fileBaseName === 'manifest') {
code = getDynamicTextRouteCode(resourcePath)
code = getDynamicTextRouteCode(filePath)
} else if (fileBaseName === 'sitemap') {
code = await getDynamicSiteMapRouteCode(resourcePath, page, this)
code = await getDynamicSiteMapRouteCode(filePath, page, this)
} else {
code = getDynamicImageRouteCode(resourcePath)
code = getDynamicImageRouteCode(filePath)
}
} else {
code = await getStaticAssetRouteCode(resourcePath, fileBaseName)
code = await getStaticAssetRouteCode(filePath, fileBaseName)
}

return code
Expand Down
1 change: 1 addition & 0 deletions packages/next/src/export/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,7 @@ export async function exportAppImpl(
distDir,
dev: false,
basePath: nextConfig.basePath,
trailingSlash: nextConfig.trailingSlash,
canonicalBase: nextConfig.amp?.canonicalBase || '',
ampSkipValidation: nextConfig.experimental.amp?.skipValidation || false,
ampOptimizerConfig: nextConfig.experimental.amp?.optimizer || undefined,
Expand Down
3 changes: 3 additions & 0 deletions packages/next/src/lib/metadata/metadata.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,19 +39,22 @@ export function createMetadataComponents({
tree,
pathname,
searchParams,
trailingSlash,
getDynamicParamFromSegment,
appUsingSizeAdjustment,
errorType,
}: {
tree: LoaderTree
pathname: string
searchParams: { [key: string]: any }
trailingSlash: boolean
getDynamicParamFromSegment: GetDynamicParamFromSegment
appUsingSizeAdjustment: boolean
errorType?: 'not-found' | 'redirect'
}): [React.ComponentType, React.ComponentType] {
const metadataContext = {
pathname,
trailingSlash,
}

let resolve: (value: Error | undefined) => void | undefined
Expand Down
1 change: 1 addition & 0 deletions packages/next/src/lib/metadata/resolve-metadata.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ function accumulateMetadata(metadataItems: MetadataItems) {
])
return originAccumulateMetadata(fullMetadataItems, {
pathname: '/test',
trailingSlash: false,
})
}

Expand Down
42 changes: 24 additions & 18 deletions packages/next/src/lib/metadata/resolvers/resolve-basics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ import type {
AlternateLinkDescriptor,
ResolvedAlternateURLs,
} from '../types/alternative-urls-types'
import type { Metadata, ResolvedMetadata } from '../types/metadata-interface'
import type {
Metadata,
ResolvedMetadata,
Viewport,
} from '../types/metadata-interface'
import type { ResolvedVerification } from '../types/metadata-types'
import type {
FieldResolver,
Expand All @@ -15,19 +19,21 @@ import { resolveAbsoluteUrlWithPathname } from './resolve-url'
function resolveAlternateUrl(
url: string | URL,
metadataBase: URL | null,
pathname: string
metadataContext: MetadataContext
) {
// If alter native url is an URL instance,
// we treat it as a URL base and resolve with current pathname
if (url instanceof URL) {
url = new URL(pathname, url)
url = new URL(metadataContext.pathname, url)
}
return resolveAbsoluteUrlWithPathname(url, metadataBase, pathname)
return resolveAbsoluteUrlWithPathname(url, metadataBase, metadataContext)
}

export const resolveThemeColor: FieldResolver<'themeColor'> = (themeColor) => {
export const resolveThemeColor: FieldResolver<'themeColor', Viewport> = (
themeColor
) => {
if (!themeColor) return null
const themeColorDescriptors: ResolvedMetadata['themeColor'] = []
const themeColorDescriptors: Viewport['themeColor'] = []

resolveAsArrayOrUndefined(themeColor)?.forEach((descriptor) => {
if (typeof descriptor === 'string')
Expand All @@ -51,7 +57,7 @@ function resolveUrlValuesOfObject(
| null
| undefined,
metadataBase: ResolvedMetadata['metadataBase'],
pathname: string
metadataContext: MetadataContext
): null | Record<string, AlternateLinkDescriptor[]> {
if (!obj) return null

Expand All @@ -60,13 +66,13 @@ function resolveUrlValuesOfObject(
if (typeof value === 'string' || value instanceof URL) {
result[key] = [
{
url: resolveAlternateUrl(value, metadataBase, pathname),
url: resolveAlternateUrl(value, metadataBase, metadataContext),
},
]
} else {
result[key] = []
value?.forEach((item, index) => {
const url = resolveAlternateUrl(item.url, metadataBase, pathname)
const url = resolveAlternateUrl(item.url, metadataBase, metadataContext)
result[key][index] = {
url,
title: item.title,
Expand All @@ -80,7 +86,7 @@ function resolveUrlValuesOfObject(
function resolveCanonicalUrl(
urlOrDescriptor: string | URL | null | AlternateLinkDescriptor | undefined,
metadataBase: URL | null,
pathname: string
metadataContext: MetadataContext
): null | AlternateLinkDescriptor {
if (!urlOrDescriptor) return null

Expand All @@ -91,35 +97,35 @@ function resolveCanonicalUrl(

// Return string url because structureClone can't handle URL instance
return {
url: resolveAlternateUrl(url, metadataBase, pathname),
url: resolveAlternateUrl(url, metadataBase, metadataContext),
}
}

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

const canonical = resolveCanonicalUrl(
alternates.canonical,
metadataBase,
pathname
context
)
const languages = resolveUrlValuesOfObject(
alternates.languages,
metadataBase,
pathname
context
)
const media = resolveUrlValuesOfObject(
alternates.media,
metadataBase,
pathname
context
)
const types = resolveUrlValuesOfObject(
alternates.types,
metadataBase,
pathname
context
)

const result: ResolvedAlternateURLs = {
Expand Down Expand Up @@ -236,12 +242,12 @@ export const resolveAppLinks: FieldResolver<'appLinks'> = (appLinks) => {
export const resolveItunes: FieldResolverExtraArgs<
'itunes',
[ResolvedMetadata['metadataBase'], MetadataContext]
> = (itunes, metadataBase, { pathname }) => {
> = (itunes, metadataBase, context) => {
if (!itunes) return null
return {
appId: itunes.appId,
appArgument: itunes.appArgument
? resolveAlternateUrl(itunes.appArgument, metadataBase, pathname)
? resolveAlternateUrl(itunes.appArgument, metadataBase, context)
: undefined,
}
}
Loading

1 comment on commit a6946b6

@AkmaludinFitra
Copy link

Choose a reason for hiding this comment

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

Commit

Please sign in to comment.