Skip to content

Commit

Permalink
Reduce remote requests in google fonts (vercel#41306)
Browse files Browse the repository at this point in the history
Reduce remote request by reusing response between server and client
compilation. Once reused it can be removed, will be cached by webpack
after that.

## 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)
  • Loading branch information
Hannes Bornö authored and Kikobeats committed Oct 24, 2022
1 parent 2e54f66 commit 602754d
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 15 deletions.
28 changes: 18 additions & 10 deletions packages/font/src/google/loader.ts
@@ -1,15 +1,17 @@
import type { AdjustFontFallback, FontLoader } from 'next/font'
// @ts-ignore
import fetch from 'next/dist/compiled/node-fetch'
// @ts-ignore
import { calculateSizeAdjustValues } from 'next/dist/server/font-utils'
import {
fetchCSSFromGoogleFonts,
fetchFontFile,
getFontAxes,
getUrl,
validateData,
} from './utils'

const cssCache = new Map<string, Promise<string>>()
const fontCache = new Map<string, any>()

const downloadGoogleFonts: FontLoader = async ({
functionName,
data,
Expand All @@ -36,7 +38,14 @@ const downloadGoogleFonts: FontLoader = async ({
const fontAxes = getFontAxes(fontFamily, weight, style, selectedVariableAxes)
const url = getUrl(fontFamily, fontAxes, display)

const fontFaceDeclarations = await fetchCSSFromGoogleFonts(url, fontFamily)
let cachedCssRequest = cssCache.get(url)
const fontFaceDeclarations =
cachedCssRequest ?? (await fetchCSSFromGoogleFonts(url, fontFamily))
if (!cachedCssRequest) {
cssCache.set(url, fontFaceDeclarations)
} else {
cssCache.delete(url)
}

// Find font files to download
const fontFiles: Array<{
Expand All @@ -63,14 +72,13 @@ const downloadGoogleFonts: FontLoader = async ({
// Download font files
const downloadedFiles = await Promise.all(
fontFiles.map(async ({ googleFontFileUrl, preloadFontFile }) => {
let fontFileBuffer: Buffer
if (process.env.NEXT_FONT_GOOGLE_MOCKED_RESPONSES) {
fontFileBuffer = Buffer.from(googleFontFileUrl)
let cachedFontRequest = fontCache.get(googleFontFileUrl)
const fontFileBuffer =
cachedFontRequest ?? (await fetchFontFile(googleFontFileUrl))
if (!cachedFontRequest) {
fontCache.set(googleFontFileUrl, fontFileBuffer)
} else {
const arrayBuffer = await fetch(googleFontFileUrl).then((r: any) =>
r.arrayBuffer()
)
fontFileBuffer = Buffer.from(arrayBuffer)
fontCache.delete(googleFontFileUrl)
}

const ext = /\.(woff|woff2|eot|ttf|otf)$/.exec(googleFontFileUrl)![1]
Expand Down
8 changes: 8 additions & 0 deletions packages/font/src/google/utils.ts
Expand Up @@ -141,6 +141,14 @@ export async function fetchCSSFromGoogleFonts(url: string, fontFamily: string) {
return cssResponse
}

export async function fetchFontFile(url: string) {
if (process.env.NEXT_FONT_GOOGLE_MOCKED_RESPONSES) {
return Buffer.from(url)
}
const arrayBuffer = await fetch(url).then((r: any) => r.arrayBuffer())
return Buffer.from(arrayBuffer)
}

export function getFontAxes(
fontFamily: string,
weight: string,
Expand Down
10 changes: 5 additions & 5 deletions test/unit/google-font-loader.test.ts
Expand Up @@ -183,17 +183,17 @@ describe('@next/font/google loader', () => {

await expect(
loader({
functionName: 'Inter',
data: [],
functionName: 'Alkalami',
data: [{ variant: '400' }],
config: { subsets: [] },
emitFontFile: jest.fn(),
resolve: jest.fn(),
fs: {} as any,
})
).rejects.toThrowErrorMatchingInlineSnapshot(`
"Failed to fetch font \`Inter\`.
URL: https://fonts.googleapis.com/css2?family=Inter:wght@100..900&display=optional"
`)
"Failed to fetch font \`Alkalami\`.
URL: https://fonts.googleapis.com/css2?family=Alkalami:wght@400&display=optional"
`)
})

test('Missing config with subsets', async () => {
Expand Down

0 comments on commit 602754d

Please sign in to comment.