Skip to content

Commit

Permalink
Revert "Revert "Initial support for metadata (#44729)" (#45111)"
Browse files Browse the repository at this point in the history
This reverts commit e0865b1.
  • Loading branch information
ijjk committed Jan 20, 2023
1 parent 6294e6a commit 5cd641f
Show file tree
Hide file tree
Showing 37 changed files with 2,318 additions and 45 deletions.
83 changes: 68 additions & 15 deletions packages/next/src/build/webpack/loaders/next-app-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { sep } from 'path'
import { verifyRootLayout } from '../../../lib/verifyRootLayout'
import * as Log from '../../../build/output/log'
import { APP_DIR_ALIAS } from '../../../lib/constants'
import { resolveFileBasedMetadataForLoader } from '../../../lib/metadata/resolve-metadata'

const FILE_TYPES = {
layout: 'layout',
Expand Down Expand Up @@ -36,21 +37,26 @@ async function createTreeCodeFromPath({
resolveParallelSegments,
}: {
pagePath: string
resolve: (pathname: string) => Promise<string | undefined>
resolve: (
pathname: string,
resolveDir?: boolean
) => Promise<string | undefined>
resolveParallelSegments: (
pathname: string
) => [key: string, segment: string][]
}) {
const splittedPath = pagePath.split(/[\\/]/)
const appDirPrefix = splittedPath[0]
const pages: string[] = []

let rootLayout: string | undefined
let globalError: string | undefined

async function createSubtreePropsFromSegmentPath(
segments: string[]
): Promise<{
treeCode: string
treeMetadataCode: string
}> {
const segmentPath = segments.join('/')

Expand All @@ -65,12 +71,26 @@ async function createTreeCodeFromPath({
parallelSegments.push(...resolveParallelSegments(segmentPath))
}

let metadataCode = ''

for (const [parallelKey, parallelSegment] of parallelSegments) {
if (parallelSegment === PAGE_SEGMENT) {
const matchedPagePath = `${appDirPrefix}${segmentPath}/page`
const resolvedPagePath = await resolve(matchedPagePath)
if (resolvedPagePath) pages.push(resolvedPagePath)

metadataCode += `{
type: 'page',
layer: ${
// There's an extra virtual segment.
segments.length - 1
},
mod: () => import(/* webpackMode: "eager" */ ${JSON.stringify(
resolvedPagePath
)}),
path: ${JSON.stringify(resolvedPagePath)},
},`

// Use '' for segment as it's the page. There can't be a segment called '' so this is the safest way to add it.
props[parallelKey] = `['', {}, {
page: [() => import(/* webpackMode: "eager" */ ${JSON.stringify(
Expand All @@ -80,9 +100,8 @@ async function createTreeCodeFromPath({
}

const parallelSegmentPath = segmentPath + '/' + parallelSegment
const { treeCode: subtreeCode } = await createSubtreePropsFromSegmentPath(
[...segments, parallelSegment]
)
const { treeCode: subtreeCode, treeMetadataCode: subTreeMetadataCode } =
await createSubtreePropsFromSegmentPath([...segments, parallelSegment])

// `page` is not included here as it's added above.
const filePaths = await Promise.all(
Expand All @@ -101,6 +120,27 @@ async function createTreeCodeFromPath({
rootLayout = layoutPath
}

// Collect metadata for the layout
if (layoutPath) {
metadataCode += `{
type: 'layout',
layer: ${segments.length},
mod: () => import(/* webpackMode: "eager" */ ${JSON.stringify(
layoutPath
)}),
path: ${JSON.stringify(layoutPath)},
},`
}
metadataCode += await resolveFileBasedMetadataForLoader(
segments.length,
(await resolve(`${appDirPrefix}${parallelSegmentPath}/`, true))!
)
metadataCode += subTreeMetadataCode

if (!rootLayout) {
rootLayout = layoutPath
}

if (!globalError) {
globalError = await resolve(
`${appDirPrefix}${parallelSegmentPath}/${GLOBAL_ERROR_FILE_TYPE}`
Expand Down Expand Up @@ -133,13 +173,16 @@ async function createTreeCodeFromPath({
.map(([key, value]) => `${key}: ${value}`)
.join(',\n')}
}`,
treeMetadataCode: metadataCode,
}
}

const { treeCode } = await createSubtreePropsFromSegmentPath([])
const { treeCode, treeMetadataCode } =
await createSubtreePropsFromSegmentPath([])
return {
treeCode: `const tree = ${treeCode}.children;`,
pages,
treeMetadataCode: `const metadata = [${treeMetadataCode}];`,
pages: `const pages = ${JSON.stringify(pages)};`,
rootLayout,
globalError,
}
Expand Down Expand Up @@ -197,7 +240,7 @@ const nextAppLoader: webpack.LoaderDefinitionFunction<{
const rest = path.slice(pathname.length + 1).split('/')

let matchedSegment = rest[0]
// It is the actual page, mark it sepcially.
// It is the actual page, mark it specially.
if (rest.length === 1 && matchedSegment === 'page') {
matchedSegment = PAGE_SEGMENT
}
Expand All @@ -212,7 +255,11 @@ const nextAppLoader: webpack.LoaderDefinitionFunction<{
return Object.entries(matched)
}

const resolver = async (pathname: string) => {
const resolver = async (pathname: string, resolveDir?: boolean) => {
if (resolveDir) {
return createAbsolutePath(appDir, pathname)
}

try {
const resolved = await resolve(this.rootContext, pathname)
this.addDependency(resolved)
Expand All @@ -230,12 +277,17 @@ const nextAppLoader: webpack.LoaderDefinitionFunction<{
}
}

const { treeCode, pages, rootLayout, globalError } =
await createTreeCodeFromPath({
pagePath,
resolve: resolver,
resolveParallelSegments,
})
const {
treeCode,
treeMetadataCode,
pages: pageListCode,
rootLayout,
globalError,
} = await createTreeCodeFromPath({
pagePath,
resolve: resolver,
resolveParallelSegments,
})

if (!rootLayout) {
const errorMessage = `${chalk.bold(
Expand Down Expand Up @@ -263,7 +315,8 @@ const nextAppLoader: webpack.LoaderDefinitionFunction<{

const result = `
export ${treeCode}
export const pages = ${JSON.stringify(pages)}
export ${treeMetadataCode}
export ${pageListCode}
export { default as AppRouter } from 'next/dist/client/components/app-router'
export { default as LayoutRouter } from 'next/dist/client/components/layout-router'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ interface IEntry {
? "runtime?: 'nodejs' | 'experimental-edge' | 'edge'"
: ''
}
metadata?: any
}
// =============
Expand Down
10 changes: 0 additions & 10 deletions packages/next/src/client/components/head.tsx

This file was deleted.

41 changes: 41 additions & 0 deletions packages/next/src/lib/metadata/default-metadata.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import type { ResolvedMetadata } from './types/metadata-interface'

export const createDefaultMetadata = (): ResolvedMetadata => {
return {
viewport: 'width=device-width, initial-scale=1',

// Other values are all null
metadataBase: null,
title: null,
description: null,
applicationName: null,
authors: null,
generator: null,
keywords: null,
referrer: null,
themeColor: null,
colorScheme: null,
creator: null,
publisher: null,
robots: null,
alternates: {
canonical: null,
languages: {},
},
icons: null,
openGraph: null,
twitter: null,
verification: {},
appleWebApp: null,
formatDetection: null,
itunes: null,
abstract: null,
appLinks: null,
archives: null,
assets: null,
bookmarks: null,
category: null,
classification: null,
other: {},
}
}
51 changes: 51 additions & 0 deletions packages/next/src/lib/metadata/generate/alternate.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import type { ResolvedMetadata } from '../types/metadata-interface'

import React from 'react'

export function ResolvedAlternatesMetadata({
metadata,
}: {
metadata: ResolvedMetadata
}) {
return (
<>
{metadata.alternates.canonical ? (
<link rel="canonical" href={metadata.alternates.canonical.toString()} />
) : null}
{Object.entries(metadata.alternates.languages).map(([locale, url]) =>
url ? (
<link
key={locale}
rel="alternate"
hrefLang={locale}
href={url.toString()}
/>
) : null
)}
{metadata.alternates.media
? Object.entries(metadata.alternates.media).map(([media, url]) =>
url ? (
<link
key={media}
rel="alternate"
media={media}
href={url.toString()}
/>
) : null
)
: null}
{metadata.alternates.types
? Object.entries(metadata.alternates.types).map(([type, url]) =>
url ? (
<link
key={type}
rel="alternate"
type={type}
href={url.toString()}
/>
) : null
)
: null}
</>
)
}
56 changes: 56 additions & 0 deletions packages/next/src/lib/metadata/generate/basic.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import type { ResolvedMetadata } from '../types/metadata-interface'

import React from 'react'
import { Meta } from './utils'

export function ResolvedBasicMetadata({
metadata,
}: {
metadata: ResolvedMetadata
}) {
return (
<>
<meta charSet="utf-8" />
{metadata.title !== null ? (
<title>{metadata.title.absolute}</title>
) : null}
<Meta name="description" content={metadata.description} />
<Meta name="application-name" content={metadata.applicationName} />
<Meta name="author" content={metadata.authors?.join(',')} />
<Meta name="generator" content={metadata.generator} />
<Meta name="keywords" content={metadata.keywords?.join(',')} />
<Meta name="referrer" content={metadata.referrer} />
<Meta name="theme-color" content={metadata.themeColor} />
<Meta name="color-scheme" content={metadata.colorScheme} />
<Meta name="viewport" content={metadata.viewport} />
<Meta name="creator" content={metadata.creator} />
<Meta name="publisher" content={metadata.publisher} />
<Meta name="robots" content={metadata.robots} />
<Meta name="abstract" content={metadata.abstract} />
{metadata.archives
? metadata.archives.map((archive) => (
<link rel="archives" href={archive} key={archive} />
))
: null}
{metadata.assets
? metadata.assets.map((asset) => (
<link rel="assets" href={asset} key={asset} />
))
: null}
{metadata.bookmarks
? metadata.bookmarks.map((bookmark) => (
<link rel="bookmarks" href={bookmark} key={bookmark} />
))
: null}
<Meta name="category" content={metadata.category} />
<Meta name="classification" content={metadata.classification} />
{Object.entries(metadata.other).map(([name, content]) => (
<Meta
key={name}
name={name}
content={Array.isArray(content) ? content.join(',') : content}
/>
))}
</>
)
}

0 comments on commit 5cd641f

Please sign in to comment.