diff --git a/packages/font/src/google/loader.ts b/packages/font/src/google/loader.ts index 96e35c06067b9..7c413b34a0430 100644 --- a/packages/font/src/google/loader.ts +++ b/packages/font/src/google/loader.ts @@ -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, } } diff --git a/packages/font/src/local/index.ts b/packages/font/src/local/index.ts index 20f22a17986ca..36688438970e2 100644 --- a/packages/font/src/local/index.ts +++ b/packages/font/src/local/index.ts @@ -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 { diff --git a/packages/font/src/local/loader.ts b/packages/font/src/local/loader.ts index 168660e94f57b..78fdd6e5af2ec 100644 --- a/packages/font/src/local/loader.ts +++ b/packages/font/src/local/loader.ts @@ -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, } } diff --git a/packages/font/src/local/utils.ts b/packages/font/src/local/utils.ts index c21fc5ace26c7..3c4c469b6f42b 100644 --- a/packages/font/src/local/utils.ts +++ b/packages/font/src/local/utils.ts @@ -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, } } diff --git a/packages/next/build/webpack/loaders/next-font-loader/index.ts b/packages/next/build/webpack/loaders/next-font-loader/index.ts index 9ab979e69e4a3..d3bb726da1fe0 100644 --- a/packages/next/build/webpack/loaders/next-font-loader/index.ts +++ b/packages/next/build/webpack/loaders/next-font-loader/index.ts @@ -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}`)}` diff --git a/packages/next/build/webpack/loaders/next-font-loader/postcss-font-loader.ts b/packages/next/build/webpack/loaders/next-font-loader/postcss-font-loader.ts index 08313dd565df3..4ce859a4579fd 100644 --- a/packages/next/build/webpack/loaders/next-font-loader/postcss-font-loader.ts +++ b/packages/next/build/webpack/loaders/next-font-loader/postcss-font-loader.ts @@ -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 + if (!fontFamily) { + fontFamily = currentFamily + } else if (fontFamily !== currentFamily) { + throw new Error( + `Font family mismatch, expected ${fontFamily} but got ${currentFamily}` + ) } - fontFamilies.push(formattedFamily) - // Only extract weight and style from first encountered family, the rest will treated as fallbacks - if (fontFamilies.length > 1) { - continue - } - - // Extract weight and style from first encountered @font-face - const weight = node.nodes.find( - (decl: Declaration) => decl.prop === 'font-weight' - ) + familyNode.value = formatFamily(fontFamily) + } + } - // Skip if the value includes ' ', then it's a range of possible values - if (weight && !weight.value.includes(' ')) { - fontWeight = weight.value - } + if (!fontFamily) { + throw new Error('Font loaders must have exactly one font family') + } - 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 - } - } + // 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 [mainFontFamily, ...adjustFontFallbacks] = fontFamilies - // If fallback fonts were provided from the font loader, they should be used before the adjustFontFallbacks 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, }, }) }, diff --git a/packages/next/client/dev/error-overlay/format-webpack-messages.js b/packages/next/client/dev/error-overlay/format-webpack-messages.js index f49af448a65ac..1e5d73d7fff8a 100644 --- a/packages/next/client/dev/error-overlay/format-webpack-messages.js +++ b/packages/next/client/dev/error-overlay/format-webpack-messages.js @@ -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)) { diff --git a/packages/next/font/index.d.ts b/packages/next/font/index.d.ts index 23c9295d02c31..128c0c98d66f1 100644 --- a/packages/next/font/index.d.ts +++ b/packages/next/font/index.d.ts @@ -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 +}> diff --git a/packages/next/server/font-utils.ts b/packages/next/server/font-utils.ts index 557d47b17612a..b7235c3942ed5 100644 --- a/packages/next/server/font-utils.ts +++ b/packages/next/server/font-utils.ts @@ -98,8 +98,7 @@ function parseGoogleFontName(css: string): Array { 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"; diff --git a/test/e2e/app-dir/next-font.test.ts b/test/e2e/app-dir/next-font.test.ts index 43da488bde3f0..8aec3a553cd14 100644 --- a/test/e2e/app-dir/next-font.test.ts +++ b/test/e2e/app-dir/next-font.test.ts @@ -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}'$/), }, diff --git a/test/e2e/app-dir/next-font/fonts/fonts.js b/test/e2e/app-dir/next-font/fonts/fonts.js index 524da89a41a31..5fbe4080b78d0 100644 --- a/test/e2e/app-dir/next-font/fonts/fonts.js +++ b/test/e2e/app-dir/next-font/fonts/fonts.js @@ -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', diff --git a/test/e2e/next-font/app/pages/variables.js b/test/e2e/next-font/app/pages/variables.js index ce3649496d1e3..5754c8e6315b9 100644 --- a/test/e2e/next-font/app/pages/variables.js +++ b/test/e2e/next-font/app/pages/variables.js @@ -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 + + {/* Local font */} +
+ With variables +
+
+ Without variables +
) } diff --git a/test/e2e/next-font/app/pages/with-local-fonts.js b/test/e2e/next-font/app/pages/with-local-fonts.js index 7294d821e407a..c3890361711c8 100644 --- a/test/e2e/next-font/app/pages/with-local-fonts.js +++ b/test/e2e/next-font/app/pages/with-local-fonts.js @@ -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() { diff --git a/test/e2e/next-font/index.test.ts b/test/e2e/next-font/index.test.ts index cbbb71868048a..159272ccdd791 100644 --- a/test/e2e/next-font/index.test.ts +++ b/test/e2e/next-font/index.test.ts @@ -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}$/ ) }) }) diff --git a/test/unit/google-font-loader.test.ts b/test/unit/google-font-loader.test.ts index 85ce628b035c0..f51772c7ea446 100644 --- a/test/unit/google-font-loader.test.ts +++ b/test/unit/google-font-loader.test.ts @@ -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']) })