Skip to content

Commit

Permalink
Add retries and clear message to font fetching (#51890)
Browse files Browse the repository at this point in the history
Improve the problems mentioned in #45080.

- Adding error retries (3 attempts) to font fetching. When you have a slow network, it might take >3s to load.
- Improve the error message to tell you that you might have unstable network connection.
- Do not cause build error (caused by missing `AbortController`) in Node 14 (even if we don't support Node 14, better to pass the build instead of hard error).
  • Loading branch information
shuding authored Jun 27, 2023
1 parent 2c856cb commit 702d3d2
Showing 1 changed file with 48 additions and 18 deletions.
66 changes: 48 additions & 18 deletions packages/font/src/google/fetch-css-from-google-fonts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,22 @@ import fetch from 'next/dist/compiled/node-fetch'
import { nextFontError } from '../next-font-error'
import { getProxyAgent } from './get-proxy-agent'

async function retry<T>(fn: () => Promise<T>, attempts: number): Promise<T> {
let cnt = attempts
while (true) {
try {
return await fn()
} catch (err) {
cnt--
if (cnt <= 0) throw err
console.error(
(err as Error).message + `\n\nRetrying ${attempts - cnt}/3...`
)
await new Promise((resolve) => setTimeout(resolve, 100))
}
}
}

/**
* Fetches the CSS containing the @font-face declarations from Google Fonts.
* The fetch has a user agent header with a modern browser to ensure we'll get .woff2 files.
Expand Down Expand Up @@ -30,26 +46,40 @@ export async function fetchCSSFromGoogleFonts(
// Just use the mocked CSS if it's set
cssResponse = mockedResponse
} else {
const controller = new AbortController()
const timeoutId = setTimeout(() => controller.abort(), 3000)
const res = await fetch(url, {
agent: getProxyAgent(),
// Add a timeout in dev
signal: isDev ? controller.signal : undefined,
headers: {
// The file format is based off of the user agent, make sure woff2 files are fetched
'user-agent':
'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',
},
}).finally(() => {
clearTimeout(timeoutId)
})
// Retry the fetch a few times in case of network issues as some font files
// are quite large:
// https://github.com/vercel/next.js/issues/45080
cssResponse = await retry(async () => {
const controller =
isDev && typeof AbortController !== 'undefined'
? new AbortController()
: undefined
const signal = controller?.signal
const timeoutId = controller
? setTimeout(() => controller.abort(), 3000)
: undefined

if (!res.ok) {
nextFontError(`Failed to fetch font \`${fontFamily}\`.\nURL: ${url}`)
}
const res = await fetch(url, {
agent: getProxyAgent(),
// Add a timeout in dev
signal,
headers: {
// The file format is based off of the user agent, make sure woff2 files are fetched
'user-agent':
'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',
},
}).finally(() => {
timeoutId && clearTimeout(timeoutId)
})

if (!res.ok) {
nextFontError(
`Failed to fetch font \`${fontFamily}\`.\nURL: ${url}\n\nPlease check if the network is available.`
)
}

cssResponse = await res.text()
return res.text()
}, 3)
}

return cssResponse
Expand Down

0 comments on commit 702d3d2

Please sign in to comment.