rsnext/packages/next/build/webpack/plugins/font-loader-manifest-plugin.ts
Hannes Bornö bf8ee1edb4
Add support for font loaders (#40746)
For some context:
[https://vercel.slack.com/archives/CGU8HUTUH/p1662124179102509](https://vercel.slack.com/archives/CGU8HUTUH/p1662124179102509)

Continuation of #40221 and #40227

Adds `experimental.fontLoaders`.

SWC next-font-loaders (#40221) transforms font loader (e.g. #40227) call
expressions into an import with the function call arguments as a query.

The imports will be matched by `next-font-loader`. It runs the
configured font loaders - emits font files and returns CSS. Exports are
added, and the font-family is made locally scoped. The returned CSS is
turned into a CSS module with `css-loader` which lets you consume the
font-family.

`FontLoaderManifestPlugin` creates a manifest of the preloaded font
files for each entrypoint. Preload/preconnect are then added in
`_document.tsx` if any font files were found for that path.

Co-authored-by: JJ Kasper <jj@jjsweb.site>
2022-09-21 22:12:59 -07:00

62 lines
2.1 KiB
TypeScript

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[]
}
}
const PLUGIN_NAME = 'FontLoaderManifestPlugin'
// Creates a manifest of all fonts that should be preloaded given a route
export class FontLoaderManifestPlugin {
apply(compiler: webpack.Compiler) {
compiler.hooks.make.tap(PLUGIN_NAME, (compilation) => {
compilation.hooks.processAssets.tap(
{
name: PLUGIN_NAME,
stage: webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONS,
},
(assets: any) => {
const fontLoaderManifest: FontLoaderManifest = {
pages: {},
}
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)
)
// 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
}
}