Skip to content

Commit

Permalink
Add support for fallback font and css variable for font/local (vercel…
Browse files Browse the repository at this point in the history
…#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>
  • Loading branch information
2 people authored and BowlingX committed Oct 5, 2022
1 parent 4b7900a commit 38c32db
Show file tree
Hide file tree
Showing 15 changed files with 315 additions and 164 deletions.
30 changes: 23 additions & 7 deletions 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,
Expand Down Expand Up @@ -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,
}
}

Expand Down
9 changes: 6 additions & 3 deletions 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 }>
Expand All @@ -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 {
Expand Down
6 changes: 6 additions & 0 deletions packages/font/src/local/loader.ts
Expand Up @@ -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(
Expand Down Expand Up @@ -61,6 +63,10 @@ ${fontFaceProperties
return {
css: fontFaces.join('\n'),
fallbackFonts: fallback,
weight,
style,
variable,
adjustFontFallback,
}
}

Expand Down
8 changes: 8 additions & 0 deletions 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[]) =>
Expand All @@ -24,6 +26,7 @@ type FontOptions = {
style?: string
fallback?: string[]
preload: boolean
variable?: string
ascentOverride?: string
descentOverride?: string
fontStretch?: string
Expand All @@ -32,6 +35,7 @@ type FontOptions = {
fontVariationSettings?: string
lineGapOverride?: string
sizeAdjust?: string
adjustFontFallback?: AdjustFontFallback
}
export function validateData(functionName: string, data: any): FontOptions {
if (functionName) {
Expand All @@ -44,6 +48,7 @@ export function validateData(functionName: string, data: any): FontOptions {
style,
fallback,
preload = true,
variable,
ascentOverride,
descentOverride,
fontStretch,
Expand All @@ -52,6 +57,7 @@ export function validateData(functionName: string, data: any): FontOptions {
fontVariationSettings,
lineGapOverride,
sizeAdjust,
adjustFontFallback,
} = data[0] || ({} as any)

if (!allowedDisplayValues.includes(display)) {
Expand Down Expand Up @@ -100,6 +106,7 @@ export function validateData(functionName: string, data: any): FontOptions {
style,
fallback,
preload,
variable,
ascentOverride,
descentOverride,
fontStretch,
Expand All @@ -108,5 +115,6 @@ export function validateData(functionName: string, data: any): FontOptions {
fontVariationSettings,
lineGapOverride,
sizeAdjust,
adjustFontFallback,
}
}
44 changes: 30 additions & 14 deletions packages/next/build/webpack/loaders/next-font-loader/index.ts
Expand Up @@ -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()

Expand All @@ -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,
})
Expand All @@ -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}`)}`
Expand Down

0 comments on commit 38c32db

Please sign in to comment.