.
This commit is contained in:
+1
@@ -0,0 +1 @@
|
||||
export declare const allowedDisplayValues: string[];
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.allowedDisplayValues = void 0;
|
||||
exports.allowedDisplayValues = [
|
||||
'auto',
|
||||
'block',
|
||||
'swap',
|
||||
'fallback',
|
||||
'optional',
|
||||
];
|
||||
+1
File diff suppressed because one or more lines are too long
+5
@@ -0,0 +1,5 @@
|
||||
/**
|
||||
* Formats an array of values into a string that can be used error messages.
|
||||
* ["a", "b", "c"] => "`a`, `b`, `c`"
|
||||
*/
|
||||
export declare const formatAvailableValues: (values: string[]) => string;
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.formatAvailableValues = void 0;
|
||||
/**
|
||||
* Formats an array of values into a string that can be used error messages.
|
||||
* ["a", "b", "c"] => "`a`, `b`, `c`"
|
||||
*/
|
||||
const formatAvailableValues = (values) => values.map((val) => `\`${val}\``).join(', ');
|
||||
exports.formatAvailableValues = formatAvailableValues;
|
||||
Generated
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* 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 declare function fetchCSSFromGoogleFonts(url: string, fontFamily: string, isDev: boolean): Promise<string>;
|
||||
Generated
Vendored
+28
@@ -0,0 +1,28 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.fetchCSSFromGoogleFonts = fetchCSSFromGoogleFonts;
|
||||
const next_font_error_1 = require("../next-font-error");
|
||||
const fetch_resource_1 = require("./fetch-resource");
|
||||
const retry_1 = require("./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.
|
||||
*/
|
||||
async function fetchCSSFromGoogleFonts(url, fontFamily, isDev) {
|
||||
if (process.env.NEXT_FONT_GOOGLE_MOCKED_RESPONSES) {
|
||||
const mockFile = require(process.env.NEXT_FONT_GOOGLE_MOCKED_RESPONSES);
|
||||
const mockedResponse = mockFile[url];
|
||||
if (!mockedResponse) {
|
||||
(0, next_font_error_1.nextFontError)('Missing mocked response for URL: ' + url);
|
||||
}
|
||||
return mockedResponse;
|
||||
}
|
||||
const buffer = await (0, retry_1.retry)(async () => {
|
||||
return (0, fetch_resource_1.fetchResource)(url, isDev, `Failed to fetch font \`${fontFamily}\`: ${url}\n` +
|
||||
`Please check your network connection.`);
|
||||
}, 3);
|
||||
return buffer.toString('utf8');
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
/**
|
||||
* 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 declare function fetchFontFile(url: string, isDev: boolean): Promise<any>;
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.fetchFontFile = fetchFontFile;
|
||||
const node_fs_1 = __importDefault(require("node:fs"));
|
||||
const retry_1 = require("./retry");
|
||||
const fetch_resource_1 = require("./fetch-resource");
|
||||
/**
|
||||
* Fetches a font file and returns its contents as a Buffer.
|
||||
* If NEXT_FONT_GOOGLE_MOCKED_RESPONSES is set, we handle mock data logic.
|
||||
*/
|
||||
async function fetchFontFile(url, isDev) {
|
||||
if (process.env.NEXT_FONT_GOOGLE_MOCKED_RESPONSES) {
|
||||
if (url.startsWith('/')) {
|
||||
return node_fs_1.default.readFileSync(url);
|
||||
}
|
||||
return Buffer.from(url);
|
||||
}
|
||||
return await (0, retry_1.retry)(async () => {
|
||||
return (0, fetch_resource_1.fetchResource)(url, isDev, `Failed to fetch font file from \`${url}\`.`);
|
||||
}, 3);
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* 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 declare function fetchResource(url: string, isDev: boolean, errorMessage?: string): Promise<Buffer>;
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.fetchResource = fetchResource;
|
||||
const node_http_1 = __importDefault(require("node:http"));
|
||||
const node_https_1 = __importDefault(require("node:https"));
|
||||
const get_proxy_agent_1 = require("./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`.
|
||||
*/
|
||||
function fetchResource(url, isDev, errorMessage) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const { protocol } = new URL(url);
|
||||
const client = protocol === 'https:' ? node_https_1.default : node_http_1.default;
|
||||
const timeout = isDev ? 3000 : undefined;
|
||||
const req = client.request(url, {
|
||||
agent: (0, get_proxy_agent_1.getProxyAgent)(),
|
||||
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',
|
||||
},
|
||||
}, (res) => {
|
||||
if (res.statusCode !== 200) {
|
||||
reject(new Error(errorMessage ||
|
||||
`Request failed: ${url} (status: ${res.statusCode})`));
|
||||
return;
|
||||
}
|
||||
const chunks = [];
|
||||
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();
|
||||
});
|
||||
}
|
||||
Generated
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* Find all font files in the CSS response and determine which files should be preloaded.
|
||||
* In Google Fonts responses, the @font-face's subset is above it in a comment.
|
||||
* Walk through the CSS from top to bottom, keeping track of the current subset.
|
||||
*/
|
||||
export declare function findFontFilesInCss(css: string, subsetsToPreload?: string[]): {
|
||||
googleFontFileUrl: string;
|
||||
preloadFontFile: boolean;
|
||||
}[];
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.findFontFilesInCss = findFontFilesInCss;
|
||||
/**
|
||||
* Find all font files in the CSS response and determine which files should be preloaded.
|
||||
* In Google Fonts responses, the @font-face's subset is above it in a comment.
|
||||
* Walk through the CSS from top to bottom, keeping track of the current subset.
|
||||
*/
|
||||
function findFontFilesInCss(css, subsetsToPreload) {
|
||||
var _a, _b;
|
||||
// Find font files to download
|
||||
const fontFiles = [];
|
||||
// Keep track of the current subset
|
||||
let currentSubset = '';
|
||||
for (const line of css.split('\n')) {
|
||||
const newSubset = (_a = /\/\* (.+?) \*\//.exec(line)) === null || _a === void 0 ? void 0 : _a[1];
|
||||
if (newSubset) {
|
||||
// Found new subset in a comment above the next @font-face declaration
|
||||
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)) {
|
||||
// Found the font file in the @font-face declaration.
|
||||
fontFiles.push({
|
||||
googleFontFileUrl,
|
||||
preloadFontFile: !!(subsetsToPreload === null || subsetsToPreload === void 0 ? void 0 : subsetsToPreload.includes(currentSubset)),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return fontFiles;
|
||||
}
|
||||
+18866
File diff suppressed because it is too large
Load Diff
Generated
Vendored
+14
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Get precalculated fallback font metrics for the Google Fonts family.
|
||||
*
|
||||
* TODO:
|
||||
* We might want to calculate these values with fontkit instead (like in next/font/local).
|
||||
* That way we don't have to update the precalculated values every time a new font is added to Google Fonts.
|
||||
*/
|
||||
export declare function getFallbackFontOverrideMetrics(fontFamily: string): {
|
||||
fallbackFont: any;
|
||||
ascentOverride: string;
|
||||
descentOverride: string;
|
||||
lineGapOverride: string;
|
||||
sizeAdjust: string;
|
||||
} | undefined;
|
||||
Generated
Vendored
+62
@@ -0,0 +1,62 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getFallbackFontOverrideMetrics = getFallbackFontOverrideMetrics;
|
||||
// @ts-ignore
|
||||
const font_utils_1 = require("next/dist/server/font-utils");
|
||||
// @ts-ignore
|
||||
const Log = __importStar(require("next/dist/build/output/log"));
|
||||
/**
|
||||
* Get precalculated fallback font metrics for the Google Fonts family.
|
||||
*
|
||||
* TODO:
|
||||
* We might want to calculate these values with fontkit instead (like in next/font/local).
|
||||
* That way we don't have to update the precalculated values every time a new font is added to Google Fonts.
|
||||
*/
|
||||
function getFallbackFontOverrideMetrics(fontFamily) {
|
||||
try {
|
||||
const { ascent, descent, lineGap, fallbackFont, sizeAdjust } = (0, font_utils_1.calculateSizeAdjustValues)(fontFamily);
|
||||
return {
|
||||
fallbackFont,
|
||||
ascentOverride: `${ascent}%`,
|
||||
descentOverride: `${descent}%`,
|
||||
lineGapOverride: `${lineGap}%`,
|
||||
sizeAdjust: `${sizeAdjust}%`,
|
||||
};
|
||||
}
|
||||
catch {
|
||||
Log.error(`Failed to find font override values for font \`${fontFamily}\``);
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Validates and gets the data for each font axis required to generate the Google Fonts URL.
|
||||
*/
|
||||
export declare function getFontAxes(fontFamily: string, weights: string[], styles: string[], selectedVariableAxes?: string[]): {
|
||||
wght?: string[];
|
||||
ital?: string[];
|
||||
variableAxes?: [string, string][];
|
||||
};
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getFontAxes = getFontAxes;
|
||||
const format_available_values_1 = require("../format-available-values");
|
||||
const next_font_error_1 = require("../next-font-error");
|
||||
const google_fonts_metadata_1 = require("./google-fonts-metadata");
|
||||
/**
|
||||
* Validates and gets the data for each font axis required to generate the Google Fonts URL.
|
||||
*/
|
||||
function getFontAxes(fontFamily, weights, styles, selectedVariableAxes) {
|
||||
const hasItalic = styles.includes('italic');
|
||||
const hasNormal = styles.includes('normal');
|
||||
// Make sure the order is correct, otherwise Google Fonts will return an error
|
||||
// If only normal is set, we can skip returning the ital axis as normal is the default
|
||||
const ital = hasItalic ? [...(hasNormal ? ['0'] : []), '1'] : undefined;
|
||||
// Weights will always contain one element if it's a variable font
|
||||
if (weights[0] === 'variable') {
|
||||
// Get all the available axes for the current font from the metadata file
|
||||
const allAxes = google_fonts_metadata_1.googleFontsMetadata[fontFamily].axes;
|
||||
if (!allAxes) {
|
||||
throw new Error('invariant variable font without axes');
|
||||
}
|
||||
if (selectedVariableAxes) {
|
||||
// The axes other than weight and style that can be defined for the current variable font
|
||||
const defineAbleAxes = allAxes
|
||||
.map(({ tag }) => tag)
|
||||
.filter((tag) => tag !== 'wght');
|
||||
if (defineAbleAxes.length === 0) {
|
||||
(0, next_font_error_1.nextFontError)(`Font \`${fontFamily}\` has no definable \`axes\``);
|
||||
}
|
||||
if (!Array.isArray(selectedVariableAxes)) {
|
||||
(0, next_font_error_1.nextFontError)(`Invalid axes value for font \`${fontFamily}\`, expected an array of axes.\nAvailable axes: ${(0, format_available_values_1.formatAvailableValues)(defineAbleAxes)}`);
|
||||
}
|
||||
selectedVariableAxes.forEach((key) => {
|
||||
if (!defineAbleAxes.some((tag) => tag === key)) {
|
||||
(0, next_font_error_1.nextFontError)(`Invalid axes value \`${key}\` for font \`${fontFamily}\`.\nAvailable axes: ${(0, format_available_values_1.formatAvailableValues)(defineAbleAxes)}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
let weightAxis;
|
||||
let variableAxes;
|
||||
for (const { tag, min, max } of allAxes) {
|
||||
if (tag === 'wght') {
|
||||
// In variable fonts the weight is a range
|
||||
weightAxis = `${min}..${max}`;
|
||||
}
|
||||
else if (selectedVariableAxes === null || selectedVariableAxes === void 0 ? void 0 : selectedVariableAxes.includes(tag)) {
|
||||
if (!variableAxes) {
|
||||
variableAxes = [];
|
||||
}
|
||||
variableAxes.push([tag, `${min}..${max}`]);
|
||||
}
|
||||
}
|
||||
return {
|
||||
wght: weightAxis ? [weightAxis] : undefined,
|
||||
ital,
|
||||
variableAxes,
|
||||
};
|
||||
}
|
||||
else {
|
||||
return {
|
||||
ital,
|
||||
wght: weights,
|
||||
};
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Generate the Google Fonts URL given the requested weight(s), style(s) and additional variable axes
|
||||
*/
|
||||
export declare function getGoogleFontsUrl(fontFamily: string, axes: {
|
||||
wght?: string[];
|
||||
ital?: string[];
|
||||
variableAxes?: [string, string][];
|
||||
}, display: string): string;
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getGoogleFontsUrl = getGoogleFontsUrl;
|
||||
const sort_fonts_variant_values_1 = require("./sort-fonts-variant-values");
|
||||
/**
|
||||
* Generate the Google Fonts URL given the requested weight(s), style(s) and additional variable axes
|
||||
*/
|
||||
function getGoogleFontsUrl(fontFamily, axes, display) {
|
||||
var _a, _b;
|
||||
// Variants are all combinations of weight and style, each variant will result in a separate font file
|
||||
const variants = [];
|
||||
if (axes.wght) {
|
||||
for (const wght of axes.wght) {
|
||||
if (!axes.ital) {
|
||||
variants.push([['wght', wght], ...((_a = axes.variableAxes) !== null && _a !== void 0 ? _a : [])]);
|
||||
}
|
||||
else {
|
||||
for (const ital of axes.ital) {
|
||||
variants.push([
|
||||
['ital', ital],
|
||||
['wght', wght],
|
||||
...((_b = axes.variableAxes) !== null && _b !== void 0 ? _b : []),
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (axes.variableAxes) {
|
||||
// Variable fonts might not have a range of weights, just add optional variable axes in that case
|
||||
variants.push([...axes.variableAxes]);
|
||||
}
|
||||
// Google api requires the axes to be sorted, starting with lowercase words
|
||||
if (axes.variableAxes) {
|
||||
variants.forEach((variant) => {
|
||||
variant.sort(([a], [b]) => {
|
||||
const aIsLowercase = a.charCodeAt(0) > 96;
|
||||
const bIsLowercase = b.charCodeAt(0) > 96;
|
||||
if (aIsLowercase && !bIsLowercase)
|
||||
return -1;
|
||||
if (bIsLowercase && !aIsLowercase)
|
||||
return 1;
|
||||
return a > b ? 1 : -1;
|
||||
});
|
||||
});
|
||||
}
|
||||
let url = `https://fonts.googleapis.com/css2?family=${fontFamily.replace(/ /g, '+')}`;
|
||||
if (variants.length > 0) {
|
||||
url = `${url}:${variants[0].map(([key]) => key).join(',')}@${variants
|
||||
.map((variant) => variant.map(([, val]) => val).join(','))
|
||||
.sort(sort_fonts_variant_values_1.sortFontsVariantValues)
|
||||
.join(';')}`;
|
||||
}
|
||||
url = `${url}&display=${display}`;
|
||||
return url;
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
import type { Agent } from 'https';
|
||||
/**
|
||||
* If the http(s)_proxy environment variables is set, return a proxy agent.
|
||||
*/
|
||||
export declare function getProxyAgent(): Agent | undefined;
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getProxyAgent = getProxyAgent;
|
||||
// @ts-ignore
|
||||
const https_proxy_agent_1 = __importDefault(require("next/dist/compiled/https-proxy-agent"));
|
||||
// @ts-ignore
|
||||
const http_proxy_agent_1 = __importDefault(require("next/dist/compiled/http-proxy-agent"));
|
||||
/**
|
||||
* If the http(s)_proxy environment variables is set, return a proxy agent.
|
||||
*/
|
||||
function getProxyAgent() {
|
||||
const httpsProxy = process.env['https_proxy'] || process.env['HTTPS_PROXY'];
|
||||
if (httpsProxy) {
|
||||
return new https_proxy_agent_1.default(httpsProxy);
|
||||
}
|
||||
const httpProxy = process.env['http_proxy'] || process.env['HTTP_PROXY'];
|
||||
if (httpProxy) {
|
||||
return new http_proxy_agent_1.default(httpProxy);
|
||||
}
|
||||
}
|
||||
Generated
Vendored
+15
@@ -0,0 +1,15 @@
|
||||
type GoogleFontsMetadata = {
|
||||
[fontFamily: string]: {
|
||||
weights: string[];
|
||||
styles: string[];
|
||||
subsets: string[];
|
||||
axes?: Array<{
|
||||
tag: string;
|
||||
min: number;
|
||||
max: number;
|
||||
defaultValue: number;
|
||||
}>;
|
||||
};
|
||||
};
|
||||
export declare const googleFontsMetadata: GoogleFontsMetadata;
|
||||
export {};
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.googleFontsMetadata = void 0;
|
||||
const font_data_json_1 = __importDefault(require("./font-data.json"));
|
||||
exports.googleFontsMetadata = font_data_json_1.default;
|
||||
+19045
File diff suppressed because it is too large
Load Diff
+2
@@ -0,0 +1,2 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
import type { FontLoader } from 'next/font';
|
||||
declare const nextFontGoogleFontLoader: FontLoader;
|
||||
export default nextFontGoogleFontLoader;
|
||||
+175
@@ -0,0 +1,175 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
// @ts-ignore
|
||||
const Log = __importStar(require("next/dist/build/output/log"));
|
||||
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();
|
||||
const fontCache = new Map();
|
||||
// regexp is based on https://github.com/sindresorhus/escape-string-regexp
|
||||
const reHasRegExp = /[|\\{}()[\]^$+*?.-]/;
|
||||
const reReplaceRegExp = /[|\\{}()[\]^$+*?.-]/g;
|
||||
function escapeStringRegexp(str) {
|
||||
// see also: https://github.com/lodash/lodash/blob/2da024c3b4f9947a48517639de7560457cd4ec6c/escapeRegExp.js#L23
|
||||
if (reHasRegExp.test(str)) {
|
||||
return str.replace(reReplaceRegExp, '\\$&');
|
||||
}
|
||||
return str;
|
||||
}
|
||||
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 = {
|
||||
fallbackFonts: fallback,
|
||||
weight: weights.length === 1 && weights[0] !== 'variable'
|
||||
? weights[0]
|
||||
: undefined,
|
||||
style: styles.length === 1 ? styles[0] : undefined,
|
||||
variable,
|
||||
adjustFontFallback: adjustFontFallbackMetrics,
|
||||
};
|
||||
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, fetch_css_from_google_fonts_1.fetchCSSFromGoogleFonts)(url, fontFamily, isDev).catch((err) => {
|
||||
console.error(err);
|
||||
return null;
|
||||
});
|
||||
if (!hasCachedCSS) {
|
||||
cssCache.set(url, fontFaceDeclarations !== null && fontFaceDeclarations !== void 0 ? fontFaceDeclarations : null);
|
||||
}
|
||||
else {
|
||||
cssCache.delete(url);
|
||||
}
|
||||
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 {', 1)[0];
|
||||
// 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, fetch_font_file_1.fetchFontFile)(googleFontFileUrl, isDev).catch((err) => {
|
||||
console.error(err);
|
||||
return null;
|
||||
});
|
||||
if (!hasCachedFont) {
|
||||
fontCache.set(googleFontFileUrl, fontFileBuffer !== null && fontFileBuffer !== void 0 ? fontFileBuffer : null);
|
||||
}
|
||||
else {
|
||||
fontCache.delete(googleFontFileUrl);
|
||||
}
|
||||
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, !!adjustFontFallbackMetrics);
|
||||
return {
|
||||
googleFontFileUrl,
|
||||
selfHostedFileUrl,
|
||||
};
|
||||
}));
|
||||
/**
|
||||
* 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;
|
||||
for (const { googleFontFileUrl, selfHostedFileUrl } of downloadedFiles) {
|
||||
updatedCssResponse = updatedCssResponse.replace(new RegExp(escapeStringRegexp(googleFontFileUrl), 'g'), selfHostedFileUrl);
|
||||
}
|
||||
return {
|
||||
...result,
|
||||
css: updatedCssResponse,
|
||||
};
|
||||
}
|
||||
catch (err) {
|
||||
if (isDev) {
|
||||
if (isServer) {
|
||||
Log.error(`Failed to download \`${fontFamily}\` from Google Fonts. Using fallback font instead.\n\n${err.message}}`);
|
||||
}
|
||||
// In dev we should return the fallback font instead of throwing an error
|
||||
let css = `@font-face {
|
||||
font-family: '${fontFamily} Fallback';
|
||||
src: local("${(_a = adjustFontFallbackMetrics === null || adjustFontFallbackMetrics === void 0 ? void 0 : adjustFontFallbackMetrics.fallbackFont) !== null && _a !== void 0 ? _a : 'Arial'}");`;
|
||||
if (adjustFontFallbackMetrics) {
|
||||
css += `
|
||||
ascent-override:${adjustFontFallbackMetrics.ascentOverride};
|
||||
descent-override:${adjustFontFallbackMetrics.descentOverride};
|
||||
line-gap-override:${adjustFontFallbackMetrics.lineGapOverride};
|
||||
size-adjust:${adjustFontFallbackMetrics.sizeAdjust};`;
|
||||
}
|
||||
css += '\n}';
|
||||
return {
|
||||
...result,
|
||||
css,
|
||||
};
|
||||
}
|
||||
else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
};
|
||||
exports.default = nextFontGoogleFontLoader;
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
import asyncRetry from 'next/dist/compiled/async-retry';
|
||||
export declare function retry<T>(fn: asyncRetry.RetryFunction<T>, retries: number): Promise<any>;
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.retry = retry;
|
||||
// @ts-expect-error File exists
|
||||
const async_retry_1 = __importDefault(require("next/dist/compiled/async-retry"));
|
||||
async function retry(fn, retries) {
|
||||
return await (0, async_retry_1.default)(fn, {
|
||||
retries,
|
||||
onRetry(e, attempt) {
|
||||
console.error(e.message + `\n\nRetrying ${attempt}/${retries}...`);
|
||||
},
|
||||
minTimeout: 100,
|
||||
});
|
||||
}
|
||||
Generated
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
/**
|
||||
* Callback function for sorting font variant values.
|
||||
* Used as a parameter in `Array.prototype.sort` function to ensure correct sorting.
|
||||
*/
|
||||
export declare function sortFontsVariantValues(valA: string, valB: string): number;
|
||||
Generated
Vendored
+26
@@ -0,0 +1,26 @@
|
||||
"use strict";
|
||||
/**
|
||||
* Callback function for sorting font variant values.
|
||||
* Used as a parameter in `Array.prototype.sort` function to ensure correct sorting.
|
||||
*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.sortFontsVariantValues = sortFontsVariantValues;
|
||||
function sortFontsVariantValues(valA, valB) {
|
||||
// If both values contain commas, it indicates they are in "ital,wght" format
|
||||
if (valA.includes(',') && valB.includes(',')) {
|
||||
// Split the values into prefix and suffix
|
||||
const [aPrefix, aSuffix] = valA.split(',', 2);
|
||||
const [bPrefix, bSuffix] = valB.split(',', 2);
|
||||
// Compare the prefixes (ital values)
|
||||
if (aPrefix === bPrefix) {
|
||||
// If prefixes are equal, then compare the suffixes (wght values)
|
||||
return parseInt(aSuffix) - parseInt(bSuffix);
|
||||
}
|
||||
else {
|
||||
// If prefixes are different, then compare the prefixes directly
|
||||
return parseInt(aPrefix) - parseInt(bPrefix);
|
||||
}
|
||||
}
|
||||
// If values are not in "ital,wght" format, then directly compare them as integers
|
||||
return parseInt(valA) - parseInt(valB);
|
||||
}
|
||||
Generated
Vendored
+17
@@ -0,0 +1,17 @@
|
||||
type FontOptions = {
|
||||
fontFamily: string;
|
||||
weights: string[];
|
||||
styles: string[];
|
||||
display: string;
|
||||
preload: boolean;
|
||||
selectedVariableAxes?: string[];
|
||||
fallback?: string[];
|
||||
adjustFontFallback: boolean;
|
||||
variable?: string;
|
||||
subsets: string[];
|
||||
};
|
||||
/**
|
||||
* Validate the data received from next-swc next-transform-font on next/font/google calls
|
||||
*/
|
||||
export declare function validateGoogleFontFunctionCall(functionName: string, fontFunctionArgument: any): FontOptions;
|
||||
export {};
|
||||
Generated
Vendored
+101
@@ -0,0 +1,101 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.validateGoogleFontFunctionCall = validateGoogleFontFunctionCall;
|
||||
const constants_1 = require("../constants");
|
||||
const format_available_values_1 = require("../format-available-values");
|
||||
const next_font_error_1 = require("../next-font-error");
|
||||
const google_fonts_metadata_1 = require("./google-fonts-metadata");
|
||||
/**
|
||||
* Validate the data received from next-swc next-transform-font on next/font/google calls
|
||||
*/
|
||||
function validateGoogleFontFunctionCall(functionName, fontFunctionArgument) {
|
||||
let { weight, style, preload = true, display = 'swap', axes, fallback, adjustFontFallback = true, variable, subsets, } = fontFunctionArgument || {};
|
||||
if (functionName === '') {
|
||||
(0, next_font_error_1.nextFontError)(`next/font/google has no default export`);
|
||||
}
|
||||
const fontFamily = functionName.replace(/_/g, ' ');
|
||||
// Get the Google font metadata, we'll use this to validate the font arguments and to print better error messages
|
||||
const fontFamilyData = google_fonts_metadata_1.googleFontsMetadata[fontFamily];
|
||||
if (!fontFamilyData) {
|
||||
(0, next_font_error_1.nextFontError)(`Unknown font \`${fontFamily}\``);
|
||||
}
|
||||
const availableSubsets = fontFamilyData.subsets;
|
||||
if (availableSubsets.length === 0) {
|
||||
// If the font doesn't have any preloadeable subsets, disable preload
|
||||
preload = false;
|
||||
}
|
||||
else if (preload) {
|
||||
if (!subsets) {
|
||||
(0, next_font_error_1.nextFontError)(`Preload is enabled but no subsets were specified for font \`${fontFamily}\`. Please specify subsets or disable preloading if your intended subset can't be preloaded.\nAvailable subsets: ${(0, format_available_values_1.formatAvailableValues)(availableSubsets)}\n\nRead more: https://nextjs.org/docs/messages/google-fonts-missing-subsets`);
|
||||
}
|
||||
subsets.forEach((subset) => {
|
||||
if (!availableSubsets.includes(subset)) {
|
||||
(0, next_font_error_1.nextFontError)(`Unknown subset \`${subset}\` for font \`${fontFamily}\`.\nAvailable subsets: ${(0, format_available_values_1.formatAvailableValues)(availableSubsets)}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
const fontWeights = fontFamilyData.weights;
|
||||
const fontStyles = fontFamilyData.styles;
|
||||
// Get the unique weights and styles from the function call
|
||||
const weights = !weight
|
||||
? []
|
||||
: [...new Set(Array.isArray(weight) ? weight : [weight])];
|
||||
const styles = !style
|
||||
? []
|
||||
: [...new Set(Array.isArray(style) ? style : [style])];
|
||||
if (weights.length === 0) {
|
||||
// Set variable as default, throw if not available
|
||||
if (fontWeights.includes('variable')) {
|
||||
weights.push('variable');
|
||||
}
|
||||
else {
|
||||
(0, next_font_error_1.nextFontError)(`Missing weight for font \`${fontFamily}\`.\nAvailable weights: ${(0, format_available_values_1.formatAvailableValues)(fontWeights)}`);
|
||||
}
|
||||
}
|
||||
if (weights.length > 1 && weights.includes('variable')) {
|
||||
(0, next_font_error_1.nextFontError)(`Unexpected \`variable\` in weight array for font \`${fontFamily}\`. You only need \`variable\`, it includes all available weights.`);
|
||||
}
|
||||
weights.forEach((selectedWeight) => {
|
||||
if (!fontWeights.includes(selectedWeight)) {
|
||||
(0, next_font_error_1.nextFontError)(`Unknown weight \`${selectedWeight}\` for font \`${fontFamily}\`.\nAvailable weights: ${(0, format_available_values_1.formatAvailableValues)(fontWeights)}`);
|
||||
}
|
||||
});
|
||||
if (styles.length === 0) {
|
||||
if (fontStyles.length === 1) {
|
||||
// Handle default style for fonts that only have italic
|
||||
styles.push(fontStyles[0]);
|
||||
}
|
||||
else {
|
||||
// Otherwise set default style to normal
|
||||
styles.push('normal');
|
||||
}
|
||||
}
|
||||
styles.forEach((selectedStyle) => {
|
||||
if (!fontStyles.includes(selectedStyle)) {
|
||||
(0, next_font_error_1.nextFontError)(`Unknown style \`${selectedStyle}\` for font \`${fontFamily}\`.\nAvailable styles: ${(0, format_available_values_1.formatAvailableValues)(fontStyles)}`);
|
||||
}
|
||||
});
|
||||
if (!constants_1.allowedDisplayValues.includes(display)) {
|
||||
(0, next_font_error_1.nextFontError)(`Invalid display value \`${display}\` for font \`${fontFamily}\`.\nAvailable display values: ${(0, format_available_values_1.formatAvailableValues)(constants_1.allowedDisplayValues)}`);
|
||||
}
|
||||
if (axes) {
|
||||
if (!fontWeights.includes('variable')) {
|
||||
(0, next_font_error_1.nextFontError)('Axes can only be defined for variable fonts.');
|
||||
}
|
||||
if (weights[0] !== 'variable') {
|
||||
(0, next_font_error_1.nextFontError)('Axes can only be defined for variable fonts when the weight property is nonexistent or set to `variable`.');
|
||||
}
|
||||
}
|
||||
return {
|
||||
fontFamily,
|
||||
weights,
|
||||
styles,
|
||||
display,
|
||||
preload,
|
||||
selectedVariableAxes: axes,
|
||||
fallback,
|
||||
adjustFontFallback,
|
||||
variable,
|
||||
subsets,
|
||||
};
|
||||
}
|
||||
Generated
Vendored
+18
@@ -0,0 +1,18 @@
|
||||
import type { Font } from 'fontkit';
|
||||
import type { AdjustFontFallback } from 'next/font';
|
||||
/**
|
||||
* Given a font file and category, calculate the fallback font override values.
|
||||
* The returned values can be used to generate a CSS @font-face declaration.
|
||||
*
|
||||
* For example:
|
||||
* @font-face {
|
||||
* font-family: local-font;
|
||||
* src: local(Arial);
|
||||
* size-adjust: 90%;
|
||||
* }
|
||||
*
|
||||
* Read more about this technique in these texts by the Google Aurora team:
|
||||
* https://developer.chrome.com/blog/font-fallbacks/
|
||||
* https://docs.google.com/document/d/e/2PACX-1vRsazeNirATC7lIj2aErSHpK26hZ6dA9GsQ069GEbq5fyzXEhXbvByoftSfhG82aJXmrQ_sJCPBqcx_/pub
|
||||
*/
|
||||
export declare function getFallbackMetricsFromFontFile(font: Font, category?: string): AdjustFontFallback;
|
||||
Generated
Vendored
+85
@@ -0,0 +1,85 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getFallbackMetricsFromFontFile = getFallbackMetricsFromFontFile;
|
||||
// The font metadata of the fallback fonts, retrieved with fontkit on system font files
|
||||
// The average width is calculated with the calcAverageWidth function below
|
||||
const DEFAULT_SANS_SERIF_FONT = {
|
||||
name: 'Arial',
|
||||
azAvgWidth: 934.5116279069767,
|
||||
unitsPerEm: 2048,
|
||||
};
|
||||
const DEFAULT_SERIF_FONT = {
|
||||
name: 'Times New Roman',
|
||||
azAvgWidth: 854.3953488372093,
|
||||
unitsPerEm: 2048,
|
||||
};
|
||||
/**
|
||||
* Calculate the average character width of a font file.
|
||||
* Used to calculate the size-adjust property by comparing the fallback average with the loaded font average.
|
||||
*/
|
||||
function calcAverageWidth(font) {
|
||||
try {
|
||||
/**
|
||||
* Finding the right characters to use when calculating the average width is tricky.
|
||||
* We can't just use the average width of all characters, because we have to take letter frequency into account.
|
||||
* We also have to take word length into account, because the font's space width usually differ a lot from other characters.
|
||||
* The goal is to find a string that'll give you a good average width, given most texts in most languages.
|
||||
*
|
||||
* TODO: Currently only works for the latin alphabet. Support more languages by finding the right characters for additional languages.
|
||||
*
|
||||
* The used characters were decided through trial and error with letter frequency and word length tables as a guideline.
|
||||
* E.g. https://en.wikipedia.org/wiki/Letter_frequency
|
||||
*/
|
||||
const avgCharacters = 'aaabcdeeeefghiijklmnnoopqrrssttuvwxyz ';
|
||||
// Check if the font file has all the characters we need to calculate the average width
|
||||
const hasAllChars = font
|
||||
.glyphsForString(avgCharacters)
|
||||
.flatMap((glyph) => glyph.codePoints)
|
||||
.every((codePoint) => font.hasGlyphForCodePoint(codePoint));
|
||||
if (!hasAllChars)
|
||||
return undefined;
|
||||
const widths = font
|
||||
.glyphsForString(avgCharacters)
|
||||
.map((glyph) => glyph.advanceWidth);
|
||||
const totalWidth = widths.reduce((sum, width) => sum + width, 0);
|
||||
return totalWidth / widths.length;
|
||||
}
|
||||
catch {
|
||||
// Could not calculate average width from the font file, skip size-adjust
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
function formatOverrideValue(val) {
|
||||
return Math.abs(val * 100).toFixed(2) + '%';
|
||||
}
|
||||
/**
|
||||
* Given a font file and category, calculate the fallback font override values.
|
||||
* The returned values can be used to generate a CSS @font-face declaration.
|
||||
*
|
||||
* For example:
|
||||
* @font-face {
|
||||
* font-family: local-font;
|
||||
* src: local(Arial);
|
||||
* size-adjust: 90%;
|
||||
* }
|
||||
*
|
||||
* Read more about this technique in these texts by the Google Aurora team:
|
||||
* https://developer.chrome.com/blog/font-fallbacks/
|
||||
* https://docs.google.com/document/d/e/2PACX-1vRsazeNirATC7lIj2aErSHpK26hZ6dA9GsQ069GEbq5fyzXEhXbvByoftSfhG82aJXmrQ_sJCPBqcx_/pub
|
||||
*/
|
||||
function getFallbackMetricsFromFontFile(font, category = 'serif') {
|
||||
const fallbackFont = category === 'serif' ? DEFAULT_SERIF_FONT : DEFAULT_SANS_SERIF_FONT;
|
||||
const azAvgWidth = calcAverageWidth(font);
|
||||
const { ascent, descent, lineGap, unitsPerEm } = font;
|
||||
const fallbackFontAvgWidth = fallbackFont.azAvgWidth / fallbackFont.unitsPerEm;
|
||||
let sizeAdjust = azAvgWidth
|
||||
? azAvgWidth / unitsPerEm / fallbackFontAvgWidth
|
||||
: 1;
|
||||
return {
|
||||
ascentOverride: formatOverrideValue(ascent / (unitsPerEm * sizeAdjust)),
|
||||
descentOverride: formatOverrideValue(descent / (unitsPerEm * sizeAdjust)),
|
||||
lineGapOverride: formatOverrideValue(lineGap / (unitsPerEm * sizeAdjust)),
|
||||
fallbackFont: fallbackFont.name,
|
||||
sizeAdjust: formatOverrideValue(sizeAdjust),
|
||||
};
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
import type { CssVariable, Display, NextFont, NextFontWithVariable } from '../types';
|
||||
type LocalFont<T extends CssVariable | undefined = undefined> = {
|
||||
src: string | Array<{
|
||||
path: string;
|
||||
weight?: string;
|
||||
style?: string;
|
||||
}>;
|
||||
display?: Display;
|
||||
weight?: string;
|
||||
style?: string;
|
||||
adjustFontFallback?: 'Arial' | 'Times New Roman' | false;
|
||||
fallback?: string[];
|
||||
preload?: boolean;
|
||||
variable?: T;
|
||||
declarations?: Array<{
|
||||
prop: string;
|
||||
value: string;
|
||||
}>;
|
||||
};
|
||||
export default function localFont<T extends CssVariable | undefined = undefined>(options: LocalFont<T>): T extends undefined ? NextFont : NextFontWithVariable;
|
||||
export {};
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.default = localFont;
|
||||
function localFont(options) {
|
||||
throw new Error();
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
import type { FontLoader } from 'next/font';
|
||||
declare const nextFontLocalFontLoader: FontLoader;
|
||||
export default nextFontLocalFontLoader;
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
// @ts-ignore
|
||||
let fontFromBuffer;
|
||||
try {
|
||||
// eslint-disable-next-line @next/internal/typechecked-require -- Module created during build.
|
||||
const mod = require('../fontkit').default;
|
||||
fontFromBuffer = mod.default || mod;
|
||||
}
|
||||
catch { }
|
||||
const util_1 = require("util");
|
||||
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, 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 = fontFromBuffer === null || fontFromBuffer === void 0 ? void 0 : fontFromBuffer(fileBuffer);
|
||||
}
|
||||
catch (e) {
|
||||
console.error(`Failed to load font file: ${resolved}\n${e}`);
|
||||
}
|
||||
// Check if `font-family` is explicitly defined in `declarations`
|
||||
const hasCustomFontFamily = declarations === null || declarations === void 0 ? void 0 : declarations.some(({ prop }) => prop === 'font-family');
|
||||
// Get all values that should be added to the @font-face declaration
|
||||
const fontFaceProperties = [
|
||||
...(declarations
|
||||
? declarations.map(({ prop, value }) => [prop, value])
|
||||
: []),
|
||||
...(hasCustomFontFamily ? [] : [['font-family', variableName]]),
|
||||
['src', `url(${fontUrl}) format('${format}')`],
|
||||
['font-display', display],
|
||||
...((weight !== null && weight !== void 0 ? weight : defaultWeight)
|
||||
? [['font-weight', weight !== null && weight !== void 0 ? weight : defaultWeight]]
|
||||
: []),
|
||||
...((style !== null && style !== void 0 ? style : defaultStyle)
|
||||
? [['font-style', style !== null && style !== void 0 ? style : defaultStyle]]
|
||||
: []),
|
||||
];
|
||||
// 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,
|
||||
fontMetadata,
|
||||
weight,
|
||||
style,
|
||||
};
|
||||
}));
|
||||
// Calculate the fallback font override values using the font file metadata
|
||||
let adjustFontFallbackMetrics;
|
||||
if (adjustFontFallback !== false) {
|
||||
const fallbackFontFile = (0, pick_font_file_for_fallback_generation_1.pickFontFileForFallbackGeneration)(fontFiles);
|
||||
if (fallbackFontFile.fontMetadata) {
|
||||
adjustFontFallbackMetrics = (0, get_fallback_metrics_from_font_file_1.getFallbackMetricsFromFontFile)(fallbackFontFile.fontMetadata, adjustFontFallback === 'Times New Roman' ? 'serif' : 'sans-serif');
|
||||
}
|
||||
}
|
||||
return {
|
||||
css: fontFiles.map(({ css }) => css).join('\n'),
|
||||
fallbackFonts: fallback,
|
||||
weight: src.length === 1 ? src[0].weight : undefined,
|
||||
style: src.length === 1 ? src[0].style : undefined,
|
||||
variable,
|
||||
adjustFontFallback: adjustFontFallbackMetrics,
|
||||
};
|
||||
};
|
||||
exports.default = nextFontLocalFontLoader;
|
||||
Generated
Vendored
+13
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* If multiple font files are provided for a font family, we need to pick one to use for the automatic fallback generation.
|
||||
* This function returns the font file that is most likely to be used for the bulk of the text on a page.
|
||||
*
|
||||
* There are some assumptions here about the text on a page when picking the font file:
|
||||
* - Most of the text will have normal weight, use the one closest to 400
|
||||
* - Most of the text will have normal style, prefer normal over italic
|
||||
* - If two font files have the same distance from normal weight, the thinner one will most likely be the bulk of the text
|
||||
*/
|
||||
export declare function pickFontFileForFallbackGeneration<T extends {
|
||||
style?: string;
|
||||
weight?: string;
|
||||
}>(fontFiles: T[]): T;
|
||||
Generated
Vendored
+85
@@ -0,0 +1,85 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.pickFontFileForFallbackGeneration = pickFontFileForFallbackGeneration;
|
||||
const next_font_error_1 = require("../next-font-error");
|
||||
const NORMAL_WEIGHT = 400;
|
||||
const BOLD_WEIGHT = 700;
|
||||
/**
|
||||
* Convert the weight string to a number so it can be used for comparison.
|
||||
* Weights can be defined as a number, 'normal' or 'bold'. https://developer.mozilla.org/docs/Web/CSS/@font-face/font-weight
|
||||
*/
|
||||
function getWeightNumber(weight) {
|
||||
return weight === 'normal'
|
||||
? NORMAL_WEIGHT
|
||||
: weight === 'bold'
|
||||
? BOLD_WEIGHT
|
||||
: Number(weight);
|
||||
}
|
||||
/**
|
||||
* Get the distance from normal (400) weight for the provided weight.
|
||||
* If it's not a variable font we can just return the distance.
|
||||
* If it's a variable font we need to compare its weight range to 400.
|
||||
*/
|
||||
function getDistanceFromNormalWeight(weight) {
|
||||
if (!weight)
|
||||
return 0;
|
||||
// If it's a variable font the weight is defined with two numbers "100 900", rather than just one "400"
|
||||
const [firstWeight, secondWeight] = weight
|
||||
.trim()
|
||||
.split(/ +/)
|
||||
.map(getWeightNumber);
|
||||
if (Number.isNaN(firstWeight) || Number.isNaN(secondWeight)) {
|
||||
(0, next_font_error_1.nextFontError)(`Invalid weight value in src array: \`${weight}\`.\nExpected \`normal\`, \`bold\` or a number.`);
|
||||
}
|
||||
// If the weight doesn't have have a second value, it's not a variable font
|
||||
// If that's the case, just return the distance from normal weight
|
||||
if (!secondWeight) {
|
||||
return firstWeight - NORMAL_WEIGHT;
|
||||
}
|
||||
// Normal weight is within variable font range
|
||||
if (firstWeight <= NORMAL_WEIGHT && secondWeight >= NORMAL_WEIGHT) {
|
||||
return 0;
|
||||
}
|
||||
// Normal weight is outside variable font range
|
||||
// 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;
|
||||
}
|
||||
/**
|
||||
* If multiple font files are provided for a font family, we need to pick one to use for the automatic fallback generation.
|
||||
* This function returns the font file that is most likely to be used for the bulk of the text on a page.
|
||||
*
|
||||
* There are some assumptions here about the text on a page when picking the font file:
|
||||
* - Most of the text will have normal weight, use the one closest to 400
|
||||
* - Most of the text will have normal style, prefer normal over italic
|
||||
* - If two font files have the same distance from normal weight, the thinner one will most likely be the bulk of the text
|
||||
*/
|
||||
function pickFontFileForFallbackGeneration(fontFiles) {
|
||||
return 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 have the same absolute distance from normal weight
|
||||
if (absUsedDistance === absCurrentDistance &&
|
||||
currentFontDistance < usedFontDistance) {
|
||||
return currentFontFile;
|
||||
}
|
||||
return usedFontFile;
|
||||
});
|
||||
}
|
||||
Generated
Vendored
+25
@@ -0,0 +1,25 @@
|
||||
type FontOptions = {
|
||||
src: Array<{
|
||||
path: string;
|
||||
weight?: string;
|
||||
style?: string;
|
||||
ext: string;
|
||||
format: string;
|
||||
}>;
|
||||
display: string;
|
||||
weight?: string;
|
||||
style?: string;
|
||||
fallback?: string[];
|
||||
preload: boolean;
|
||||
variable?: string;
|
||||
adjustFontFallback?: string | false;
|
||||
declarations?: Array<{
|
||||
prop: string;
|
||||
value: string;
|
||||
}>;
|
||||
};
|
||||
/**
|
||||
* Validate the data received from next-swc next-transform-font on next/font/local calls
|
||||
*/
|
||||
export declare function validateLocalFontFunctionCall(functionName: string, fontData: any): FontOptions;
|
||||
export {};
|
||||
Generated
Vendored
+66
@@ -0,0 +1,66 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.validateLocalFontFunctionCall = validateLocalFontFunctionCall;
|
||||
const constants_1 = require("../constants");
|
||||
const format_available_values_1 = require("../format-available-values");
|
||||
const next_font_error_1 = require("../next-font-error");
|
||||
const extToFormat = {
|
||||
woff: 'woff',
|
||||
woff2: 'woff2',
|
||||
ttf: 'truetype',
|
||||
otf: 'opentype',
|
||||
eot: 'embedded-opentype',
|
||||
};
|
||||
/**
|
||||
* Validate the data received from next-swc next-transform-font on next/font/local calls
|
||||
*/
|
||||
function validateLocalFontFunctionCall(functionName, fontData) {
|
||||
if (functionName) {
|
||||
(0, next_font_error_1.nextFontError)(`next/font/local has no named exports`);
|
||||
}
|
||||
let { src, display = 'swap', weight, style, fallback, preload = true, variable, adjustFontFallback, declarations, } = fontData || {};
|
||||
if (!constants_1.allowedDisplayValues.includes(display)) {
|
||||
(0, next_font_error_1.nextFontError)(`Invalid display value \`${display}\`.\nAvailable display values: ${(0, format_available_values_1.formatAvailableValues)(constants_1.allowedDisplayValues)}`);
|
||||
}
|
||||
if (!src) {
|
||||
(0, next_font_error_1.nextFontError)('Missing required `src` property');
|
||||
}
|
||||
if (!Array.isArray(src)) {
|
||||
src = [{ path: src, weight, style }];
|
||||
}
|
||||
else {
|
||||
if (src.length === 0) {
|
||||
(0, next_font_error_1.nextFontError)('Unexpected empty `src` array.');
|
||||
}
|
||||
}
|
||||
src = src.map((fontFile) => {
|
||||
var _a;
|
||||
const ext = (_a = /\.(woff|woff2|eot|ttf|otf)$/.exec(fontFile.path)) === null || _a === void 0 ? void 0 : _a[1];
|
||||
if (!ext) {
|
||||
(0, next_font_error_1.nextFontError)(`Unexpected file \`${fontFile.path}\``);
|
||||
}
|
||||
return {
|
||||
...fontFile,
|
||||
ext,
|
||||
format: extToFormat[ext],
|
||||
};
|
||||
});
|
||||
if (Array.isArray(declarations)) {
|
||||
declarations.forEach((declaration) => {
|
||||
if (['src', 'font-display', 'font-weight', 'font-style'].includes(declaration === null || declaration === void 0 ? void 0 : declaration.prop)) {
|
||||
(0, next_font_error_1.nextFontError)(`Invalid declaration prop: \`${declaration.prop}\``);
|
||||
}
|
||||
});
|
||||
}
|
||||
return {
|
||||
src,
|
||||
display,
|
||||
weight,
|
||||
style,
|
||||
fallback,
|
||||
preload,
|
||||
variable,
|
||||
adjustFontFallback,
|
||||
declarations,
|
||||
};
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Throw NextFontError error. Used by the WellKnownErrorsPlugin to format errors thrown by next/font.
|
||||
*/
|
||||
export declare function nextFontError(message: string): never;
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.nextFontError = nextFontError;
|
||||
/**
|
||||
* Throw NextFontError error. Used by the WellKnownErrorsPlugin to format errors thrown by next/font.
|
||||
*/
|
||||
function nextFontError(message) {
|
||||
const err = new Error(message);
|
||||
err.name = 'NextFontError';
|
||||
throw err;
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
export type CssVariable = `--${string}`;
|
||||
export type Display = 'auto' | 'block' | 'swap' | 'fallback' | 'optional';
|
||||
export type NextFont = {
|
||||
className: string;
|
||||
style: {
|
||||
fontFamily: string;
|
||||
fontWeight?: number;
|
||||
fontStyle?: string;
|
||||
};
|
||||
};
|
||||
export type NextFontWithVariable = NextFont & {
|
||||
variable: string;
|
||||
};
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
+1
@@ -0,0 +1 @@
|
||||
export * from '../dist/google'
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
// Validate next version
|
||||
const semver = require('next/dist/compiled/semver')
|
||||
if (semver.lt(require('next/package.json').version, '13.0.0')) {
|
||||
throw new Error('`@next/font` is only available in Next.js 13 and newer.')
|
||||
}
|
||||
|
||||
let message = '@next/font/google failed to run or is incorrectly configured.'
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
message +=
|
||||
'\nIf you just installed `@next/font`, please try restarting `next dev` and resaving your file.'
|
||||
}
|
||||
|
||||
message += `\n\nRead more: https://nextjs.org/docs/app/building-your-application/optimizing/fonts`
|
||||
|
||||
throw new Error(message)
|
||||
+1
@@ -0,0 +1 @@
|
||||
export { default } from '../dist/google/loader'
|
||||
+1
@@ -0,0 +1 @@
|
||||
module.exports = require('../dist/google/loader')
|
||||
+1
@@ -0,0 +1 @@
|
||||
export { default } from '../dist/local/index'
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
// Validate next version
|
||||
const semver = require('next/dist/compiled/semver')
|
||||
if (semver.lt(require('next/package.json').version, '13.0.0')) {
|
||||
throw new Error('`@next/font` is only available in Next.js 13 and newer.')
|
||||
}
|
||||
|
||||
let message = '@next/font/local failed to run or is incorrectly configured.'
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
message +=
|
||||
'\nIf you just installed `@next/font`, please try restarting `next dev` and resaving your file.'
|
||||
}
|
||||
|
||||
message += `\n\nRead more: https://nextjs.org/docs/app/building-your-application/optimizing/fonts`
|
||||
|
||||
throw new Error(message)
|
||||
+1
@@ -0,0 +1 @@
|
||||
export { default } from '../dist/local/loader'
|
||||
+1
@@ -0,0 +1 @@
|
||||
module.exports = require('../dist/local/loader')
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"name":"@next/font","license":"MIT","types":"dist/types.d.ts"}
|
||||
Reference in New Issue
Block a user