Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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