full site update

This commit is contained in:
2025-07-24 18:46:24 +02:00
parent bfe2b90d8d
commit 37a6e0ab31
6912 changed files with 540482 additions and 361712 deletions

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

@@ -0,0 +1,363 @@
import { z } from 'zod';
export declare const styleSchema: z.ZodEnum<["normal", "italic", "oblique"]>;
export declare const fontProviderSchema: z.ZodObject<{
/**
* URL, path relative to the root or package import.
*/
entrypoint: z.ZodUnion<[z.ZodString, z.ZodType<URL, z.ZodTypeDef, URL>]>;
/**
* Optional serializable object passed to the unifont provider.
*/
config: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
}, "strict", z.ZodTypeAny, {
entrypoint: string | URL;
config?: Record<string, any> | undefined;
}, {
entrypoint: string | URL;
config?: Record<string, any> | undefined;
}>;
export declare const localFontFamilySchema: z.ZodObject<z.objectUtil.extendShape<z.objectUtil.extendShape<{
/**
* The font family name, as identified by your font provider.
*/
name: z.ZodString;
/**
* 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: z.ZodString;
}, {
/**
* @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: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
/**
* @default `true`
*
* Whether or not to enable optimized fallback generation. You may disable this default optimization to have full control over `fallbacks`.
*/
optimizedFallbacks: z.ZodOptional<z.ZodBoolean>;
}>, {
/**
* The source of your font files. Set to `"local"` to use local font files.
*/
provider: z.ZodLiteral<"local">;
/**
* Each variant represents a [`@font-face` declaration](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/).
*/
variants: z.ZodArray<z.ZodObject<z.objectUtil.extendShape<{
/**
* 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: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
/**
* A [font style](https://developer.mozilla.org/en-US/docs/Web/CSS/font-style).
*/
style: z.ZodOptional<z.ZodEnum<["normal", "italic", "oblique"]>>;
/**
* @default `"swap"`
*
* A [font display](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display).
*/
display: z.ZodOptional<z.ZodEnum<["auto", "block", "swap", "fallback", "optional"]>>;
/**
* A [font stretch](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-stretch).
*/
stretch: z.ZodOptional<z.ZodString>;
/**
* Font [feature settings](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-feature-settings).
*/
featureSettings: z.ZodOptional<z.ZodString>;
/**
* Font [variation settings](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-variation-settings).
*/
variationSettings: z.ZodOptional<z.ZodString>;
}, {
/**
* 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: z.ZodArray<z.ZodUnion<[z.ZodUnion<[z.ZodString, z.ZodType<URL, z.ZodTypeDef, URL>]>, z.ZodObject<{
url: z.ZodUnion<[z.ZodString, z.ZodType<URL, z.ZodTypeDef, URL>]>;
tech: z.ZodOptional<z.ZodString>;
}, "strict", z.ZodTypeAny, {
url: string | URL;
tech?: string | undefined;
}, {
url: string | URL;
tech?: string | undefined;
}>]>, "atleastone">;
/**
* A [unicode range](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/unicode-range).
*/
unicodeRange: z.ZodOptional<z.ZodArray<z.ZodString, "atleastone">>;
}>, "strict", z.ZodTypeAny, {
src: [string | URL | {
url: string | URL;
tech?: string | undefined;
}, ...(string | URL | {
url: string | URL;
tech?: string | undefined;
})[]];
weight?: string | number | undefined;
style?: "normal" | "italic" | "oblique" | undefined;
display?: "auto" | "block" | "swap" | "fallback" | "optional" | undefined;
stretch?: string | undefined;
featureSettings?: string | undefined;
variationSettings?: string | undefined;
unicodeRange?: [string, ...string[]] | undefined;
}, {
src: [string | URL | {
url: string | URL;
tech?: string | undefined;
}, ...(string | URL | {
url: string | URL;
tech?: string | undefined;
})[]];
weight?: string | number | undefined;
style?: "normal" | "italic" | "oblique" | undefined;
display?: "auto" | "block" | "swap" | "fallback" | "optional" | undefined;
stretch?: string | undefined;
featureSettings?: string | undefined;
variationSettings?: string | undefined;
unicodeRange?: [string, ...string[]] | undefined;
}>, "atleastone">;
}>, "strict", z.ZodTypeAny, {
name: string;
cssVariable: string;
provider: "local";
variants: [{
src: [string | URL | {
url: string | URL;
tech?: string | undefined;
}, ...(string | URL | {
url: string | URL;
tech?: string | undefined;
})[]];
weight?: string | number | undefined;
style?: "normal" | "italic" | "oblique" | undefined;
display?: "auto" | "block" | "swap" | "fallback" | "optional" | undefined;
stretch?: string | undefined;
featureSettings?: string | undefined;
variationSettings?: string | undefined;
unicodeRange?: [string, ...string[]] | undefined;
}, ...{
src: [string | URL | {
url: string | URL;
tech?: string | undefined;
}, ...(string | URL | {
url: string | URL;
tech?: string | undefined;
})[]];
weight?: string | number | undefined;
style?: "normal" | "italic" | "oblique" | undefined;
display?: "auto" | "block" | "swap" | "fallback" | "optional" | undefined;
stretch?: string | undefined;
featureSettings?: string | undefined;
variationSettings?: string | undefined;
unicodeRange?: [string, ...string[]] | undefined;
}[]];
fallbacks?: string[] | undefined;
optimizedFallbacks?: boolean | undefined;
}, {
name: string;
cssVariable: string;
provider: "local";
variants: [{
src: [string | URL | {
url: string | URL;
tech?: string | undefined;
}, ...(string | URL | {
url: string | URL;
tech?: string | undefined;
})[]];
weight?: string | number | undefined;
style?: "normal" | "italic" | "oblique" | undefined;
display?: "auto" | "block" | "swap" | "fallback" | "optional" | undefined;
stretch?: string | undefined;
featureSettings?: string | undefined;
variationSettings?: string | undefined;
unicodeRange?: [string, ...string[]] | undefined;
}, ...{
src: [string | URL | {
url: string | URL;
tech?: string | undefined;
}, ...(string | URL | {
url: string | URL;
tech?: string | undefined;
})[]];
weight?: string | number | undefined;
style?: "normal" | "italic" | "oblique" | undefined;
display?: "auto" | "block" | "swap" | "fallback" | "optional" | undefined;
stretch?: string | undefined;
featureSettings?: string | undefined;
variationSettings?: string | undefined;
unicodeRange?: [string, ...string[]] | undefined;
}[]];
fallbacks?: string[] | undefined;
optimizedFallbacks?: boolean | undefined;
}>;
export declare const remoteFontFamilySchema: z.ZodObject<z.objectUtil.extendShape<z.objectUtil.extendShape<z.objectUtil.extendShape<{
/**
* The font family name, as identified by your font provider.
*/
name: z.ZodString;
/**
* 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: z.ZodString;
}, Omit<{
/**
* 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: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
/**
* A [font style](https://developer.mozilla.org/en-US/docs/Web/CSS/font-style).
*/
style: z.ZodOptional<z.ZodEnum<["normal", "italic", "oblique"]>>;
/**
* @default `"swap"`
*
* A [font display](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display).
*/
display: z.ZodOptional<z.ZodEnum<["auto", "block", "swap", "fallback", "optional"]>>;
/**
* A [font stretch](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-stretch).
*/
stretch: z.ZodOptional<z.ZodString>;
/**
* Font [feature settings](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-feature-settings).
*/
featureSettings: z.ZodOptional<z.ZodString>;
/**
* Font [variation settings](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-variation-settings).
*/
variationSettings: z.ZodOptional<z.ZodString>;
}, "weight" | "style">>, {
/**
* @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: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
/**
* @default `true`
*
* Whether or not to enable optimized fallback generation. You may disable this default optimization to have full control over `fallbacks`.
*/
optimizedFallbacks: z.ZodOptional<z.ZodBoolean>;
}>, {
/**
* The source of your font files. You can use a built-in provider or write your own custom provider.
*/
provider: z.ZodObject<{
/**
* URL, path relative to the root or package import.
*/
entrypoint: z.ZodUnion<[z.ZodString, z.ZodType<URL, z.ZodTypeDef, URL>]>;
/**
* Optional serializable object passed to the unifont provider.
*/
config: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
}, "strict", z.ZodTypeAny, {
entrypoint: string | URL;
config?: Record<string, any> | undefined;
}, {
entrypoint: string | URL;
config?: Record<string, any> | undefined;
}>;
/**
* @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: z.ZodOptional<z.ZodArray<z.ZodUnion<[z.ZodString, z.ZodNumber]>, "atleastone">>;
/**
* @default `["normal", "italic"]`
*
* An array of [font styles](https://developer.mozilla.org/en-US/docs/Web/CSS/font-style).
*/
styles: z.ZodOptional<z.ZodArray<z.ZodEnum<["normal", "italic", "oblique"]>, "atleastone">>;
/**
* @default `["cyrillic-ext", "cyrillic", "greek-ext", "greek", "vietnamese", "latin-ext", "latin"]`
*
* An array of [font subsets](https://knaap.dev/posts/font-subsetting/):
*/
subsets: z.ZodOptional<z.ZodArray<z.ZodString, "atleastone">>;
/**
* A [unicode range](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/unicode-range).
*/
unicodeRange: z.ZodOptional<z.ZodArray<z.ZodString, "atleastone">>;
}>, "strict", z.ZodTypeAny, {
name: string;
cssVariable: string;
provider: {
entrypoint: string | URL;
config?: Record<string, any> | undefined;
};
weights?: [string | number, ...(string | number)[]] | undefined;
styles?: ["normal" | "italic" | "oblique", ...("normal" | "italic" | "oblique")[]] | undefined;
subsets?: [string, ...string[]] | undefined;
fallbacks?: string[] | undefined;
optimizedFallbacks?: boolean | undefined;
display?: "auto" | "block" | "swap" | "fallback" | "optional" | undefined;
stretch?: string | undefined;
featureSettings?: string | undefined;
variationSettings?: string | undefined;
unicodeRange?: [string, ...string[]] | undefined;
}, {
name: string;
cssVariable: string;
provider: {
entrypoint: string | URL;
config?: Record<string, any> | undefined;
};
weights?: [string | number, ...(string | number)[]] | undefined;
styles?: ["normal" | "italic" | "oblique", ...("normal" | "italic" | "oblique")[]] | undefined;
subsets?: [string, ...string[]] | undefined;
fallbacks?: string[] | undefined;
optimizedFallbacks?: boolean | undefined;
display?: "auto" | "block" | "swap" | "fallback" | "optional" | undefined;
stretch?: string | undefined;
featureSettings?: string | undefined;
variationSettings?: string | undefined;
unicodeRange?: [string, ...string[]] | undefined;
}>;

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

@@ -0,0 +1,161 @@
import { z } from "zod";
import { LOCAL_PROVIDER_NAME } from "./constants.js";
const weightSchema = z.union([z.string(), z.number()]);
const styleSchema = z.enum(["normal", "italic", "oblique"]);
const unicodeRangeSchema = z.array(z.string()).nonempty();
const familyPropertiesSchema = z.object({
/**
* 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: weightSchema.optional(),
/**
* A [font style](https://developer.mozilla.org/en-US/docs/Web/CSS/font-style).
*/
style: styleSchema.optional(),
/**
* @default `"swap"`
*
* A [font display](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display).
*/
display: z.enum(["auto", "block", "swap", "fallback", "optional"]).optional(),
/**
* A [font stretch](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-stretch).
*/
stretch: z.string().optional(),
/**
* Font [feature settings](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-feature-settings).
*/
featureSettings: z.string().optional(),
/**
* Font [variation settings](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-variation-settings).
*/
variationSettings: z.string().optional()
});
const fallbacksSchema = z.object({
/**
* @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: z.array(z.string()).optional(),
/**
* @default `true`
*
* Whether or not to enable optimized fallback generation. You may disable this default optimization to have full control over `fallbacks`.
*/
optimizedFallbacks: z.boolean().optional()
});
const requiredFamilyAttributesSchema = z.object({
/**
* The font family name, as identified by your font provider.
*/
name: z.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: z.string()
});
const entrypointSchema = z.union([z.string(), z.instanceof(URL)]);
const fontProviderSchema = z.object({
/**
* URL, path relative to the root or package import.
*/
entrypoint: entrypointSchema,
/**
* Optional serializable object passed to the unifont provider.
*/
config: z.record(z.string(), z.any()).optional()
}).strict();
const localFontFamilySchema = requiredFamilyAttributesSchema.merge(fallbacksSchema).merge(
z.object({
/**
* The source of your font files. Set to `"local"` to use local font files.
*/
provider: z.literal(LOCAL_PROVIDER_NAME),
/**
* Each variant represents a [`@font-face` declaration](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/).
*/
variants: z.array(
familyPropertiesSchema.merge(
z.object({
/**
* 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: z.array(
z.union([
entrypointSchema,
z.object({ url: entrypointSchema, tech: z.string().optional() }).strict()
])
).nonempty(),
/**
* A [unicode range](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/unicode-range).
*/
unicodeRange: unicodeRangeSchema.optional()
// TODO: find a way to support subsets (through fontkit?)
}).strict()
)
).nonempty()
})
).strict();
const remoteFontFamilySchema = requiredFamilyAttributesSchema.merge(
familyPropertiesSchema.omit({
weight: true,
style: true
})
).merge(fallbacksSchema).merge(
z.object({
/**
* The source of your font files. You can use a built-in provider or write your own custom provider.
*/
provider: fontProviderSchema,
/**
* @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: z.array(weightSchema).nonempty().optional(),
/**
* @default `["normal", "italic"]`
*
* An array of [font styles](https://developer.mozilla.org/en-US/docs/Web/CSS/font-style).
*/
styles: z.array(styleSchema).nonempty().optional(),
/**
* @default `["cyrillic-ext", "cyrillic", "greek-ext", "greek", "vietnamese", "latin-ext", "latin"]`
*
* An array of [font subsets](https://knaap.dev/posts/font-subsetting/):
*/
subsets: z.array(z.string()).nonempty().optional(),
/**
* A [unicode range](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/unicode-range).
*/
unicodeRange: unicodeRangeSchema.optional()
})
).strict();
export {
fontProviderSchema,
localFontFamilySchema,
remoteFontFamilySchema,
styleSchema
};

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

@@ -0,0 +1,14 @@
import type { Defaults, FontType } from './types.js';
export declare const LOCAL_PROVIDER_NAME = "local";
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 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";

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

@@ -0,0 +1,49 @@
const LOCAL_PROVIDER_NAME = "local";
const DEFAULTS = {
weights: ["400"],
styles: ["normal", "italic"],
subsets: ["cyrillic-ext", "cyrillic", "greek-ext", "greek", "vietnamese", "latin-ext", "latin"],
// Technically serif is the browser default but most websites these days use sans-serif
fallbacks: ["sans-serif"],
optimizedFallbacks: true
};
const VIRTUAL_MODULE_ID = "virtual:astro:assets/fonts/internal";
const RESOLVED_VIRTUAL_MODULE_ID = "\0" + 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,
LOCAL_PROVIDER_NAME,
RESOLVED_VIRTUAL_MODULE_ID,
VIRTUAL_MODULE_ID
};

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

@@ -0,0 +1,88 @@
import type * as unifont from 'unifont';
import type { CollectedFontForMetrics } from './logic/optimize-fallbacks.js';
import type { AstroFontProvider, FontFaceMetrics, FontFileData, FontType, GenericFallbackName, PreloadData, ResolvedFontProvider, Style } from './types.js';
export interface Hasher {
hashString: (input: string) => string;
hashObject: (input: Record<string, any>) => string;
}
export interface RemoteFontProviderModResolver {
resolve: (id: string) => Promise<any>;
}
export interface RemoteFontProviderResolver {
resolve: (provider: AstroFontProvider) => Promise<ResolvedFontProvider>;
}
export interface LocalProviderUrlResolver {
resolve: (input: string) => string;
}
type SingleErrorInput<TType extends string, TData extends Record<string, any>> = {
type: TType;
data: TData;
cause: unknown;
};
export type ErrorHandlerInput = SingleErrorInput<'cannot-load-font-provider', {
entrypoint: string;
}> | SingleErrorInput<'unknown-fs-error', {}> | SingleErrorInput<'cannot-fetch-font-file', {
url: string;
}> | SingleErrorInput<'cannot-extract-font-type', {
url: string;
}> | SingleErrorInput<'cannot-extract-data', {
family: string;
url: string;
}>;
export interface ErrorHandler {
handle: (input: ErrorHandlerInput) => Error;
}
export interface UrlProxy {
proxy: (input: Pick<FontFileData, 'url' | 'init'> & {
type: FontType;
collectPreload: boolean;
data: Partial<unifont.FontFaceData>;
}) => string;
}
export interface UrlResolver {
resolve: (hash: string) => string;
}
export interface UrlProxyContentResolver {
resolve: (url: string) => string;
}
export interface DataCollector {
collect: (input: FontFileData & {
data: Partial<unifont.FontFaceData>;
preload: PreloadData | null;
}) => void;
}
export type CssProperties = Record<string, string | undefined>;
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 {};

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

View File

@@ -0,0 +1,9 @@
import type { CssProperties, CssRenderer } from '../definitions.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 function createMinifiableCssRenderer({ minify }: {
minify: boolean;
}): CssRenderer;

View File

@@ -0,0 +1,42 @@
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;
}
function createMinifiableCssRenderer({ minify }) {
return {
generateFontFace(family, properties) {
return renderFontFace(withFamily(family, properties), minify);
},
generateCssVariable(key, values) {
return renderCssVariable(key, values, minify);
}
};
}
export {
createMinifiableCssRenderer,
handleValueWithSpaces,
renderCssVariable,
renderFontFace,
withFamily
};

View File

@@ -0,0 +1,3 @@
import type { DataCollector } from '../definitions.js';
import type { CreateUrlProxyParams } from '../types.js';
export declare function createDataCollector({ hasUrl, saveUrl, savePreload, saveFontData, }: Omit<CreateUrlProxyParams, 'local'>): DataCollector;

View File

@@ -0,0 +1,21 @@
function createDataCollector({
hasUrl,
saveUrl,
savePreload,
saveFontData
}) {
return {
collect({ hash, url, init, preload, data }) {
if (!hasUrl(hash)) {
saveUrl({ hash, url, init });
if (preload) {
savePreload(preload);
}
}
saveFontData({ hash, url, data, init });
}
};
}
export {
createDataCollector
};

View File

@@ -0,0 +1,2 @@
import type { ErrorHandler } from '../definitions.js';
export declare function createAstroErrorHandler(): ErrorHandler;

View File

@@ -0,0 +1,41 @@
import { AstroError, AstroErrorData } from "../../../core/errors/index.js";
function getProps(input) {
if (input.type === "cannot-load-font-provider") {
return {
...AstroErrorData.CannotLoadFontProvider,
message: AstroErrorData.CannotLoadFontProvider.message(input.data.entrypoint)
};
} else if (input.type === "unknown-fs-error") {
return AstroErrorData.UnknownFilesystemError;
} else if (input.type === "cannot-fetch-font-file") {
return {
...AstroErrorData.CannotFetchFontFile,
message: AstroErrorData.CannotFetchFontFile.message(input.data.url)
};
} else if (input.type === "cannot-extract-font-type") {
return {
...AstroErrorData.CannotExtractFontType,
message: AstroErrorData.CannotExtractFontType.message(input.data.url)
};
} else if (input.type === "cannot-extract-data") {
return {
...AstroErrorData.CannotDetermineWeightAndStyleFromFontFile,
message: AstroErrorData.CannotDetermineWeightAndStyleFromFontFile.message(
input.data.family,
input.data.url
)
};
}
input;
return AstroErrorData.UnknownError;
}
function createAstroErrorHandler() {
return {
handle(input) {
return new AstroError(getProps(input), { cause: input.cause });
}
};
}
export {
createAstroErrorHandler
};

View File

@@ -0,0 +1,8 @@
import type { Storage } from 'unstorage';
import type { ErrorHandler, FontFetcher } from '../definitions.js';
export declare function createCachedFontFetcher({ storage, errorHandler, fetch, readFile, }: {
storage: Storage;
errorHandler: ErrorHandler;
fetch: (url: string, init?: RequestInit) => Promise<Response>;
readFile: (url: string) => Promise<Buffer>;
}): FontFetcher;

View File

@@ -0,0 +1,34 @@
import { isAbsolute } from "node:path";
import { cache } from "../utils.js";
function createCachedFontFetcher({
storage,
errorHandler,
fetch,
readFile
}) {
return {
async fetch({ hash, url, init }) {
return await cache(storage, hash, async () => {
try {
if (isAbsolute(url)) {
return await readFile(url);
}
const response = await 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 errorHandler.handle({
type: "cannot-fetch-font-file",
data: { url },
cause
});
}
});
}
};
}
export {
createCachedFontFetcher
};

View File

@@ -0,0 +1,4 @@
import type { ErrorHandler, FontFileReader } from '../definitions.js';
export declare function createFontaceFontFileReader({ errorHandler, }: {
errorHandler: ErrorHandler;
}): FontFileReader;

View File

@@ -0,0 +1,26 @@
import { readFileSync } from "node:fs";
import { fontace } from "fontace";
function createFontaceFontFileReader({
errorHandler
}) {
return {
extract({ family, url }) {
try {
const data = fontace(readFileSync(url));
return {
weight: data.weight,
style: data.style
};
} catch (cause) {
throw errorHandler.handle({
type: "cannot-extract-data",
data: { family, url },
cause
});
}
}
};
}
export {
createFontaceFontFileReader
};

View File

@@ -0,0 +1,5 @@
import type { CssRenderer, FontFetcher, FontMetricsResolver } from '../definitions.js';
export declare function createCapsizeFontMetricsResolver({ fontFetcher, cssRenderer, }: {
fontFetcher: FontFetcher;
cssRenderer: CssRenderer;
}): FontMetricsResolver;

View File

@@ -0,0 +1,60 @@
import { fromBuffer } from "@capsizecss/unpack";
import { renderFontSrc } from "../utils.js";
function filterRequiredMetrics({
ascent,
descent,
lineGap,
unitsPerEm,
xWidthAvg
}) {
return {
ascent,
descent,
lineGap,
unitsPerEm,
xWidthAvg
};
}
function toPercentage(value, fractionDigits = 4) {
const percentage = value * 100;
return `${+percentage.toFixed(fractionDigits)}%`;
}
function createCapsizeFontMetricsResolver({
fontFetcher,
cssRenderer
}) {
const cache = {};
return {
async getMetrics(name, input) {
cache[name] ??= filterRequiredMetrics(await fromBuffer(await fontFetcher.fetch(input)));
return cache[name];
},
// Source: https://github.com/unjs/fontaine/blob/f00f84032c5d5da72c8798eae4cd68d3ddfbf340/src/css.ts#L170
generateFontFace({
metrics,
fallbackMetrics,
name: fallbackName,
font: fallbackFontName,
properties
}) {
const preferredFontXAvgRatio = metrics.xWidthAvg / metrics.unitsPerEm;
const fallbackFontXAvgRatio = fallbackMetrics.xWidthAvg / fallbackMetrics.unitsPerEm;
const sizeAdjust = preferredFontXAvgRatio / fallbackFontXAvgRatio;
const adjustedEmSquare = metrics.unitsPerEm * sizeAdjust;
const ascentOverride = metrics.ascent / adjustedEmSquare;
const descentOverride = Math.abs(metrics.descent) / adjustedEmSquare;
const lineGapOverride = metrics.lineGap / adjustedEmSquare;
return cssRenderer.generateFontFace(fallbackName, {
...properties,
src: renderFontSrc([{ name: fallbackFontName }]),
"size-adjust": toPercentage(sizeAdjust),
"ascent-override": toPercentage(ascentOverride),
"descent-override": toPercentage(descentOverride),
"line-gap-override": toPercentage(lineGapOverride)
});
}
};
}
export {
createCapsizeFontMetricsResolver
};

View File

@@ -0,0 +1,4 @@
import type { ErrorHandler, FontTypeExtractor } from '../definitions.js';
export declare function createFontTypeExtractor({ errorHandler, }: {
errorHandler: ErrorHandler;
}): FontTypeExtractor;

View File

@@ -0,0 +1,22 @@
import { extname } from "node:path";
import { isFontType } from "../utils.js";
function createFontTypeExtractor({
errorHandler
}) {
return {
extract(url) {
const extension = extname(url).slice(1);
if (!isFontType(extension)) {
throw errorHandler.handle({
type: "cannot-extract-font-type",
data: { url },
cause: `Unexpected extension, got "${extension}"`
});
}
return extension;
}
};
}
export {
createFontTypeExtractor
};

View File

@@ -0,0 +1,2 @@
import type { Hasher } from '../definitions.js';
export declare function createXxHasher(): Promise<Hasher>;

View File

@@ -0,0 +1,14 @@
import xxhash from "xxhash-wasm";
import { sortObjectByKey } from "../utils.js";
async function createXxHasher() {
const { h64ToString: hashString } = await xxhash();
return {
hashString,
hashObject(input) {
return hashString(JSON.stringify(sortObjectByKey(input)));
}
};
}
export {
createXxHasher
};

View File

@@ -0,0 +1,5 @@
import type { LocalProviderUrlResolver } from '../definitions.js';
export declare function createRequireLocalProviderUrlResolver({ root, intercept, }: {
root: URL;
intercept?: (path: string) => void;
}): LocalProviderUrlResolver;

View File

@@ -0,0 +1,17 @@
import { fileURLToPath } from "node:url";
import { resolveEntrypoint } from "../utils.js";
function createRequireLocalProviderUrlResolver({
root,
intercept
}) {
return {
resolve(input) {
const path = fileURLToPath(resolveEntrypoint(root, input));
intercept?.(path);
return path;
}
};
}
export {
createRequireLocalProviderUrlResolver
};

View File

@@ -0,0 +1,6 @@
import type { ViteDevServer } from 'vite';
import type { RemoteFontProviderModResolver } from '../definitions.js';
export declare function createBuildRemoteFontProviderModResolver(): RemoteFontProviderModResolver;
export declare function createDevServerRemoteFontProviderModResolver({ server, }: {
server: ViteDevServer;
}): RemoteFontProviderModResolver;

View File

@@ -0,0 +1,20 @@
function createBuildRemoteFontProviderModResolver() {
return {
resolve(id) {
return import(id);
}
};
}
function createDevServerRemoteFontProviderModResolver({
server
}) {
return {
resolve(id) {
return server.ssrLoadModule(id);
}
};
}
export {
createBuildRemoteFontProviderModResolver,
createDevServerRemoteFontProviderModResolver
};

View File

@@ -0,0 +1,6 @@
import type { ErrorHandler, RemoteFontProviderModResolver, RemoteFontProviderResolver } from '../definitions.js';
export declare function createRemoteFontProviderResolver({ root, modResolver, errorHandler, }: {
root: URL;
modResolver: RemoteFontProviderModResolver;
errorHandler: ErrorHandler;
}): RemoteFontProviderResolver;

View File

@@ -0,0 +1,47 @@
import { resolveEntrypoint } from "../utils.js";
function validateMod({
mod,
entrypoint,
errorHandler
}) {
try {
if (typeof mod !== "object" || mod === null) {
throw new Error(`Expected an object for the module, but received ${typeof mod}.`);
}
if (typeof mod.provider !== "function") {
throw new Error(`Invalid provider export in module, expected a function.`);
}
return {
provider: mod.provider
};
} catch (cause) {
throw errorHandler.handle({
type: "cannot-load-font-provider",
data: {
entrypoint
},
cause
});
}
}
function createRemoteFontProviderResolver({
root,
modResolver,
errorHandler
}) {
return {
async resolve({ entrypoint, config }) {
const id = resolveEntrypoint(root, entrypoint.toString()).href;
const mod = await modResolver.resolve(id);
const { provider } = validateMod({
mod,
entrypoint: id,
errorHandler
});
return { config, provider };
}
};
}
export {
createRemoteFontProviderResolver
};

View File

@@ -0,0 +1,4 @@
import { type Storage } from 'unstorage';
export declare function createFsStorage({ base }: {
base: URL;
}): Storage;

View File

@@ -0,0 +1,14 @@
import { fileURLToPath } from "node:url";
import { createStorage } from "unstorage";
import fsLiteDriver from "unstorage/drivers/fs-lite";
function createFsStorage({ base }) {
return createStorage({
// Types are weirly exported
driver: fsLiteDriver({
base: fileURLToPath(base)
})
});
}
export {
createFsStorage
};

View File

@@ -0,0 +1,11 @@
import type { SystemFallbacksProvider } from '../definitions.js';
export declare const DEFAULT_FALLBACKS: {
serif: "Times New Roman"[];
'sans-serif': "Arial"[];
monospace: "Courier New"[];
'system-ui': ("Arial" | "BlinkMacSystemFont" | "Segoe UI" | "Roboto" | "Helvetica Neue")[];
'ui-serif': "Times New Roman"[];
'ui-sans-serif': "Arial"[];
'ui-monospace': "Courier New"[];
};
export declare function createSystemFallbacksProvider(): SystemFallbacksProvider;

View File

@@ -0,0 +1,74 @@
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"]
};
function createSystemFallbacksProvider() {
return {
getLocalFonts(fallback) {
return DEFAULT_FALLBACKS[fallback] ?? null;
},
getMetricsForLocalFont(family) {
return SYSTEM_METRICS[family];
}
};
}
export {
DEFAULT_FALLBACKS,
createSystemFallbacksProvider
};

View File

@@ -0,0 +1,5 @@
import type { ErrorHandler, UrlProxyContentResolver } from '../definitions.js';
export declare function createLocalUrlProxyContentResolver({ errorHandler, }: {
errorHandler: ErrorHandler;
}): UrlProxyContentResolver;
export declare function createRemoteUrlProxyContentResolver(): UrlProxyContentResolver;

View File

@@ -0,0 +1,28 @@
import { readFileSync } from "node:fs";
function createLocalUrlProxyContentResolver({
errorHandler
}) {
return {
resolve(url) {
try {
return url + readFileSync(url, "utf-8");
} catch (cause) {
throw errorHandler.handle({
type: "unknown-fs-error",
data: {},
cause
});
}
}
};
}
function createRemoteUrlProxyContentResolver() {
return {
// Passthrough, the remote provider URL is enough
resolve: (url) => url
};
}
export {
createLocalUrlProxyContentResolver,
createRemoteUrlProxyContentResolver
};

View File

@@ -0,0 +1,7 @@
import type { DataCollector, Hasher, UrlProxy, UrlProxyContentResolver, UrlResolver } from '../definitions.js';
export declare function createUrlProxy({ contentResolver, hasher, dataCollector, urlResolver, }: {
contentResolver: UrlProxyContentResolver;
hasher: Hasher;
dataCollector: DataCollector;
urlResolver: UrlResolver;
}): UrlProxy;

View File

@@ -0,0 +1,24 @@
function createUrlProxy({
contentResolver,
hasher,
dataCollector,
urlResolver
}) {
return {
proxy({ url: originalUrl, type, data, collectPreload, init }) {
const hash = `${hasher.hashString(contentResolver.resolve(originalUrl))}.${type}`;
const url = urlResolver.resolve(hash);
dataCollector.collect({
url: originalUrl,
hash,
preload: collectPreload ? { url, type } : null,
data,
init
});
return url;
}
};
}
export {
createUrlProxy
};

View File

@@ -0,0 +1,9 @@
import type { AssetsPrefix } from '../../../types/public/index.js';
import type { UrlResolver } from '../definitions.js';
export declare function createDevUrlResolver({ base }: {
base: string;
}): UrlResolver;
export declare function createBuildUrlResolver({ base, assetsPrefix, }: {
base: string;
assetsPrefix: AssetsPrefix;
}): UrlResolver;

View File

@@ -0,0 +1,27 @@
import { fileExtension, joinPaths, prependForwardSlash } from "../../../core/path.js";
import { getAssetsPrefix } from "../../utils/getAssetsPrefix.js";
function createDevUrlResolver({ base }) {
return {
resolve(hash) {
return prependForwardSlash(joinPaths(base, hash));
}
};
}
function createBuildUrlResolver({
base,
assetsPrefix
}) {
return {
resolve(hash) {
const prefix = assetsPrefix ? getAssetsPrefix(fileExtension(hash), assetsPrefix) : void 0;
if (prefix) {
return joinPaths(prefix, base, hash);
}
return prependForwardSlash(joinPaths(base, hash));
}
};
}
export {
createBuildUrlResolver,
createDevUrlResolver
};

View File

@@ -0,0 +1,10 @@
import type * as unifont from 'unifont';
import type { Hasher } from '../definitions.js';
import type { ResolvedFontFamily } from '../types.js';
export declare function extractUnifontProviders({ families, hasher, }: {
families: Array<ResolvedFontFamily>;
hasher: Hasher;
}): {
families: Array<ResolvedFontFamily>;
providers: Array<unifont.Provider>;
};

View File

@@ -0,0 +1,28 @@
import { LOCAL_PROVIDER_NAME } from "../constants.js";
function extractUnifontProviders({
families,
hasher
}) {
const hashes = /* @__PURE__ */ new Set();
const providers = [];
for (const { provider } of families) {
if (provider === LOCAL_PROVIDER_NAME) {
continue;
}
const unifontProvider = provider.provider(provider.config);
const hash = hasher.hashObject({
name: unifontProvider._name,
...provider.config
});
unifontProvider._name += `-${hash}`;
provider.name = unifontProvider._name;
if (!hashes.has(hash)) {
hashes.add(hash);
providers.push(unifontProvider);
}
}
return { families, providers };
}
export {
extractUnifontProviders
};

View File

@@ -0,0 +1,7 @@
import type * as unifont from 'unifont';
import type { FontTypeExtractor, UrlProxy } from '../definitions.js';
export declare function normalizeRemoteFontFaces({ fonts, urlProxy, fontTypeExtractor, }: {
fonts: Array<unifont.FontFaceData>;
urlProxy: UrlProxy;
fontTypeExtractor: FontTypeExtractor;
}): Array<unifont.FontFaceData>;

View File

@@ -0,0 +1,40 @@
import { FONT_FORMATS } from "../constants.js";
function normalizeRemoteFontFaces({
fonts,
urlProxy,
fontTypeExtractor
}) {
return fonts.filter((font) => typeof font.meta?.priority === "number" ? font.meta.priority === 0 : true).map((font) => {
let index = 0;
return {
...font,
src: font.src.map((source) => {
if ("name" in source) {
return source;
}
const url = source.url.startsWith("//") ? `https:${source.url}` : source.url;
const proxied = {
...source,
originalURL: url,
url: urlProxy.proxy({
url,
type: FONT_FORMATS.find((e) => e.format === source.format)?.type ?? fontTypeExtractor.extract(source.url),
// We only collect the first URL to avoid preloading fallback sources (eg. we only
// preload woff2 if woff is available)
collectPreload: index === 0,
data: {
weight: font.weight,
style: font.style
},
init: font.meta?.init ?? null
})
};
index++;
return proxied;
})
};
});
}
export {
normalizeRemoteFontFaces
};

View File

@@ -0,0 +1,17 @@
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, enabled, systemFallbacksProvider, fontMetricsResolver, }: {
family: Pick<ResolvedFontFamily, 'name' | 'nameWithHash'>;
fallbacks: Array<string>;
collectedFonts: Array<CollectedFontForMetrics>;
enabled: boolean;
systemFallbacksProvider: SystemFallbacksProvider;
fontMetricsResolver: FontMetricsResolver;
}): Promise<null | {
css: string;
fallbacks: Array<string>;
}>;

View File

@@ -0,0 +1,47 @@
import { isGenericFontFamily, unifontFontFaceDataToProperties } from "../utils.js";
async function optimizeFallbacks({
family,
fallbacks: _fallbacks,
collectedFonts,
enabled,
systemFallbacksProvider,
fontMetricsResolver
}) {
let fallbacks = [..._fallbacks];
if (fallbacks.length === 0 || !enabled || 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 must't wrap in quote because that's handled by the CSS renderer
name: `${family.nameWithHash} 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,17 @@
import type { Hasher, LocalProviderUrlResolver, RemoteFontProviderResolver } from '../definitions.js';
import type { FontFamily, ResolvedFontFamily } from '../types.js';
/**
* Dedupes properties if applicable and resolves entrypoints.
*/
export declare function resolveFamily({ family, hasher, remoteFontProviderResolver, localProviderUrlResolver, }: {
family: FontFamily;
hasher: Hasher;
remoteFontProviderResolver: RemoteFontProviderResolver;
localProviderUrlResolver: LocalProviderUrlResolver;
}): Promise<ResolvedFontFamily>;
/**
* A function for convenience. The actual logic lives in resolveFamily
*/
export declare function resolveFamilies({ families, ...dependencies }: {
families: Array<FontFamily>;
} & Omit<Parameters<typeof resolveFamily>[0], 'family'>): Promise<Array<ResolvedFontFamily>>;

View File

@@ -0,0 +1,67 @@
import { LOCAL_PROVIDER_NAME } from "../constants.js";
import { dedupe, withoutQuotes } from "../utils.js";
function resolveVariants({
variants,
localProviderUrlResolver
}) {
return variants.map((variant) => ({
...variant,
weight: variant.weight?.toString(),
src: variant.src.map((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: localProviderUrlResolver.resolve(url),
tech
};
})
}));
}
async function resolveFamily({
family,
hasher,
remoteFontProviderResolver,
localProviderUrlResolver
}) {
const name = withoutQuotes(family.name);
const nameWithHash = `${name}-${hasher.hashObject(family)}`;
if (family.provider === LOCAL_PROVIDER_NAME) {
return {
...family,
name,
nameWithHash,
variants: resolveVariants({ variants: family.variants, localProviderUrlResolver }),
fallbacks: family.fallbacks ? dedupe(family.fallbacks) : void 0
};
}
return {
...family,
name,
nameWithHash,
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,
fallbacks: family.fallbacks ? dedupe(family.fallbacks) : void 0,
unicodeRange: family.unicodeRange ? dedupe(family.unicodeRange) : void 0,
// This will be Astro specific eventually
provider: await remoteFontProviderResolver.resolve(family.provider)
};
}
async function resolveFamilies({
families,
...dependencies
}) {
return await Promise.all(
families.map(
(family) => resolveFamily({
family,
...dependencies
})
)
);
}
export {
resolveFamilies,
resolveFamily
};

40
node_modules/astro/dist/assets/fonts/orchestrate.d.ts generated vendored Normal file
View File

@@ -0,0 +1,40 @@
import type { Storage } from 'unstorage';
import type { Logger } from '../../core/logger/core.js';
import type { CssRenderer, FontFileReader, FontMetricsResolver, FontTypeExtractor, Hasher, LocalProviderUrlResolver, RemoteFontProviderResolver, SystemFallbacksProvider, UrlProxy } from './definitions.js';
import type { ConsumableMap, CreateUrlProxyParams, Defaults, FontFamily, FontFileDataMap } from './types.js';
/**
* Manages how fonts are resolved:
*
* - families are resolved
* - unifont providers are extracted from families
* - unifont is initialized
*
* For each family:
* - We create a URL proxy
* - We resolve the font and normalize the result
*
* For each resolved font:
* - We generate the CSS font face
* - We generate optimized fallbacks if applicable
* - We generate CSS variables
*
* Once that's done, the collected data is returned
*/
export declare function orchestrate({ families, hasher, remoteFontProviderResolver, localProviderUrlResolver, storage, cssRenderer, systemFallbacksProvider, fontMetricsResolver, fontTypeExtractor, fontFileReader, logger, createUrlProxy, defaults, }: {
families: Array<FontFamily>;
hasher: Hasher;
remoteFontProviderResolver: RemoteFontProviderResolver;
localProviderUrlResolver: LocalProviderUrlResolver;
storage: Storage;
cssRenderer: CssRenderer;
systemFallbacksProvider: SystemFallbacksProvider;
fontMetricsResolver: FontMetricsResolver;
fontTypeExtractor: FontTypeExtractor;
fontFileReader: FontFileReader;
logger: Logger;
createUrlProxy: (params: CreateUrlProxyParams) => UrlProxy;
defaults: Defaults;
}): Promise<{
fontFileDataMap: FontFileDataMap;
consumableMap: ConsumableMap;
}>;

135
node_modules/astro/dist/assets/fonts/orchestrate.js generated vendored Normal file
View File

@@ -0,0 +1,135 @@
import { bold } from "kleur/colors";
import * as unifont from "unifont";
import { LOCAL_PROVIDER_NAME } from "./constants.js";
import { extractUnifontProviders } from "./logic/extract-unifont-providers.js";
import { normalizeRemoteFontFaces } from "./logic/normalize-remote-font-faces.js";
import { optimizeFallbacks } from "./logic/optimize-fallbacks.js";
import { resolveFamilies } from "./logic/resolve-families.js";
import { resolveLocalFont } from "./providers/local.js";
import { pickFontFaceProperty, unifontFontFaceDataToProperties } from "./utils.js";
async function orchestrate({
families,
hasher,
remoteFontProviderResolver,
localProviderUrlResolver,
storage,
cssRenderer,
systemFallbacksProvider,
fontMetricsResolver,
fontTypeExtractor,
fontFileReader,
logger,
createUrlProxy,
defaults
}) {
let resolvedFamilies = await resolveFamilies({
families,
hasher,
remoteFontProviderResolver,
localProviderUrlResolver
});
const extractedUnifontProvidersResult = extractUnifontProviders({
families: resolvedFamilies,
hasher
});
resolvedFamilies = extractedUnifontProvidersResult.families;
const unifontProviders = extractedUnifontProvidersResult.providers;
const { resolveFont } = await unifont.createUnifont(unifontProviders, {
storage
});
const fontFileDataMap = /* @__PURE__ */ new Map();
const consumableMap = /* @__PURE__ */ new Map();
for (const family of resolvedFamilies) {
const preloadData = [];
let css = "";
const collectedFonts = [];
const fallbacks = family.fallbacks ?? defaults.fallbacks ?? [];
const urlProxy = createUrlProxy({
local: family.provider === LOCAL_PROVIDER_NAME,
hasUrl: (hash) => fontFileDataMap.has(hash),
saveUrl: ({ hash, url, init }) => {
fontFileDataMap.set(hash, { url, init });
},
savePreload: (preload) => {
preloadData.push(preload);
},
saveFontData: (collected) => {
if (fallbacks && 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.
!collectedFonts.some((f) => JSON.stringify(f.data) === JSON.stringify(collected.data))) {
collectedFonts.push(collected);
}
}
});
let fonts;
if (family.provider === LOCAL_PROVIDER_NAME) {
const result = resolveLocalFont({
family,
urlProxy,
fontTypeExtractor,
fontFileReader
});
fonts = result.fonts;
} else {
const result = await resolveFont(
family.name,
// 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,
fallbacks: family.fallbacks ?? defaults.fallbacks
},
// By default, unifont goes through all providers. We use a different approach where
// we specify a provider per font. Name has been set while extracting unifont providers
// from families (inside extractUnifontProviders).
[family.provider.name]
);
if (result.fonts.length === 0) {
logger.warn(
"assets",
`No data found for font family ${bold(family.name)}. Review your configuration`
);
}
fonts = normalizeRemoteFontFaces({ fonts: result.fonts, urlProxy, fontTypeExtractor });
}
for (const data of fonts) {
css += cssRenderer.generateFontFace(
family.nameWithHash,
unifontFontFaceDataToProperties({
src: data.src,
weight: data.weight,
style: data.style,
// User settings override the generated font settings. We use a helper function
// because local and remote providers store this data in different places.
display: pickFontFaceProperty("display", { data, family }),
unicodeRange: pickFontFaceProperty("unicodeRange", { data, family }),
stretch: pickFontFaceProperty("stretch", { data, family }),
featureSettings: pickFontFaceProperty("featureSettings", { data, family }),
variationSettings: pickFontFaceProperty("variationSettings", { data, family })
})
);
}
const cssVarValues = [family.nameWithHash];
const optimizeFallbacksResult = await optimizeFallbacks({
family,
fallbacks,
collectedFonts,
enabled: family.optimizedFallbacks ?? defaults.optimizedFallbacks ?? false,
systemFallbacksProvider,
fontMetricsResolver
});
if (optimizeFallbacksResult) {
css += optimizeFallbacksResult.css;
cssVarValues.push(...optimizeFallbacksResult.fallbacks);
} else {
cssVarValues.push(...fallbacks);
}
css += cssRenderer.generateCssVariable(family.cssVariable, cssVarValues);
consumableMap.set(family.cssVariable, { preloadData, css });
}
return { fontFileDataMap, consumableMap };
}
export {
orchestrate
};

View File

@@ -0,0 +1,2 @@
import { providers } from 'unifont';
export declare const provider: typeof providers.adobe;

View File

@@ -0,0 +1,5 @@
import { providers } from "unifont";
const provider = providers.adobe;
export {
provider
};

View File

@@ -0,0 +1 @@
export declare const provider: () => import("unifont").Provider;

View File

@@ -0,0 +1,5 @@
import { providers } from "unifont";
const provider = providers.bunny;
export {
provider
};

View File

@@ -0,0 +1 @@
export declare const provider: () => import("unifont").Provider;

View File

@@ -0,0 +1,5 @@
import { providers } from "unifont";
const provider = providers.fontshare;
export {
provider
};

View File

@@ -0,0 +1 @@
export declare const provider: () => import("unifont").Provider;

View File

@@ -0,0 +1,5 @@
import { providers } from "unifont";
const provider = providers.fontsource;
export {
provider
};

View File

@@ -0,0 +1,2 @@
import { providers } from 'unifont';
export declare const provider: typeof providers.google;

View File

@@ -0,0 +1,5 @@
import { providers } from "unifont";
const provider = providers.google;
export {
provider
};

View File

@@ -0,0 +1,48 @@
import type { providers } from 'unifont';
import type { AstroFontProvider } from '../types.js';
/** [Adobe](https://fonts.adobe.com/) */
declare function adobe(config: Parameters<typeof providers.adobe>[0]): {
entrypoint: string | URL;
config?: Record<string, any> | undefined;
};
/** [Bunny](https://fonts.bunny.net/) */
declare function bunny(): {
entrypoint: string | URL;
config?: Record<string, any> | undefined;
};
/** [Fontshare](https://www.fontshare.com/) */
declare function fontshare(): {
entrypoint: string | URL;
config?: Record<string, any> | undefined;
};
/** [Fontsource](https://fontsource.org/) */
declare function fontsource(): {
entrypoint: string | URL;
config?: Record<string, any> | undefined;
};
/** [Google](https://fonts.google.com/) */
declare function google(config?: Parameters<typeof providers.google>[0]): {
entrypoint: string | URL;
config?: Record<string, any> | undefined;
};
/**
* Astro re-exports most [unifont](https://github.com/unjs/unifont/) 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/)
*/
export declare const fontProviders: {
adobe: typeof adobe;
bunny: typeof bunny;
fontshare: typeof fontshare;
fontsource: typeof fontsource;
google: typeof google;
};
/** A type helper for defining Astro font providers config objects */
export declare function defineAstroFontProvider(provider: AstroFontProvider): {
entrypoint: string | URL;
config?: Record<string, any> | undefined;
};
export {};

View File

@@ -0,0 +1,41 @@
function adobe(config) {
return defineAstroFontProvider({
entrypoint: "astro/assets/fonts/providers/adobe",
config
});
}
function bunny() {
return defineAstroFontProvider({
entrypoint: "astro/assets/fonts/providers/bunny"
});
}
function fontshare() {
return defineAstroFontProvider({
entrypoint: "astro/assets/fonts/providers/fontshare"
});
}
function fontsource() {
return defineAstroFontProvider({
entrypoint: "astro/assets/fonts/providers/fontsource"
});
}
function google(config) {
return defineAstroFontProvider({
entrypoint: "astro/assets/fonts/providers/google",
config
});
}
const fontProviders = {
adobe,
bunny,
fontshare,
fontsource,
google
};
function defineAstroFontProvider(provider) {
return provider;
}
export {
defineAstroFontProvider,
fontProviders
};

View File

@@ -0,0 +1,13 @@
import type * as unifont from 'unifont';
import type { FontFileReader, FontTypeExtractor, UrlProxy } from '../definitions.js';
import type { ResolvedLocalFontFamily } from '../types.js';
interface Options {
family: ResolvedLocalFontFamily;
urlProxy: UrlProxy;
fontTypeExtractor: FontTypeExtractor;
fontFileReader: FontFileReader;
}
export declare function resolveLocalFont({ family, urlProxy, fontTypeExtractor, fontFileReader, }: Options): {
fonts: Array<unifont.FontFaceData>;
};
export {};

View File

@@ -0,0 +1,53 @@
import { FONT_FORMATS } from "../constants.js";
function resolveLocalFont({
family,
urlProxy,
fontTypeExtractor,
fontFileReader
}) {
return {
fonts: family.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((source, index) => {
if (shouldInfer && index === 0) {
const result = fontFileReader.extract({ family: family.name, url: source.url });
if (variant.weight === void 0) data.weight = result.weight;
if (variant.style === void 0) data.style = result.style;
}
const type = fontTypeExtractor.extract(source.url);
return {
originalURL: source.url,
url: urlProxy.proxy({
url: source.url,
type,
// We only use the first source for preloading. For example if woff2 and woff
// are available, we only keep woff2.
collectPreload: index === 0,
data: {
weight: data.weight,
style: data.style
},
init: null
}),
format: FONT_FORMATS.find((e) => e.type === type)?.format,
tech: source.tech
};
});
return data;
})
};
}
export {
resolveLocalFont
};

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 FontFamily = (${JSON.stringify(settings.config.experimental.fonts.map((family) => family.cssVariable))})[number];
}
`
});
}
export {
syncFonts
};

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

@@ -0,0 +1,76 @@
import type { Font } from '@capsizecss/unpack';
import type * as unifont from 'unifont';
import type { z } from 'zod';
import type { fontProviderSchema, localFontFamilySchema, remoteFontFamilySchema, styleSchema } from './config.js';
import type { FONT_TYPES, GENERIC_FALLBACK_NAMES } from './constants.js';
import type { CollectedFontForMetrics } from './logic/optimize-fallbacks.js';
export type AstroFontProvider = z.infer<typeof fontProviderSchema>;
export interface ResolvedFontProvider {
name?: string;
provider: (config?: Record<string, any>) => unifont.Provider;
config?: Record<string, any>;
}
export type LocalFontFamily = z.infer<typeof localFontFamilySchema>;
interface ResolvedFontFamilyAttributes {
nameWithHash: string;
}
export interface ResolvedLocalFontFamily extends ResolvedFontFamilyAttributes, Omit<LocalFontFamily, 'variants'> {
variants: Array<Omit<LocalFontFamily['variants'][number], 'weight' | 'src'> & {
weight?: string;
src: Array<{
url: string;
tech?: string;
}>;
}>;
}
type RemoteFontFamily = z.infer<typeof remoteFontFamilySchema>;
/** @lintignore somehow required by pickFontFaceProperty in utils */
export interface ResolvedRemoteFontFamily extends ResolvedFontFamilyAttributes, Omit<z.output<typeof remoteFontFamilySchema>, 'provider' | 'weights'> {
provider: ResolvedFontProvider;
weights?: Array<string>;
}
export type FontFamily = LocalFontFamily | RemoteFontFamily;
export type ResolvedFontFamily = ResolvedLocalFontFamily | ResolvedRemoteFontFamily;
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;
}
export type FontFaceMetrics = Pick<Font, 'ascent' | 'descent' | 'lineGap' | 'unitsPerEm' | 'xWidthAvg'>;
export type GenericFallbackName = (typeof GENERIC_FALLBACK_NAMES)[number];
export type Defaults = Partial<Pick<ResolvedRemoteFontFamily, 'weights' | 'styles' | 'subsets' | 'fallbacks' | 'optimizedFallbacks'>>;
export interface FontFileData {
hash: string;
url: string;
init: RequestInit | null;
}
export interface CreateUrlProxyParams {
local: boolean;
hasUrl: (hash: string) => boolean;
saveUrl: (input: FontFileData) => void;
savePreload: (preload: PreloadData) => void;
saveFontData: (collected: CollectedFontForMetrics) => void;
}
/**
* Holds associations of hash and original font file URLs, so they can be
* downloaded whenever the hash is requested.
*/
export type FontFileDataMap = Map<FontFileData['hash'], Pick<FontFileData, 'url' | 'init'>>;
/**
* Holds associations of CSS variables and preloadData/css to be passed to the virtual module.
*/
export type ConsumableMap = Map<string, {
preloadData: Array<PreloadData>;
css: string;
}>;
export type Style = z.output<typeof styleSchema>;
export {};

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

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

@@ -0,0 +1,27 @@
import type * as unifont from 'unifont';
import type { Storage } from 'unstorage';
import type { CssProperties } from './definitions.js';
import type { FontType, GenericFallbackName, ResolvedFontFamily } 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;
/**
* 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 cache(storage: Storage, key: string, cb: () => Promise<Buffer>): Promise<Buffer>;
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;
export declare function resolveEntrypoint(root: URL, entrypoint: string): URL;
export declare function pickFontFaceProperty<T extends keyof Pick<unifont.FontFaceData, 'display' | 'unicodeRange' | 'stretch' | 'featureSettings' | 'variationSettings'>>(property: T, { data, family }: {
data: unifont.FontFaceData;
family: ResolvedFontFamily;
}): import("./types.js").ResolvedRemoteFontFamily[T] | NonNullable<unifont.FontFaceData[T]> | undefined;

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

@@ -0,0 +1,83 @@
import { createRequire } from "node:module";
import { pathToFileURL } from "node:url";
import { FONT_TYPES, GENERIC_FALLBACK_NAMES, LOCAL_PROVIDER_NAME } 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": Array.isArray(font.weight) ? font.weight.join(" ") : font.weight?.toString(),
"font-style": font.style,
"font-stretch": font.stretch,
"font-feature-settings": font.featureSettings,
"font-variation-settings": font.variationSettings
};
}
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);
}
async function 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;
}
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;
}
function resolveEntrypoint(root, entrypoint) {
const require2 = createRequire(root);
try {
return pathToFileURL(require2.resolve(entrypoint));
} catch {
return new URL(entrypoint, root);
}
}
function pickFontFaceProperty(property, { data, family }) {
return data[property] ?? (family.provider === LOCAL_PROVIDER_NAME ? void 0 : family[property]);
}
export {
cache,
dedupe,
isFontType,
isGenericFontFamily,
pickFontFaceProperty,
renderFontSrc,
resolveEntrypoint,
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,252 @@
import { mkdirSync, writeFileSync } from "node:fs";
import { readFile } from "node:fs/promises";
import { isAbsolute } from "node:path";
import { fileURLToPath } from "node:url";
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_VIRTUAL_MODULE_ID,
VIRTUAL_MODULE_ID
} from "./constants.js";
import { createMinifiableCssRenderer } from "./implementations/css-renderer.js";
import { createDataCollector } from "./implementations/data-collector.js";
import { createAstroErrorHandler } from "./implementations/error-handler.js";
import { createCachedFontFetcher } from "./implementations/font-fetcher.js";
import { createFontaceFontFileReader } from "./implementations/font-file-reader.js";
import { createCapsizeFontMetricsResolver } from "./implementations/font-metrics-resolver.js";
import { createFontTypeExtractor } from "./implementations/font-type-extractor.js";
import { createXxHasher } from "./implementations/hasher.js";
import { createRequireLocalProviderUrlResolver } from "./implementations/local-provider-url-resolver.js";
import {
createBuildRemoteFontProviderModResolver,
createDevServerRemoteFontProviderModResolver
} from "./implementations/remote-font-provider-mod-resolver.js";
import { createRemoteFontProviderResolver } from "./implementations/remote-font-provider-resolver.js";
import { createFsStorage } from "./implementations/storage.js";
import { createSystemFallbacksProvider } from "./implementations/system-fallbacks-provider.js";
import { createUrlProxy } from "./implementations/url-proxy.js";
import {
createLocalUrlProxyContentResolver,
createRemoteUrlProxyContentResolver
} from "./implementations/url-proxy-content-resolver.js";
import { createBuildUrlResolver, createDevUrlResolver } from "./implementations/url-resolver.js";
import { orchestrate } from "./orchestrate.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;
}
},
load(id) {
if (id === RESOLVED_VIRTUAL_MODULE_ID) {
return {
code: ""
};
}
}
};
}
const assetsDir = prependForwardSlash(
appendForwardSlash(joinPaths(settings.config.build.assets, ASSETS_DIR))
);
const baseUrl = joinPaths(settings.config.base, assetsDir);
let fontFileDataMap = null;
let consumableMap = null;
let isBuild;
let fontFetcher = null;
let fontTypeExtractor = null;
const cleanup = () => {
consumableMap = null;
fontFileDataMap = null;
fontFetcher = null;
};
async function initialize({
cacheDir,
modResolver,
cssRenderer,
urlResolver
}) {
const { root } = settings.config;
const hasher = await createXxHasher();
const errorHandler = createAstroErrorHandler();
const remoteFontProviderResolver = createRemoteFontProviderResolver({
root,
modResolver,
errorHandler
});
const pathsToWarn = /* @__PURE__ */ new Set();
const localProviderUrlResolver = createRequireLocalProviderUrlResolver({
root,
intercept: (path) => {
if (path.startsWith(fileURLToPath(settings.config.publicDir))) {
if (pathsToWarn.has(path)) {
return;
}
pathsToWarn.add(path);
logger.warn(
"assets",
`Found a local font file ${JSON.stringify(path)} in the \`public/\` folder. To avoid duplicated files in the build output, move this file into \`src/\``
);
}
}
});
const storage = createFsStorage({ base: cacheDir });
const systemFallbacksProvider = createSystemFallbacksProvider();
fontFetcher = createCachedFontFetcher({ storage, errorHandler, fetch, readFile });
const fontMetricsResolver = createCapsizeFontMetricsResolver({ fontFetcher, cssRenderer });
fontTypeExtractor = createFontTypeExtractor({ errorHandler });
const fontFileReader = createFontaceFontFileReader({ errorHandler });
const res = await orchestrate({
families: settings.config.experimental.fonts,
hasher,
remoteFontProviderResolver,
localProviderUrlResolver,
storage,
cssRenderer,
systemFallbacksProvider,
fontMetricsResolver,
fontTypeExtractor,
fontFileReader,
logger,
createUrlProxy: ({ local, ...params }) => {
const dataCollector = createDataCollector(params);
const contentResolver = local ? createLocalUrlProxyContentResolver({ errorHandler }) : createRemoteUrlProxyContentResolver();
return createUrlProxy({
urlResolver,
contentResolver,
hasher,
dataCollector
});
},
defaults: DEFAULTS
});
fontFileDataMap = res.fontFileDataMap;
consumableMap = res.consumableMap;
}
return {
name: "astro:fonts",
config(_, { command }) {
isBuild = command === "build";
},
async buildStart() {
if (isBuild) {
await initialize({
cacheDir: new URL(CACHE_DIR, settings.config.cacheDir),
modResolver: createBuildRemoteFontProviderModResolver(),
cssRenderer: createMinifiableCssRenderer({ minify: true }),
urlResolver: createBuildUrlResolver({
base: baseUrl,
assetsPrefix: settings.config.build.assetsPrefix
})
});
}
},
async configureServer(server) {
await initialize({
// In dev, we cache fonts data in .astro so it can be easily inspected and cleared
cacheDir: new URL(CACHE_DIR, settings.dotAstroDir),
modResolver: createDevServerRemoteFontProviderModResolver({ server }),
cssRenderer: createMinifiableCssRenderer({ minify: false }),
urlResolver: createDevUrlResolver({ base: baseUrl })
});
const localPaths = [...fontFileDataMap.values()].filter(({ url }) => isAbsolute(url)).map((v) => v.url);
server.watcher.on("change", (path) => {
if (localPaths.includes(path)) {
logger.info("assets", "Font file updated");
server.restart();
}
});
server.watcher.on("unlink", (path) => {
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 (!req.url) {
return next();
}
const hash = req.url.slice(1);
const associatedData = fontFileDataMap?.get(hash);
if (!associatedData) {
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 data = await fontFetcher.fetch({ hash, ...associatedData });
res.setHeader("Content-Length", data.length);
res.setHeader("Content-Type", `font/${fontTypeExtractor.extract(hash)}`);
res.end(data);
} 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;
}
},
load(id) {
if (id === RESOLVED_VIRTUAL_MODULE_ID) {
return {
code: `export const fontsData = new Map(${JSON.stringify(Array.from(consumableMap?.entries() ?? []))})`
};
}
},
async buildEnd() {
if (sync || settings.config.experimental.fonts.length === 0) {
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 (fontFileDataMap) {
logger.info("assets", "Copying fonts...");
await Promise.all(
Array.from(fontFileDataMap.entries()).map(async ([hash, associatedData]) => {
const data = await fontFetcher.fetch({ hash, ...associatedData });
try {
writeFileSync(new URL(hash, fontsDir), data);
} catch (cause) {
throw new AstroError(AstroErrorData.UnknownFilesystemError, { cause });
}
})
);
}
} finally {
cleanup();
}
}
};
}
export {
fontsPlugin
};