Initial commit

This commit is contained in:
Alejandro Martinez
2026-02-12 02:04:10 +01:00
commit f09af719cf
13433 changed files with 2193445 additions and 0 deletions

55
node_modules/astro/dist/assets/fonts/config.d.ts generated vendored Normal file
View File

@@ -0,0 +1,55 @@
import { z } from 'zod';
import type { FontProvider } from './types.js';
export declare const weightSchema: z.ZodUnion<[z.ZodString, z.ZodNumber]>;
export declare const styleSchema: z.ZodEnum<["normal", "italic", "oblique"]>;
export declare const displaySchema: z.ZodEnum<["auto", "block", "swap", "fallback", "optional"]>;
export declare const fontProviderSchema: z.ZodType<FontProvider<never>, z.ZodTypeDef, FontProvider<never>>;
export declare const fontFamilySchema: z.ZodObject<{
provider: z.ZodType<FontProvider<never>, z.ZodTypeDef, FontProvider<never>>;
options: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
weights: z.ZodOptional<z.ZodArray<z.ZodUnion<[z.ZodString, z.ZodNumber]>, "atleastone">>;
styles: z.ZodOptional<z.ZodArray<z.ZodEnum<["normal", "italic", "oblique"]>, "atleastone">>;
subsets: z.ZodOptional<z.ZodArray<z.ZodString, "atleastone">>;
formats: z.ZodOptional<z.ZodArray<z.ZodEnum<["woff2", "woff", "otf", "ttf", "eot"]>, "atleastone">>;
display: z.ZodOptional<z.ZodEnum<["auto", "block", "swap", "fallback", "optional"]>>;
stretch: z.ZodOptional<z.ZodString>;
featureSettings: z.ZodOptional<z.ZodString>;
variationSettings: z.ZodOptional<z.ZodString>;
unicodeRange: z.ZodOptional<z.ZodArray<z.ZodString, "atleastone">>;
fallbacks: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
optimizedFallbacks: z.ZodOptional<z.ZodBoolean>;
name: z.ZodString;
cssVariable: z.ZodString;
}, "strict", z.ZodTypeAny, {
name: string;
cssVariable: string;
provider: FontProvider<never>;
weights?: [string | number, ...(string | number)[]] | undefined;
styles?: ["normal" | "italic" | "oblique", ...("normal" | "italic" | "oblique")[]] | undefined;
subsets?: [string, ...string[]] | undefined;
fallbacks?: string[] | undefined;
optimizedFallbacks?: boolean | undefined;
formats?: ["woff2" | "woff" | "otf" | "ttf" | "eot", ...("woff2" | "woff" | "otf" | "ttf" | "eot")[]] | undefined;
display?: "auto" | "block" | "swap" | "fallback" | "optional" | undefined;
stretch?: string | undefined;
featureSettings?: string | undefined;
variationSettings?: string | undefined;
unicodeRange?: [string, ...string[]] | undefined;
options?: Record<string, any> | undefined;
}, {
name: string;
cssVariable: string;
provider: FontProvider<never>;
weights?: [string | number, ...(string | number)[]] | undefined;
styles?: ["normal" | "italic" | "oblique", ...("normal" | "italic" | "oblique")[]] | undefined;
subsets?: [string, ...string[]] | undefined;
fallbacks?: string[] | undefined;
optimizedFallbacks?: boolean | undefined;
formats?: ["woff2" | "woff" | "otf" | "ttf" | "eot", ...("woff2" | "woff" | "otf" | "ttf" | "eot")[]] | undefined;
display?: "auto" | "block" | "swap" | "fallback" | "optional" | undefined;
stretch?: string | undefined;
featureSettings?: string | undefined;
variationSettings?: string | undefined;
unicodeRange?: [string, ...string[]] | undefined;
options?: Record<string, any> | undefined;
}>;

53
node_modules/astro/dist/assets/fonts/config.js generated vendored Normal file
View File

@@ -0,0 +1,53 @@
import { z } from "zod";
import { FONT_TYPES } from "./constants.js";
const weightSchema = z.union([z.string(), z.number()]);
const styleSchema = z.enum(["normal", "italic", "oblique"]);
const displaySchema = z.enum(["auto", "block", "swap", "fallback", "optional"]);
const familyPropertiesSchema = z.object({
weight: weightSchema.optional(),
style: styleSchema.optional(),
display: displaySchema.optional(),
stretch: z.string().optional(),
featureSettings: z.string().optional(),
variationSettings: z.string().optional(),
unicodeRange: z.array(z.string()).nonempty().optional()
});
const fallbacksSchema = z.object({
fallbacks: z.array(z.string()).optional(),
optimizedFallbacks: z.boolean().optional()
});
const requiredFamilyAttributesSchema = z.object({
name: z.string(),
cssVariable: z.string()
});
const _fontProviderSchema = z.object({
name: z.string(),
config: z.record(z.string(), z.any()).optional(),
init: z.custom((v) => typeof v === "function").optional(),
resolveFont: z.custom((v) => typeof v === "function"),
listFonts: z.custom((v) => typeof v === "function").optional()
}).strict();
const fontProviderSchema = z.custom((v) => {
return _fontProviderSchema.safeParse(v).success;
}, "Invalid FontProvider object");
const fontFamilySchema = z.object({
...requiredFamilyAttributesSchema.shape,
...fallbacksSchema.shape,
...familyPropertiesSchema.omit({
weight: true,
style: true
}).shape,
provider: fontProviderSchema,
options: z.record(z.string(), z.any()).optional(),
weights: z.array(weightSchema).nonempty().optional(),
styles: z.array(styleSchema).nonempty().optional(),
subsets: z.array(z.string()).nonempty().optional(),
formats: z.array(z.enum(FONT_TYPES)).nonempty().optional()
}).strict();
export {
displaySchema,
fontFamilySchema,
fontProviderSchema,
styleSchema,
weightSchema
};

15
node_modules/astro/dist/assets/fonts/constants.d.ts generated vendored Normal file
View File

@@ -0,0 +1,15 @@
import type { Defaults, FontType } from './types.js';
export declare const DEFAULTS: Defaults;
export declare const VIRTUAL_MODULE_ID = "virtual:astro:assets/fonts/internal";
export declare const RESOLVED_VIRTUAL_MODULE_ID: string;
export declare const RUNTIME_VIRTUAL_MODULE_ID = "virtual:astro:assets/fonts/runtime";
export declare const RESOLVED_RUNTIME_VIRTUAL_MODULE_ID: string;
export declare const ASSETS_DIR = "fonts";
export declare const CACHE_DIR = "./fonts/";
export declare const FONT_TYPES: readonly ["woff2", "woff", "otf", "ttf", "eot"];
export declare const FONT_FORMATS: Array<{
type: FontType;
format: string;
}>;
export declare const GENERIC_FALLBACK_NAMES: readonly ["serif", "sans-serif", "monospace", "cursive", "fantasy", "system-ui", "ui-serif", "ui-sans-serif", "ui-monospace", "ui-rounded", "emoji", "math", "fangsong"];
export declare const FONTS_TYPES_FILE = "fonts.d.ts";

52
node_modules/astro/dist/assets/fonts/constants.js generated vendored Normal file
View File

@@ -0,0 +1,52 @@
const DEFAULTS = {
weights: ["400"],
styles: ["normal", "italic"],
subsets: ["latin"],
// Technically serif is the browser default but most websites these days use sans-serif
fallbacks: ["sans-serif"],
optimizedFallbacks: true,
formats: ["woff2"]
};
const VIRTUAL_MODULE_ID = "virtual:astro:assets/fonts/internal";
const RESOLVED_VIRTUAL_MODULE_ID = "\0" + VIRTUAL_MODULE_ID;
const RUNTIME_VIRTUAL_MODULE_ID = "virtual:astro:assets/fonts/runtime";
const RESOLVED_RUNTIME_VIRTUAL_MODULE_ID = "\0" + RUNTIME_VIRTUAL_MODULE_ID;
const ASSETS_DIR = "fonts";
const CACHE_DIR = "./fonts/";
const FONT_TYPES = ["woff2", "woff", "otf", "ttf", "eot"];
const FONT_FORMATS = [
{ type: "woff2", format: "woff2" },
{ type: "woff", format: "woff" },
{ type: "otf", format: "opentype" },
{ type: "ttf", format: "truetype" },
{ type: "eot", format: "embedded-opentype" }
];
const GENERIC_FALLBACK_NAMES = [
"serif",
"sans-serif",
"monospace",
"cursive",
"fantasy",
"system-ui",
"ui-serif",
"ui-sans-serif",
"ui-monospace",
"ui-rounded",
"emoji",
"math",
"fangsong"
];
const FONTS_TYPES_FILE = "fonts.d.ts";
export {
ASSETS_DIR,
CACHE_DIR,
DEFAULTS,
FONTS_TYPES_FILE,
FONT_FORMATS,
FONT_TYPES,
GENERIC_FALLBACK_NAMES,
RESOLVED_RUNTIME_VIRTUAL_MODULE_ID,
RESOLVED_VIRTUAL_MODULE_ID,
RUNTIME_VIRTUAL_MODULE_ID,
VIRTUAL_MODULE_ID
};

View File

@@ -0,0 +1,9 @@
import type { CssRenderer } from '../definitions.js';
import type { Collaborator, ComponentDataByCssVariable, Defaults, FontFamilyAssets } from '../types.js';
import type { optimizeFallbacks as _optimizeFallbacks } from './optimize-fallbacks.js';
export declare function collectComponentData({ fontFamilyAssets, cssRenderer, defaults, optimizeFallbacks, }: {
fontFamilyAssets: Array<FontFamilyAssets>;
cssRenderer: CssRenderer;
defaults: Pick<Defaults, 'fallbacks' | 'optimizedFallbacks'>;
optimizeFallbacks: Collaborator<typeof _optimizeFallbacks, 'family' | 'fallbacks' | 'collectedFonts'>;
}): Promise<ComponentDataByCssVariable>;

View File

@@ -0,0 +1,47 @@
import { unifontFontFaceDataToProperties } from "../utils.js";
async function collectComponentData({
fontFamilyAssets,
cssRenderer,
defaults,
optimizeFallbacks
}) {
const componentDataByCssVariable = /* @__PURE__ */ new Map();
for (const { family, fonts, collectedFontsForMetricsByUniqueKey, preloads } of fontFamilyAssets) {
let css = "";
for (const data of fonts) {
css += cssRenderer.generateFontFace(
family.uniqueName,
unifontFontFaceDataToProperties({
src: data.src,
weight: data.weight,
style: data.style,
// User settings override the generated font settings
display: data.display ?? family.display,
unicodeRange: data.unicodeRange ?? family.unicodeRange,
stretch: data.stretch ?? family.stretch,
featureSettings: data.featureSettings ?? family.featureSettings,
variationSettings: data.variationSettings ?? family.variationSettings
})
);
}
const fallbacks = family.fallbacks ?? defaults.fallbacks;
const cssVarValues = [family.uniqueName];
const optimizeFallbacksResult = family.optimizedFallbacks ?? defaults.optimizedFallbacks ? await optimizeFallbacks({
family,
fallbacks,
collectedFonts: Array.from(collectedFontsForMetricsByUniqueKey.values())
}) : null;
if (optimizeFallbacksResult) {
css += optimizeFallbacksResult.css;
cssVarValues.push(...optimizeFallbacksResult.fallbacks);
} else {
cssVarValues.push(...fallbacks);
}
css += cssRenderer.generateCssVariable(family.cssVariable, cssVarValues);
componentDataByCssVariable.set(family.cssVariable, { preloads, css });
}
return componentDataByCssVariable;
}
export {
collectComponentData
};

View File

@@ -0,0 +1,17 @@
import type * as unifont from 'unifont';
import type { FontFileIdGenerator, Hasher } from '../definitions.js';
import type { Defaults, FontFileById, PreloadData, ResolvedFontFamily } from '../types.js';
import type { CollectedFontForMetrics } from './optimize-fallbacks.js';
export declare function collectFontAssetsFromFaces({ fonts, fontFileIdGenerator, family, fontFilesIds, collectedFontsIds, hasher, defaults, }: {
fonts: Array<unifont.FontFaceData>;
fontFileIdGenerator: FontFileIdGenerator;
family: Pick<ResolvedFontFamily, 'cssVariable' | 'fallbacks'>;
fontFilesIds: Set<string>;
collectedFontsIds: Set<string>;
hasher: Hasher;
defaults: Pick<Defaults, 'fallbacks'>;
}): {
fontFileById: FontFileById;
preloads: PreloadData[];
collectedFontsForMetricsByUniqueKey: Map<string, CollectedFontForMetrics>;
};

View File

@@ -0,0 +1,71 @@
import { FONT_FORMATS } from "../constants.js";
import { renderFontWeight } from "../utils.js";
function collectFontAssetsFromFaces({
fonts,
fontFileIdGenerator,
family,
fontFilesIds,
collectedFontsIds,
hasher,
defaults
}) {
const fontFileById = /* @__PURE__ */ new Map();
const collectedFontsForMetricsByUniqueKey = /* @__PURE__ */ new Map();
const preloads = [];
for (const font of fonts) {
let index = 0;
for (const source of font.src) {
if ("name" in source) {
continue;
}
const format = FONT_FORMATS.find((e) => e.format === source.format);
const originalUrl = source.originalURL;
const id = fontFileIdGenerator.generate({
cssVariable: family.cssVariable,
font,
originalUrl,
type: format.type
});
if (!fontFilesIds.has(id) && !fontFileById.has(id)) {
fontFileById.set(id, { url: originalUrl, init: font.meta?.init });
if (index === 0) {
preloads.push({
style: font.style,
subset: font.meta?.subset,
type: format.type,
url: source.url,
weight: renderFontWeight(font.weight)
});
}
}
const collected = {
id,
url: originalUrl,
init: font.meta?.init,
data: {
weight: font.weight,
style: font.style,
meta: {
subset: font.meta?.subset
}
}
};
const collectedKey = hasher.hashObject(collected.data);
const fallbacks = family.fallbacks ?? defaults.fallbacks;
if (fallbacks.length > 0 && // If the same data has already been sent for this family, we don't want to have
// duplicated fallbacks. Such scenario can occur with unicode ranges.
!collectedFontsIds.has(collectedKey) && !collectedFontsForMetricsByUniqueKey.has(collectedKey)) {
collectedFontsForMetricsByUniqueKey.set(collectedKey, collected);
}
index++;
}
}
return {
fontFileById,
preloads,
collectedFontsForMetricsByUniqueKey
};
}
export {
collectFontAssetsFromFaces
};

View File

@@ -0,0 +1,4 @@
import type { FontDataByCssVariable, FontFamilyAssets } from '../types.js';
export declare function collectFontData(fontFamilyAssets: Array<Pick<FontFamilyAssets, 'fonts'> & {
family: Pick<FontFamilyAssets['family'], 'cssVariable'>;
}>): FontDataByCssVariable;

View File

@@ -0,0 +1,23 @@
import { renderFontWeight } from "../utils.js";
function collectFontData(fontFamilyAssets) {
const fontDataByCssVariable = {};
for (const { family, fonts } of fontFamilyAssets) {
const fontData = [];
for (const data of fonts) {
fontData.push({
weight: renderFontWeight(data.weight),
style: data.style,
src: data.src.filter((src) => "url" in src).map((src) => ({
url: src.url,
format: src.format,
tech: src.tech
}))
});
}
fontDataByCssVariable[family.cssVariable] = fontData;
}
return fontDataByCssVariable;
}
export {
collectFontData
};

View File

@@ -0,0 +1,20 @@
import type { Logger } from '../../../core/logger/core.js';
import type { FontResolver, StringMatcher } from '../definitions.js';
import type { Collaborator, Defaults, FontFileById, ResolvedFontFamily } from '../types.js';
import type { collectFontAssetsFromFaces as _collectFontAssetsFromFaces } from './collect-font-assets-from-faces.js';
import type { filterAndTransformFontFaces as _filterAndTransformFontFaces } from './filter-and-transform-font-faces.js';
import type { getOrCreateFontFamilyAssets as _getOrCreateFontFamilyAssets } from './get-or-create-font-family-assets.js';
export declare function computeFontFamiliesAssets({ resolvedFamilies, fontResolver, logger, bold, defaults, stringMatcher, getOrCreateFontFamilyAssets, collectFontAssetsFromFaces, filterAndTransformFontFaces, }: {
resolvedFamilies: Array<ResolvedFontFamily>;
fontResolver: FontResolver;
logger: Logger;
bold: (input: string) => string;
defaults: Defaults;
stringMatcher: StringMatcher;
getOrCreateFontFamilyAssets: Collaborator<typeof _getOrCreateFontFamilyAssets, 'family' | 'fontFamilyAssetsByUniqueKey'>;
filterAndTransformFontFaces: Collaborator<typeof _filterAndTransformFontFaces, 'family' | 'fonts'>;
collectFontAssetsFromFaces: Collaborator<typeof _collectFontAssetsFromFaces, 'family' | 'fonts' | 'collectedFontsIds' | 'fontFilesIds'>;
}): Promise<{
fontFamilyAssets: import("../types.js").FontFamilyAssets[];
fontFileById: FontFileById;
}>;

View File

@@ -0,0 +1,67 @@
async function computeFontFamiliesAssets({
resolvedFamilies,
fontResolver,
logger,
bold,
defaults,
stringMatcher,
getOrCreateFontFamilyAssets,
collectFontAssetsFromFaces,
filterAndTransformFontFaces
}) {
const fontFamilyAssetsByUniqueKey = /* @__PURE__ */ new Map();
const fontFileById = /* @__PURE__ */ new Map();
for (const family of resolvedFamilies) {
const fontAssets = getOrCreateFontFamilyAssets({
fontFamilyAssetsByUniqueKey,
family
});
const _fonts = await fontResolver.resolveFont({
familyName: family.name,
provider: family.provider,
// We do not merge the defaults, we only provide defaults as a fallback
weights: family.weights ?? defaults.weights,
styles: family.styles ?? defaults.styles,
subsets: family.subsets ?? defaults.subsets,
formats: family.formats ?? defaults.formats,
options: family.options
});
if (_fonts.length === 0) {
logger.warn(
"assets",
`No data found for font family ${bold(family.name)}. Review your configuration`
);
const availableFamilies = await fontResolver.listFonts({ provider: family.provider });
if (availableFamilies && availableFamilies.length > 0 && !availableFamilies.includes(family.name)) {
logger.warn(
"assets",
`${bold(family.name)} font family cannot be retrieved by the provider. Did you mean ${bold(stringMatcher.getClosestMatch(family.name, availableFamilies))}?`
);
}
continue;
}
fontAssets.fonts.push(
...filterAndTransformFontFaces({
fonts: _fonts,
family
})
);
const result = collectFontAssetsFromFaces({
fonts: fontAssets.fonts,
family,
fontFilesIds: new Set(fontFileById.keys()),
collectedFontsIds: new Set(fontAssets.collectedFontsForMetricsByUniqueKey.keys())
});
for (const [key, value] of result.fontFileById.entries()) {
fontFileById.set(key, value);
}
for (const [key, value] of result.collectedFontsForMetricsByUniqueKey.entries()) {
fontAssets.collectedFontsForMetricsByUniqueKey.set(key, value);
}
fontAssets.preloads.push(...result.preloads);
}
return { fontFamilyAssets: Array.from(fontFamilyAssetsByUniqueKey.values()), fontFileById };
}
export {
computeFontFamiliesAssets
};

View File

@@ -0,0 +1,20 @@
import type * as unifont from 'unifont';
import type { FontFileIdGenerator, FontTypeExtractor, UrlResolver } from '../definitions.js';
import type { ResolvedFontFamily } from '../types.js';
export declare function filterAndTransformFontFaces({ fonts, fontTypeExtractor, fontFileIdGenerator, urlResolver, family, }: {
fonts: Array<unifont.FontFaceData>;
fontTypeExtractor: FontTypeExtractor;
fontFileIdGenerator: FontFileIdGenerator;
urlResolver: UrlResolver;
family: Pick<ResolvedFontFamily, 'cssVariable'>;
}): {
src: (unifont.LocalFontSource | unifont.RemoteFontSource)[];
display?: "auto" | "block" | "swap" | "fallback" | "optional";
weight?: string | number | [number, number];
stretch?: string;
style?: string;
unicodeRange?: string[];
featureSettings?: string;
variationSettings?: string;
meta?: unifont.FontFaceMeta;
}[];

View File

@@ -0,0 +1,39 @@
import { FONT_FORMATS } from "../constants.js";
function filterAndTransformFontFaces({
fonts,
fontTypeExtractor,
fontFileIdGenerator,
urlResolver,
family
}) {
return fonts.filter((font) => typeof font.meta?.priority === "number" ? font.meta.priority <= 1 : true).map((font) => ({
...font,
src: font.src.map((source) => {
if ("name" in source) {
return source;
}
const originalUrl = source.url.startsWith("//") ? `https:${source.url}` : source.url;
let format = FONT_FORMATS.find((e) => e.format === source.format);
if (!format) {
format = FONT_FORMATS.find((e) => e.type === fontTypeExtractor.extract(source.url));
}
const id = fontFileIdGenerator.generate({
cssVariable: family.cssVariable,
font,
originalUrl,
type: format.type
});
const url = urlResolver.resolve(id);
const newSource = {
originalURL: originalUrl,
url,
format: format.format,
tech: source.tech
};
return newSource;
})
}));
}
export {
filterAndTransformFontFaces
};

View File

@@ -0,0 +1,2 @@
import type { PreloadData, PreloadFilter } from '../types.js';
export declare function filterPreloads(data: Array<PreloadData>, preload: PreloadFilter): Array<PreloadData> | null;

View File

@@ -0,0 +1,37 @@
function filterPreloads(data, preload) {
if (!preload) {
return null;
}
if (preload === true) {
return data;
}
return data.filter(
({ weight, style, subset }) => preload.some((p) => {
if (p.weight !== void 0 && weight !== void 0 && !checkWeight(p.weight.toString(), weight)) {
return false;
}
if (p.style !== void 0 && p.style !== style) {
return false;
}
if (p.subset !== void 0 && p.subset !== subset) {
return false;
}
return true;
})
);
}
function checkWeight(input, target) {
const trimmedInput = input.trim();
if (trimmedInput.includes(" ")) {
return trimmedInput === target;
}
if (target.includes(" ")) {
const [a, b] = target.split(" ");
const parsedInput = Number.parseInt(input);
return parsedInput >= Number.parseInt(a) && parsedInput <= Number.parseInt(b);
}
return input === target;
}
export {
filterPreloads
};

View File

@@ -0,0 +1,8 @@
import type { Logger } from '../../../core/logger/core.js';
import type { FontFamilyAssetsByUniqueKey, ResolvedFontFamily } from '../types.js';
export declare function getOrCreateFontFamilyAssets({ fontFamilyAssetsByUniqueKey, logger, bold, family, }: {
fontFamilyAssetsByUniqueKey: FontFamilyAssetsByUniqueKey;
logger: Logger;
bold: (input: string) => string;
family: ResolvedFontFamily;
}): import("../types.js").FontFamilyAssets;

View File

@@ -0,0 +1,34 @@
function getOrCreateFontFamilyAssets({
fontFamilyAssetsByUniqueKey,
logger,
bold,
family
}) {
const key = `${family.cssVariable}:${family.name}:${family.provider.name}`;
let fontAssets = fontFamilyAssetsByUniqueKey.get(key);
if (!fontAssets) {
if (Array.from(fontFamilyAssetsByUniqueKey.keys()).find(
(k) => k.startsWith(`${family.cssVariable}:`)
)) {
logger.warn(
"assets",
`Several font families have been registered for the ${bold(family.cssVariable)} cssVariable but they do not share the same name and provider.`
);
logger.warn(
"assets",
"These families will not be merged together. The last occurrence will override previous families for this cssVariable. Review your Astro configuration."
);
}
fontAssets = {
family,
fonts: [],
collectedFontsForMetricsByUniqueKey: /* @__PURE__ */ new Map(),
preloads: []
};
fontFamilyAssetsByUniqueKey.set(key, fontAssets);
}
return fontAssets;
}
export {
getOrCreateFontFamilyAssets
};

View File

@@ -0,0 +1,16 @@
import type * as unifont from 'unifont';
import type { FontMetricsResolver, SystemFallbacksProvider } from '../definitions.js';
import type { FontFileData, ResolvedFontFamily } from '../types.js';
export interface CollectedFontForMetrics extends FontFileData {
data: Partial<unifont.FontFaceData>;
}
export declare function optimizeFallbacks({ family, fallbacks: _fallbacks, collectedFonts, systemFallbacksProvider, fontMetricsResolver, }: {
family: Pick<ResolvedFontFamily, 'name' | 'uniqueName'>;
fallbacks: Array<string>;
collectedFonts: Array<CollectedFontForMetrics>;
systemFallbacksProvider: SystemFallbacksProvider;
fontMetricsResolver: FontMetricsResolver;
}): Promise<null | {
css: string;
fallbacks: Array<string>;
}>;

View File

@@ -0,0 +1,46 @@
import { isGenericFontFamily, unifontFontFaceDataToProperties } from "../utils.js";
async function optimizeFallbacks({
family,
fallbacks: _fallbacks,
collectedFonts,
systemFallbacksProvider,
fontMetricsResolver
}) {
let fallbacks = [..._fallbacks];
if (fallbacks.length === 0 || collectedFonts.length === 0) {
return null;
}
const lastFallback = fallbacks[fallbacks.length - 1];
if (!isGenericFontFamily(lastFallback)) {
return null;
}
const localFonts = systemFallbacksProvider.getLocalFonts(lastFallback);
if (!localFonts || localFonts.length === 0) {
return null;
}
if (localFonts.includes(family.name)) {
return null;
}
const localFontsMappings = localFonts.map((font) => ({
font,
// We mustn't wrap in quote because that's handled by the CSS renderer
name: `${family.uniqueName} fallback: ${font}`
}));
fallbacks = [...localFontsMappings.map((m) => m.name), ...fallbacks];
let css = "";
for (const { font, name } of localFontsMappings) {
for (const collected of collectedFonts) {
css += fontMetricsResolver.generateFontFace({
metrics: await fontMetricsResolver.getMetrics(family.name, collected),
fallbackMetrics: systemFallbacksProvider.getMetricsForLocalFont(font),
font,
name,
properties: unifontFontFaceDataToProperties(collected.data)
});
}
}
return { css, fallbacks };
}
export {
optimizeFallbacks
};

View File

@@ -0,0 +1,6 @@
import type { Hasher } from '../definitions.js';
import type { FontFamily, ResolvedFontFamily } from '../types.js';
export declare function resolveFamily({ family, hasher, }: {
family: FontFamily;
hasher: Hasher;
}): ResolvedFontFamily;

View File

@@ -0,0 +1,23 @@
import { dedupe, withoutQuotes } from "../utils.js";
function resolveFamily({
family,
hasher
}) {
const name = withoutQuotes(family.name);
return {
...family,
name,
// This will be used in CSS font faces. Quotes are added by the CSS renderer if
// this value contains a space.
uniqueName: `${name}-${hasher.hashObject(family)}`,
weights: family.weights ? dedupe(family.weights.map((weight) => weight.toString())) : void 0,
styles: family.styles ? dedupe(family.styles) : void 0,
subsets: family.subsets ? dedupe(family.subsets) : void 0,
formats: family.formats ? dedupe(family.formats) : void 0,
fallbacks: family.fallbacks ? dedupe(family.fallbacks) : void 0,
unicodeRange: family.unicodeRange ? dedupe(family.unicodeRange) : void 0
};
}
export {
resolveFamily
};

72
node_modules/astro/dist/assets/fonts/definitions.d.ts generated vendored Normal file
View File

@@ -0,0 +1,72 @@
import type * as unifont from 'unifont';
import type { CollectedFontForMetrics } from './core/optimize-fallbacks.js';
import type { CssProperties, FontFaceMetrics, FontFileData, FontProvider, FontType, GenericFallbackName, ResolveFontOptions, Style } from './types.js';
export interface Hasher {
hashString: (input: string) => string;
hashObject: (input: Record<string, any>) => string;
}
export interface UrlResolver {
resolve: (id: string) => string;
readonly cspResources: Array<string>;
}
export interface FontFileContentResolver {
resolve: (url: string) => string;
}
export interface CssRenderer {
generateFontFace: (family: string, properties: CssProperties) => string;
generateCssVariable: (key: string, values: Array<string>) => string;
}
export interface FontMetricsResolver {
getMetrics: (name: string, font: CollectedFontForMetrics) => Promise<FontFaceMetrics>;
generateFontFace: (input: {
metrics: FontFaceMetrics;
fallbackMetrics: FontFaceMetrics;
name: string;
font: string;
properties: CssProperties;
}) => string;
}
export interface SystemFallbacksProvider {
getLocalFonts: (fallback: GenericFallbackName) => Array<string> | null;
getMetricsForLocalFont: (family: string) => FontFaceMetrics;
}
export interface FontFetcher {
fetch: (input: FontFileData) => Promise<Buffer>;
}
export interface FontTypeExtractor {
extract: (url: string) => FontType;
}
export interface FontFileReader {
extract: (input: {
family: string;
url: string;
}) => {
weight: string;
style: Style;
};
}
export interface FontFileIdGenerator {
generate: (input: {
originalUrl: string;
type: FontType;
cssVariable: string;
font: unifont.FontFaceData;
}) => string;
}
export interface StringMatcher {
getClosestMatch: (target: string, candidates: Array<string>) => string;
}
export interface Storage {
getItem: (key: string) => Promise<any | null>;
getItemRaw: (key: string) => Promise<Buffer | null>;
setItem: (key: string, value: any) => Promise<void>;
setItemRaw: (key: string, value: any) => Promise<void>;
}
export interface FontResolver {
resolveFont: (options: ResolveFontOptions<Record<string, any>> & {
provider: FontProvider;
}) => Promise<Array<unifont.FontFaceData>>;
listFonts: (options: {
provider: FontProvider;
}) => Promise<string[] | undefined>;
}

0
node_modules/astro/dist/assets/fonts/definitions.js generated vendored Normal file
View File

View File

@@ -0,0 +1,13 @@
import type { FontFileContentResolver, FontFileIdGenerator, Hasher } from '../definitions.js';
import type { FontType } from '../types.js';
export declare class BuildFontFileIdGenerator implements FontFileIdGenerator {
#private;
constructor({ hasher, contentResolver, }: {
hasher: Hasher;
contentResolver: FontFileContentResolver;
});
generate({ originalUrl, type }: {
originalUrl: string;
type: FontType;
}): string;
}

View File

@@ -0,0 +1,17 @@
class BuildFontFileIdGenerator {
#hasher;
#contentResolver;
constructor({
hasher,
contentResolver
}) {
this.#hasher = hasher;
this.#contentResolver = contentResolver;
}
generate({ originalUrl, type }) {
return `${this.#hasher.hashString(this.#contentResolver.resolve(originalUrl))}.${type}`;
}
}
export {
BuildFontFileIdGenerator
};

View File

@@ -0,0 +1,12 @@
import type { AssetsPrefix } from '../../../types/public/index.js';
import type { UrlResolver } from '../definitions.js';
export declare class BuildUrlResolver implements UrlResolver {
#private;
constructor({ base, assetsPrefix, searchParams, }: {
base: string;
assetsPrefix: AssetsPrefix;
searchParams: URLSearchParams;
});
resolve(id: string): string;
get cspResources(): Array<string>;
}

View File

@@ -0,0 +1,40 @@
import { fileExtension, joinPaths, prependForwardSlash } from "../../../core/path.js";
import { getAssetsPrefix } from "../../utils/getAssetsPrefix.js";
import { createPlaceholderURL, stringifyPlaceholderURL } from "../../utils/url.js";
class BuildUrlResolver {
#resources = /* @__PURE__ */ new Set();
#base;
#assetsPrefix;
#searchParams;
constructor({
base,
assetsPrefix,
searchParams
}) {
this.#base = base;
this.#assetsPrefix = assetsPrefix;
this.#searchParams = searchParams;
}
resolve(id) {
const prefix = this.#assetsPrefix ? getAssetsPrefix(fileExtension(id), this.#assetsPrefix) : void 0;
let urlPath;
if (prefix) {
this.#resources.add(prefix);
urlPath = joinPaths(prefix, this.#base, id);
} else {
this.#resources.add("'self'");
urlPath = prependForwardSlash(joinPaths(this.#base, id));
}
const url = createPlaceholderURL(urlPath);
this.#searchParams.forEach((value, key) => {
url.searchParams.set(key, value);
});
return stringifyPlaceholderURL(url);
}
get cspResources() {
return Array.from(this.#resources);
}
}
export {
BuildUrlResolver
};

View File

@@ -0,0 +1,11 @@
import type { FontFetcher, Storage } from '../definitions.js';
import type { FontFileData } from '../types.js';
export declare class CachedFontFetcher implements FontFetcher {
#private;
constructor({ storage, fetch, readFile, }: {
storage: Storage;
fetch: (url: string, init?: RequestInit) => Promise<Response>;
readFile: (url: string) => Promise<Buffer>;
});
fetch({ id, url, init }: FontFileData): Promise<Buffer>;
}

View File

@@ -0,0 +1,50 @@
import { isAbsolute } from "node:path";
import { AstroError, AstroErrorData } from "../../../core/errors/index.js";
class CachedFontFetcher {
#storage;
#fetch;
#readFile;
constructor({
storage,
fetch,
readFile
}) {
this.#storage = storage;
this.#fetch = fetch;
this.#readFile = readFile;
}
async #cache(storage, key, cb) {
const existing = await storage.getItemRaw(key);
if (existing) {
return existing;
}
const data = await cb();
await storage.setItemRaw(key, data);
return data;
}
async fetch({ id, url, init }) {
return await this.#cache(this.#storage, id, async () => {
try {
if (isAbsolute(url)) {
return await this.#readFile(url);
}
const response = await this.#fetch(url, init ?? void 0);
if (!response.ok) {
throw new Error(`Response was not successful, received status code ${response.status}`);
}
return Buffer.from(await response.arrayBuffer());
} catch (cause) {
throw new AstroError(
{
...AstroErrorData.CannotFetchFontFile,
message: AstroErrorData.CannotFetchFontFile.message(url)
},
{ cause }
);
}
});
}
}
export {
CachedFontFetcher
};

View File

@@ -0,0 +1,18 @@
import type { CollectedFontForMetrics } from '../core/optimize-fallbacks.js';
import type { CssRenderer, FontFetcher, FontMetricsResolver } from '../definitions.js';
import type { CssProperties, FontFaceMetrics } from '../types.js';
export declare class CapsizeFontMetricsResolver implements FontMetricsResolver {
#private;
constructor({ fontFetcher, cssRenderer, }: {
fontFetcher: FontFetcher;
cssRenderer: CssRenderer;
});
getMetrics(name: string, font: CollectedFontForMetrics): Promise<FontFaceMetrics>;
generateFontFace({ metrics, fallbackMetrics, name: fallbackName, font: fallbackFontName, properties, }: {
metrics: FontFaceMetrics;
fallbackMetrics: FontFaceMetrics;
name: string;
font: string;
properties: CssProperties;
}): string;
}

View File

@@ -0,0 +1,71 @@
import { fromBuffer } from "@capsizecss/unpack";
import { renderFontSrc } from "../utils.js";
function filterRequiredMetrics({
ascent,
descent,
lineGap,
unitsPerEm,
xWidthAvg
}) {
return {
ascent,
descent,
lineGap,
unitsPerEm,
xWidthAvg
};
}
function round(value) {
return parseFloat(value.toFixed(4));
}
function toPercentString(value) {
return `${round(value * 100)}%`;
}
class CapsizeFontMetricsResolver {
#cache = {};
#fontFetcher;
#cssRenderer;
constructor({
fontFetcher,
cssRenderer
}) {
this.#fontFetcher = fontFetcher;
this.#cssRenderer = cssRenderer;
}
async getMetrics(name, font) {
return this.#cache[name] ??= filterRequiredMetrics(
await fromBuffer(await this.#fontFetcher.fetch(font))
);
}
// Adapted from Capsize
// Source: https://github.com/seek-oss/capsize/blob/b752693428b45994442433f7e3476ca4e3e3c507/packages/core/src/createFontStack.ts
generateFontFace({
metrics,
fallbackMetrics,
name: fallbackName,
font: fallbackFontName,
properties
}) {
const preferredFontXAvgRatio = metrics.xWidthAvg / metrics.unitsPerEm;
const fallbackFontXAvgRatio = fallbackMetrics.xWidthAvg / fallbackMetrics.unitsPerEm;
const sizeAdjust = preferredFontXAvgRatio && fallbackFontXAvgRatio ? preferredFontXAvgRatio / fallbackFontXAvgRatio : 1;
const adjustedEmSquare = metrics.unitsPerEm * sizeAdjust;
const ascentOverride = metrics.ascent / adjustedEmSquare;
const descentOverride = Math.abs(metrics.descent) / adjustedEmSquare;
const lineGapOverride = metrics.lineGap / adjustedEmSquare;
const fallbackAscentOverride = fallbackMetrics.ascent / adjustedEmSquare;
const fallbackDescentOverride = Math.abs(fallbackMetrics.descent) / adjustedEmSquare;
const fallbackLineGapOverride = fallbackMetrics.lineGap / adjustedEmSquare;
return this.#cssRenderer.generateFontFace(fallbackName, {
...properties,
src: renderFontSrc([{ name: fallbackFontName }]),
"size-adjust": sizeAdjust && sizeAdjust !== 1 ? toPercentString(sizeAdjust) : void 0,
"ascent-override": ascentOverride && ascentOverride !== fallbackAscentOverride ? toPercentString(ascentOverride) : void 0,
"descent-override": descentOverride && descentOverride !== fallbackDescentOverride ? toPercentString(descentOverride) : void 0,
"line-gap-override": lineGapOverride !== fallbackLineGapOverride ? toPercentString(lineGapOverride) : void 0
});
}
}
export {
CapsizeFontMetricsResolver
};

View File

@@ -0,0 +1,16 @@
import type * as unifont from 'unifont';
import type { FontFileContentResolver, FontFileIdGenerator, Hasher } from '../definitions.js';
import type { FontType } from '../types.js';
export declare class DevFontFileIdGenerator implements FontFileIdGenerator {
#private;
constructor({ hasher, contentResolver, }: {
hasher: Hasher;
contentResolver: FontFileContentResolver;
});
generate({ cssVariable, originalUrl, type, font, }: {
originalUrl: string;
type: FontType;
cssVariable: string;
font: unifont.FontFaceData;
}): string;
}

View File

@@ -0,0 +1,37 @@
class DevFontFileIdGenerator {
#hasher;
#contentResolver;
constructor({
hasher,
contentResolver
}) {
this.#hasher = hasher;
this.#contentResolver = contentResolver;
}
#formatWeight(weight) {
if (Array.isArray(weight)) {
return weight.join("-");
}
if (typeof weight === "number") {
return weight.toString();
}
return weight?.replace(/\s+/g, "-");
}
generate({
cssVariable,
originalUrl,
type,
font
}) {
return [
cssVariable.slice(2),
this.#formatWeight(font.weight),
font.style,
font.meta?.subset,
`${this.#hasher.hashString(this.#contentResolver.resolve(originalUrl))}.${type}`
].filter(Boolean).join("-");
}
}
export {
DevFontFileIdGenerator
};

View File

@@ -0,0 +1,10 @@
import type { UrlResolver } from '../definitions.js';
export declare class DevUrlResolver implements UrlResolver {
#private;
constructor({ base, searchParams, }: {
base: string;
searchParams: URLSearchParams;
});
resolve(id: string): string;
get cspResources(): Array<string>;
}

View File

@@ -0,0 +1,29 @@
import { joinPaths, prependForwardSlash } from "../../../core/path.js";
import { createPlaceholderURL, stringifyPlaceholderURL } from "../../utils/url.js";
class DevUrlResolver {
#resolved = false;
#base;
#searchParams;
constructor({
base,
searchParams
}) {
this.#base = base;
this.#searchParams = searchParams;
}
resolve(id) {
this.#resolved ||= true;
const urlPath = prependForwardSlash(joinPaths(this.#base, id));
const url = createPlaceholderURL(urlPath);
this.#searchParams.forEach((value, key) => {
url.searchParams.set(key, value);
});
return stringifyPlaceholderURL(url);
}
get cspResources() {
return this.#resolved ? ["'self'"] : [];
}
}
export {
DevUrlResolver
};

View File

@@ -0,0 +1,11 @@
import type { FontFileReader } from '../definitions.js';
import type { Style } from '../types.js';
export declare class FontaceFontFileReader implements FontFileReader {
extract({ family, url }: {
family: string;
url: string;
}): {
weight: string;
style: Style;
};
}

View File

@@ -0,0 +1,25 @@
import { readFileSync } from "node:fs";
import { fontace } from "fontace";
import { AstroError, AstroErrorData } from "../../../core/errors/index.js";
class FontaceFontFileReader {
extract({ family, url }) {
try {
const data = fontace(readFileSync(url));
return {
weight: data.weight,
style: data.style
};
} catch (cause) {
throw new AstroError(
{
...AstroErrorData.CannotDetermineWeightAndStyleFromFontFile,
message: AstroErrorData.CannotDetermineWeightAndStyleFromFontFile.message(family, url)
},
{ cause }
);
}
}
}
export {
FontaceFontFileReader
};

View File

@@ -0,0 +1,10 @@
import type { FontFileContentResolver } from '../definitions.js';
type ReadFileSync = (path: string) => string;
export declare class FsFontFileContentResolver implements FontFileContentResolver {
#private;
constructor({ readFileSync }: {
readFileSync: ReadFileSync;
});
resolve(url: string): string;
}
export {};

View File

@@ -0,0 +1,21 @@
import { isAbsolute } from "node:path";
import { AstroError, AstroErrorData } from "../../../core/errors/index.js";
class FsFontFileContentResolver {
#readFileSync;
constructor({ readFileSync }) {
this.#readFileSync = readFileSync;
}
resolve(url) {
if (!isAbsolute(url)) {
return url;
}
try {
return url + this.#readFileSync(url);
} catch (cause) {
throw new AstroError(AstroErrorData.UnknownFilesystemError, { cause });
}
}
}
export {
FsFontFileContentResolver
};

View File

@@ -0,0 +1,5 @@
import type { StringMatcher } from '../definitions.js';
export declare class LevenshteinStringMatcher implements StringMatcher {
#private;
getClosestMatch(target: string, candidates: Array<string>): string;
}

View File

@@ -0,0 +1,145 @@
class LevenshteinStringMatcher {
#peq = new Uint32Array(65536);
#myers_32(a, b) {
const n = a.length;
const m = b.length;
const lst = 1 << n - 1;
let pv = -1;
let mv = 0;
let sc = n;
let i = n;
while (i--) {
this.#peq[a.charCodeAt(i)] |= 1 << i;
}
for (i = 0; i < m; i++) {
let eq = this.#peq[b.charCodeAt(i)];
const xv = eq | mv;
eq |= (eq & pv) + pv ^ pv;
mv |= ~(eq | pv);
pv &= eq;
if (mv & lst) {
sc++;
}
if (pv & lst) {
sc--;
}
mv = mv << 1 | 1;
pv = pv << 1 | ~(xv | mv);
mv &= xv;
}
i = n;
while (i--) {
this.#peq[a.charCodeAt(i)] = 0;
}
return sc;
}
#myers_x(b, a) {
const n = a.length;
const m = b.length;
const mhc = [];
const phc = [];
const hsize = Math.ceil(n / 32);
const vsize = Math.ceil(m / 32);
for (let i = 0; i < hsize; i++) {
phc[i] = -1;
mhc[i] = 0;
}
let j = 0;
for (; j < vsize - 1; j++) {
let mv2 = 0;
let pv2 = -1;
const start2 = j * 32;
const vlen2 = Math.min(32, m) + start2;
for (let k = start2; k < vlen2; k++) {
this.#peq[b.charCodeAt(k)] |= 1 << k;
}
for (let i = 0; i < n; i++) {
const eq = this.#peq[a.charCodeAt(i)];
const pb = phc[i / 32 | 0] >>> i & 1;
const mb = mhc[i / 32 | 0] >>> i & 1;
const xv = eq | mv2;
const xh = ((eq | mb) & pv2) + pv2 ^ pv2 | eq | mb;
let ph = mv2 | ~(xh | pv2);
let mh = pv2 & xh;
if (ph >>> 31 ^ pb) {
phc[i / 32 | 0] ^= 1 << i;
}
if (mh >>> 31 ^ mb) {
mhc[i / 32 | 0] ^= 1 << i;
}
ph = ph << 1 | pb;
mh = mh << 1 | mb;
pv2 = mh | ~(xv | ph);
mv2 = ph & xv;
}
for (let k = start2; k < vlen2; k++) {
this.#peq[b.charCodeAt(k)] = 0;
}
}
let mv = 0;
let pv = -1;
const start = j * 32;
const vlen = Math.min(32, m - start) + start;
for (let k = start; k < vlen; k++) {
this.#peq[b.charCodeAt(k)] |= 1 << k;
}
let score = m;
for (let i = 0; i < n; i++) {
const eq = this.#peq[a.charCodeAt(i)];
const pb = phc[i / 32 | 0] >>> i & 1;
const mb = mhc[i / 32 | 0] >>> i & 1;
const xv = eq | mv;
const xh = ((eq | mb) & pv) + pv ^ pv | eq | mb;
let ph = mv | ~(xh | pv);
let mh = pv & xh;
score += ph >>> m - 1 & 1;
score -= mh >>> m - 1 & 1;
if (ph >>> 31 ^ pb) {
phc[i / 32 | 0] ^= 1 << i;
}
if (mh >>> 31 ^ mb) {
mhc[i / 32 | 0] ^= 1 << i;
}
ph = ph << 1 | pb;
mh = mh << 1 | mb;
pv = mh | ~(xv | ph);
mv = ph & xv;
}
for (let k = start; k < vlen; k++) {
this.#peq[b.charCodeAt(k)] = 0;
}
return score;
}
#distance(a, b) {
if (a.length < b.length) {
const tmp = b;
b = a;
a = tmp;
}
if (b.length === 0) {
return a.length;
}
if (a.length <= 32) {
return this.#myers_32(a, b);
}
return this.#myers_x(a, b);
}
#closest(str, arr) {
let min_distance = Infinity;
let min_index = 0;
for (let i = 0; i < arr.length; i++) {
const dist = this.#distance(str, arr[i]);
if (dist < min_distance) {
min_distance = dist;
min_index = i;
}
}
return arr[min_index];
}
getClosestMatch(target, candidates) {
return this.#closest(target, candidates);
}
}
export {
LevenshteinStringMatcher
};

View File

@@ -0,0 +1,15 @@
import type { CssRenderer } from '../definitions.js';
import type { CssProperties } from '../types.js';
export declare function renderFontFace(properties: CssProperties, minify: boolean): string;
export declare function renderCssVariable(key: string, values: Array<string>, minify: boolean): string;
export declare function withFamily(family: string, properties: CssProperties): CssProperties;
/** If the value contains spaces (which would be incorrectly interpreted), we wrap it in quotes. */
export declare function handleValueWithSpaces(value: string): string;
export declare class MinifiableCssRenderer implements CssRenderer {
#private;
constructor({ minify }: {
minify: boolean;
});
generateFontFace(family: string, properties: CssProperties): string;
generateCssVariable(key: string, values: Array<string>): string;
}

View File

@@ -0,0 +1,44 @@
function renderFontFace(properties, minify) {
const lf = minify ? "" : `
`;
const sp = minify ? "" : " ";
return `@font-face${sp}{${lf}${Object.entries(properties).filter(([, value]) => Boolean(value)).map(([key, value]) => `${sp}${sp}${key}:${sp}${value};`).join(lf)}${lf}}${lf}`;
}
function renderCssVariable(key, values, minify) {
const lf = minify ? "" : `
`;
const sp = minify ? "" : " ";
return `:root${sp}{${lf}${sp}${sp}${key}:${sp}${values.map((v) => handleValueWithSpaces(v)).join(`,${sp}`)};${lf}}${lf}`;
}
function withFamily(family, properties) {
return {
"font-family": handleValueWithSpaces(family),
...properties
};
}
const SPACE_RE = /\s/;
function handleValueWithSpaces(value) {
if (SPACE_RE.test(value)) {
return JSON.stringify(value);
}
return value;
}
class MinifiableCssRenderer {
#minify;
constructor({ minify }) {
this.#minify = minify;
}
generateFontFace(family, properties) {
return renderFontFace(withFamily(family, properties), this.#minify);
}
generateCssVariable(key, values) {
return renderCssVariable(key, values, this.#minify);
}
}
export {
MinifiableCssRenderer,
handleValueWithSpaces,
renderCssVariable,
renderFontFace,
withFamily
};

View File

@@ -0,0 +1,5 @@
import type { FontTypeExtractor } from '../definitions.js';
import type { FontType } from '../types.js';
export declare class NodeFontTypeExtractor implements FontTypeExtractor {
extract(url: string): FontType;
}

View File

@@ -0,0 +1,21 @@
import { extname } from "node:path";
import { AstroError, AstroErrorData } from "../../../core/errors/index.js";
import { isFontType } from "../utils.js";
class NodeFontTypeExtractor {
extract(url) {
const extension = extname(url).slice(1);
if (!isFontType(extension)) {
throw new AstroError(
{
...AstroErrorData.CannotExtractFontType,
message: AstroErrorData.CannotExtractFontType.message(url)
},
{ cause: `Unexpected extension, got "${extension}"` }
);
}
return extension;
}
}
export {
NodeFontTypeExtractor
};

View File

@@ -0,0 +1,6 @@
import type { SystemFallbacksProvider } from '../definitions.js';
import type { FontFaceMetrics, GenericFallbackName } from '../types.js';
export declare class RealSystemFallbacksProvider implements SystemFallbacksProvider {
getLocalFonts(fallback: GenericFallbackName): Array<string> | null;
getMetricsForLocalFont(family: string): FontFaceMetrics;
}

View File

@@ -0,0 +1,71 @@
const SYSTEM_METRICS = {
"Times New Roman": {
ascent: 1825,
descent: -443,
lineGap: 87,
unitsPerEm: 2048,
xWidthAvg: 832
},
Arial: {
ascent: 1854,
descent: -434,
lineGap: 67,
unitsPerEm: 2048,
xWidthAvg: 913
},
"Courier New": {
ascent: 1705,
descent: -615,
lineGap: 0,
unitsPerEm: 2048,
xWidthAvg: 1229
},
BlinkMacSystemFont: {
ascent: 1980,
descent: -432,
lineGap: 0,
unitsPerEm: 2048,
xWidthAvg: 853
},
"Segoe UI": {
ascent: 2210,
descent: -514,
lineGap: 0,
unitsPerEm: 2048,
xWidthAvg: 908
},
Roboto: {
ascent: 1900,
descent: -500,
lineGap: 0,
unitsPerEm: 2048,
xWidthAvg: 911
},
"Helvetica Neue": {
ascent: 952,
descent: -213,
lineGap: 28,
unitsPerEm: 1e3,
xWidthAvg: 450
}
};
const DEFAULT_FALLBACKS = {
serif: ["Times New Roman"],
"sans-serif": ["Arial"],
monospace: ["Courier New"],
"system-ui": ["BlinkMacSystemFont", "Segoe UI", "Roboto", "Helvetica Neue", "Arial"],
"ui-serif": ["Times New Roman"],
"ui-sans-serif": ["Arial"],
"ui-monospace": ["Courier New"]
};
class RealSystemFallbacksProvider {
getLocalFonts(fallback) {
return DEFAULT_FALLBACKS[fallback] ?? null;
}
getMetricsForLocalFont(family) {
return SYSTEM_METRICS[family];
}
}
export {
RealSystemFallbacksProvider
};

View File

@@ -0,0 +1,34 @@
import type { FontFaceData, Provider } from 'unifont';
import type { FontResolver, Hasher, Storage } from '../definitions.js';
import type { FontProvider, ResolvedFontFamily, ResolveFontOptions } from '../types.js';
type NonEmptyProviders = [
Provider<string, Record<string, any>>,
...Array<Provider<string, Record<string, any>>>
];
export declare class UnifontFontResolver implements FontResolver {
#private;
private constructor();
static idFromProvider({ hasher, provider }: {
hasher: Hasher;
provider: FontProvider;
}): string;
static astroToUnifontProvider(astroProvider: FontProvider, root: URL): Provider;
static extractUnifontProviders({ families, hasher, root, }: {
families: Array<ResolvedFontFamily>;
hasher: Hasher;
root: URL;
}): NonEmptyProviders;
static create({ families, hasher, storage, root, }: {
families: Array<ResolvedFontFamily>;
hasher: Hasher;
storage: Storage;
root: URL;
}): Promise<UnifontFontResolver>;
resolveFont({ familyName, provider, options, ...rest }: ResolveFontOptions<Record<string, any>> & {
provider: FontProvider;
}): Promise<Array<FontFaceData>>;
listFonts({ provider }: {
provider: FontProvider;
}): Promise<string[] | undefined>;
}
export {};

View File

@@ -0,0 +1,98 @@
import { createUnifont, defineFontProvider } from "unifont";
class UnifontFontResolver {
#unifont;
#hasher;
constructor({
unifont,
hasher
}) {
this.#unifont = unifont;
this.#hasher = hasher;
}
static idFromProvider({ hasher, provider }) {
const hash = hasher.hashObject({
name: provider.name,
...provider.config
});
return `${provider.name}-${hash}`;
}
static astroToUnifontProvider(astroProvider, root) {
return defineFontProvider(astroProvider.name, async (_options, ctx) => {
await astroProvider?.init?.({ ...ctx, root });
return {
async resolveFont(familyName, { options, ...rest }) {
return await astroProvider.resolveFont({ familyName, options, ...rest });
},
async listFonts() {
return astroProvider.listFonts?.();
}
};
})(astroProvider.config);
}
static extractUnifontProviders({
families,
hasher,
root
}) {
const providers = /* @__PURE__ */ new Map();
for (const { provider } of families) {
const id = this.idFromProvider({ hasher, provider });
if (!providers.has(id)) {
const unifontProvider = this.astroToUnifontProvider(provider, root);
unifontProvider._name = this.idFromProvider({ hasher, provider });
providers.set(id, unifontProvider);
}
}
return Array.from(providers.values());
}
static async create({
families,
hasher,
storage,
root
}) {
return new UnifontFontResolver({
unifont: await createUnifont(this.extractUnifontProviders({ families, hasher, root }), {
storage,
// TODO: consider enabling, would require new astro errors
throwOnError: false
}),
hasher
});
}
async resolveFont({
familyName,
provider,
options,
...rest
}) {
const id = UnifontFontResolver.idFromProvider({
hasher: this.#hasher,
provider
});
const { fonts } = await this.#unifont.resolveFont(
familyName,
{
// Options are currently namespaced by provider name, it may change in
// https://github.com/unjs/unifont/pull/287
options: {
[id]: options
},
...rest
},
[id]
);
return fonts;
}
async listFonts({ provider }) {
return await this.#unifont.listFonts([
UnifontFontResolver.idFromProvider({
hasher: this.#hasher,
provider
})
]);
}
}
export {
UnifontFontResolver
};

View File

@@ -0,0 +1,11 @@
import type { Storage } from '../definitions.js';
export declare class UnstorageFsStorage implements Storage {
#private;
constructor({ base }: {
base: URL;
});
getItem(key: string): Promise<any | null>;
getItemRaw(key: string): Promise<Buffer | null>;
setItem(key: string, value: any): Promise<void>;
setItemRaw(key: string, value: any): Promise<void>;
}

View File

@@ -0,0 +1,26 @@
import { fileURLToPath } from "node:url";
import * as unstorage from "unstorage";
import fsLiteDriver from "unstorage/drivers/fs-lite";
class UnstorageFsStorage {
#unstorage;
constructor({ base }) {
this.#unstorage = unstorage.createStorage({
driver: fsLiteDriver({ base: fileURLToPath(base) })
});
}
async getItem(key) {
return await this.#unstorage.getItem(key);
}
async getItemRaw(key) {
return await this.#unstorage.getItemRaw(key);
}
async setItem(key, value) {
return await this.#unstorage.setItem(key, value);
}
async setItemRaw(key, value) {
return await this.#unstorage.setItemRaw(key, value);
}
}
export {
UnstorageFsStorage
};

View File

@@ -0,0 +1,7 @@
import type { Hasher } from '../definitions.js';
export declare class XxhashHasher implements Hasher {
hashString: (input: string) => string;
private constructor();
static create(): Promise<XxhashHasher>;
hashObject(input: Record<string, any>): string;
}

View File

@@ -0,0 +1,18 @@
import xxhash from "xxhash-wasm";
import { sortObjectByKey } from "../utils.js";
class XxhashHasher {
hashString;
constructor(hashString) {
this.hashString = hashString;
}
static async create() {
const { h64ToString } = await xxhash();
return new XxhashHasher(h64ToString);
}
hashObject(input) {
return this.hashString(JSON.stringify(sortObjectByKey(input)));
}
}
export {
XxhashHasher
};

View File

@@ -0,0 +1,37 @@
import { type AdobeProviderOptions, type GoogleFamilyOptions, type GoogleiconsFamilyOptions } from 'unifont';
import type { FontProvider } from '../types.js';
import { type LocalFamilyOptions } from './local.js';
/** [Adobe](https://fonts.adobe.com/) */
declare function adobe(config: AdobeProviderOptions): FontProvider;
/** [Bunny](https://fonts.bunny.net/) */
declare function bunny(): FontProvider;
/** [Fontshare](https://www.fontshare.com/) */
declare function fontshare(): FontProvider;
/** [Fontsource](https://fontsource.org/) */
declare function fontsource(): FontProvider;
/** [Google](https://fonts.google.com/) */
declare function google(): FontProvider<GoogleFamilyOptions | undefined>;
/** [Google Icons](https://fonts.google.com/icons) */
declare function googleicons(): FontProvider<GoogleiconsFamilyOptions | undefined>;
/** A provider that handles local files. */
declare function local(): FontProvider<LocalFamilyOptions>;
/**
* Astro exports a few built-in providers:
* - [Adobe](https://fonts.adobe.com/)
* - [Bunny](https://fonts.bunny.net/)
* - [Fontshare](https://www.fontshare.com/)
* - [Fontsource](https://fontsource.org/)
* - [Google](https://fonts.google.com/)
* - [Google Icons](https://fonts.google.com/icons)
* - Local
*/
export declare const fontProviders: {
adobe: typeof adobe;
bunny: typeof bunny;
fontshare: typeof fontshare;
fontsource: typeof fontsource;
google: typeof google;
googleicons: typeof googleicons;
local: typeof local;
};
export {};

119
node_modules/astro/dist/assets/fonts/providers/index.js generated vendored Normal file
View File

@@ -0,0 +1,119 @@
import {
providers
} from "unifont";
import { FontaceFontFileReader } from "../infra/fontace-font-file-reader.js";
import { LocalFontProvider } from "./local.js";
function adobe(config) {
const provider = providers.adobe(config);
let initializedProvider;
return {
name: provider._name,
config,
async init(context) {
initializedProvider = await provider(context);
},
async resolveFont({ familyName, ...rest }) {
return await initializedProvider?.resolveFont(familyName, rest);
},
async listFonts() {
return await initializedProvider?.listFonts?.();
}
};
}
function bunny() {
const provider = providers.bunny();
let initializedProvider;
return {
name: provider._name,
async init(context) {
initializedProvider = await provider(context);
},
async resolveFont({ familyName, ...rest }) {
return await initializedProvider?.resolveFont(familyName, rest);
},
async listFonts() {
return await initializedProvider?.listFonts?.();
}
};
}
function fontshare() {
const provider = providers.fontshare();
let initializedProvider;
return {
name: provider._name,
async init(context) {
initializedProvider = await provider(context);
},
async resolveFont({ familyName, ...rest }) {
return await initializedProvider?.resolveFont(familyName, rest);
},
async listFonts() {
return await initializedProvider?.listFonts?.();
}
};
}
function fontsource() {
const provider = providers.fontsource();
let initializedProvider;
return {
name: provider._name,
async init(context) {
initializedProvider = await provider(context);
},
async resolveFont({ familyName, ...rest }) {
return await initializedProvider?.resolveFont(familyName, rest);
},
async listFonts() {
return await initializedProvider?.listFonts?.();
}
};
}
function google() {
const provider = providers.google();
let initializedProvider;
return {
name: provider._name,
async init(context) {
initializedProvider = await provider(context);
},
async resolveFont({ familyName, ...rest }) {
return await initializedProvider?.resolveFont(familyName, rest);
},
async listFonts() {
return await initializedProvider?.listFonts?.();
}
};
}
function googleicons() {
const provider = providers.googleicons();
let initializedProvider;
return {
name: provider._name,
async init(context) {
initializedProvider = await provider(context);
},
async resolveFont({ familyName, ...rest }) {
return await initializedProvider?.resolveFont(familyName, rest);
},
async listFonts() {
return await initializedProvider?.listFonts?.();
}
};
}
function local() {
return new LocalFontProvider({
fontFileReader: new FontaceFontFileReader()
});
}
const fontProviders = {
adobe,
bunny,
fontshare,
fontsource,
google,
googleicons,
local
};
export {
fontProviders
};

View File

@@ -0,0 +1,44 @@
import type * as unifont from 'unifont';
import type { FontFileReader } from '../definitions.js';
import type { FamilyProperties, FontProvider, FontProviderInitContext, ResolveFontOptions, Style, Weight } from '../types.js';
type RawSource = string | URL | {
url: string | URL;
tech?: string | undefined;
};
interface Variant extends FamilyProperties {
/**
* Font [sources](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/src). It can be a path relative to the root, a package import or a URL. URLs are particularly useful if you inject local fonts through an integration.
*/
src: [RawSource, ...Array<RawSource>];
/**
* A [font weight](https://developer.mozilla.org/en-US/docs/Web/CSS/font-weight). If the associated font is a [variable font](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_fonts/Variable_fonts_guide), you can specify a range of weights:
*
* ```js
* weight: "100 900"
* ```
*/
weight?: Weight | undefined;
/**
* A [font style](https://developer.mozilla.org/en-US/docs/Web/CSS/font-style).
*/
style?: Style | undefined;
}
export interface LocalFamilyOptions {
/**
* Each variant represents a [`@font-face` declaration](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/).
*/
variants: [Variant, ...Array<Variant>];
}
export declare class LocalFontProvider implements FontProvider<LocalFamilyOptions> {
#private;
name: string;
config?: Record<string, any> | undefined;
constructor({ fontFileReader, }: {
fontFileReader: FontFileReader;
});
init(context: Pick<FontProviderInitContext, 'root'>): void;
resolveFont(options: ResolveFontOptions<LocalFamilyOptions>): {
fonts: Array<unifont.FontFaceData>;
};
}
export {};

View File

@@ -0,0 +1,69 @@
import { createRequire } from "node:module";
import { fileURLToPath, pathToFileURL } from "node:url";
class LocalFontProvider {
name = "local";
config;
#fontFileReader;
#root;
constructor({
fontFileReader
}) {
this.config = void 0;
this.#fontFileReader = fontFileReader;
this.#root = void 0;
}
init(context) {
this.#root = context.root;
}
#resolveEntrypoint(root, entrypoint) {
const require2 = createRequire(root);
try {
return pathToFileURL(require2.resolve(entrypoint));
} catch {
return new URL(entrypoint, root);
}
}
#normalizeSource(value) {
const isValue = typeof value === "string" || value instanceof URL;
const url = (isValue ? value : value.url).toString();
const tech = isValue ? void 0 : value.tech;
return {
url: fileURLToPath(this.#resolveEntrypoint(this.#root ?? new URL(import.meta.url), url)),
tech
};
}
resolveFont(options) {
return {
fonts: options.options?.variants.map((variant) => {
const shouldInfer = variant.weight === void 0 || variant.style === void 0;
const data = {
// If it should be inferred, we don't want to set the value
weight: variant.weight,
style: variant.style,
src: [],
unicodeRange: variant.unicodeRange,
display: variant.display,
stretch: variant.stretch,
featureSettings: variant.featureSettings,
variationSettings: variant.variationSettings
};
data.src = variant.src.map((rawSource, index) => {
const source = this.#normalizeSource(rawSource);
if (shouldInfer && index === 0) {
const result = this.#fontFileReader.extract({
family: options.familyName,
url: source.url
});
if (variant.weight === void 0) data.weight = result.weight;
if (variant.style === void 0) data.style = result.style;
}
return source;
});
return data;
}) ?? []
};
}
}
export {
LocalFontProvider
};

1
node_modules/astro/dist/assets/fonts/runtime.d.ts generated vendored Normal file
View File

@@ -0,0 +1 @@
export declare const fontData: import("./types.js").FontDataByCssVariable;

5
node_modules/astro/dist/assets/fonts/runtime.js generated vendored Normal file
View File

@@ -0,0 +1,5 @@
import * as fontsMod from "virtual:astro:assets/fonts/internal";
const fontData = fontsMod.fontDataByCssVariable ?? {};
export {
fontData
};

2
node_modules/astro/dist/assets/fonts/sync.d.ts generated vendored Normal file
View File

@@ -0,0 +1,2 @@
import type { AstroSettings } from '../../types/astro.js';
export declare function syncFonts(settings: AstroSettings): void;

17
node_modules/astro/dist/assets/fonts/sync.js generated vendored Normal file
View File

@@ -0,0 +1,17 @@
import { FONTS_TYPES_FILE } from "./constants.js";
function syncFonts(settings) {
if (!settings.config.experimental.fonts) {
return;
}
settings.injectedTypes.push({
filename: FONTS_TYPES_FILE,
content: `declare module 'astro:assets' {
/** @internal */
export type CssVariable = (${JSON.stringify(settings.config.experimental.fonts.map((family) => family.cssVariable))})[number];
}
`
});
}
export {
syncFonts
};

233
node_modules/astro/dist/assets/fonts/types.d.ts generated vendored Normal file
View File

@@ -0,0 +1,233 @@
import type { Font } from '@capsizecss/unpack';
import type * as unifont from 'unifont';
import type { z } from 'zod';
import type { displaySchema, styleSchema, weightSchema } from './config.js';
import type { FONT_TYPES, GENERIC_FALLBACK_NAMES } from './constants.js';
import type { CollectedFontForMetrics } from './core/optimize-fallbacks.js';
export type Weight = z.infer<typeof weightSchema>;
type Display = z.infer<typeof displaySchema>;
export interface FontProviderInitContext {
storage: {
getItem: {
<T = unknown>(key: string): Promise<T | null>;
<T = unknown>(key: string, init: () => Awaitable<T>): Promise<T>;
};
setItem: (key: string, value: unknown) => Awaitable<void>;
};
root: URL;
}
type Awaitable<T> = T | Promise<T>;
export interface FontProvider<TFamilyOptions extends Record<string, any> | undefined | never = never> {
/**
* The font provider name, used for display and deduplication.
*/
name: string;
/**
* Optional serializable object, used for deduplication.
*/
config?: Record<string, any> | undefined;
/**
* Optional callback, used to perform any initialization logic.
*/
init?: ((context: FontProviderInitContext) => Awaitable<void>) | undefined;
/**
* Required callback, used to retrieve and return font face data based on the given options.
*/
resolveFont: (options: ResolveFontOptions<TFamilyOptions>) => Awaitable<{
fonts: Array<unifont.FontFaceData>;
} | undefined>;
/**
* Optional callback, used to return the list of available font names.
*/
listFonts?: (() => Awaitable<Array<string> | undefined>) | undefined;
}
export interface FamilyProperties {
/**
* @default `"swap"`
*
* A [font display](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display).
*/
display?: Display | undefined;
/**
* A [font stretch](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-stretch).
*/
stretch?: string | undefined;
/**
* Font [feature settings](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-feature-settings).
*/
featureSettings?: string | undefined;
/**
* Font [variation settings](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-variation-settings).
*/
variationSettings?: string | undefined;
/**
* A [unicode range](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/unicode-range).
*/
unicodeRange?: [string, ...Array<string>] | undefined;
}
type WithOptions<TFontProvider extends FontProvider> = TFontProvider extends FontProvider<infer TFamilyOptions> ? [TFamilyOptions] extends [never] ? {
/**
* Options forwarded to the font provider while resolving this font family.
*/
options?: undefined;
} : undefined extends TFamilyOptions ? {
/**
* Options forwarded to the font provider while resolving this font family.
*/
options?: TFamilyOptions;
} : {
/**
* Options forwarded to the font provider while resolving this font family.
*/
options: TFamilyOptions;
} : {
/**
* Options forwarded to the font provider while resolving this font family.
*/
options?: undefined;
};
export type FontFamily<TFontProvider extends FontProvider = FontProvider> = FamilyProperties & WithOptions<NoInfer<TFontProvider>> & {
/**
* The font family name, as identified by your font provider.
*/
name: string;
/**
* A valid [ident](https://developer.mozilla.org/en-US/docs/Web/CSS/ident) in the form of a CSS variable (i.e. starting with `--`).
*/
cssVariable: string;
/**
* The source of your font files. You can use a built-in provider or write your own custom provider.
*/
provider: TFontProvider;
/**
* @default `[400]`
*
* An array of [font weights](https://developer.mozilla.org/en-US/docs/Web/CSS/font-weight). If the associated font is a [variable font](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_fonts/Variable_fonts_guide), you can specify a range of weights:
*
* ```js
* weight: "100 900"
* ```
*/
weights?: [Weight, ...Array<Weight>] | undefined;
/**
* @default `["normal", "italic"]`
*
* An array of [font styles](https://developer.mozilla.org/en-US/docs/Web/CSS/font-style).
*/
styles?: [Style, ...Array<Style>] | undefined;
/**
* @default `["latin"]`
*
* An array of [font subsets](https://knaap.dev/posts/font-subsetting/):
*/
subsets?: [string, ...Array<string>] | undefined;
/**
* @default `["woff2"]`
*
* An array of [font formats](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@font-face/src#font_formats).
*/
formats?: [FontType, ...Array<FontType>] | undefined;
/**
* @default `["sans-serif"]`
*
* An array of fonts to use when your chosen font is unavailable, or loading. Fallback fonts will be chosen in the order listed. The first available font will be used:
*
* ```js
* fallbacks: ["CustomFont", "serif"]
* ```
*
* To disable fallback fonts completely, configure an empty array:
*
* ```js
* fallbacks: []
* ```
*
* If the last font in the `fallbacks` array is a [generic family name](https://developer.mozilla.org/en-US/docs/Web/CSS/font-family#generic-name), Astro will attempt to generate [optimized fallbacks](https://developer.chrome.com/blog/font-fallbacks) using font metrics will be generated. To disable this optimization, set `optimizedFallbacks` to false.
*/
fallbacks?: Array<string> | undefined;
/**
* @default `true`
*
* Whether or not to enable optimized fallback generation. You may disable this default optimization to have full control over `fallbacks`.
*/
optimizedFallbacks?: boolean | undefined;
};
export interface ResolvedFontFamily extends Omit<FontFamily, 'weights'> {
uniqueName: string;
weights?: Array<string>;
}
export type FontType = (typeof FONT_TYPES)[number];
/**
* Preload data is used for links generation inside the <Font /> component
*/
export interface PreloadData {
/**
* Absolute link to a font file, eg. /_astro/fonts/abc.woff
*/
url: string;
/**
* A font type, eg. woff2, woff, ttf...
*/
type: FontType;
weight: string | undefined;
style: string | undefined;
subset: string | undefined;
}
export type FontFaceMetrics = Pick<Font, 'ascent' | 'descent' | 'lineGap' | 'unitsPerEm' | 'xWidthAvg'>;
export type GenericFallbackName = (typeof GENERIC_FALLBACK_NAMES)[number];
export type Defaults = Required<Pick<ResolvedFontFamily, 'weights' | 'styles' | 'subsets' | 'fallbacks' | 'optimizedFallbacks' | 'formats'>>;
export interface FontFileData {
id: string;
url: string;
init: RequestInit | undefined;
}
/**
* Holds associations of id and original font file URLs, so they can be
* downloaded whenever the id is requested.
*/
export type FontFileById = Map<FontFileData['id'], Pick<FontFileData, 'url' | 'init'>>;
export type ComponentDataByCssVariable = Map<string, {
preloads: Array<PreloadData>;
css: string;
}>;
export interface FontData {
src: Array<{
url: string;
format?: string;
tech?: string;
}>;
weight?: string;
style?: string;
}
/**
* Holds associations of CSS variables and font data to be exposed via virtual module.
*/
export type FontDataByCssVariable = Record<string, Array<FontData>>;
export type Style = z.output<typeof styleSchema>;
export type PreloadFilter = boolean | Array<{
weight?: string | number;
style?: string;
subset?: string;
}>;
export interface ResolveFontOptions<FamilyOptions extends Record<string, any> | undefined | never = never> {
familyName: string;
weights: string[];
styles: Style[];
subsets: string[];
formats: FontType[];
options: [FamilyOptions] extends [never] ? undefined : FamilyOptions | undefined;
}
export type CssProperties = Record<string, string | undefined>;
export interface FontFamilyAssets {
family: ResolvedFontFamily;
fonts: Array<unifont.FontFaceData>;
/**
* Holds a list of font files to be used for optimized fallbacks generation
*/
collectedFontsForMetricsByUniqueKey: Map<string, CollectedFontForMetrics>;
preloads: Array<PreloadData>;
}
export type FontFamilyAssetsByUniqueKey = Map<string, FontFamilyAssets>;
export type Collaborator<T extends (input: any) => any, U extends keyof Parameters<T>[0]> = (params: Pick<Parameters<T>[0], U>) => ReturnType<T>;
export {};

0
node_modules/astro/dist/assets/fonts/types.js generated vendored Normal file
View File

20
node_modules/astro/dist/assets/fonts/utils.d.ts generated vendored Normal file
View File

@@ -0,0 +1,20 @@
import type * as unifont from 'unifont';
import type { CssProperties, FontType, GenericFallbackName } from './types.js';
/**
* Turns unifont font face data into generic CSS properties, to be consumed by the CSS renderer.
*/
export declare function unifontFontFaceDataToProperties(font: Partial<unifont.FontFaceData>): CssProperties;
export declare function renderFontWeight(weight: unifont.FontFaceData['weight']): string | undefined;
/**
* Turns unifont font face data src into a valid CSS property.
* Adapted from https://github.com/nuxt/fonts/blob/main/src/css/render.ts#L68-L81
*/
export declare function renderFontSrc(sources: Exclude<unifont.FontFaceData['src'][number], string>[]): string;
/**
* Removes the quotes from a string. Used for family names
*/
export declare function withoutQuotes(str: string): string;
export declare function isFontType(str: string): str is FontType;
export declare function isGenericFontFamily(str: string): str is GenericFallbackName;
export declare function dedupe<const T extends Array<any>>(arr: T): T;
export declare function sortObjectByKey<T extends Record<string, any>>(unordered: T): T;

62
node_modules/astro/dist/assets/fonts/utils.js generated vendored Normal file
View File

@@ -0,0 +1,62 @@
import { FONT_TYPES, GENERIC_FALLBACK_NAMES } from "./constants.js";
function unifontFontFaceDataToProperties(font) {
return {
src: font.src ? renderFontSrc(font.src) : void 0,
"font-display": font.display ?? "swap",
"unicode-range": font.unicodeRange?.length ? font.unicodeRange.join(",") : void 0,
"font-weight": renderFontWeight(font.weight),
"font-style": font.style,
"font-stretch": font.stretch,
"font-feature-settings": font.featureSettings,
"font-variation-settings": font.variationSettings
};
}
function renderFontWeight(weight) {
return Array.isArray(weight) ? weight.join(" ") : weight?.toString();
}
function renderFontSrc(sources) {
return sources.map((src) => {
if ("name" in src) {
return `local("${src.name}")`;
}
let rendered = `url("${src.url}")`;
if (src.format) {
rendered += ` format("${src.format}")`;
}
if (src.tech) {
rendered += ` tech(${src.tech})`;
}
return rendered;
}).join(", ");
}
const QUOTES_RE = /^["']|["']$/g;
function withoutQuotes(str) {
return str.trim().replace(QUOTES_RE, "");
}
function isFontType(str) {
return FONT_TYPES.includes(str);
}
function isGenericFontFamily(str) {
return GENERIC_FALLBACK_NAMES.includes(str);
}
function dedupe(arr) {
return [...new Set(arr)];
}
function sortObjectByKey(unordered) {
const ordered = Object.keys(unordered).sort().reduce((obj, key) => {
const value = unordered[key];
obj[key] = Array.isArray(value) ? value.map((v) => typeof v === "object" && v !== null ? sortObjectByKey(v) : v) : typeof value === "object" && value !== null ? sortObjectByKey(value) : value;
return obj;
}, {});
return ordered;
}
export {
dedupe,
isFontType,
isGenericFontFamily,
renderFontSrc,
renderFontWeight,
sortObjectByKey,
unifontFontFaceDataToProperties,
withoutQuotes
};

View File

@@ -0,0 +1,10 @@
import type { Plugin } from 'vite';
import type { Logger } from '../../core/logger/core.js';
import type { AstroSettings } from '../../types/astro.js';
interface Options {
settings: AstroSettings;
sync: boolean;
logger: Logger;
}
export declare function fontsPlugin({ settings, sync, logger }: Options): Plugin;
export {};

View File

@@ -0,0 +1,300 @@
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { readFile } from "node:fs/promises";
import { isAbsolute } from "node:path";
import colors from "piccolore";
import { getAlgorithm, shouldTrackCspHashes } from "../../core/csp/common.js";
import { generateCspDigest } from "../../core/encryption.js";
import { collectErrorMetadata } from "../../core/errors/dev/utils.js";
import { AstroError, AstroErrorData, isAstroError } from "../../core/errors/index.js";
import { formatErrorMessage } from "../../core/messages.js";
import { appendForwardSlash, joinPaths, prependForwardSlash } from "../../core/path.js";
import { getClientOutputDirectory } from "../../prerender/utils.js";
import {
ASSETS_DIR,
CACHE_DIR,
DEFAULTS,
RESOLVED_RUNTIME_VIRTUAL_MODULE_ID,
RESOLVED_VIRTUAL_MODULE_ID,
RUNTIME_VIRTUAL_MODULE_ID,
VIRTUAL_MODULE_ID
} from "./constants.js";
import { collectComponentData } from "./core/collect-component-data.js";
import { collectFontAssetsFromFaces } from "./core/collect-font-assets-from-faces.js";
import { collectFontData } from "./core/collect-font-data.js";
import { computeFontFamiliesAssets } from "./core/compute-font-families-assets.js";
import { filterAndTransformFontFaces } from "./core/filter-and-transform-font-faces.js";
import { getOrCreateFontFamilyAssets } from "./core/get-or-create-font-family-assets.js";
import { optimizeFallbacks } from "./core/optimize-fallbacks.js";
import { resolveFamily } from "./core/resolve-family.js";
import { BuildFontFileIdGenerator } from "./infra/build-font-file-id-generator.js";
import { BuildUrlResolver } from "./infra/build-url-resolver.js";
import { CachedFontFetcher } from "./infra/cached-font-fetcher.js";
import { CapsizeFontMetricsResolver } from "./infra/capsize-font-metrics-resolver.js";
import { DevFontFileIdGenerator } from "./infra/dev-font-file-id-generator.js";
import { DevUrlResolver } from "./infra/dev-url-resolver.js";
import { FsFontFileContentResolver } from "./infra/fs-font-file-content-resolver.js";
import { LevenshteinStringMatcher } from "./infra/levenshtein-string-matcher.js";
import { MinifiableCssRenderer } from "./infra/minifiable-css-renderer.js";
import { NodeFontTypeExtractor } from "./infra/node-font-type-extractor.js";
import { RealSystemFallbacksProvider } from "./infra/system-fallbacks-provider.js";
import { UnifontFontResolver } from "./infra/unifont-font-resolver.js";
import { UnstorageFsStorage } from "./infra/unstorage-fs-storage.js";
import { XxhashHasher } from "./infra/xxhash-hasher.js";
function fontsPlugin({ settings, sync, logger }) {
if (!settings.config.experimental.fonts) {
return {
name: "astro:fonts:fallback",
resolveId(id) {
if (id === VIRTUAL_MODULE_ID) {
return RESOLVED_VIRTUAL_MODULE_ID;
}
if (id === RUNTIME_VIRTUAL_MODULE_ID) {
return RESOLVED_RUNTIME_VIRTUAL_MODULE_ID;
}
},
load(id) {
if (id === RESOLVED_VIRTUAL_MODULE_ID || id === RESOLVED_RUNTIME_VIRTUAL_MODULE_ID) {
return {
code: ""
};
}
}
};
}
const assetsDir = prependForwardSlash(
appendForwardSlash(joinPaths(settings.config.build.assets, ASSETS_DIR))
);
const baseUrl = joinPaths(settings.config.base, assetsDir);
let fontFileById = null;
let componentDataByCssVariable = null;
let fontDataByCssVariable = null;
let isBuild;
let fontFetcher = null;
let fontTypeExtractor = null;
const cleanup = () => {
componentDataByCssVariable = null;
fontDataByCssVariable = null;
fontFileById = null;
fontFetcher = null;
};
return {
name: "astro:fonts",
config(_, { command }) {
isBuild = command === "build";
},
async buildStart() {
const { root } = settings.config;
const hasher = await XxhashHasher.create();
const storage = new UnstorageFsStorage({
// In dev, we cache fonts data in .astro so it can be easily inspected and cleared
base: new URL(CACHE_DIR, isBuild ? settings.config.cacheDir : settings.dotAstroDir)
});
const systemFallbacksProvider = new RealSystemFallbacksProvider();
fontFetcher = new CachedFontFetcher({ storage, fetch, readFile });
const cssRenderer = new MinifiableCssRenderer({ minify: isBuild });
const fontMetricsResolver = new CapsizeFontMetricsResolver({ fontFetcher, cssRenderer });
fontTypeExtractor = new NodeFontTypeExtractor();
const stringMatcher = new LevenshteinStringMatcher();
const urlResolver = isBuild ? new BuildUrlResolver({
base: baseUrl,
assetsPrefix: settings.config.build.assetsPrefix,
searchParams: settings.adapter?.client?.assetQueryParams ?? new URLSearchParams()
}) : new DevUrlResolver({
base: baseUrl,
searchParams: settings.adapter?.client?.assetQueryParams ?? new URLSearchParams()
});
const contentResolver = new FsFontFileContentResolver({
readFileSync: (path) => readFileSync(path, "utf-8")
});
const fontFileIdGenerator = isBuild ? new BuildFontFileIdGenerator({
hasher,
contentResolver
}) : new DevFontFileIdGenerator({
hasher,
contentResolver
});
const { bold } = colors;
const defaults = DEFAULTS;
const resolvedFamilies = settings.config.experimental.fonts.map(
(family) => resolveFamily({ family, hasher })
);
const { fontFamilyAssets, fontFileById: _fontFileById } = await computeFontFamiliesAssets({
resolvedFamilies,
defaults,
bold,
logger,
stringMatcher,
fontResolver: await UnifontFontResolver.create({
families: resolvedFamilies,
hasher,
storage,
root
}),
getOrCreateFontFamilyAssets: ({ family, fontFamilyAssetsByUniqueKey }) => getOrCreateFontFamilyAssets({
family,
fontFamilyAssetsByUniqueKey,
bold,
logger
}),
filterAndTransformFontFaces: ({ family, fonts }) => filterAndTransformFontFaces({
family,
fonts,
fontFileIdGenerator,
fontTypeExtractor,
urlResolver
}),
collectFontAssetsFromFaces: ({ collectedFontsIds, family, fontFilesIds, fonts }) => collectFontAssetsFromFaces({
collectedFontsIds,
family,
fontFilesIds,
fonts,
fontFileIdGenerator,
hasher,
defaults
})
});
fontDataByCssVariable = collectFontData(fontFamilyAssets);
componentDataByCssVariable = await collectComponentData({
cssRenderer,
defaults,
fontFamilyAssets,
optimizeFallbacks: ({ collectedFonts, fallbacks, family }) => optimizeFallbacks({
collectedFonts,
fallbacks,
family,
fontMetricsResolver,
systemFallbacksProvider
})
});
fontFileById = _fontFileById;
if (shouldTrackCspHashes(settings.config.experimental.csp)) {
const algorithm = getAlgorithm(settings.config.experimental.csp);
for (const { css } of componentDataByCssVariable.values()) {
settings.injectedCsp.styleHashes.push(await generateCspDigest(css, algorithm));
}
for (const resource of urlResolver.cspResources) {
settings.injectedCsp.fontResources.add(resource);
}
}
},
async configureServer(server) {
server.watcher.on("change", (path) => {
if (!fontFileById) {
return;
}
const localPaths = [...fontFileById.values()].filter(({ url }) => isAbsolute(url)).map((v) => v.url);
if (localPaths.includes(path)) {
logger.info("assets", "Font file updated");
server.restart();
}
});
server.watcher.on("unlink", (path) => {
if (!fontFileById) {
return;
}
const localPaths = [...fontFileById.values()].filter(({ url }) => isAbsolute(url)).map((v) => v.url);
if (localPaths.includes(path)) {
logger.warn(
"assets",
`The font file ${JSON.stringify(path)} referenced in your config has been deleted. Restore the file or remove this font from your configuration if it is no longer needed.`
);
}
});
server.middlewares.use(assetsDir, async (req, res, next) => {
if (!fontFetcher || !fontTypeExtractor) {
logger.debug(
"assets",
"Fonts dependencies should be initialized by now, skipping dev middleware."
);
return next();
}
if (!req.url) {
return next();
}
const fontId = req.url.slice(1);
const fontData = fontFileById?.get(fontId);
if (!fontData) {
return next();
}
res.setHeader("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0");
res.setHeader("Pragma", "no-cache");
res.setHeader("Expires", 0);
try {
const buffer = await fontFetcher.fetch({ id: fontId, ...fontData });
res.setHeader("Content-Length", buffer.length);
res.setHeader("Content-Type", `font/${fontTypeExtractor.extract(fontId)}`);
res.end(buffer);
} catch (err) {
logger.error("assets", "Cannot download font file");
if (isAstroError(err)) {
logger.error(
"SKIP_FORMAT",
formatErrorMessage(collectErrorMetadata(err), logger.level() === "debug")
);
}
res.statusCode = 500;
res.end();
}
});
},
resolveId(id) {
if (id === VIRTUAL_MODULE_ID) {
return RESOLVED_VIRTUAL_MODULE_ID;
}
if (id === RUNTIME_VIRTUAL_MODULE_ID) {
return RESOLVED_RUNTIME_VIRTUAL_MODULE_ID;
}
},
async load(id) {
if (id === RESOLVED_VIRTUAL_MODULE_ID) {
return {
code: `
export const componentDataByCssVariable = new Map(${JSON.stringify(Array.from(componentDataByCssVariable?.entries() ?? []))});
export const fontDataByCssVariable = ${JSON.stringify(fontDataByCssVariable ?? {})}
`
};
}
if (id === RESOLVED_RUNTIME_VIRTUAL_MODULE_ID) {
return {
code: `export * from 'astro/assets/fonts/runtime.js';`
};
}
},
async buildEnd() {
if (sync || settings.config.experimental.fonts.length === 0 || !isBuild) {
cleanup();
return;
}
try {
const dir = getClientOutputDirectory(settings);
const fontsDir = new URL(`.${assetsDir}`, dir);
try {
mkdirSync(fontsDir, { recursive: true });
} catch (cause) {
throw new AstroError(AstroErrorData.UnknownFilesystemError, { cause });
}
if (fontFileById) {
logger.info(
"assets",
`Copying fonts (${fontFileById.size} file${fontFileById.size === 1 ? "" : "s"})...`
);
await Promise.all(
Array.from(fontFileById.entries()).map(async ([id, associatedData]) => {
const data = await fontFetcher.fetch({ id, ...associatedData });
try {
writeFileSync(new URL(id, fontsDir), data);
} catch (cause) {
throw new AstroError(AstroErrorData.UnknownFilesystemError, { cause });
}
})
);
}
} finally {
cleanup();
}
}
};
}
export {
fontsPlugin
};