forked from vercel/next.js
-
Notifications
You must be signed in to change notification settings - Fork 0
/
discover.ts
157 lines (144 loc) · 3.93 KB
/
discover.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
import type webpack from 'webpack'
import type {
CollectingMetadata,
PossibleImageFileNameConvention,
} from './types'
import path from 'path'
import { stringify } from 'querystring'
const METADATA_TYPE = 'metadata'
export const METADATA_RESOURCE_QUERY = '?__next_metadata'
export const STATIC_METADATA_IMAGES = {
icon: {
filename: 'icon',
extensions: ['ico', 'jpg', 'jpeg', 'png', 'svg'],
},
apple: {
filename: 'apple-icon',
extensions: ['jpg', 'jpeg', 'png'],
},
favicon: {
filename: 'favicon',
extensions: ['ico'],
},
opengraph: {
filename: 'opengraph-image',
extensions: ['jpg', 'jpeg', 'png', 'gif'],
},
twitter: {
filename: 'twitter-image',
extensions: ['jpg', 'jpeg', 'png', 'gif'],
},
} as const
// Produce all compositions with filename (icon, apple-icon, etc.) with extensions (png, jpg, etc.)
async function enumMetadataFiles(
dir: string,
filename: string,
extensions: readonly string[],
{
resolvePath,
loaderContext,
}: {
resolvePath: (pathname: string) => Promise<string>
loaderContext: webpack.LoaderContext<any>
}
) {
const collectedFiles: string[] = []
// Possible filename without extension could: icon, icon0, ..., icon9
const possibleFileNames = [filename].concat(
Array(10)
.fill(0)
.map((_, index) => filename + index)
)
for (const name of possibleFileNames) {
for (const ext of extensions) {
const pathname = path.join(dir, `${name}.${ext}`)
try {
const resolved = await resolvePath(pathname)
loaderContext.addDependency(resolved)
collectedFiles.push(resolved)
} catch (err: any) {
if (!err.message.includes("Can't resolve")) {
throw err
}
loaderContext.addMissingDependency(pathname)
}
}
}
return collectedFiles
}
export async function createStaticMetadataFromRoute(
resolvedDir: string,
{
route,
resolvePath,
isRootLayer,
loaderContext,
}: {
route: string
resolvePath: (pathname: string) => Promise<string>
isRootLayer: boolean
loaderContext: webpack.LoaderContext<any>
}
) {
let hasStaticMetadataFiles = false
const staticImagesMetadata: CollectingMetadata = {
icon: [],
apple: [],
twitter: [],
opengraph: [],
}
const opts = {
resolvePath,
loaderContext,
}
async function collectIconModuleIfExists(
type: PossibleImageFileNameConvention
) {
const resolvedMetadataFiles = await enumMetadataFiles(
resolvedDir,
STATIC_METADATA_IMAGES[type].filename,
STATIC_METADATA_IMAGES[type].extensions,
opts
)
resolvedMetadataFiles
.sort((a, b) => a.localeCompare(b))
.forEach((filepath) => {
const imageModule = `() => import(/* webpackMode: "eager" */ ${JSON.stringify(
`next-metadata-image-loader?${stringify({
route,
numericSizes:
type === 'twitter' || type === 'opengraph' ? '1' : undefined,
type,
})}!` +
filepath +
METADATA_RESOURCE_QUERY
)})`
hasStaticMetadataFiles = true
if (type === 'favicon') {
staticImagesMetadata.icon.unshift(imageModule)
} else {
staticImagesMetadata[type].push(imageModule)
}
})
}
await Promise.all([
collectIconModuleIfExists('icon'),
collectIconModuleIfExists('apple'),
collectIconModuleIfExists('opengraph'),
collectIconModuleIfExists('twitter'),
isRootLayer && collectIconModuleIfExists('favicon'),
])
return hasStaticMetadataFiles ? staticImagesMetadata : null
}
export function createMetadataExportsCode(
metadata: Awaited<ReturnType<typeof createStaticMetadataFromRoute>>
) {
return metadata
? `${METADATA_TYPE}: {
icon: [${metadata.icon.join(',')}],
apple: [${metadata.apple.join(',')}],
opengraph: [${metadata.opengraph.join(',')}],
twitter: [${metadata.twitter.join(',')}],
}`
: ''
}