-
Notifications
You must be signed in to change notification settings - Fork 27.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
931f9b3
commit dabbab2
Showing
11 changed files
with
91 additions
and
104 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,71 +1,29 @@ | ||
// @ts-ignore | ||
import fetch from 'next/dist/compiled/node-fetch' | ||
import { nextFontError } from '../next-font-error' | ||
import { getProxyAgent } from './get-proxy-agent' | ||
import { fetchResource } from './fetch-resource' | ||
import { retry } from './retry' | ||
|
||
/** | ||
* 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. | ||
* | ||
* The env variable NEXT_FONT_GOOGLE_MOCKED_RESPONSES may be set containing a path to mocked data. | ||
* It's used to define mocked data to avoid hitting the Google Fonts API during tests. | ||
*/ | ||
export async function fetchCSSFromGoogleFonts( | ||
url: string, | ||
fontFamily: string, | ||
isDev: boolean | ||
): Promise<string> { | ||
// Check if mocked responses are defined, if so use them instead of fetching from Google Fonts | ||
let mockedResponse: string | undefined | ||
if (process.env.NEXT_FONT_GOOGLE_MOCKED_RESPONSES) { | ||
const mockFile = require(process.env.NEXT_FONT_GOOGLE_MOCKED_RESPONSES) | ||
mockedResponse = mockFile[url] | ||
const mockedResponse = mockFile[url] | ||
if (!mockedResponse) { | ||
nextFontError('Missing mocked response for URL: ' + url) | ||
} | ||
return mockedResponse | ||
} | ||
|
||
let cssResponse: string | ||
if (mockedResponse) { | ||
// Just use the mocked CSS if it's set | ||
cssResponse = mockedResponse | ||
} else { | ||
// 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 | ||
const buffer = await retry(async () => { | ||
return fetchResource( | ||
url, | ||
isDev, | ||
`Failed to fetch font \`${fontFamily}\`: ${url}\n` + | ||
`Please check your network connection.` | ||
) | ||
}, 3) | ||
|
||
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.` | ||
) | ||
} | ||
|
||
return res.text() | ||
}, 3) | ||
} | ||
|
||
return cssResponse | ||
return buffer.toString('utf8') | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,34 +1,24 @@ | ||
// @ts-ignore | ||
import fetch from 'next/dist/compiled/node-fetch' | ||
import { getProxyAgent } from './get-proxy-agent' | ||
import fs from 'node:fs' | ||
import { retry } from './retry' | ||
import { fetchResource } from './fetch-resource' | ||
|
||
/** | ||
* Fetch the url and return a buffer with the font file. | ||
* Fetches a font file and returns its contents as a Buffer. | ||
* If NEXT_FONT_GOOGLE_MOCKED_RESPONSES is set, we handle mock data logic. | ||
*/ | ||
export async function fetchFontFile(url: string, isDev: boolean) { | ||
// Check if we're using mocked data | ||
if (process.env.NEXT_FONT_GOOGLE_MOCKED_RESPONSES) { | ||
// If it's an absolute path, read the file from the filesystem | ||
if (url.startsWith('/')) { | ||
return require('fs').readFileSync(url) | ||
return fs.readFileSync(url) | ||
} | ||
// Otherwise just return a unique buffer | ||
return Buffer.from(url) | ||
} | ||
|
||
return await retry(async () => { | ||
const controller = new AbortController() | ||
const timeoutId = setTimeout(() => controller.abort(), 3000) | ||
const arrayBuffer = await fetch(url, { | ||
agent: getProxyAgent(), | ||
// Add a timeout in dev | ||
signal: isDev ? controller.signal : undefined, | ||
}) | ||
.then((r: any) => r.arrayBuffer()) | ||
.finally(() => { | ||
clearTimeout(timeoutId) | ||
}) | ||
return Buffer.from(arrayBuffer) | ||
return fetchResource( | ||
url, | ||
isDev, | ||
`Failed to fetch font file from \`${url}\`.` | ||
) | ||
}, 3) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import http from 'node:http' | ||
import https from 'node:https' | ||
import { getProxyAgent } from './get-proxy-agent' | ||
|
||
/** | ||
* Makes a simple GET request and returns the entire response as a Buffer. | ||
* - Throws if the response status is not 200. | ||
* - Applies a 3000 ms timeout when `isDev` is `true`. | ||
*/ | ||
export function fetchResource( | ||
url: string, | ||
isDev: boolean, | ||
errorMessage?: string | ||
): Promise<Buffer> { | ||
return new Promise((resolve, reject) => { | ||
const { protocol } = new URL(url) | ||
const client = protocol === 'https:' ? https : http | ||
const timeout = isDev ? 3000 : undefined | ||
|
||
const req = client.request( | ||
url, | ||
{ | ||
agent: getProxyAgent(), | ||
headers: { | ||
'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', | ||
}, | ||
}, | ||
(res) => { | ||
if (res.statusCode !== 200) { | ||
reject( | ||
new Error( | ||
errorMessage || | ||
`Request failed: ${url} (status: ${res.statusCode})` | ||
) | ||
) | ||
return | ||
} | ||
const chunks: Buffer[] = [] | ||
res.on('data', (chunk) => chunks.push(Buffer.from(chunk))) | ||
res.on('end', () => resolve(Buffer.concat(chunks))) | ||
} | ||
) | ||
|
||
if (timeout) { | ||
req.setTimeout(timeout, () => { | ||
req.destroy(new Error(`Request timed out after ${timeout}ms`)) | ||
}) | ||
} | ||
|
||
req.on('error', (err) => reject(err)) | ||
req.end() | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
packages/next/src/compiled/@ampproject/toolbox-optimizer/index.js
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters