-
Notifications
You must be signed in to change notification settings - Fork 27.5k
/
Copy pathfont-loader-manifest-plugin.ts
133 lines (115 loc) · 4.62 KB
/
font-loader-manifest-plugin.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
import { webpack, sources } from 'next/dist/compiled/webpack/webpack'
import getRouteFromEntrypoint from '../../../server/get-route-from-entrypoint'
import { FONT_LOADER_MANIFEST } from '../../../shared/lib/constants'
export type FontLoaderManifest = {
pages: {
[path: string]: string[]
}
app: {
[moduleRequest: string]: string[]
}
appUsingSizeAdjust: boolean
pagesUsingSizeAdjust: boolean
}
const PLUGIN_NAME = 'FontLoaderManifestPlugin'
const fontLoaderTargets = [
require.resolve('next/font/google/target.css'),
require.resolve('next/font/local/target.css'),
// TODO: remove this in the next major version
/node_modules\/@next\/font\/google\/target\.css\?{.+}$/,
/node_modules\/@next\/font\/local\/target\.css\?{.+}$/,
]
// Creates a manifest of all fonts that should be preloaded given a route
export class FontLoaderManifestPlugin {
private appDirEnabled: boolean
constructor(options: { appDirEnabled: boolean }) {
this.appDirEnabled = options.appDirEnabled
}
apply(compiler: webpack.Compiler) {
compiler.hooks.make.tap(PLUGIN_NAME, (compilation) => {
let fontLoaderModules: webpack.Module[]
// Get all font loader modules
if (this.appDirEnabled) {
compilation.hooks.finishModules.tap(PLUGIN_NAME, (modules) => {
const modulesArr = Array.from(modules)
fontLoaderModules = modulesArr.filter((mod: any) =>
fontLoaderTargets.some((fontLoaderTarget) =>
typeof fontLoaderTarget === 'string'
? mod.userRequest?.startsWith(`${fontLoaderTarget}?`)
: fontLoaderTarget.test(mod.userRequest)
)
)
})
}
compilation.hooks.processAssets.tap(
{
name: PLUGIN_NAME,
stage: webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONS,
},
(assets: any) => {
const fontLoaderManifest: FontLoaderManifest = {
pages: {},
app: {},
appUsingSizeAdjust: false,
pagesUsingSizeAdjust: false,
}
if (this.appDirEnabled) {
for (const mod of fontLoaderModules) {
if (!mod.buildInfo?.assets) continue
const modAssets = Object.keys(mod.buildInfo.assets)
const fontFiles: string[] = modAssets.filter((file: string) =>
/\.(woff|woff2|eot|ttf|otf)$/.test(file)
)
if (!fontLoaderManifest.appUsingSizeAdjust) {
fontLoaderManifest.appUsingSizeAdjust = fontFiles.some((file) =>
file.includes('-s')
)
}
// Font files ending with .p.(woff|woff2|eot|ttf|otf) are preloaded
const preloadedFontFiles: string[] = fontFiles.filter(
(file: string) => /\.p.(woff|woff2|eot|ttf|otf)$/.test(file)
)
// Create an entry for the request even if no files should preload. If that's the case a preconnect tag is added.
if (fontFiles.length > 0) {
fontLoaderManifest.app[(mod as any).userRequest] =
preloadedFontFiles
}
}
}
for (const entrypoint of compilation.entrypoints.values()) {
const pagePath = getRouteFromEntrypoint(entrypoint.name!)
if (!pagePath) {
continue
}
const fontFiles: string[] = entrypoint.chunks
.flatMap((chunk: any) => [...chunk.auxiliaryFiles])
.filter((file: string) =>
/\.(woff|woff2|eot|ttf|otf)$/.test(file)
)
if (!fontLoaderManifest.pagesUsingSizeAdjust) {
fontLoaderManifest.pagesUsingSizeAdjust = fontFiles.some((file) =>
file.includes('-s')
)
}
// Font files ending with .p.(woff|woff2|eot|ttf|otf) are preloaded
const preloadedFontFiles: string[] = fontFiles.filter(
(file: string) => /\.p.(woff|woff2|eot|ttf|otf)$/.test(file)
)
// Create an entry for the path even if no files should preload. If that's the case a preconnect tag is added.
if (fontFiles.length > 0) {
fontLoaderManifest.pages[pagePath] = preloadedFontFiles
}
}
const manifest = JSON.stringify(fontLoaderManifest, null, 2)
assets[`server/${FONT_LOADER_MANIFEST}.js`] = new sources.RawSource(
`self.__FONT_LOADER_MANIFEST=${manifest}`
)
assets[`server/${FONT_LOADER_MANIFEST}.json`] = new sources.RawSource(
manifest
)
}
)
})
return
}
}