Skip to content

Commit 14bfe87

Browse files
authored
Add a timeout to next/font/google in dev (#46834)
Add a timeout to the Google Fonts fetch calls in dev. In case they aren't fetched in time, the fallback font is used instead. Currently if font fetching fails due to network errors in dev, the loader is not cached. Every change on a page that uses a font makes it retry to refetch the font. But, if the network is slow enough to cause timeouts, that means that every change would force the user to wait for the timeout before the page being updated. Because of this, the PR also enables the loader to be cached on error. The drawback is that you would have to delete the `.next` folder to retry to get the actual font in case it got cached after failing. Closes NEXT-726 ## Bug - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Errors have a helpful link attached, see [`contributing.md`](https://github.com/vercel/next.js/blob/canary/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` - [ ] [e2e](https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs) 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`](https://github.com/vercel/next.js/blob/canary/contributing.md) ## Documentation / Examples - [ ] Make sure the linting passes by running `pnpm build && pnpm lint` - [ ] The "examples guidelines" are followed from [our contributing doc](https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md)
1 parent a88b033 commit 14bfe87

File tree

3 files changed

+21
-7
lines changed

3 files changed

+21
-7
lines changed

packages/font/src/google/fetch-css-from-google-fonts.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ import { nextFontError } from '../next-font-error'
1111
*/
1212
export async function fetchCSSFromGoogleFonts(
1313
url: string,
14-
fontFamily: string
14+
fontFamily: string,
15+
isDev: boolean
1516
): Promise<string> {
1617
// Check if mocked responses are defined, if so use them instead of fetching from Google Fonts
1718
let mockedResponse: string | undefined
@@ -28,12 +29,18 @@ export async function fetchCSSFromGoogleFonts(
2829
// Just use the mocked CSS if it's set
2930
cssResponse = mockedResponse
3031
} else {
32+
const controller = new AbortController()
33+
const timeoutId = setTimeout(() => controller.abort(), 3000)
3134
const res = await fetch(url, {
35+
// Add a timeout in dev
36+
signal: isDev ? controller.signal : undefined,
3237
headers: {
3338
// The file format is based off of the user agent, make sure woff2 files are fetched
3439
'user-agent':
3540
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36',
3641
},
42+
}).finally(() => {
43+
clearTimeout(timeoutId)
3744
})
3845

3946
if (!res.ok) {

packages/font/src/google/fetch-font-file.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import fetch from 'next/dist/compiled/node-fetch'
44
/**
55
* Fetch the url and return a buffer with the font file.
66
*/
7-
export async function fetchFontFile(url: string) {
7+
export async function fetchFontFile(url: string, isDev: boolean) {
88
// Check if we're using mocked data
99
if (process.env.NEXT_FONT_GOOGLE_MOCKED_RESPONSES) {
1010
// If it's an absolute path, read the file from the filesystem
@@ -15,6 +15,15 @@ export async function fetchFontFile(url: string) {
1515
return Buffer.from(url)
1616
}
1717

18-
const arrayBuffer = await fetch(url).then((r: any) => r.arrayBuffer())
18+
const controller = new AbortController()
19+
const timeoutId = setTimeout(() => controller.abort(), 3000)
20+
const arrayBuffer = await fetch(url, {
21+
// Add a timeout in dev
22+
signal: isDev ? controller.signal : undefined,
23+
})
24+
.then((r: any) => r.arrayBuffer())
25+
.finally(() => {
26+
clearTimeout(timeoutId)
27+
})
1928
return Buffer.from(arrayBuffer)
2029
}

packages/font/src/google/loader.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ const nextFontGoogleFontLoader: FontLoader = async ({
3232
emitFontFile,
3333
isDev,
3434
isServer,
35-
loaderContext,
3635
}) => {
3736
const {
3837
fontFamily,
@@ -83,7 +82,7 @@ const nextFontGoogleFontLoader: FontLoader = async ({
8382
// Fetch CSS from Google Fonts or get it from the cache
8483
let fontFaceDeclarations = hasCachedCSS
8584
? cssCache.get(url)
86-
: await fetchCSSFromGoogleFonts(url, fontFamily).catch(() => null)
85+
: await fetchCSSFromGoogleFonts(url, fontFamily, isDev).catch(() => null)
8786
if (!hasCachedCSS) {
8887
cssCache.set(url, fontFaceDeclarations ?? null)
8988
} else {
@@ -109,7 +108,7 @@ const nextFontGoogleFontLoader: FontLoader = async ({
109108
// Download the font file or get it from cache
110109
const fontFileBuffer = hasCachedFont
111110
? fontCache.get(googleFontFileUrl)
112-
: await fetchFontFile(googleFontFileUrl).catch(() => null)
111+
: await fetchFontFile(googleFontFileUrl, isDev).catch(() => null)
113112
if (!hasCachedFont) {
114113
fontCache.set(googleFontFileUrl, fontFileBuffer ?? null)
115114
} else {
@@ -157,7 +156,6 @@ const nextFontGoogleFontLoader: FontLoader = async ({
157156
css: updatedCssResponse,
158157
}
159158
} catch (err) {
160-
loaderContext.cacheable(false)
161159
if (isDev) {
162160
if (isServer) {
163161
console.error(err)

0 commit comments

Comments
 (0)