Add support for fallback font and css variable for font/local (#40990)

Adds support for generating a fallback font by providing font override
metrics for the given local font. Also adds support for providing a CSS
variable name that then can be accessed through the `.variable` export,
it contains the hashed font family name.

## Bug

- [ ] Related issues linked using `fixes #number`
- [ ] Integration tests added
- [ ] Errors have a helpful link attached, see `contributing.md`

## Feature

- [ ] Implements an existing feature request or RFC. Make sure the
feature request has been accepted for implementation before opening a
PR.
- [ ] Related issues linked using `fixes #number`
- [ ] Integration tests added
- [ ] Documentation added
- [ ] Telemetry added. In case of a feature if it's used or not.
- [ ] Errors have a helpful link attached, see `contributing.md`

## Documentation / Examples

- [ ] Make sure the linting passes by running `pnpm lint`
- [ ] The "examples guidelines" are followed from [our contributing
doc](https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md)

Co-authored-by: JJ Kasper <jj@jjsweb.site>
This commit is contained in:
Hannes Bornö 2022-09-28 21:19:52 +02:00 committed by GitHub
parent a89d760d60
commit 299f392d7b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 316 additions and 165 deletions

View file

@ -1,8 +1,8 @@
import type { FontLoader } from 'next/font'
import type { AdjustFontFallback, FontLoader } from 'next/font'
// @ts-ignore
import fetch from 'next/dist/compiled/node-fetch'
// @ts-ignore
import { calculateOverrideCSS } from 'next/dist/server/font-utils'
import { calculateOverrideValues } from 'next/dist/server/font-utils'
import {
fetchCSSFromGoogleFonts,
getFontAxes,
@ -97,20 +97,36 @@ const downloadGoogleFonts: FontLoader = async ({
}
// Add fallback font
let adjustFontFallbackMetrics: AdjustFontFallback | undefined
if (adjustFontFallback) {
try {
updatedCssResponse += calculateOverrideCSS(
fontFamily,
require('next/dist/server/google-font-metrics.json')
const { ascent, descent, lineGap, fallbackFont } =
calculateOverrideValues(
fontFamily,
require('next/dist/server/google-font-metrics.json')
)
adjustFontFallbackMetrics = {
fallbackFont,
ascentOverride: ascent,
descentOverride: descent,
lineGapOverride: lineGap,
}
} catch {
console.error(
`Failed to find font override values for font \`${fontFamily}\``
)
} catch (e) {
console.log('Error getting font override values - ', e)
}
}
return {
css: updatedCssResponse,
fallbackFonts: fallback,
weight: weight === 'variable' ? undefined : weight,
style,
variable: `--next-font-${fontFamily.toLowerCase().replace(/ /g, '-')}${
weight !== 'variable' ? `-${weight}` : ''
}${style === 'italic' ? `-italic` : ''}`,
adjustFontFallback: adjustFontFallbackMetrics,
}
}

View file

@ -1,5 +1,5 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import type { FontModule } from 'next/font'
import type { AdjustFontFallback, FontModule } from 'next/font'
type Display = 'auto' | 'block' | 'swap' | 'fallback' | 'optional'
type LocalFont = {
src: string | Array<{ file: string; unicodeRange: string }>
@ -8,15 +8,18 @@ type LocalFont = {
style?: string
fallback?: string[]
preload?: boolean
variable?: string
ascentOverride?: string
descentOverride?: string
fontStretch?: string
fontVariant?: string
fontFeatureSettings?: string
fontVariationSettings?: string
ascentOverride?: string
descentOverride?: string
lineGapOverride?: string
sizeAdjust?: string
adjustFontFallback?: AdjustFontFallback
}
export default function localFont(options: LocalFont): FontModule {

View file

@ -18,12 +18,14 @@ const fetchFonts: FontLoader = async ({
style,
fallback,
preload,
variable,
ascentOverride,
descentOverride,
lineGapOverride,
fontStretch,
fontFeatureSettings,
sizeAdjust,
adjustFontFallback,
} = validateData(functionName, data)
const fontFaces = await Promise.all(
@ -61,6 +63,10 @@ ${fontFaceProperties
return {
css: fontFaces.join('\n'),
fallbackFonts: fallback,
weight,
style,
variable,
adjustFontFallback,
}
}

View file

@ -1,3 +1,5 @@
import { AdjustFontFallback } from 'next/font'
const allowedDisplayValues = ['auto', 'block', 'swap', 'fallback', 'optional']
const formatValues = (values: string[]) =>
@ -24,6 +26,7 @@ type FontOptions = {
style?: string
fallback?: string[]
preload: boolean
variable?: string
ascentOverride?: string
descentOverride?: string
fontStretch?: string
@ -32,6 +35,7 @@ type FontOptions = {
fontVariationSettings?: string
lineGapOverride?: string
sizeAdjust?: string
adjustFontFallback?: AdjustFontFallback
}
export function validateData(functionName: string, data: any): FontOptions {
if (functionName) {
@ -44,6 +48,7 @@ export function validateData(functionName: string, data: any): FontOptions {
style,
fallback,
preload = true,
variable,
ascentOverride,
descentOverride,
fontStretch,
@ -52,6 +57,7 @@ export function validateData(functionName: string, data: any): FontOptions {
fontVariationSettings,
lineGapOverride,
sizeAdjust,
adjustFontFallback,
} = data[0] || ({} as any)
if (!allowedDisplayValues.includes(display)) {
@ -100,6 +106,7 @@ export function validateData(functionName: string, data: any): FontOptions {
style,
fallback,
preload,
variable,
ascentOverride,
descentOverride,
fontStretch,
@ -108,5 +115,6 @@ export function validateData(functionName: string, data: any): FontOptions {
fontVariationSettings,
lineGapOverride,
sizeAdjust,
adjustFontFallback,
}
}

View file

@ -43,18 +43,21 @@ export default async function nextFontLoader(this: any) {
this.resourcePath,
'../loader.js'
)).default
let { css, fallbackFonts } = await fontLoader({
functionName,
data,
config: fontLoaderOptions,
emitFontFile,
resolve: (src: string) =>
promisify(this.resolve)(
path.dirname(path.join(this.rootContext, relativeFilePathFromRoot)),
src
),
fs: this.fs,
})
let { css, fallbackFonts, adjustFontFallback, weight, style, variable } =
await fontLoader({
functionName,
data,
config: fontLoaderOptions,
emitFontFile,
resolve: (src: string) =>
promisify(this.resolve)(
path.dirname(
path.join(this.rootContext, relativeFilePathFromRoot)
),
src
),
fs: this.fs,
})
const { postcss } = await getPostcss()
@ -68,7 +71,15 @@ export default async function nextFontLoader(this: any) {
)
// Add CSS classes, exports and make the font-family localy scoped by turning it unguessable
const result = await postcss(
postcssFontLoaderPlugn(exports, fontFamilyHash, fallbackFonts)
postcssFontLoaderPlugn({
exports,
fontFamilyHash,
fallbackFonts,
weight,
style,
adjustFontFallback,
variable,
})
).process(css, {
from: undefined,
})
@ -79,9 +90,14 @@ export default async function nextFontLoader(this: any) {
version: result.processor.version,
root: result.root,
}
callback(null, result.css, null, { exports, ast, fontFamilyHash })
callback(null, result.css, null, {
exports,
ast,
fontFamilyHash,
})
} catch (err: any) {
err.stack = false
err.message = `Font loader error:\n${err.message}`
err.message += `
${chalk.cyan(`Location: ${relativeFilePathFromRoot}`)}`

View file

@ -1,26 +1,38 @@
import type { AdjustFontFallback } from '../../../../font'
import postcss, { Declaration } from 'postcss'
const postcssFontLoaderPlugn = (
exports: { name: any; value: any }[],
fontFamilyHash: string,
fallbackFonts: string[] = []
) => {
const postcssFontLoaderPlugn = ({
exports,
fontFamilyHash,
fallbackFonts = [],
adjustFontFallback,
variable,
weight,
style,
}: {
exports: { name: any; value: any }[]
fontFamilyHash: string
fallbackFonts?: string[]
adjustFontFallback?: AdjustFontFallback
variable?: string
weight?: string
style?: string
}) => {
return {
postcssPlugin: 'postcss-font-loader',
Once(root: any) {
const fontFamilies: string[] = []
let rawFamily: string | undefined
let fontWeight: string | undefined
let fontStyle: string | undefined
let fontFamily: string | undefined
const normalizeFamily = (family: string) => {
return family.replace(/['"]/g, '')
}
const formatFamily = (family: string) => {
if (family[0] === "'" || family[0] === '"') {
family = family.slice(1, family.length - 1)
}
// Turn the font family unguessable to make it localy scoped
return `'__${family.replace(/ /g, '_')}_${fontFamilyHash}'`
}
// Hash font-family name
for (const node of root.nodes) {
if (node.type === 'atrule' && node.name === 'font-face') {
const familyNode = node.nodes.find(
@ -30,52 +42,85 @@ const postcssFontLoaderPlugn = (
continue
}
if (!rawFamily) {
let family: string = familyNode.value
if (family[0] === "'" || family[0] === '"') {
family = family.slice(1, family.length - 1)
}
rawFamily = family
}
const formattedFamily = formatFamily(familyNode.value)
familyNode.value = formattedFamily
const currentFamily = normalizeFamily(familyNode.value)
if (fontFamilies.includes(formattedFamily)) {
continue
}
fontFamilies.push(formattedFamily)
// Only extract weight and style from first encountered family, the rest will treated as fallbacks
if (fontFamilies.length > 1) {
continue
if (!fontFamily) {
fontFamily = currentFamily
} else if (fontFamily !== currentFamily) {
throw new Error(
`Font family mismatch, expected ${fontFamily} but got ${currentFamily}`
)
}
// Extract weight and style from first encountered @font-face
const weight = node.nodes.find(
(decl: Declaration) => decl.prop === 'font-weight'
)
// Skip if the value includes ' ', then it's a range of possible values
if (weight && !weight.value.includes(' ')) {
fontWeight = weight.value
}
const style = node.nodes.find(
(decl: Declaration) => decl.prop === 'font-style'
)
// Skip if the value includes ' ', then it's a range of possible values
if (style && !style.value.includes(' ')) {
fontStyle = style.value
}
familyNode.value = formatFamily(fontFamily)
}
}
const [mainFontFamily, ...adjustFontFallbacks] = fontFamilies
// If fallback fonts were provided from the font loader, they should be used before the adjustFontFallbacks
if (!fontFamily) {
throw new Error('Font loaders must have exactly one font family')
}
// Add fallback font with override values
let adjustFontFallbackFamily: string | undefined
if (adjustFontFallback) {
adjustFontFallbackFamily = formatFamily(`${fontFamily} Fallback`)
const fallbackFontFace = postcss.atRule({ name: 'font-face' })
const {
fallbackFont,
ascentOverride,
descentOverride,
lineGapOverride,
sizeAdjust,
} = adjustFontFallback
fallbackFontFace.nodes = [
new postcss.Declaration({
prop: 'font-family',
value: adjustFontFallbackFamily,
}),
new postcss.Declaration({
prop: 'src',
value: `local("${fallbackFont}")`,
}),
...(ascentOverride
? [
new postcss.Declaration({
prop: 'ascent-override',
value: ascentOverride,
}),
]
: []),
...(descentOverride
? [
new postcss.Declaration({
prop: 'descent-override',
value: descentOverride,
}),
]
: []),
...(lineGapOverride
? [
new postcss.Declaration({
prop: 'line-gap-override',
value: lineGapOverride,
}),
]
: []),
...(sizeAdjust
? [
new postcss.Declaration({
prop: 'size-adjust',
value: sizeAdjust,
}),
]
: []),
]
root.nodes.push(fallbackFontFace)
}
const formattedFontFamilies = [
mainFontFamily,
formatFamily(fontFamily),
...fallbackFonts,
...adjustFontFallbacks,
...(adjustFontFallbackFamily ? [adjustFontFallbackFamily] : []),
].join(', ')
// Add class with family, weight and style
const classRule = new postcss.Rule({ selector: '.className' })
@ -84,19 +129,19 @@ const postcssFontLoaderPlugn = (
prop: 'font-family',
value: formattedFontFamilies,
}),
...(fontWeight
...(weight
? [
new postcss.Declaration({
prop: 'font-weight',
value: fontWeight,
value: weight,
}),
]
: []),
...(fontStyle
...(style
? [
new postcss.Declaration({
prop: 'font-style',
value: fontStyle,
value: style,
}),
]
: []),
@ -104,26 +149,24 @@ const postcssFontLoaderPlugn = (
root.nodes.push(classRule)
// Add class that defines a variable with the font family
const varialbeRule = new postcss.Rule({ selector: '.variable' })
varialbeRule.nodes = [
new postcss.Declaration({
prop: rawFamily
? `--next-font-${rawFamily.toLowerCase().replace(/ /g, '-')}${
fontWeight ? `-${fontWeight}` : ''
}${fontStyle === 'italic' ? `-${fontStyle}` : ''}`
: '',
value: formattedFontFamilies,
}),
]
root.nodes.push(varialbeRule)
if (variable) {
const varialbeRule = new postcss.Rule({ selector: '.variable' })
varialbeRule.nodes = [
new postcss.Declaration({
prop: variable,
value: formattedFontFamilies,
}),
]
root.nodes.push(varialbeRule)
}
// Export @font-face values as is
exports.push({
name: 'style',
value: {
fontFamily: formattedFontFamilies,
fontWeight: fontWeight && Number(fontWeight),
fontStyle,
fontWeight: weight && Number(weight),
fontStyle: style,
},
})
},

View file

@ -173,6 +173,16 @@ function formatWebpackMessages(json, verbose) {
const formattedErrors = json.errors.map(function (message) {
let importTraceNote
if (
message &&
message.message &&
/Font loader error:/.test(message.message)
) {
return message.message.slice(
message.message.indexOf('Font loader error:')
)
}
// TODO: Shall we use invisible characters in the original error
// message as meta information?
if (message && message.message && /NEXT_RSC_ERR_/.test(message.message)) {

View file

@ -1,9 +1,17 @@
export type FontModule = {
className: string
variable: string
variable?: string
style: { fontFamily: string; fontWeight?: number; fontStyle?: string }
}
export type AdjustFontFallback = {
fallbackFont: string
ascentOverride?: string
descentOverride?: string
lineGapOverride?: string
sizeAdjust?: string
}
export type FontLoader = (options: {
functionName: string
data: any[]
@ -11,4 +19,11 @@ export type FontLoader = (options: {
emitFontFile: (content: Buffer, ext: string, preload: boolean) => string
resolve: (src: string) => string
fs: any
}) => Promise<{ css: string; fallbackFonts?: string[] }>
}) => Promise<{
css: string
fallbackFonts?: string[]
variable?: string
adjustFontFallback?: AdjustFontFallback
weight?: string
style?: string
}>

View file

@ -98,8 +98,7 @@ function parseGoogleFontName(css: string): Array<string> {
return [...fontNames]
}
export function calculateOverrideCSS(font: string, fontMetrics: any) {
const fontName = font.toLowerCase().trim().replace(/ /g, '-')
export function calculateOverrideValues(font: string, fontMetrics: any) {
const fontKey = font.toLowerCase().trim().replace(/ /g, '')
const { category, ascentOverride, descentOverride, lineGapOverride } =
fontMetrics[fontKey]
@ -109,6 +108,22 @@ export function calculateOverrideCSS(font: string, fontMetrics: any) {
const descent = (descentOverride * 100).toFixed(2)
const lineGap = (lineGapOverride * 100).toFixed(2)
return {
ascent,
descent,
lineGap,
fallbackFont,
}
}
function calculateOverrideCSS(font: string, fontMetrics: any) {
const fontName = font.toLowerCase().trim().replace(/ /g, '-')
const { ascent, descent, lineGap, fallbackFont } = calculateOverrideValues(
font,
fontMetrics
)
return `
@font-face {
font-family: "${fontName}-fallback";

View file

@ -55,7 +55,6 @@ describe('app dir next-font', () => {
// Comp
expect(JSON.parse($('#root-comp').text())).toEqual({
className: expect.stringMatching(/^__className_.{6}$/),
variable: expect.stringMatching(/^__variable_.{6}$/),
style: {
fontFamily: expect.stringMatching(/^'__font3_.{6}'$/),
fontStyle: 'italic',
@ -80,7 +79,6 @@ describe('app dir next-font', () => {
// layout
expect(JSON.parse($('#client-layout').text())).toEqual({
className: expect.stringMatching(/^__className_.{6}$/),
variable: expect.stringMatching(/^__variable_.{6}$/),
style: {
fontFamily: expect.stringMatching(/^'__font4_.{6}'$/),
fontWeight: 100,
@ -89,7 +87,6 @@ describe('app dir next-font', () => {
// page
expect(JSON.parse($('#client-page').text())).toEqual({
className: expect.stringMatching(/^__className_.{6}$/),
variable: expect.stringMatching(/^__variable_.{6}$/),
style: {
fontFamily: expect.stringMatching(/^'__font5_.{6}'$/),
fontStyle: 'italic',
@ -98,7 +95,6 @@ describe('app dir next-font', () => {
// Comp
expect(JSON.parse($('#client-comp').text())).toEqual({
className: expect.stringMatching(/^__className_.{6}$/),
variable: expect.stringMatching(/^__variable_.{6}$/),
style: {
fontFamily: expect.stringMatching(/^'__font6_.{6}'$/),
},

View file

@ -1,7 +1,7 @@
import localFont from '@next/font/local'
export const font1 = localFont({ src: './font1.woff2' })
export const font2 = localFont({ src: './font2.woff2' })
export const font1 = localFont({ src: './font1.woff2', variable: '--font-1' })
export const font2 = localFont({ src: './font2.woff2', variable: '--font-2' })
export const font3 = localFont({
src: './font3.woff2',
weight: '900',

View file

@ -1,4 +1,6 @@
import { Fira_Code, Albert_Sans, Inter, Roboto } from '@next/font/google'
import localFont from '@next/font/local'
const firaCode = Fira_Code()
const albertSans = Albert_Sans({
variant: 'variable-italic',
@ -10,6 +12,11 @@ const roboto = Roboto({
display: 'swap',
preload: true,
})
const myFont = localFont({
src: '../fonts/my-font.woff2',
preload: false,
variable: '--my-font',
})
export default function WithFonts() {
return (
@ -73,6 +80,21 @@ export default function WithFonts() {
>
Without variables
</div>
{/* Local font */}
<div
id="variables-local-font"
className={myFont.variable}
style={{ fontFamily: 'var(--my-font)' }}
>
With variables
</div>
<div
id="without-variables-local-font"
style={{ fontFamily: 'var(--my-font)' }}
>
Without variables
</div>
</>
)
}

View file

@ -4,10 +4,16 @@ const myFont1 = localFont({
src: '../fonts/my-font.woff2',
style: 'italic',
weight: '100',
fallback: ['system-ui'],
})
const myFont2 = localFont({
src: '../fonts/my-other-font.woff',
preload: false,
variable: '--my-font',
adjustFontFallback: {
fallbackFont: 'Arial',
sizeAdjust: '120%',
},
})
export default function WithFonts() {

View file

@ -46,9 +46,8 @@ describe('@next/font/google', () => {
variable: expect.stringMatching(/^__variable_.{6}$/),
style: {
fontFamily: expect.stringMatching(
/^'__Open_Sans_.{6}', '__open-sans-fallback_.{6}'$/
/^'__Open_Sans_.{6}', '__Open_Sans_Fallback_.{6}'$/
),
fontStyle: 'normal',
},
})
@ -58,9 +57,8 @@ describe('@next/font/google', () => {
variable: expect.stringMatching(/^__variable_.{6}$/),
style: {
fontFamily: expect.stringMatching(
/^'__Open_Sans_.{6}', '__open-sans-fallback_.{6}'$/
/^'__Open_Sans_.{6}', '__Open_Sans_Fallback_.{6}'$/
),
fontStyle: 'normal',
},
})
@ -70,9 +68,8 @@ describe('@next/font/google', () => {
variable: expect.stringMatching(/^__variable_.{6}$/),
style: {
fontFamily: expect.stringMatching(
/^'__Inter_.{6}', '__inter-fallback_.{6}'$/
/^'__Inter_.{6}', '__Inter_Fallback_.{6}'$/
),
fontStyle: 'normal',
fontWeight: 900,
},
})
@ -81,7 +78,7 @@ describe('@next/font/google', () => {
variable: expect.stringMatching(/^__variable_.{6}$/),
style: {
fontFamily: expect.stringMatching(
/^'__Roboto_.{6}', '__roboto-fallback_.{6}'$/
/^'__Roboto_.{6}', '__Roboto_Fallback_.{6}'$/
),
fontStyle: 'italic',
fontWeight: 100,
@ -95,31 +92,31 @@ describe('@next/font/google', () => {
// _app.js
expect(JSON.parse($('#app-open-sans').text())).toEqual({
className: '__className_f32d04',
variable: '__variable_f32d04',
className: expect.stringMatching(/__className_.{6}/),
variable: expect.stringMatching(/__variable_.{6}/),
style: {
fontFamily: expect.stringMatching(
/^'__Open_Sans_.{6}', '__open-sans-fallback_.{6}'$/
/^'__Open_Sans_.{6}', '__Open_Sans_Fallback_.{6}'$/
),
fontStyle: 'normal',
},
})
// with-local-fonts.js
expect(JSON.parse($('#first-local-font').text())).toEqual({
className: '__className_410624',
variable: '__variable_410624',
className: expect.stringMatching(/__className_.{6}/),
style: {
fontFamily: expect.stringMatching(/^'__my-font_.{6}'$/),
fontFamily: expect.stringMatching(/^'__my-font_.{6}', system-ui$/),
fontStyle: 'italic',
fontWeight: 100,
},
})
expect(JSON.parse($('#second-local-font').text())).toEqual({
className: '__className_3ff726',
variable: '__variable_3ff726',
className: expect.stringMatching(/^__className_.{6}$/),
variable: expect.stringMatching(/^__variable_.{6}$/),
style: {
fontFamily: expect.stringMatching(/^'__my-other-font_.{6}'$/),
fontFamily: expect.stringMatching(
/^'__my-other-font_.{6}', '__my-other-font_Fallback_.{6}'$/
),
},
})
})
@ -134,7 +131,7 @@ describe('@next/font/google', () => {
await browser.eval(
'getComputedStyle(document.querySelector("#app-open-sans")).fontFamily'
)
).toMatch(/^__Open_Sans_.{6}, __open-sans-fallback_.{6}$/)
).toMatch(/^__Open_Sans_.{6}, __Open_Sans_Fallback_.{6}$/)
expect(
await browser.eval(
'getComputedStyle(document.querySelector("#app-open-sans")).fontWeight'
@ -151,7 +148,7 @@ describe('@next/font/google', () => {
await browser.eval(
'getComputedStyle(document.querySelector("#with-fonts-open-sans")).fontFamily'
)
).toMatch(/^__Open_Sans_.{6}, __open-sans-fallback_.{6}$/)
).toMatch(/^__Open_Sans_.{6}, __Open_Sans_Fallback_.{6}$/)
expect(
await browser.eval(
'getComputedStyle(document.querySelector("#with-fonts-open-sans")).fontWeight'
@ -178,7 +175,7 @@ describe('@next/font/google', () => {
await browser.eval(
'getComputedStyle(document.querySelector("#comp-with-fonts-inter")).fontFamily'
)
).toMatch(/^__Inter_.{6}, __inter-fallback_.{6}$/)
).toMatch(/^__Inter_.{6}, __Inter_Fallback_.{6}$/)
expect(
await browser.eval(
'getComputedStyle(document.querySelector("#comp-with-fonts-inter")).fontWeight'
@ -194,7 +191,7 @@ describe('@next/font/google', () => {
await browser.eval(
'getComputedStyle(document.querySelector("#comp-with-fonts-roboto")).fontFamily'
)
).toMatch(/^__Roboto_.{6}, __roboto-fallback_.{6}$/)
).toMatch(/^__Roboto_.{6}, __Roboto_Fallback_.{6}$/)
expect(
await browser.eval(
'getComputedStyle(document.querySelector("#comp-with-fonts-roboto")).fontWeight'
@ -211,7 +208,7 @@ describe('@next/font/google', () => {
const browser = await webdriver(next.url, '/variables')
// Fira Code Variable
const firaCodeRegex = /^__Fira_Code_.{6}, __fira-code-fallback_.{6}$/
const firaCodeRegex = /^__Fira_Code_.{6}, __Fira_Code_Fallback_.{6}$/
expect(
await browser.eval(
'getComputedStyle(document.querySelector("#variables-fira-code")).fontFamily'
@ -237,7 +234,7 @@ describe('@next/font/google', () => {
).not.toMatch(albertSansItalicRegex)
// Inter 900
const inter900Regex = /^__Inter_.{6}, __inter-fallback_.{6}$/
const inter900Regex = /^__Inter_.{6}, __Inter_Fallback_.{6}$/
expect(
await browser.eval(
'getComputedStyle(document.querySelector("#variables-inter-900")).fontFamily'
@ -250,7 +247,7 @@ describe('@next/font/google', () => {
).not.toMatch(inter900Regex)
// Roboto 100 Italic
const roboto100ItalicRegex = /^__Roboto_.{6}, __roboto-fallback_.{6}$/
const roboto100ItalicRegex = /^__Roboto_.{6}, __Roboto_Fallback_.{6}$/
expect(
await browser.eval(
'getComputedStyle(document.querySelector("#variables-roboto-100-italic")).fontFamily'
@ -261,6 +258,19 @@ describe('@next/font/google', () => {
'getComputedStyle(document.querySelector("#without-variables-roboto-100-italic")).fontFamily'
)
).not.toMatch(roboto100ItalicRegex)
// Local font
const localFontRegex = /^__my-font_.{6}$/
expect(
await browser.eval(
'getComputedStyle(document.querySelector("#variables-local-font")).fontFamily'
)
).toMatch(localFontRegex)
expect(
await browser.eval(
'getComputedStyle(document.querySelector("#without-variables-local-font")).fontFamily'
)
).not.toMatch(localFontRegex)
})
test('page using fallback fonts', async () => {
@ -272,7 +282,7 @@ describe('@next/font/google', () => {
'getComputedStyle(document.querySelector("#with-fallback-fonts-classname")).fontFamily'
)
).toMatch(
/^__Open_Sans_.{6}, system-ui, Arial, __open-sans-fallback_.{6}$/
/^__Open_Sans_.{6}, system-ui, Arial, __Open_Sans_Fallback_.{6}$/
)
// .style
@ -281,7 +291,7 @@ describe('@next/font/google', () => {
'getComputedStyle(document.querySelector("#with-fallback-fonts-style")).fontFamily'
)
).toMatch(
/^__Open_Sans_.{6}, system-ui, Arial, __open-sans-fallback_.{6}$/
/^__Open_Sans_.{6}, system-ui, Arial, __Open_Sans_Fallback_.{6}$/
)
// .variable
@ -290,7 +300,7 @@ describe('@next/font/google', () => {
'getComputedStyle(document.querySelector("#with-fallback-fonts-variable")).fontFamily'
)
).toMatch(
/^__Open_Sans_.{6}, system-ui, Arial, __open-sans-fallback_.{6}$/
/^__Open_Sans_.{6}, system-ui, Arial, __Open_Sans_Fallback_.{6}$/
)
})
})

View file

@ -93,7 +93,7 @@ describe('@next/font/google loader', () => {
ok: true,
text: async () => '',
})
const { css, fallbackFonts } = await loader({
const { adjustFontFallback, fallbackFonts } = await loader({
functionName: 'Inter',
data: [],
config: { subsets: [] },
@ -101,17 +101,12 @@ describe('@next/font/google loader', () => {
resolve: jest.fn(),
fs: {} as any,
})
expect(css).toMatchInlineSnapshot(`
"
@font-face {
font-family: \\"inter-fallback\\";
ascent-override: 96.88%;
descent-override: 24.15%;
line-gap-override: 0.00%;
src: local(\\"Arial\\");
}
"
`)
expect(adjustFontFallback).toEqual({
ascentOverride: '96.88',
descentOverride: '24.15',
fallbackFont: 'Arial',
lineGapOverride: '0.00',
})
expect(fallbackFonts).toBeUndefined()
})
@ -120,7 +115,7 @@ describe('@next/font/google loader', () => {
ok: true,
text: async () => '',
})
const { css, fallbackFonts } = await loader({
const { fallbackFonts, adjustFontFallback } = await loader({
functionName: 'Source_Code_Pro',
data: [],
config: { subsets: [] },
@ -128,17 +123,12 @@ describe('@next/font/google loader', () => {
resolve: jest.fn(),
fs: {} as any,
})
expect(css).toMatchInlineSnapshot(`
"
@font-face {
font-family: \\"source-code-pro-fallback\\";
ascent-override: 98.40%;
descent-override: 27.30%;
line-gap-override: 0.00%;
src: local(\\"Arial\\");
}
"
`)
expect(adjustFontFallback).toEqual({
ascentOverride: '98.40',
descentOverride: '27.30',
fallbackFont: 'Arial',
lineGapOverride: '0.00',
})
expect(fallbackFonts).toBeUndefined()
})
@ -147,7 +137,7 @@ describe('@next/font/google loader', () => {
ok: true,
text: async () => '',
})
const { css, fallbackFonts } = await loader({
const { adjustFontFallback, fallbackFonts } = await loader({
functionName: 'Fraunces',
data: [{ fallback: ['Abc', 'Def'] }],
config: { subsets: [] },
@ -155,17 +145,12 @@ describe('@next/font/google loader', () => {
resolve: jest.fn(),
fs: {} as any,
})
expect(css).toMatchInlineSnapshot(`
"
@font-face {
font-family: \\"fraunces-fallback\\";
ascent-override: 97.80%;
descent-override: 25.50%;
line-gap-override: 0.00%;
src: local(\\"Times New Roman\\");
}
"
`)
expect(adjustFontFallback).toEqual({
ascentOverride: '97.80',
descentOverride: '25.50',
fallbackFont: 'Times New Roman',
lineGapOverride: '0.00',
})
expect(fallbackFonts).toEqual(['Abc', 'Def'])
})