@next/font
Advanced tools
Comparing version 13.1.1 to 13.4.4
import type { FontLoader } from 'next/font'; | ||
declare const downloadGoogleFonts: FontLoader; | ||
export default downloadGoogleFonts; | ||
declare const nextFontGoogleFontLoader: FontLoader; | ||
export default nextFontGoogleFontLoader; |
@@ -25,14 +25,13 @@ "use strict"; | ||
}; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
// @ts-ignore | ||
const font_utils_1 = require("next/dist/server/font-utils"); | ||
// @ts-ignore | ||
const Log = __importStar(require("next/dist/build/output/log")); | ||
// @ts-ignore | ||
const chalk_1 = __importDefault(require("next/dist/compiled/chalk")); | ||
const utils_1 = require("./utils"); | ||
const utils_2 = require("../utils"); | ||
const validate_google_font_function_call_1 = require("./validate-google-font-function-call"); | ||
const get_font_axes_1 = require("./get-font-axes"); | ||
const get_google_fonts_url_1 = require("./get-google-fonts-url"); | ||
const next_font_error_1 = require("../next-font-error"); | ||
const find_font_files_in_css_1 = require("./find-font-files-in-css"); | ||
const get_fallback_font_override_metrics_1 = require("./get-fallback-font-override-metrics"); | ||
const fetch_css_from_google_fonts_1 = require("./fetch-css-from-google-fonts"); | ||
const fetch_font_file_1 = require("./fetch-font-file"); | ||
const cssCache = new Map(); | ||
@@ -50,28 +49,11 @@ const fontCache = new Map(); | ||
} | ||
const downloadGoogleFonts = async ({ functionName, data, config, emitFontFile, isDev, isServer, loaderContext, }) => { | ||
var _a, _b, _c; | ||
const subsets = (config === null || config === void 0 ? void 0 : config.subsets) || []; | ||
const { fontFamily, weights, styles, display, preload, selectedVariableAxes, fallback, adjustFontFallback, variable, subsets: callSubsets, } = (0, utils_1.validateData)(functionName, data); | ||
if (isServer && preload && !callSubsets && !(config === null || config === void 0 ? void 0 : config.subsets)) { | ||
Log.warn(`The ${chalk_1.default.bold('@next/font/google')} font ${chalk_1.default.bold(fontFamily)} has no selected subsets. Please specify subsets in the function call or in your ${chalk_1.default.bold('next.config.js')}, otherwise no fonts will be preloaded. Read more: https://nextjs.org/docs/messages/google-fonts-missing-subsets`); | ||
} | ||
const fontAxes = (0, utils_1.getFontAxes)(fontFamily, weights, styles, selectedVariableAxes); | ||
const url = (0, utils_1.getUrl)(fontFamily, fontAxes, display); | ||
// Find fallback font metrics | ||
let adjustFontFallbackMetrics; | ||
if (adjustFontFallback) { | ||
try { | ||
const { ascent, descent, lineGap, fallbackFont, sizeAdjust } = (0, font_utils_1.calculateSizeAdjustValues)(require('next/dist/server/google-font-metrics.json')[fontFamily]); | ||
adjustFontFallbackMetrics = { | ||
fallbackFont, | ||
ascentOverride: `${ascent}%`, | ||
descentOverride: `${descent}%`, | ||
lineGapOverride: `${lineGap}%`, | ||
sizeAdjust: `${sizeAdjust}%`, | ||
}; | ||
} | ||
catch { | ||
Log.error(`Failed to find font override values for font \`${fontFamily}\``); | ||
} | ||
} | ||
const nextFontGoogleFontLoader = async ({ functionName, data, emitFontFile, isDev, isServer, }) => { | ||
var _a; | ||
const { fontFamily, weights, styles, display, preload, selectedVariableAxes, fallback, adjustFontFallback, variable, subsets, } = (0, validate_google_font_function_call_1.validateGoogleFontFunctionCall)(functionName, data[0]); | ||
// Validate and get the font axes required to generated the URL | ||
const fontAxes = (0, get_font_axes_1.getFontAxes)(fontFamily, weights, styles, selectedVariableAxes); | ||
// Generate the Google Fonts URL from the font family, axes and display value | ||
const url = (0, get_google_fonts_url_1.getGoogleFontsUrl)(fontFamily, fontAxes, display); | ||
// Get precalculated fallback font metrics, used to generate the fallback font CSS | ||
const adjustFontFallbackMetrics = adjustFontFallback ? (0, get_fallback_font_override_metrics_1.getFallbackFontOverrideMetrics)(fontFamily) : undefined; | ||
const result = { | ||
@@ -87,8 +69,17 @@ fallbackFonts: fallback, | ||
try { | ||
/** | ||
* Hacky way to make sure the fetch is only done once. | ||
* Otherwise both the client and server compiler would fetch the CSS. | ||
* The reason we need to return the actual CSS from both the server and client is because a hash is generated based on the CSS content. | ||
*/ | ||
const hasCachedCSS = cssCache.has(url); | ||
// Fetch CSS from Google Fonts or get it from the cache | ||
let fontFaceDeclarations = hasCachedCSS | ||
? cssCache.get(url) | ||
: await (0, utils_1.fetchCSSFromGoogleFonts)(url, fontFamily).catch(() => null); | ||
: await (0, fetch_css_from_google_fonts_1.fetchCSSFromGoogleFonts)(url, fontFamily, isDev).catch((err) => { | ||
console.error(err); | ||
return null; | ||
}); | ||
if (!hasCachedCSS) { | ||
cssCache.set(url, fontFaceDeclarations); | ||
cssCache.set(url, fontFaceDeclarations !== null && fontFaceDeclarations !== void 0 ? fontFaceDeclarations : null); | ||
} | ||
@@ -98,35 +89,21 @@ else { | ||
} | ||
if (fontFaceDeclarations === null) { | ||
(0, utils_2.nextFontError)(`Failed to fetch \`${fontFamily}\` from Google Fonts.`); | ||
if (fontFaceDeclarations == null) { | ||
(0, next_font_error_1.nextFontError)(`Failed to fetch \`${fontFamily}\` from Google Fonts.`); | ||
} | ||
// CSS Variables may be set on a body tag, ignore them to keep the CSS module pure | ||
fontFaceDeclarations = fontFaceDeclarations.split('body {')[0]; | ||
// Find font files to download | ||
const fontFiles = []; | ||
let currentSubset = ''; | ||
for (const line of fontFaceDeclarations.split('\n')) { | ||
// Each @font-face has the subset above it in a comment | ||
const newSubset = (_a = /\/\* (.+?) \*\//.exec(line)) === null || _a === void 0 ? void 0 : _a[1]; | ||
if (newSubset) { | ||
currentSubset = newSubset; | ||
} | ||
else { | ||
const googleFontFileUrl = (_b = /src: url\((.+?)\)/.exec(line)) === null || _b === void 0 ? void 0 : _b[1]; | ||
if (googleFontFileUrl && | ||
!fontFiles.some((foundFile) => foundFile.googleFontFileUrl === googleFontFileUrl)) { | ||
fontFiles.push({ | ||
googleFontFileUrl, | ||
preloadFontFile: !!preload && (callSubsets !== null && callSubsets !== void 0 ? callSubsets : subsets).includes(currentSubset), | ||
}); | ||
} | ||
} | ||
} | ||
// Download font files | ||
// Find font files to download, provide the array of subsets we want to preload if preloading is enabled | ||
const fontFiles = (0, find_font_files_in_css_1.findFontFilesInCss)(fontFaceDeclarations, preload ? subsets : undefined); | ||
// Download the font files extracted from the CSS | ||
const downloadedFiles = await Promise.all(fontFiles.map(async ({ googleFontFileUrl, preloadFontFile }) => { | ||
const hasCachedFont = fontCache.has(googleFontFileUrl); | ||
// Download the font file or get it from cache | ||
const fontFileBuffer = hasCachedFont | ||
? fontCache.get(googleFontFileUrl) | ||
: await (0, utils_1.fetchFontFile)(googleFontFileUrl).catch(() => null); | ||
: await (0, fetch_font_file_1.fetchFontFile)(googleFontFileUrl, isDev).catch((err) => { | ||
console.error(err); | ||
return null; | ||
}); | ||
if (!hasCachedFont) { | ||
fontCache.set(googleFontFileUrl, fontFileBuffer); | ||
fontCache.set(googleFontFileUrl, fontFileBuffer !== null && fontFileBuffer !== void 0 ? fontFileBuffer : null); | ||
} | ||
@@ -136,8 +113,8 @@ else { | ||
} | ||
if (fontFileBuffer === null) { | ||
(0, utils_2.nextFontError)(`Failed to fetch \`${fontFamily}\` from Google Fonts.`); | ||
if (fontFileBuffer == null) { | ||
(0, next_font_error_1.nextFontError)(`Failed to fetch \`${fontFamily}\` from Google Fonts.`); | ||
} | ||
const ext = /\.(woff|woff2|eot|ttf|otf)$/.exec(googleFontFileUrl)[1]; | ||
// Emit font file to .next/static/media | ||
const selfHostedFileUrl = emitFontFile(fontFileBuffer, ext, preloadFontFile); | ||
const selfHostedFileUrl = emitFontFile(fontFileBuffer, ext, preloadFontFile, !!adjustFontFallbackMetrics); | ||
return { | ||
@@ -148,3 +125,11 @@ googleFontFileUrl, | ||
})); | ||
// Replace @font-face sources with self-hosted files | ||
/** | ||
* Replace the @font-face sources with the self-hosted files we just downloaded to .next/static/media | ||
* | ||
* E.g. | ||
* @font-face { | ||
* font-family: 'Inter'; | ||
* src: url(https://fonts.gstatic.com/...) -> url(/_next/static/media/_.woff2) | ||
* } | ||
*/ | ||
let updatedCssResponse = fontFaceDeclarations; | ||
@@ -160,3 +145,2 @@ for (const { googleFontFileUrl, selfHostedFileUrl } of downloadedFiles) { | ||
catch (err) { | ||
loaderContext.cacheable(false); | ||
if (isDev) { | ||
@@ -169,3 +153,3 @@ if (isServer) { | ||
font-family: '${fontFamily} Fallback'; | ||
src: local("${(_c = adjustFontFallbackMetrics === null || adjustFontFallbackMetrics === void 0 ? void 0 : adjustFontFallbackMetrics.fallbackFont) !== null && _c !== void 0 ? _c : 'Arial'}");`; | ||
src: local("${(_a = adjustFontFallbackMetrics === null || adjustFontFallbackMetrics === void 0 ? void 0 : adjustFontFallbackMetrics.fallbackFont) !== null && _a !== void 0 ? _a : 'Arial'}");`; | ||
if (adjustFontFallbackMetrics) { | ||
@@ -189,2 +173,2 @@ css += ` | ||
}; | ||
exports.default = downloadGoogleFonts; | ||
exports.default = nextFontGoogleFontLoader; |
import type { FontLoader } from 'next/font'; | ||
declare const fetchFonts: FontLoader; | ||
export default fetchFonts; | ||
declare const nextFontLocalFontLoader: FontLoader; | ||
export default nextFontLocalFontLoader; |
"use strict"; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
// @ts-ignore | ||
// eslint-disable-next-line import/no-extraneous-dependencies | ||
const fontkit_1 = __importDefault(require("@next/font/dist/fontkit")); | ||
let fontFromBuffer; | ||
try { | ||
const mod = require('../fontkit').default; | ||
fontFromBuffer = mod.default || mod; | ||
} | ||
catch { } | ||
const util_1 = require("util"); | ||
const utils_1 = require("./utils"); | ||
const utils_2 = require("../utils"); | ||
const NORMAL_WEIGHT = 400; | ||
const BOLD_WEIGHT = 700; | ||
function getWeightNumber(weight) { | ||
// Weight can be 'normal', 'bold' or a number https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-weight | ||
return weight === 'normal' | ||
? NORMAL_WEIGHT | ||
: weight === 'bold' | ||
? BOLD_WEIGHT | ||
: Number(weight); | ||
} | ||
function getDistanceFromNormalWeight(weight) { | ||
if (!weight) | ||
return 0; | ||
const [firstWeight, secondWeight] = weight | ||
.trim() | ||
.split(/ +/) | ||
.map(getWeightNumber); | ||
if (Number.isNaN(firstWeight) || Number.isNaN(secondWeight)) { | ||
(0, utils_2.nextFontError)(`Invalid weight value in src array: \`${weight}\`.\nExpected \`normal\`, \`bold\` or a number.`); | ||
} | ||
// Not a variable font | ||
if (!secondWeight) { | ||
return firstWeight - NORMAL_WEIGHT; | ||
} | ||
// Normal weight is within variable font range | ||
if (firstWeight <= NORMAL_WEIGHT && secondWeight >= NORMAL_WEIGHT) { | ||
return 0; | ||
} | ||
// Return the distance of normal weight to the variable font range | ||
const firstWeightDistance = firstWeight - NORMAL_WEIGHT; | ||
const secondWeightDistance = secondWeight - NORMAL_WEIGHT; | ||
if (Math.abs(firstWeightDistance) < Math.abs(secondWeightDistance)) { | ||
return firstWeightDistance; | ||
} | ||
return secondWeightDistance; | ||
} | ||
const fetchFonts = async ({ functionName, variableName, data, emitFontFile, resolve, loaderContext, }) => { | ||
const { src, display, fallback, preload, variable, adjustFontFallback, declarations, weight: defaultWeight, style: defaultStyle, } = (0, utils_1.validateData)(functionName, data[0]); | ||
const pick_font_file_for_fallback_generation_1 = require("./pick-font-file-for-fallback-generation"); | ||
const get_fallback_metrics_from_font_file_1 = require("./get-fallback-metrics-from-font-file"); | ||
const validate_local_font_function_call_1 = require("./validate-local-font-function-call"); | ||
const nextFontLocalFontLoader = async ({ functionName, variableName, data, emitFontFile, resolve, loaderContext, }) => { | ||
const { src, display, fallback, preload, variable, adjustFontFallback, declarations, weight: defaultWeight, style: defaultStyle, } = (0, validate_local_font_function_call_1.validateLocalFontFunctionCall)(functionName, data[0]); | ||
// Load all font files and emit them to the .next output directory | ||
// Also generate a @font-face CSS for each font file | ||
const fontFiles = await Promise.all(src.map(async ({ path, style, weight, ext, format }) => { | ||
const resolved = await resolve(path); | ||
const fileBuffer = await (0, util_1.promisify)(loaderContext.fs.readFile)(resolved); | ||
const fontUrl = emitFontFile(fileBuffer, ext, preload); | ||
const fontUrl = emitFontFile(fileBuffer, ext, preload, typeof adjustFontFallback === 'undefined' || !!adjustFontFallback); | ||
// Try to load font metadata from the font file using fontkit. | ||
// The data is used to calculate the fallback font override values. | ||
let fontMetadata; | ||
try { | ||
fontMetadata = (0, fontkit_1.default)(fileBuffer); | ||
fontMetadata = fontFromBuffer === null || fontFromBuffer === void 0 ? void 0 : fontFromBuffer(fileBuffer); | ||
} | ||
@@ -61,2 +32,3 @@ catch (e) { | ||
} | ||
// Get all values that should be added to the @font-face declaration | ||
const fontFaceProperties = [ | ||
@@ -76,8 +48,8 @@ ...(declarations | ||
]; | ||
// Generate the @font-face CSS from the font-face properties | ||
const css = `@font-face {\n${fontFaceProperties | ||
.map(([property, value]) => `${property}: ${value};`) | ||
.join('\n')}\n}\n`; | ||
return { | ||
css: `@font-face { | ||
${fontFaceProperties | ||
.map(([property, value]) => `${property}: ${value};`) | ||
.join('\n')} | ||
}\n`, | ||
css, | ||
fontMetadata, | ||
@@ -88,32 +60,8 @@ weight, | ||
})); | ||
// Add fallback font | ||
// Calculate the fallback font override values using the font file metadata | ||
let adjustFontFallbackMetrics; | ||
if (adjustFontFallback !== false) { | ||
// Pick the font file to generate a fallback font from. | ||
// Prefer the file closest to normal weight, this will typically make up most of the text on a page. | ||
const fallbackFontFile = fontFiles.reduce((usedFontFile, currentFontFile) => { | ||
if (!usedFontFile) | ||
return currentFontFile; | ||
const usedFontDistance = getDistanceFromNormalWeight(usedFontFile.weight); | ||
const currentFontDistance = getDistanceFromNormalWeight(currentFontFile.weight); | ||
// Prefer normal style if they have the same weight | ||
if (usedFontDistance === currentFontDistance && | ||
(typeof currentFontFile.style === 'undefined' || | ||
currentFontFile.style === 'normal')) { | ||
return currentFontFile; | ||
} | ||
const absUsedDistance = Math.abs(usedFontDistance); | ||
const absCurrentDistance = Math.abs(currentFontDistance); | ||
// Use closest absolute distance to normal weight | ||
if (absCurrentDistance < absUsedDistance) | ||
return currentFontFile; | ||
// Prefer the thinner font if both are the same absolute distance from normal weight | ||
if (absUsedDistance === absCurrentDistance && | ||
currentFontDistance < usedFontDistance) { | ||
return currentFontFile; | ||
} | ||
return usedFontFile; | ||
}); | ||
const fallbackFontFile = (0, pick_font_file_for_fallback_generation_1.pickFontFileForFallbackGeneration)(fontFiles); | ||
if (fallbackFontFile.fontMetadata) { | ||
adjustFontFallbackMetrics = (0, utils_2.calculateFallbackFontValues)(fallbackFontFile.fontMetadata, adjustFontFallback === 'Times New Roman' ? 'serif' : 'sans-serif'); | ||
adjustFontFallbackMetrics = (0, get_fallback_metrics_from_font_file_1.getFallbackMetricsFromFontFile)(fallbackFontFile.fontMetadata, adjustFontFallback === 'Times New Roman' ? 'serif' : 'sans-serif'); | ||
} | ||
@@ -130,2 +78,2 @@ } | ||
}; | ||
exports.default = fetchFonts; | ||
exports.default = nextFontLocalFontLoader; |
{ | ||
"name": "@next/font", | ||
"version": "13.1.1", | ||
"version": "13.4.4", | ||
"repository": { | ||
@@ -8,2 +8,3 @@ "url": "vercel/next.js", | ||
}, | ||
"types": "dist/types.d.ts", | ||
"files": [ | ||
@@ -16,3 +17,3 @@ "dist", | ||
"scripts": { | ||
"build": "rimraf dist && pnpm ncc-fontkit && tsc -d -p tsconfig.json", | ||
"build": "node ../../scripts/rm.mjs dist && pnpm ncc-fontkit && tsc -d -p tsconfig.json", | ||
"prepublishOnly": "cd ../../ && turbo run build", | ||
@@ -19,0 +20,0 @@ "dev": "pnpm ncc-fontkit && tsc -d -w -p tsconfig.json", |
@@ -7,2 +7,2 @@ # `@next/font` | ||
[Read more](https://beta.nextjs.org/docs/optimizing/fonts) | ||
[Read more](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 4 instances in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
2533914
68
56699
12