Skip to content

Commit

Permalink
Rename FontLoaderManifest to NextFontManifest and add better comments (
Browse files Browse the repository at this point in the history
…#46922)

Rename FontLoaderManifest to NextFontManifest to make it more obvious
that it has to do with `next/font`.

There's a change to how the plugin determines if a module is generated
by `next/font`. Instead of looking at the target CSS file
https://github.com/vercel/next.js/blob/v13.2.4-canary.6/packages/next/src/build/webpack/plugins/font-loader-manifest-plugin.ts#L37-L49.
It simply looks if the module has `next-font-loader` in its loaders:
https://github.com/vercel/next.js/compare/canary...hanneslund:next.js:next-font-build-cleanup?expand=1#diff-34d7972c68687e875c868847f782f369c3af162a9af10949f3577660775afcb2R65-R74

Also more comments added to the manifest plugin and the `next/font`
postcss plugin.

---------

Co-authored-by: JJ Kasper <jj@jjsweb.site>
  • Loading branch information
hanneslund and ijjk authored Mar 8, 2023
1 parent bae5d6f commit de6a1e3
Show file tree
Hide file tree
Showing 19 changed files with 268 additions and 196 deletions.
6 changes: 3 additions & 3 deletions packages/next/src/build/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ import {
APP_BUILD_MANIFEST,
FLIGHT_SERVER_CSS_MANIFEST,
RSC_MODULE_TYPES,
FONT_LOADER_MANIFEST,
NEXT_FONT_MANIFEST,
SUBRESOURCE_INTEGRITY_MANIFEST,
MIDDLEWARE_BUILD_MANIFEST,
MIDDLEWARE_REACT_LOADABLE_MANIFEST,
Expand Down Expand Up @@ -992,8 +992,8 @@ export default async function build(
: null,
BUILD_ID_FILE,
appDir ? path.join(SERVER_DIRECTORY, APP_PATHS_MANIFEST) : null,
path.join(SERVER_DIRECTORY, FONT_LOADER_MANIFEST + '.js'),
path.join(SERVER_DIRECTORY, FONT_LOADER_MANIFEST + '.json'),
path.join(SERVER_DIRECTORY, NEXT_FONT_MANIFEST + '.js'),
path.join(SERVER_DIRECTORY, NEXT_FONT_MANIFEST + '.json'),
...(hasInstrumentationHook
? [
path.join(
Expand Down
4 changes: 2 additions & 2 deletions packages/next/src/build/webpack-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ import loadJsConfig from './load-jsconfig'
import { loadBindings } from './swc'
import { AppBuildManifestPlugin } from './webpack/plugins/app-build-manifest-plugin'
import { SubresourceIntegrityPlugin } from './webpack/plugins/subresource-integrity-plugin'
import { FontLoaderManifestPlugin } from './webpack/plugins/font-loader-manifest-plugin'
import { NextFontManifestPlugin } from './webpack/plugins/next-font-manifest-plugin'
import { getSupportedBrowsers } from './utils'
import { METADATA_IMAGE_RESOURCE_QUERY } from './webpack/loaders/metadata/discover'

Expand Down Expand Up @@ -2210,7 +2210,7 @@ export default async function getBaseWebpackConfig(
!!config.experimental.sri?.algorithm &&
new SubresourceIntegrityPlugin(config.experimental.sri.algorithm),
isClient &&
new FontLoaderManifestPlugin({
new NextFontManifestPlugin({
appDirEnabled: !!config.experimental.appDir,
}),
!dev &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ export default async function edgeSSRLoader(this: any) {
const subresourceIntegrityManifest = ${
sriEnabled ? 'self.__SUBRESOURCE_INTEGRITY_MANIFEST' : 'undefined'
}
const fontLoaderManifest = self.__FONT_LOADER_MANIFEST
const nextFontManifest = self.__NEXT_FONT_MANIFEST
const render = getRender({
pageType,
Expand All @@ -155,7 +155,7 @@ export default async function edgeSSRLoader(this: any) {
subresourceIntegrityManifest,
config: ${stringifiedConfig},
buildId: ${JSON.stringify(buildId)},
fontLoaderManifest,
nextFontManifest,
incrementalCacheHandler,
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { NextConfigComplete } from '../../../../server/config-shared'
import type { DocumentType, AppType } from '../../../../shared/lib/utils'
import type { BuildManifest } from '../../../../server/get-page-files'
import type { ReactLoadableManifest } from '../../../../server/load-components'
import type { FontLoaderManifest } from '../../plugins/font-loader-manifest-plugin'
import type { NextFontManifestPlugin } from '../../plugins/next-font-manifest-plugin'

import WebServer from '../../../../server/web-server'
import {
Expand Down Expand Up @@ -31,7 +31,7 @@ export function getRender({
serverActionsManifest,
config,
buildId,
fontLoaderManifest,
nextFontManifest,
incrementalCacheHandler,
}: {
pagesType: 'app' | 'pages' | 'root'
Expand All @@ -53,7 +53,7 @@ export function getRender({
appServerMod: any
config: NextConfigComplete
buildId: string
fontLoaderManifest: FontLoaderManifest
nextFontManifest: NextFontManifestPlugin
incrementalCacheHandler?: any
}) {
const isAppPath = pagesType === 'app'
Expand All @@ -62,7 +62,7 @@ export function getRender({
buildManifest,
reactLoadableManifest,
subresourceIntegrityManifest,
fontLoaderManifest,
nextFontManifest,
Document,
App: appMod?.default as AppType,
}
Expand Down
32 changes: 29 additions & 3 deletions packages/next/src/build/webpack/loaders/next-font-loader/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,16 @@ export default async function nextFontLoader(this: any) {
return fontLoaderSpan.traceAsyncFn(async () => {
const callback = this.async()

// next-swc next_font_loaders turns each font loader call into JSON
/**
* The next-swc plugin next_font_loaders turns font function calls into CSS imports.
* At the end of the import, it adds the call arguments and some additional data as a resourceQuery.
* e.g:
* const inter = Inter({ subset: ['latin'] })
* ->
* import inter from 'next/font/google/target.css?{"import":"Inter","subsets":["latin"]}'
*
* Here we parse the resourceQuery to get the font function name, call arguments, and the path to the file that called the font function.
*/
const {
path: relativeFilePathFromRoot,
import: functionName,
Expand Down Expand Up @@ -60,6 +69,16 @@ export default async function nextFontLoader(this: any) {
})
)

/**
* Emit font files to .next/static/media as [hash].[ext].
*
* If the font should be preloaded, add .p to the filename: [hash].p.[ext]
* NextFontManifestPlugin adds these files to the next/font manifest.
*
* If the font is using a size-adjust fallback font, add -s to the filename: [hash]-s.[ext]
* NextFontManifestPlugin uses this to see if fallback fonts are being used.
* This is used to collect stats on fallback fonts usage by the Google Aurora team.
*/
const emitFontFile = (
content: Buffer,
ext: string,
Expand All @@ -69,20 +88,23 @@ export default async function nextFontLoader(this: any) {
const opts = { context: this.rootContext, content }
const interpolatedName = loaderUtils.interpolateName(
this,
// Font files ending with .p.(woff|woff2|eot|ttf|otf) are preloaded
`static/media/[hash]${isUsingSizeAdjust ? '-s' : ''}${
preload ? '.p' : ''
}.${ext}`,
opts
)
const outputPath = `${assetPrefix}/_next/${interpolatedName}`
// Only the client emits the font file
if (!isServer) {
this.emitFile(interpolatedName, content, null)
}
// But both the server and client must get the resulting path
return outputPath
}

try {
// Import the font loader function from either next/font/local or next/font/google
// The font loader function emits font files and returns @font-faces and fallback font metrics
const fontLoader: FontLoader = require(fontLoaderPath).default
let { css, fallbackFonts, adjustFontFallback, weight, style, variable } =
await fontLoader({
Expand All @@ -107,12 +129,15 @@ export default async function nextFontLoader(this: any) {

// Exports will be exported as is from css-loader instead of a CSS module export
const exports: { name: any; value: any }[] = []

// Generate a hash from the CSS content. Used to generate classnames and font families
const fontFamilyHash = loaderUtils.getHashDigest(
Buffer.from(css),
'md5',
'hex',
6
)

// Add CSS classes, exports and make the font-family localy scoped by turning it unguessable
const result = await postcss(
postcssNextFontPlugin({
Expand All @@ -128,12 +153,13 @@ export default async function nextFontLoader(this: any) {
from: undefined,
})

// Reuse ast in css-loader
const ast = {
type: 'postcss',
version: result.processor.version,
root: result.root,
}

// Return the resulting CSS and send the postcss ast, font exports and the hash to the css-loader in the meta argument.
callback(null, result.css, null, {
exports,
ast,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,22 @@
import type { AdjustFontFallback } from '../../../../../font'
import postcss, { Declaration } from 'postcss'

/**
* The next/font postcss plugin recieves the @font-face declarations returned from the next/font loaders.
*
* It hashes the font-family name to make it unguessable, it shouldn't be globally accessible.
* If it were global, we wouldn't be able to tell which pages are using which fonts when generating preload tags.
*
* If the font loader returned fallback metrics, generate a fallback @font-face.
*
* If the font loader returned a variable name, add a CSS class that declares a variable containing the font and fallback fonts.
*
* Lastly, it adds the font-family to the exports object.
* This enables you to access the actual font-family name, not just through the CSS class.
* e.g:
* const inter = Inter({ subsets: ['latin'] })
* inter.style.fontFamily // => '__Inter_123456'
*/
const postcssNextFontPlugin = ({
exports,
fontFamilyHash,
Expand Down Expand Up @@ -54,7 +70,7 @@ const postcssNextFontPlugin = ({
throw new Error("Font loaders must return one or more @font-face's")
}

// Add fallback font with override values
// Add fallback @font-face with the provided override values
let adjustFontFallbackFamily: string | undefined
if (adjustFontFallback) {
adjustFontFallbackFamily = formatFamily(`${fontFamily} Fallback`)
Expand Down Expand Up @@ -113,6 +129,8 @@ const postcssNextFontPlugin = ({

// Variable fonts can define ranges of values
const isRange = (value: string) => value.trim().includes(' ')

// Format the font families to be used in the CSS
const formattedFontFamilies = [
formatFamily(fontFamily),
...(adjustFontFallbackFamily ? [adjustFontFallbackFamily] : []),
Expand All @@ -126,6 +144,7 @@ const postcssNextFontPlugin = ({
prop: 'font-family',
value: formattedFontFamilies,
}),
// If the font only has one weight or style, we can set it on the class
...(weight && !isRange(weight)
? [
new postcss.Declaration({
Expand All @@ -145,7 +164,7 @@ const postcssNextFontPlugin = ({
]
root.nodes.push(classRule)

// Add class that defines a variable with the font family
// Add CSS class that defines a variable with the font families
if (variable) {
const varialbeRule = new postcss.Rule({ selector: '.variable' })
varialbeRule.nodes = [
Expand Down
133 changes: 0 additions & 133 deletions packages/next/src/build/webpack/plugins/font-loader-manifest-plugin.ts

This file was deleted.

4 changes: 2 additions & 2 deletions packages/next/src/build/webpack/plugins/middleware-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
NEXT_CLIENT_SSR_ENTRY_SUFFIX,
FLIGHT_SERVER_CSS_MANIFEST,
SUBRESOURCE_INTEGRITY_MANIFEST,
FONT_LOADER_MANIFEST,
NEXT_FONT_MANIFEST,
SERVER_REFERENCE_MANIFEST,
} from '../../../shared/lib/constants'
import {
Expand Down Expand Up @@ -126,7 +126,7 @@ function getEntryFiles(
`server/${MIDDLEWARE_REACT_LOADABLE_MANIFEST}.js`
)

files.push(`server/${FONT_LOADER_MANIFEST}.js`)
files.push(`server/${NEXT_FONT_MANIFEST}.js`)

if (NextBuildContext!.hasInstrumentationHook) {
files.push(`server/edge-${INSTRUMENTATION_HOOK_FILENAME}.js`)
Expand Down
Loading

0 comments on commit de6a1e3

Please sign in to comment.