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

Auto collect custom manifest into metadata #48310

Merged
merged 3 commits into from Apr 13, 2023
Merged
Show file tree
Hide file tree
Changes from 2 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
41 changes: 34 additions & 7 deletions packages/next/src/build/webpack/loaders/metadata/discover.ts
@@ -1,7 +1,7 @@
import type webpack from 'webpack'
import type {
CollectingMetadata,
PossibleImageFileNameConvention,
PossibleStaticMetadataFileNameConvention,
} from './types'
import path from 'path'
import { stringify } from 'querystring'
Expand All @@ -20,17 +20,22 @@ async function enumMetadataFiles(
{
resolvePath,
loaderContext,
// When set to true, possible filename without extension could: icon, icon0, ..., icon9
numericSuffix,
}: {
resolvePath: (pathname: string) => Promise<string>
loaderContext: webpack.LoaderContext<any>
numericSuffix: boolean
}
) {
const collectedFiles: string[] = []
// Possible filename without extension could: icon, icon0, ..., icon9

const possibleFileNames = [filename].concat(
Array(10)
.fill(0)
.map((_, index) => filename + index)
numericSuffix
? Array(10)
.fill(0)
.map((_, index) => filename + index)
: []
)
for (const name of possibleFileNames) {
for (const ext of extensions) {
Expand Down Expand Up @@ -74,6 +79,7 @@ export async function createStaticMetadataFromRoute(
apple: [],
twitter: [],
openGraph: [],
manifest: undefined,
}

const opts = {
Expand All @@ -82,16 +88,35 @@ export async function createStaticMetadataFromRoute(
}

async function collectIconModuleIfExists(
type: PossibleImageFileNameConvention
type: PossibleStaticMetadataFileNameConvention
) {
if (type === 'manifest') {
const staticManifestExtension = ['webmanifest', 'json']
const manifestFile = await enumMetadataFiles(
resolvedDir,
'manifest',
staticManifestExtension.concat(pageExtensions),
{ ...opts, numericSuffix: false }
)
if (manifestFile.length > 0) {
hasStaticMetadataFiles = true
const { name, ext } = path.parse(manifestFile[0])
const extension = staticManifestExtension.includes(ext.slice(1))
? ext.slice(1)
: 'webmanifest'
staticImagesMetadata.manifest = JSON.stringify(`/${name}.${extension}`)
}
return
}

const resolvedMetadataFiles = await enumMetadataFiles(
resolvedDir,
STATIC_METADATA_IMAGES[type].filename,
[
...STATIC_METADATA_IMAGES[type].extensions,
...(type === 'favicon' ? [] : pageExtensions),
],
opts
{ ...opts, numericSuffix: true }
)
resolvedMetadataFiles
.sort((a, b) => a.localeCompare(b))
Expand Down Expand Up @@ -123,6 +148,7 @@ export async function createStaticMetadataFromRoute(
collectIconModuleIfExists('openGraph'),
collectIconModuleIfExists('twitter'),
isRootLayer && collectIconModuleIfExists('favicon'),
isRootLayer && collectIconModuleIfExists('manifest'),
])

return hasStaticMetadataFiles ? staticImagesMetadata : null
Expand All @@ -137,6 +163,7 @@ export function createMetadataExportsCode(
apple: [${metadata.apple.join(',')}],
openGraph: [${metadata.openGraph.join(',')}],
twitter: [${metadata.twitter.join(',')}],
manifest: ${metadata.manifest ? metadata.manifest : 'undefined'}
}`
: ''
}
6 changes: 6 additions & 0 deletions packages/next/src/build/webpack/loaders/metadata/types.ts
Expand Up @@ -11,6 +11,7 @@ export type CollectingMetadata = {
apple: string[]
twitter: string[]
openGraph: string[]
manifest?: string
}

// Contain the collecting evaluated image module
Expand All @@ -19,6 +20,7 @@ export type CollectedMetadata = {
apple: ComponentModule[]
twitter: ComponentModule[] | null
openGraph: ComponentModule[] | null
manifest?: string
}

export type MetadataImageModule = {
Expand All @@ -39,3 +41,7 @@ export type PossibleImageFileNameConvention =
| 'favicon'
| 'twitter'
| 'openGraph'

export type PossibleStaticMetadataFileNameConvention =
| PossibleImageFileNameConvention
| 'manifest'
6 changes: 5 additions & 1 deletion packages/next/src/lib/metadata/resolve-metadata.ts
Expand Up @@ -45,7 +45,7 @@ function mergeStaticMetadata(
staticFilesMetadata: StaticMetadata
) {
if (!staticFilesMetadata) return
const { icon, apple, openGraph, twitter } = staticFilesMetadata
const { icon, apple, openGraph, twitter, manifest } = staticFilesMetadata
if (icon || apple) {
metadata.icons = {
icon: icon || [],
Expand All @@ -67,6 +67,9 @@ function mergeStaticMetadata(
)
metadata.openGraph = resolvedOpenGraph
}
if (manifest) {
metadata.manifest = manifest
}

return metadata
}
Expand Down Expand Up @@ -249,6 +252,7 @@ async function resolveStaticMetadata(components: ComponentsType, props: any) {
apple,
openGraph,
twitter,
manifest: metadata.manifest,
}

return staticMetadata
Expand Down
5 changes: 5 additions & 0 deletions test/e2e/app-dir/metadata-dynamic-routes/index.test.ts
Expand Up @@ -201,6 +201,11 @@ createNextDescribe(

expect($('link[rel="favicon"]')).toHaveLength(0)

// manifest
expect($('link[rel="manifest"]').attr('href')).toBe(
'/manifest.webmanifest'
)

// non absolute urls
expect($icon.attr('href')).toContain('/icon')
expect($icon.attr('href')).toMatch(hashRegex)
Expand Down