Initial commit: New MoreminiMore website with fresh design
This commit is contained in:
18
node_modules/unifont/dist/chunk-DQk6qfdC.mjs
generated
vendored
Normal file
18
node_modules/unifont/dist/chunk-DQk6qfdC.mjs
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
//#region \0rolldown/runtime.js
|
||||
var __defProp = Object.defineProperty;
|
||||
var __exportAll = (all, no_symbols) => {
|
||||
let target = {};
|
||||
for (var name in all) {
|
||||
__defProp(target, name, {
|
||||
get: all[name],
|
||||
enumerable: true
|
||||
});
|
||||
}
|
||||
if (!no_symbols) {
|
||||
__defProp(target, Symbol.toStringTag, { value: "Module" });
|
||||
}
|
||||
return target;
|
||||
};
|
||||
|
||||
//#endregion
|
||||
export { __exportAll as t };
|
||||
267
node_modules/unifont/dist/index.d.mts
generated
vendored
Normal file
267
node_modules/unifont/dist/index.d.mts
generated
vendored
Normal file
@@ -0,0 +1,267 @@
|
||||
//#region src/utils.d.ts
|
||||
declare function defineFontProvider<Name extends string, Provider extends ProviderDefinition<never, never>>(name: Name, provider: Provider): Provider extends ProviderDefinition<infer Options, infer FamilyOptions> ? ProviderFactory<Name, Options, FamilyOptions> : never;
|
||||
declare const formatMap: {
|
||||
woff2: string;
|
||||
woff: string;
|
||||
otf: string;
|
||||
ttf: string;
|
||||
eot: string;
|
||||
};
|
||||
//#endregion
|
||||
//#region src/types.d.ts
|
||||
type Awaitable$1<T> = T | Promise<T>;
|
||||
interface ProviderContext {
|
||||
storage: {
|
||||
getItem: {
|
||||
<T = unknown>(key: string): Promise<T | null>;
|
||||
<T = unknown>(key: string, init: () => Awaitable$1<T>): Promise<T>;
|
||||
};
|
||||
setItem: (key: string, value: unknown) => Awaitable$1<void>;
|
||||
};
|
||||
}
|
||||
type FontStyles = 'normal' | 'italic' | 'oblique';
|
||||
type FontFormat = keyof typeof formatMap;
|
||||
interface ResolveFontOptions<FamilyOptions extends Record<string, any> | never = never> {
|
||||
weights: string[];
|
||||
styles: FontStyles[];
|
||||
subsets: string[];
|
||||
formats: FontFormat[];
|
||||
options?: [FamilyOptions] extends [never] ? undefined : FamilyOptions;
|
||||
}
|
||||
interface RemoteFontSource {
|
||||
url: string;
|
||||
originalURL?: string;
|
||||
format?: string;
|
||||
tech?: string;
|
||||
}
|
||||
interface LocalFontSource {
|
||||
name: string;
|
||||
}
|
||||
interface FontFaceMeta {
|
||||
/** The priority of the font face, usually used to indicate fallbacks. Smaller is more prioritized. */
|
||||
priority?: number;
|
||||
/**
|
||||
* The subset name of the font face. Many fonts provides font subsets such as latin, latin-ext, cyrillic, etc.
|
||||
*/
|
||||
subset?: string;
|
||||
/**
|
||||
* A `RequestInit` object that should be used when fetching this font. This can be useful for
|
||||
* adding authorization headers and other metadata required for a font request.
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/API/RequestInit
|
||||
*/
|
||||
init?: RequestInit;
|
||||
}
|
||||
interface FontFaceData {
|
||||
src: Array<LocalFontSource | RemoteFontSource>;
|
||||
/**
|
||||
* The font-display descriptor.
|
||||
* @default 'swap'
|
||||
*/
|
||||
display?: 'auto' | 'block' | 'swap' | 'fallback' | 'optional';
|
||||
/** A font-weight value. */
|
||||
weight?: string | number | [number, number];
|
||||
/** A font-stretch value. */
|
||||
stretch?: string;
|
||||
/** A font-style value. */
|
||||
style?: string;
|
||||
/** The range of Unicode code points to be used from the font. */
|
||||
unicodeRange?: string[];
|
||||
/** Allows control over advanced typographic features in OpenType fonts. */
|
||||
featureSettings?: string;
|
||||
/** Allows low-level control over OpenType or TrueType font variations, by specifying the four letter axis names of the features to vary, along with their variation values. */
|
||||
variationSettings?: string;
|
||||
/** Metadata for the font face used by unifont */
|
||||
meta?: FontFaceMeta;
|
||||
}
|
||||
interface ResolveFontResult {
|
||||
/**
|
||||
* Return data used to generate @font-face declarations.
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face
|
||||
*/
|
||||
fonts: FontFaceData[];
|
||||
fallbacks?: string[];
|
||||
}
|
||||
interface InitializedProvider<FamilyOptions extends Record<string, any> = never> {
|
||||
resolveFont: (family: string, options: ResolveFontOptions<FamilyOptions>) => Awaitable$1<ResolveFontResult | undefined>;
|
||||
listFonts?: (() => Awaitable$1<string[] | undefined>) | undefined;
|
||||
}
|
||||
interface ProviderDefinition<Options extends Record<string, any> = never, FamilyOptions extends Record<string, any> = never> {
|
||||
(options: Options, ctx: ProviderContext): Awaitable$1<InitializedProvider<FamilyOptions> | undefined>;
|
||||
}
|
||||
interface Provider<Name extends string = string, FamilyOptions extends Record<string, any> = never> {
|
||||
_name: Name;
|
||||
_options: unknown;
|
||||
(ctx: ProviderContext): Awaitable$1<InitializedProvider<FamilyOptions> | undefined>;
|
||||
}
|
||||
type ProviderFactory<Name extends string, Options extends Record<string, any> = never, FamilyOptions extends Record<string, any> = never> = [Options] extends [never] ? () => Provider<Name, FamilyOptions> : Partial<Options> extends Options ? (options?: Options) => Provider<Name, FamilyOptions> : (options: Options) => Provider<Name, FamilyOptions>;
|
||||
//#endregion
|
||||
//#region src/providers/google.d.ts
|
||||
type VariableAxis = 'opsz' | 'slnt' | 'wdth' | (string & {});
|
||||
interface GoogleProviderOptions {
|
||||
experimental?: {
|
||||
/**
|
||||
* Experimental: Setting variable axis configuration on a per-font basis.
|
||||
*/
|
||||
variableAxis?: {
|
||||
[fontFamily: string]: Partial<Record<VariableAxis, ([string, string] | string)[]>>;
|
||||
};
|
||||
/**
|
||||
* Experimental: Specifying a list of glyphs to be included in the font for each font family.
|
||||
* This can reduce the size of the font file.
|
||||
*/
|
||||
glyphs?: {
|
||||
[fontFamily: string]: string[];
|
||||
};
|
||||
};
|
||||
}
|
||||
interface GoogleFamilyOptions {
|
||||
experimental?: {
|
||||
/**
|
||||
* Experimental: Setting variable axis configuration on a per-font basis.
|
||||
*/
|
||||
variableAxis?: Partial<Record<VariableAxis, ([string, string] | string)[]>>;
|
||||
/**
|
||||
* Experimental: Specifying a list of glyphs to be included in the font for each font family.
|
||||
* This can reduce the size of the font file.
|
||||
*/
|
||||
glyphs?: string[];
|
||||
};
|
||||
}
|
||||
declare const _default$6: (options?: GoogleProviderOptions | undefined) => Provider<"google", GoogleFamilyOptions>;
|
||||
//#endregion
|
||||
//#region src/providers/googleicons.d.ts
|
||||
interface GoogleiconsProviderOptions {
|
||||
experimental?: {
|
||||
/**
|
||||
* Experimental: Specifying a list of icons to be included in the font for each font family.
|
||||
* This can reduce the size of the font file.
|
||||
*
|
||||
* **Only available when resolving the new `Material Symbols` icons.**
|
||||
*/
|
||||
glyphs?: {
|
||||
[fontFamily: string]: string[];
|
||||
};
|
||||
};
|
||||
}
|
||||
interface GoogleiconsFamilyOptions {
|
||||
experimental?: {
|
||||
/**
|
||||
* Experimental: Specifying a list of icons to be included in the font for each font family.
|
||||
* This can reduce the size of the font file.
|
||||
*
|
||||
* **Only available when resolving the new `Material Symbols` icons.**
|
||||
*/
|
||||
glyphs?: string[];
|
||||
};
|
||||
}
|
||||
declare const _default$5: (options?: GoogleiconsProviderOptions | undefined) => Provider<"googleicons", GoogleiconsFamilyOptions>;
|
||||
//#endregion
|
||||
//#region src/providers/adobe.d.ts
|
||||
interface AdobeProviderOptions {
|
||||
id: string[] | string;
|
||||
}
|
||||
declare const _default$4: (options: AdobeProviderOptions) => Provider<"adobe", never>;
|
||||
//#endregion
|
||||
//#region src/providers/bunny.d.ts
|
||||
declare const _default$3: () => Provider<"bunny", never>;
|
||||
//#endregion
|
||||
//#region src/providers/fontshare.d.ts
|
||||
declare const _default$2: () => Provider<"fontshare", never>;
|
||||
//#endregion
|
||||
//#region src/providers/fontsource.d.ts
|
||||
declare const _default$1: () => Provider<"fontsource", never>;
|
||||
//#endregion
|
||||
//#region src/providers/npm.d.ts
|
||||
interface NpmProviderOptions {
|
||||
/**
|
||||
* CDN to use for fetching npm packages remotely.
|
||||
* @default 'https://cdn.jsdelivr.net/npm'
|
||||
*/
|
||||
cdn?: string;
|
||||
/**
|
||||
* Whether to fall back to fetching from the CDN when local resolution
|
||||
* fails or `readFile` is not provided.
|
||||
*
|
||||
* Set to `false` to only resolve from locally installed packages.
|
||||
* This is useful when another provider (e.g. `fontsource`) already
|
||||
* handles CDN resolution.
|
||||
*
|
||||
* @default true
|
||||
*/
|
||||
remote?: boolean;
|
||||
/**
|
||||
* Optional function to read a file from the local filesystem.
|
||||
* When provided, the provider will try to resolve fonts from locally
|
||||
* installed packages in `node_modules` before falling back to the CDN
|
||||
* (unless `remote` is set to `false`).
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* import { readFile } from 'node:fs/promises'
|
||||
* providers.npm({
|
||||
* readFile: path => readFile(path, 'utf-8').catch(() => null),
|
||||
* remote: false, // only resolve from local node_modules
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
readFile?: (path: string) => Promise<string | null>;
|
||||
/**
|
||||
* Root directory of the project for resolving local packages.
|
||||
* Used to find `package.json` and `node_modules`.
|
||||
* @default '.' (current working directory)
|
||||
*/
|
||||
root?: string;
|
||||
}
|
||||
interface NpmFamilyOptions {
|
||||
/**
|
||||
* The npm package name.
|
||||
* When not specified, the provider will try to find the font family
|
||||
* in known font package patterns (e.g. `@fontsource/${family}`).
|
||||
*/
|
||||
package?: string;
|
||||
/**
|
||||
* The version of the package (used for CDN resolution only).
|
||||
* @default 'latest'
|
||||
*/
|
||||
version?: string;
|
||||
/**
|
||||
* The entry CSS file to parse.
|
||||
* @default 'index.css'
|
||||
*/
|
||||
file?: string;
|
||||
}
|
||||
declare const _default: (options?: NpmProviderOptions | undefined) => Provider<"npm", NpmFamilyOptions>;
|
||||
declare namespace providers_d_exports {
|
||||
export { _default$4 as adobe, _default$3 as bunny, _default$2 as fontshare, _default$1 as fontsource, _default$6 as google, _default$5 as googleicons, _default as npm };
|
||||
}
|
||||
//#endregion
|
||||
//#region src/cache.d.ts
|
||||
type Awaitable<T> = T | Promise<T>;
|
||||
type StorageValue = string | Record<string, unknown>;
|
||||
interface Storage {
|
||||
getItem: (key: string) => Awaitable<any | null>;
|
||||
setItem: <T extends StorageValue = StorageValue>(key: string, value: T) => Awaitable<void>;
|
||||
}
|
||||
//#endregion
|
||||
//#region src/unifont.d.ts
|
||||
interface UnifontOptions {
|
||||
storage?: Storage;
|
||||
throwOnError?: boolean;
|
||||
}
|
||||
type ExtractFamilyOptions<T extends Provider> = Exclude<Parameters<NonNullable<Awaited<ReturnType<T>>>['resolveFont']>[1]['options'], undefined>;
|
||||
interface Unifont<T extends Provider[]> {
|
||||
resolveFont: (fontFamily: string, options?: Partial<ResolveFontOptions<{ [K in T[number] as K['_name']]?: ExtractFamilyOptions<K> }>>, providers?: T[number]['_name'][]) => Promise<ResolveFontResult & {
|
||||
provider?: T[number]['_name'];
|
||||
}>;
|
||||
listFonts: (providers?: T[number]['_name'][]) => Promise<string[] | undefined>;
|
||||
}
|
||||
declare const defaultResolveOptions: ResolveFontOptions;
|
||||
declare function createUnifont<T extends [Provider, ...Provider[]]>(providers: T, unifontOptions?: UnifontOptions): Promise<Unifont<T>>;
|
||||
//#endregion
|
||||
//#region src/index.d.ts
|
||||
/** @deprecated */
|
||||
type GoogleOptions = GoogleProviderOptions;
|
||||
/** @deprecated */
|
||||
type GoogleiconsOptions = GoogleiconsProviderOptions;
|
||||
//#endregion
|
||||
export { type AdobeProviderOptions, type FontFaceData, type FontFaceMeta, type FontStyles, type GoogleFamilyOptions, GoogleOptions, type GoogleProviderOptions, type GoogleiconsFamilyOptions, GoogleiconsOptions, type GoogleiconsProviderOptions, type InitializedProvider, type LocalFontSource, type NpmFamilyOptions, type NpmProviderOptions, type Provider, type ProviderContext, type ProviderDefinition, type ProviderFactory, type RemoteFontSource, type ResolveFontOptions, type ResolveFontResult, type Unifont, type UnifontOptions, createUnifont, defaultResolveOptions, defineFontProvider, providers_d_exports as providers };
|
||||
922
node_modules/unifont/dist/index.mjs
generated
vendored
Normal file
922
node_modules/unifont/dist/index.mjs
generated
vendored
Normal file
@@ -0,0 +1,922 @@
|
||||
import { t as __exportAll } from "./chunk-DQk6qfdC.mjs";
|
||||
import { hash } from "ohash";
|
||||
import { findAll, generate, parse } from "css-tree";
|
||||
import { ofetch } from "ofetch";
|
||||
|
||||
//#region src/css/parse.ts
|
||||
const extractableKeyMap = {
|
||||
"src": "src",
|
||||
"font-display": "display",
|
||||
"font-weight": "weight",
|
||||
"font-style": "style",
|
||||
"font-feature-settings": "featureSettings",
|
||||
"font-variation-settings": "variationSettings",
|
||||
"unicode-range": "unicodeRange"
|
||||
};
|
||||
const formatPriorityList = Object.values({
|
||||
woff2: "woff2",
|
||||
woff: "woff",
|
||||
otf: "opentype",
|
||||
ttf: "truetype",
|
||||
eot: "embedded-opentype",
|
||||
svg: "svg"
|
||||
});
|
||||
function extractFontFaceData(css, family) {
|
||||
const fontFaces = [];
|
||||
for (const node of findAll(parse(css), (node) => node.type === "Atrule" && node.name === "font-face")) {
|
||||
/* v8 ignore next 3 */
|
||||
if (node.type !== "Atrule" || node.name !== "font-face") continue;
|
||||
if (family) {
|
||||
if (!node.block?.children.some((child) => {
|
||||
if (child.type !== "Declaration" || child.property !== "font-family") return false;
|
||||
const value = extractCSSValue(child);
|
||||
const slug = family.toLowerCase();
|
||||
if (typeof value === "string" && value.toLowerCase() === slug) return true;
|
||||
if (Array.isArray(value) && value.length > 0 && value.some((v) => v.toLowerCase() === slug)) return true;
|
||||
return false;
|
||||
})) continue;
|
||||
}
|
||||
const data = {};
|
||||
for (const child of node.block?.children || []) if (child.type === "Declaration" && child.property in extractableKeyMap) {
|
||||
const value = extractCSSValue(child);
|
||||
data[extractableKeyMap[child.property]] = ["src", "unicode-range"].includes(child.property) && !Array.isArray(value) ? [value] : value;
|
||||
}
|
||||
if (!data.src) continue;
|
||||
fontFaces.push(data);
|
||||
}
|
||||
return mergeFontSources(fontFaces);
|
||||
}
|
||||
function processRawValue(value) {
|
||||
return value.split(",").map((v) => v.trim().replace(/^(?<quote>['"])(.*)\k<quote>$/, "$2"));
|
||||
}
|
||||
function extractCSSValue(node) {
|
||||
if (node.value.type === "Raw") return processRawValue(node.value.value);
|
||||
const values = [];
|
||||
let buffer = "";
|
||||
for (const child of node.value.children) {
|
||||
if (child.type === "Function") {
|
||||
if (child.name === "local" && child.children.first?.type === "String") values.push({ name: child.children.first.value });
|
||||
if (child.name === "format") {
|
||||
if (child.children.first?.type === "String") values.at(-1).format = child.children.first.value;
|
||||
else if (child.children.first?.type === "Identifier") values.at(-1).format = child.children.first.name;
|
||||
}
|
||||
if (child.name === "tech") {
|
||||
if (child.children.first?.type === "String") values.at(-1).tech = child.children.first.value;
|
||||
else if (child.children.first?.type === "Identifier") values.at(-1).tech = child.children.first.name;
|
||||
}
|
||||
}
|
||||
if (child.type === "Url") values.push({ url: child.value });
|
||||
if (child.type === "Identifier") buffer = buffer ? `${buffer} ${child.name}` : child.name;
|
||||
if (child.type === "String") values.push(child.value);
|
||||
if (child.type === "Dimension") {
|
||||
const dimensionValue = child.value + child.unit;
|
||||
buffer = buffer ? `${buffer} ${dimensionValue}` : dimensionValue;
|
||||
}
|
||||
if (child.type === "Operator" && child.value === "," && buffer) {
|
||||
values.push(buffer);
|
||||
buffer = "";
|
||||
}
|
||||
if (child.type === "UnicodeRange") values.push(child.value);
|
||||
if (child.type === "Number") values.push(Number(child.value));
|
||||
}
|
||||
if (buffer) values.push(buffer);
|
||||
if (values.length === 1) return values[0];
|
||||
return values;
|
||||
}
|
||||
function mergeFontSources(data) {
|
||||
const mergedData = [];
|
||||
for (const face of data) {
|
||||
const keys = Object.keys(face).filter((k) => k !== "src");
|
||||
const existing = mergedData.find((f) => Object.keys(f).length === keys.length + 1 && keys.every((key) => f[key]?.toString() === face[key]?.toString()));
|
||||
if (existing) {
|
||||
for (const s of face.src) if (existing.src.every((src) => "url" in src ? !("url" in s) || s.url !== src.url : !("name" in s) || s.name !== src.name)) existing.src.push(s);
|
||||
} else mergedData.push(face);
|
||||
}
|
||||
for (const face of mergedData) face.src.sort((a, b) => {
|
||||
return ("format" in a ? formatPriorityList.indexOf(a.format || "woff2") : -2) - ("format" in b ? formatPriorityList.indexOf(b.format || "woff2") : -2);
|
||||
});
|
||||
return mergedData;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
//#region src/fetch.ts
|
||||
function mini$fetch(url, options) {
|
||||
const retries = options?.retries ?? 3;
|
||||
const retryDelay = options?.retryDelay ?? 1e3;
|
||||
return ofetch(url, {
|
||||
baseURL: options?.baseURL,
|
||||
query: options?.query,
|
||||
responseType: options?.responseType ?? "text",
|
||||
headers: options?.headers,
|
||||
retry: false
|
||||
}).catch((err) => {
|
||||
if (retries <= 0) throw err;
|
||||
console.warn(`Could not fetch from \`${(options?.baseURL ?? "") + url}\`. Will retry in \`${retryDelay}ms\`. \`${retries}\` retries left.`);
|
||||
return new Promise((resolve) => setTimeout(resolve, retryDelay)).then(() => mini$fetch(url, {
|
||||
...options,
|
||||
retries: retries - 1
|
||||
}));
|
||||
});
|
||||
}
|
||||
const $fetch = Object.assign(mini$fetch, { create: (defaults) => (url, options) => mini$fetch(url, {
|
||||
...defaults,
|
||||
...options
|
||||
}) });
|
||||
|
||||
//#endregion
|
||||
//#region src/utils.ts
|
||||
function defineFontProvider(name, provider) {
|
||||
return ((options) => Object.assign(provider.bind(null, options || {}), {
|
||||
_name: name,
|
||||
_options: options
|
||||
}));
|
||||
}
|
||||
function prepareWeights({ inputWeights, weights, hasVariableWeights }) {
|
||||
const collectedWeights = [];
|
||||
for (const weight of inputWeights) {
|
||||
if (weight.includes(" ")) {
|
||||
if (hasVariableWeights) {
|
||||
collectedWeights.push(weight);
|
||||
continue;
|
||||
}
|
||||
const [min, max] = weight.split(" ");
|
||||
collectedWeights.push(...weights.filter((_w) => {
|
||||
const w = Number(_w);
|
||||
return w >= Number(min) && w <= Number(max);
|
||||
}).map((w) => String(w)));
|
||||
continue;
|
||||
}
|
||||
if (weights.includes(weight)) collectedWeights.push(weight);
|
||||
}
|
||||
return [...new Set(collectedWeights)].map((weight) => ({
|
||||
weight,
|
||||
variable: weight.includes(" ")
|
||||
}));
|
||||
}
|
||||
function splitCssIntoSubsets(input) {
|
||||
const data = [];
|
||||
const comments = [];
|
||||
const nodes = findAll(parse(input, {
|
||||
positions: true,
|
||||
onComment(value, loc) {
|
||||
comments.push({
|
||||
value: value.trim(),
|
||||
endLine: loc.end.line
|
||||
});
|
||||
}
|
||||
}), (node) => node.type === "Atrule" && node.name === "font-face");
|
||||
if (comments.length === 0) return [{
|
||||
subset: null,
|
||||
css: input
|
||||
}];
|
||||
for (const node of nodes) {
|
||||
const comment = comments.filter((comment) => comment.endLine < node.loc.start.line).at(-1);
|
||||
data.push({
|
||||
subset: comment?.value ?? null,
|
||||
css: generate(node)
|
||||
});
|
||||
}
|
||||
return data;
|
||||
}
|
||||
const formatMap = {
|
||||
woff2: "woff2",
|
||||
woff: "woff",
|
||||
otf: "opentype",
|
||||
ttf: "truetype",
|
||||
eot: "embedded-opentype"
|
||||
};
|
||||
/** Maps variation format strings (e.g. 'woff2-variations') to their base CSS format name (e.g. 'woff2') */
|
||||
const variationFormatMap = {
|
||||
"woff2-variations": "woff2",
|
||||
"woff-variations": "woff",
|
||||
"opentype-variations": "opentype",
|
||||
"truetype-variations": "truetype"
|
||||
};
|
||||
function computeIdFromSource(source) {
|
||||
return "name" in source ? source.name : source.url;
|
||||
}
|
||||
function cleanFontFaces(fonts, _formats) {
|
||||
const formats = _formats.map((format) => formatMap[format]);
|
||||
const result = [];
|
||||
const hashToIndex = /* @__PURE__ */ new Map();
|
||||
for (const { src: _src, meta, ...font } of fonts) {
|
||||
const key = hash(font);
|
||||
const index = hashToIndex.get(key);
|
||||
const src = _src.map((source) => "name" in source ? source : {
|
||||
...source,
|
||||
...source.format ? { format: formatMap[source.format] ?? source.format } : {}
|
||||
}).filter((source) => {
|
||||
if ("name" in source) return true;
|
||||
if (!source.format) return true;
|
||||
if (formats.includes(source.format)) return true;
|
||||
const baseFormat = variationFormatMap[source.format];
|
||||
return !!baseFormat && formats.includes(baseFormat);
|
||||
});
|
||||
if (src.length === 0) continue;
|
||||
if (index === void 0) {
|
||||
hashToIndex.set(key, result.push({
|
||||
...font,
|
||||
...meta ? { meta } : {},
|
||||
src
|
||||
}) - 1);
|
||||
continue;
|
||||
}
|
||||
const existing = result[index];
|
||||
const ids = new Set(existing.src.map((source) => computeIdFromSource(source)));
|
||||
existing.src.push(...src.filter((source) => {
|
||||
const id = computeIdFromSource(source);
|
||||
return !ids.has(id) && ids.add(id);
|
||||
}));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
//#region src/providers/adobe.ts
|
||||
const fontCSSAPI = $fetch.create({ baseURL: "https://use.typekit.net" });
|
||||
async function getAdobeFontMeta(id) {
|
||||
const { kit } = await $fetch(`https://typekit.com/api/v1/json/kits/${id}/published`, { responseType: "json" });
|
||||
return kit;
|
||||
}
|
||||
const KIT_REFRESH_TIMEOUT = 300 * 1e3;
|
||||
var adobe_default = defineFontProvider("adobe", async (options, ctx) => {
|
||||
if (!options.id) return;
|
||||
const familyMap = /* @__PURE__ */ new Map();
|
||||
const notFoundFamilies = /* @__PURE__ */ new Set();
|
||||
const fonts = { kits: [] };
|
||||
let lastRefreshKitTime;
|
||||
let fetchKitsPromise;
|
||||
const kits = typeof options.id === "string" ? [options.id] : options.id;
|
||||
await fetchKits();
|
||||
async function fetchKits(bypassCache = false) {
|
||||
familyMap.clear();
|
||||
notFoundFamilies.clear();
|
||||
fonts.kits = [];
|
||||
await Promise.all(kits.map(async (id) => {
|
||||
let meta;
|
||||
const key = `adobe:meta-${id}.json`;
|
||||
if (bypassCache) {
|
||||
meta = await getAdobeFontMeta(id);
|
||||
await ctx.storage.setItem(key, meta);
|
||||
} else meta = await ctx.storage.getItem(key, () => getAdobeFontMeta(id));
|
||||
if (!meta) throw new TypeError("No font metadata found in adobe response.");
|
||||
fonts.kits.push(meta);
|
||||
for (const family of meta.families) familyMap.set(family.name, family.id);
|
||||
}));
|
||||
}
|
||||
async function getFontDetails(family, options) {
|
||||
options.weights = options.weights.map(String);
|
||||
for (const kit of fonts.kits) {
|
||||
const font = kit.families.find((f) => f.name === family);
|
||||
if (!font) continue;
|
||||
const weights = prepareWeights({
|
||||
inputWeights: options.weights,
|
||||
hasVariableWeights: false,
|
||||
weights: font.variations.map((v) => `${v.slice(-1)}00`)
|
||||
}).map((w) => w.weight);
|
||||
const styles = [];
|
||||
for (const style of font.variations) {
|
||||
if (style.includes("i") && !options.styles.includes("italic")) continue;
|
||||
if (!weights.includes(String(`${style.slice(-1)}00`))) continue;
|
||||
styles.push(style);
|
||||
}
|
||||
if (styles.length === 0) continue;
|
||||
return extractFontFaceData(await fontCSSAPI(`/${kit.id}.css`), font.css_names[0] ?? family.toLowerCase().split(" ").join("-")).filter((font) => {
|
||||
const [lowerWeight, upperWeight] = Array.isArray(font.weight) ? font.weight : [0, 0];
|
||||
return (!options.styles || !font.style || options.styles.includes(font.style)) && (!weights || !font.weight || Array.isArray(font.weight) ? weights.some((weight) => Number(weight) <= upperWeight || Number(weight) >= lowerWeight) : weights.includes(String(font.weight)));
|
||||
});
|
||||
}
|
||||
return [];
|
||||
}
|
||||
return {
|
||||
listFonts() {
|
||||
return [...familyMap.keys()];
|
||||
},
|
||||
async resolveFont(family, options) {
|
||||
if (notFoundFamilies.has(family)) return;
|
||||
if (!familyMap.has(family)) if (fetchKitsPromise) await fetchKitsPromise;
|
||||
else {
|
||||
const lastRefetch = lastRefreshKitTime || 0;
|
||||
const now = Date.now();
|
||||
if (now - lastRefetch > KIT_REFRESH_TIMEOUT) {
|
||||
lastRefreshKitTime = now;
|
||||
fetchKitsPromise = fetchKits(true).finally(() => {
|
||||
fetchKitsPromise = void 0;
|
||||
});
|
||||
await fetchKitsPromise;
|
||||
}
|
||||
}
|
||||
if (!familyMap.has(family)) {
|
||||
notFoundFamilies.add(family);
|
||||
return;
|
||||
}
|
||||
return { fonts: await ctx.storage.getItem(`adobe:${family}-${hash(options)}-data.json`, () => getFontDetails(family, options)) };
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
//#endregion
|
||||
//#region src/providers/bunny.ts
|
||||
const fontAPI$2 = $fetch.create({ baseURL: "https://fonts.bunny.net" });
|
||||
var bunny_default = defineFontProvider("bunny", async (_options, ctx) => {
|
||||
const familyMap = /* @__PURE__ */ new Map();
|
||||
const fonts = await ctx.storage.getItem("bunny:meta.json", () => fontAPI$2("/list", { responseType: "json" }));
|
||||
for (const [id, family] of Object.entries(fonts)) familyMap.set(family.familyName, id);
|
||||
async function getFontDetails(family, options) {
|
||||
const id = familyMap.get(family);
|
||||
const font = fonts[id];
|
||||
const weights = prepareWeights({
|
||||
inputWeights: options.weights,
|
||||
hasVariableWeights: false,
|
||||
weights: font.weights.map(String)
|
||||
});
|
||||
const styleMap = {
|
||||
italic: "i",
|
||||
oblique: "i",
|
||||
normal: ""
|
||||
};
|
||||
const styles = new Set(options.styles.map((i) => styleMap[i]));
|
||||
if (weights.length === 0 || styles.size === 0) return [];
|
||||
const css = await fontAPI$2("/css", { query: { family: `${id}:${weights.flatMap((w) => [...styles].map((s) => `${w.weight}${s}`)).join(",")}` } });
|
||||
const resolvedFontFaceData = [];
|
||||
const groups = splitCssIntoSubsets(css).filter((group) => group.subset ? options.subsets.includes(group.subset) : true);
|
||||
for (const group of groups) {
|
||||
const data = extractFontFaceData(group.css);
|
||||
data.map((f) => {
|
||||
f.meta ??= {};
|
||||
if (group.subset) f.meta.subset = group.subset;
|
||||
return f;
|
||||
});
|
||||
resolvedFontFaceData.push(...data);
|
||||
}
|
||||
return cleanFontFaces(resolvedFontFaceData, options.formats);
|
||||
}
|
||||
return {
|
||||
listFonts() {
|
||||
return [...familyMap.keys()];
|
||||
},
|
||||
async resolveFont(fontFamily, defaults) {
|
||||
if (!familyMap.has(fontFamily)) return;
|
||||
return { fonts: await ctx.storage.getItem(`bunny:${fontFamily}-${hash(defaults)}-data.json`, () => getFontDetails(fontFamily, defaults)) };
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
//#endregion
|
||||
//#region src/providers/fontshare.ts
|
||||
const fontAPI$1 = $fetch.create({ baseURL: "https://api.fontshare.com/v2" });
|
||||
var fontshare_default = defineFontProvider("fontshare", async (_options, ctx) => {
|
||||
const fontshareFamilies = /* @__PURE__ */ new Set();
|
||||
const fonts = await ctx.storage.getItem("fontshare:meta.json", async () => {
|
||||
const fonts = [];
|
||||
let offset = 0;
|
||||
let chunk;
|
||||
do {
|
||||
chunk = await fontAPI$1("/fonts", {
|
||||
responseType: "json",
|
||||
query: {
|
||||
offset,
|
||||
limit: 100
|
||||
}
|
||||
});
|
||||
fonts.push(...chunk.fonts);
|
||||
offset++;
|
||||
} while (chunk.has_more);
|
||||
return fonts;
|
||||
});
|
||||
for (const font of fonts) fontshareFamilies.add(font.name);
|
||||
async function getFontDetails(family, options) {
|
||||
const font = fonts.find((f) => f.name === family);
|
||||
const numbers = [];
|
||||
const weights = prepareWeights({
|
||||
inputWeights: options.weights,
|
||||
hasVariableWeights: false,
|
||||
weights: font.styles.map((s) => String(s.weight.weight))
|
||||
}).map((w) => w.weight);
|
||||
for (const style of font.styles) {
|
||||
if (style.is_italic && !options.styles.includes("italic")) continue;
|
||||
if (!style.is_italic && !options.styles.includes("normal")) continue;
|
||||
if (!weights.includes(String(style.weight.weight))) continue;
|
||||
numbers.push(style.weight.number);
|
||||
}
|
||||
if (numbers.length === 0) return [];
|
||||
return cleanFontFaces(extractFontFaceData(await fontAPI$1(`/css?f[]=${`${font.slug}@${numbers.join(",")}`}`)), options.formats);
|
||||
}
|
||||
return {
|
||||
listFonts() {
|
||||
return [...fontshareFamilies];
|
||||
},
|
||||
async resolveFont(fontFamily, defaults) {
|
||||
if (!fontshareFamilies.has(fontFamily)) return;
|
||||
return { fonts: await ctx.storage.getItem(`fontshare:${fontFamily}-${hash(defaults)}-data.json`, () => getFontDetails(fontFamily, defaults)) };
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
//#endregion
|
||||
//#region src/providers/fontsource.ts
|
||||
const fontAPI = $fetch.create({ baseURL: "https://api.fontsource.org/v1" });
|
||||
var fontsource_default = defineFontProvider("fontsource", async (_options, ctx) => {
|
||||
const fonts = await ctx.storage.getItem("fontsource:meta.json", () => fontAPI("/fonts", { responseType: "json" }));
|
||||
const familyMap = /* @__PURE__ */ new Map();
|
||||
for (const meta of fonts) familyMap.set(meta.family, meta);
|
||||
async function getFontDetails(family, options) {
|
||||
const font = familyMap.get(family);
|
||||
const weights = prepareWeights({
|
||||
inputWeights: options.weights,
|
||||
hasVariableWeights: font.variable,
|
||||
weights: font.weights.map(String)
|
||||
});
|
||||
const styles = options.styles.filter((style) => font.styles.includes(style));
|
||||
const subsets = options.subsets ? options.subsets.filter((subset) => font.subsets.includes(subset)) : [font.defSubset];
|
||||
if (weights.length === 0 || styles.length === 0) return [];
|
||||
const fontDetail = await fontAPI(`/fonts/${font.id}`, { responseType: "json" });
|
||||
const fontFaceData = [];
|
||||
for (const subset of subsets) for (const style of styles) for (const { weight, variable } of weights) {
|
||||
if (variable) {
|
||||
try {
|
||||
const variableAxes = await ctx.storage.getItem(`fontsource:${font.family}-axes.json`, () => fontAPI(`/variable/${font.id}`, { responseType: "json" }));
|
||||
if (variableAxes && variableAxes.axes.wght) fontFaceData.push({
|
||||
style,
|
||||
weight: [Number(variableAxes.axes.wght.min), Number(variableAxes.axes.wght.max)],
|
||||
src: [{
|
||||
url: `https://cdn.jsdelivr.net/fontsource/fonts/${font.id}:vf@latest/${subset}-wght-${style}.woff2`,
|
||||
format: "woff2"
|
||||
}],
|
||||
unicodeRange: fontDetail.unicodeRange[subset]?.split(","),
|
||||
meta: { subset }
|
||||
});
|
||||
} catch {
|
||||
console.error(`Could not download variable axes metadata for \`${font.family}\` from \`fontsource\`. \`unifont\` will not be able to inject variable axes for ${font.family}.`);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
const variantUrl = fontDetail.variants[weight][style][subset].url;
|
||||
fontFaceData.push({
|
||||
style,
|
||||
weight,
|
||||
src: Object.entries(variantUrl).map(([format, url]) => ({
|
||||
url,
|
||||
format
|
||||
})),
|
||||
unicodeRange: fontDetail.unicodeRange[subset]?.split(","),
|
||||
meta: { subset }
|
||||
});
|
||||
}
|
||||
return cleanFontFaces(fontFaceData, options.formats);
|
||||
}
|
||||
return {
|
||||
listFonts() {
|
||||
return [...familyMap.keys()];
|
||||
},
|
||||
async resolveFont(fontFamily, options) {
|
||||
if (!familyMap.has(fontFamily)) return;
|
||||
return { fonts: await ctx.storage.getItem(`fontsource:${fontFamily}-${hash(options)}-data.json`, () => getFontDetails(fontFamily, options)) };
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
//#endregion
|
||||
//#region src/providers/google.ts
|
||||
const userAgents = {
|
||||
eot: "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0)",
|
||||
ttf: "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; de-at) AppleWebKit/533.21.1 (KHTML, like Gecko) Version/5.0.5 Safari/533.21.1",
|
||||
woff: "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:27.0) Gecko/20100101 Firefox/27.0",
|
||||
woff2: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36"
|
||||
};
|
||||
var google_default = defineFontProvider("google", async (providerOptions, ctx) => {
|
||||
const googleFonts = await ctx.storage.getItem("google:meta.json", () => $fetch("https://fonts.google.com/metadata/fonts", { responseType: "json" }).then((r) => r.familyMetadataList));
|
||||
const styleMap = {
|
||||
italic: "1",
|
||||
oblique: "1",
|
||||
normal: "0"
|
||||
};
|
||||
async function getFontDetails(family, options) {
|
||||
const font = googleFonts.find((font) => font.family === family);
|
||||
const styles = [...new Set(options.styles.map((i) => styleMap[i]))].sort();
|
||||
const glyphs = (options.options?.experimental?.glyphs ?? providerOptions.experimental?.glyphs?.[family])?.join("");
|
||||
const weights = prepareWeights({
|
||||
inputWeights: options.weights,
|
||||
hasVariableWeights: font.axes.some((a) => a.tag === "wght"),
|
||||
weights: Object.keys(font.fonts)
|
||||
}).map((v) => v.variable ? {
|
||||
weight: v.weight.replace(" ", ".."),
|
||||
variable: v.variable
|
||||
} : v);
|
||||
if (weights.length === 0 || styles.length === 0) return [];
|
||||
const resolvedAxes = [];
|
||||
let resolvedVariants = [];
|
||||
const variableAxis = options.options?.experimental?.variableAxis ?? providerOptions.experimental?.variableAxis?.[family];
|
||||
const candidateAxes = [
|
||||
"wght",
|
||||
"ital",
|
||||
...Object.keys(variableAxis ?? {})
|
||||
].sort(googleFlavoredSorting);
|
||||
for (const axis of candidateAxes) {
|
||||
const axisValue = {
|
||||
wght: weights.map((v) => v.weight),
|
||||
ital: styles
|
||||
}[axis] ?? variableAxis[axis].map((v) => Array.isArray(v) ? `${v[0]}..${v[1]}` : v);
|
||||
if (resolvedVariants.length === 0) resolvedVariants = axisValue;
|
||||
else resolvedVariants = resolvedVariants.flatMap((v) => [...axisValue].map((o) => [v, o].join(","))).sort();
|
||||
resolvedAxes.push(axis);
|
||||
}
|
||||
let priority = 0;
|
||||
const resolvedFontFaceData = [];
|
||||
for (const format of options.formats) {
|
||||
const userAgent = userAgents[format];
|
||||
if (!userAgent) continue;
|
||||
const groups = splitCssIntoSubsets(await $fetch("/css2", {
|
||||
baseURL: "https://fonts.googleapis.com",
|
||||
headers: { "user-agent": userAgent },
|
||||
query: {
|
||||
family: `${family}:${resolvedAxes.join(",")}@${resolvedVariants.join(";")}`,
|
||||
...glyphs && { text: glyphs }
|
||||
}
|
||||
})).filter((group) => group.subset ? options.subsets.includes(group.subset) : true);
|
||||
for (const group of groups) {
|
||||
const data = extractFontFaceData(group.css);
|
||||
data.map((f) => {
|
||||
f.meta ??= {};
|
||||
f.meta.priority = priority;
|
||||
if (group.subset) f.meta.subset = group.subset;
|
||||
return f;
|
||||
});
|
||||
resolvedFontFaceData.push(...data);
|
||||
}
|
||||
priority++;
|
||||
}
|
||||
return cleanFontFaces(resolvedFontFaceData, options.formats);
|
||||
}
|
||||
return {
|
||||
listFonts() {
|
||||
return googleFonts.map((font) => font.family);
|
||||
},
|
||||
async resolveFont(fontFamily, options) {
|
||||
if (!googleFonts.some((font) => font.family === fontFamily)) return;
|
||||
return { fonts: await ctx.storage.getItem(`google:${fontFamily}-${hash(options)}-data.json`, () => getFontDetails(fontFamily, options)) };
|
||||
}
|
||||
};
|
||||
});
|
||||
function googleFlavoredSorting(a, b) {
|
||||
const isALowercase = a.charAt(0) === a.charAt(0).toLowerCase();
|
||||
const isBLowercase = b.charAt(0) === b.charAt(0).toLowerCase();
|
||||
if (isALowercase !== isBLowercase) return Number(isBLowercase) - Number(isALowercase);
|
||||
else return a.localeCompare(b);
|
||||
}
|
||||
|
||||
//#endregion
|
||||
//#region src/providers/googleicons.ts
|
||||
var googleicons_default = defineFontProvider("googleicons", async (providerOptions, ctx) => {
|
||||
const googleIcons = await ctx.storage.getItem("googleicons:meta.json", async () => {
|
||||
const data = await $fetch("https://fonts.google.com/metadata/icons?key=material_symbols&incomplete=true");
|
||||
return JSON.parse(data.substring(data.indexOf("\n") + 1)).families;
|
||||
});
|
||||
async function getFontDetails(family, options) {
|
||||
const iconNames = (options.options?.experimental?.glyphs ?? providerOptions.experimental?.glyphs?.[family])?.join("");
|
||||
let css = "";
|
||||
for (const format of options.formats) {
|
||||
const userAgent = userAgents[format];
|
||||
if (!userAgent) continue;
|
||||
if (family.includes("Icons")) css += await $fetch("/icon", {
|
||||
baseURL: "https://fonts.googleapis.com",
|
||||
headers: { "user-agent": userAgent },
|
||||
query: { family }
|
||||
});
|
||||
else css += await $fetch("/css2", {
|
||||
baseURL: "https://fonts.googleapis.com",
|
||||
headers: { "user-agent": userAgent },
|
||||
query: {
|
||||
family: `${family}:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200`,
|
||||
...iconNames && { icon_names: iconNames }
|
||||
}
|
||||
});
|
||||
}
|
||||
return cleanFontFaces(extractFontFaceData(css), options.formats);
|
||||
}
|
||||
return {
|
||||
listFonts() {
|
||||
return googleIcons;
|
||||
},
|
||||
async resolveFont(fontFamily, options) {
|
||||
if (!googleIcons.includes(fontFamily)) return;
|
||||
return { fonts: await ctx.storage.getItem(`googleicons:${fontFamily}-${hash(options)}-data.json`, () => getFontDetails(fontFamily, options)) };
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
//#endregion
|
||||
//#region src/providers/npm.ts
|
||||
const DEFAULT_CDN = "https://cdn.jsdelivr.net/npm";
|
||||
const KNOWN_FONT_PACKAGES = [
|
||||
{
|
||||
match: /^@fontsource-variable\//,
|
||||
family: (pkg) => {
|
||||
return `${slugToFamily(pkg.replace("@fontsource-variable/", ""))} Variable`;
|
||||
}
|
||||
},
|
||||
{
|
||||
match: /^@fontsource\//,
|
||||
family: (pkg) => {
|
||||
return slugToFamily(pkg.replace("@fontsource/", ""));
|
||||
}
|
||||
},
|
||||
{
|
||||
match: /^cal-sans$/,
|
||||
family: () => "Cal Sans"
|
||||
}
|
||||
];
|
||||
/** Convert a slug like "geist-sans" to "Geist Sans" */
|
||||
function slugToFamily(slug) {
|
||||
return slug.split("-").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join(" ");
|
||||
}
|
||||
/** Convert a family name like "Geist Sans" to a fontsource slug "geist-sans" */
|
||||
function familyToSlug(family) {
|
||||
return family.toLowerCase().replace(/\s+/g, "-");
|
||||
}
|
||||
/**
|
||||
* Guess the npm package name and CSS file for a font family that wasn't
|
||||
* found in the auto-detected packages. Uses fontsource conventions as fallback.
|
||||
*/
|
||||
function guessPackageForFamily(family) {
|
||||
if (family.endsWith(" Variable")) return {
|
||||
pkgName: `@fontsource-variable/${familyToSlug(family.replace(/ Variable$/, ""))}`,
|
||||
file: "index.css"
|
||||
};
|
||||
return {
|
||||
pkgName: `@fontsource/${familyToSlug(family)}`,
|
||||
file: "index.css"
|
||||
};
|
||||
}
|
||||
var npm_default = defineFontProvider("npm", (providerOptions, ctx) => {
|
||||
const cdn = providerOptions.cdn || DEFAULT_CDN;
|
||||
const remote = providerOptions.remote ?? true;
|
||||
const npmFetch = $fetch.create({ baseURL: cdn });
|
||||
const readFile = providerOptions.readFile;
|
||||
const root = providerOptions.root || ".";
|
||||
let detectedFonts;
|
||||
let detectedFontsHash;
|
||||
async function getDetectedFonts() {
|
||||
if (!readFile) return detectedFonts ??= /* @__PURE__ */ new Map();
|
||||
let pkgJsonContent;
|
||||
try {
|
||||
pkgJsonContent = await readFile(`${root}/package.json`);
|
||||
} catch {
|
||||
return detectedFonts ??= /* @__PURE__ */ new Map();
|
||||
}
|
||||
if (!pkgJsonContent) return detectedFonts ??= /* @__PURE__ */ new Map();
|
||||
const contentHash = hash(pkgJsonContent);
|
||||
if (detectedFonts && detectedFontsHash === contentHash) return detectedFonts;
|
||||
detectedFontsHash = contentHash;
|
||||
detectedFonts = /* @__PURE__ */ new Map();
|
||||
try {
|
||||
const pkgJson = JSON.parse(pkgJsonContent);
|
||||
const allDeps = {
|
||||
...pkgJson.dependencies,
|
||||
...pkgJson.devDependencies
|
||||
};
|
||||
for (const depName of Object.keys(allDeps)) for (const pattern of KNOWN_FONT_PACKAGES) if (pattern.match.test(depName)) {
|
||||
const family = pattern.family(depName);
|
||||
detectedFonts.set(family.toLowerCase(), {
|
||||
family,
|
||||
pkgName: depName,
|
||||
file: pattern.file || "index.css"
|
||||
});
|
||||
break;
|
||||
}
|
||||
} catch {}
|
||||
return detectedFonts;
|
||||
}
|
||||
function resolveUrlsToAbsolute(fontFaces, baseUrl) {
|
||||
for (const face of fontFaces) if (Array.isArray(face.src)) face.src = face.src.map((src) => {
|
||||
if ("url" in src) {
|
||||
const url = src.url;
|
||||
if (url.startsWith("http") || url.startsWith("data:") || url.startsWith("//")) return src;
|
||||
return {
|
||||
...src,
|
||||
url: new URL(url, baseUrl).href
|
||||
};
|
||||
}
|
||||
return src;
|
||||
});
|
||||
}
|
||||
async function resolveFromLocal(pkgName, cssFile, family, formats) {
|
||||
if (!readFile) return null;
|
||||
const css = await readFile(`${root}/node_modules/${pkgName}/${cssFile}`).catch(() => null);
|
||||
if (!css) return null;
|
||||
const fontFaces = extractFontFaceData(css, family);
|
||||
if (fontFaces.length === 0) return null;
|
||||
let version = "latest";
|
||||
try {
|
||||
const localPkgJson = await readFile(`${root}/node_modules/${pkgName}/package.json`);
|
||||
if (localPkgJson) {
|
||||
const parsed = JSON.parse(localPkgJson);
|
||||
if (parsed.version) version = parsed.version;
|
||||
}
|
||||
} catch {}
|
||||
resolveUrlsToAbsolute(fontFaces, `${cdn}/${pkgName}@${version}/`);
|
||||
return cleanFontFaces(fontFaces, formats);
|
||||
}
|
||||
async function resolveFromCdn(pkgName, pkgVersion, cssFile, family, formats) {
|
||||
let css;
|
||||
try {
|
||||
css = await npmFetch(`${pkgName}@${pkgVersion}/${cssFile}`);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
if (!css) return null;
|
||||
const fontFaces = extractFontFaceData(css, family);
|
||||
resolveUrlsToAbsolute(fontFaces, `${cdn}/${pkgName}@${pkgVersion}/`);
|
||||
return cleanFontFaces(fontFaces, formats);
|
||||
}
|
||||
return {
|
||||
async listFonts() {
|
||||
const fonts = await getDetectedFonts();
|
||||
if (fonts.size === 0) return;
|
||||
return [...fonts.values()].map((f) => f.family);
|
||||
},
|
||||
async resolveFont(family, options) {
|
||||
const familyOptions = options.options || {};
|
||||
let pkgName;
|
||||
let cssFile;
|
||||
let pkgVersion;
|
||||
if (familyOptions.package) {
|
||||
pkgName = familyOptions.package;
|
||||
cssFile = familyOptions.file || "index.css";
|
||||
pkgVersion = familyOptions.version || "latest";
|
||||
} else {
|
||||
const detected = (await getDetectedFonts()).get(family.toLowerCase());
|
||||
if (detected) {
|
||||
pkgName = detected.pkgName;
|
||||
cssFile = familyOptions.file || detected.file;
|
||||
pkgVersion = familyOptions.version || "latest";
|
||||
} else {
|
||||
const guessed = guessPackageForFamily(family);
|
||||
pkgName = guessed.pkgName;
|
||||
cssFile = familyOptions.file || guessed.file;
|
||||
pkgVersion = familyOptions.version || "latest";
|
||||
}
|
||||
}
|
||||
const key = `npm:${pkgName}/${cssFile}-${hash(options)}`;
|
||||
const fonts = await ctx.storage.getItem(key, async () => {
|
||||
const localResult = await resolveFromLocal(pkgName, cssFile, family, options.formats);
|
||||
if (localResult) return localResult;
|
||||
if (!remote) return null;
|
||||
return await resolveFromCdn(pkgName, pkgVersion, cssFile, family, options.formats);
|
||||
});
|
||||
if (!fonts) return;
|
||||
return { fonts };
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
//#endregion
|
||||
//#region src/providers.ts
|
||||
var providers_exports = /* @__PURE__ */ __exportAll({
|
||||
adobe: () => adobe_default,
|
||||
bunny: () => bunny_default,
|
||||
fontshare: () => fontshare_default,
|
||||
fontsource: () => fontsource_default,
|
||||
google: () => google_default,
|
||||
googleicons: () => googleicons_default,
|
||||
npm: () => npm_default
|
||||
});
|
||||
|
||||
//#endregion
|
||||
//#region package.json
|
||||
var version = "0.7.4";
|
||||
|
||||
//#endregion
|
||||
//#region src/cache.ts
|
||||
function memoryStorage() {
|
||||
const cache = /* @__PURE__ */ new Map();
|
||||
return {
|
||||
getItem(key) {
|
||||
return cache.get(key);
|
||||
},
|
||||
setItem(key, value) {
|
||||
cache.set(key, value);
|
||||
}
|
||||
};
|
||||
}
|
||||
const ONE_WEEK = 1e3 * 60 * 60 * 24 * 7;
|
||||
function createAsyncStorage(storage, options = {}) {
|
||||
const prefix = options?.cachedBy?.length ? `${createCacheKey(...options.cachedBy)}:` : "";
|
||||
const resolveKey = (key) => `${prefix}${key}`;
|
||||
return {
|
||||
async getItem(key, init) {
|
||||
const resolvedKey = resolveKey(key);
|
||||
const now = Date.now();
|
||||
const res = await storage.getItem(resolvedKey);
|
||||
if (res && res.expires > now && res.version === version) return res.data;
|
||||
if (!init) return null;
|
||||
const data = await init();
|
||||
await storage.setItem(resolvedKey, {
|
||||
expires: now + ONE_WEEK,
|
||||
version,
|
||||
data
|
||||
});
|
||||
return data;
|
||||
},
|
||||
async setItem(key, data) {
|
||||
await storage.setItem(resolveKey(key), {
|
||||
expires: Date.now() + ONE_WEEK,
|
||||
version,
|
||||
data
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
function createCacheKey(...fragments) {
|
||||
return fragments.map((f) => {
|
||||
return sanitize(typeof f === "string" ? f : hash(f));
|
||||
}).join(":");
|
||||
}
|
||||
function sanitize(input) {
|
||||
if (!input) return "";
|
||||
return input.replace(/[^\w.-]/g, "_");
|
||||
}
|
||||
|
||||
//#endregion
|
||||
//#region src/unifont.ts
|
||||
const defaultResolveOptions = {
|
||||
weights: ["400"],
|
||||
styles: ["normal", "italic"],
|
||||
subsets: [
|
||||
"cyrillic-ext",
|
||||
"cyrillic",
|
||||
"greek-ext",
|
||||
"greek",
|
||||
"vietnamese",
|
||||
"latin-ext",
|
||||
"latin"
|
||||
],
|
||||
formats: ["woff2"]
|
||||
};
|
||||
async function createUnifont(providers, unifontOptions) {
|
||||
const stack = {};
|
||||
const storage = unifontOptions?.storage ?? memoryStorage();
|
||||
for (const provider of providers) stack[provider._name] = void 0;
|
||||
await Promise.all(providers.map(async (provider) => {
|
||||
const context = { storage: createAsyncStorage(storage, { cachedBy: [provider._name, provider._options] }) };
|
||||
try {
|
||||
const initializedProvider = await provider(context);
|
||||
if (initializedProvider) stack[provider._name] = initializedProvider;
|
||||
} catch (cause) {
|
||||
const message = `Could not initialize provider \`${provider._name}\`. \`unifont\` will not be able to process fonts provided by this provider.`;
|
||||
if (unifontOptions?.throwOnError) throw new Error(message, { cause });
|
||||
console.error(message, cause);
|
||||
}
|
||||
if (!stack[provider._name]?.resolveFont) delete stack[provider._name];
|
||||
}));
|
||||
const allProviders = Object.keys(stack);
|
||||
async function resolveFont(fontFamily, options = {}, providers = allProviders) {
|
||||
const mergedOptions = {
|
||||
...defaultResolveOptions,
|
||||
...options
|
||||
};
|
||||
for (const id of providers) {
|
||||
const provider = stack[id];
|
||||
try {
|
||||
const result = await provider?.resolveFont(fontFamily, {
|
||||
...mergedOptions,
|
||||
options: mergedOptions.options?.[id]
|
||||
});
|
||||
if (result) return {
|
||||
provider: id,
|
||||
...result
|
||||
};
|
||||
} catch (cause) {
|
||||
const message = `Could not resolve font face for \`${fontFamily}\` from \`${id}\` provider.`;
|
||||
if (unifontOptions?.throwOnError) throw new Error(message, { cause });
|
||||
console.error(message, cause);
|
||||
}
|
||||
}
|
||||
return { fonts: [] };
|
||||
}
|
||||
async function listFonts(providers = allProviders) {
|
||||
let names;
|
||||
for (const id of providers) {
|
||||
const provider = stack[id];
|
||||
try {
|
||||
const result = await provider?.listFonts?.();
|
||||
if (result) {
|
||||
names ??= [];
|
||||
names.push(...result);
|
||||
}
|
||||
} catch (cause) {
|
||||
const message = `Could not list names from \`${id}\` provider.`;
|
||||
if (unifontOptions?.throwOnError) throw new Error(message, { cause });
|
||||
console.error(message, cause);
|
||||
}
|
||||
}
|
||||
return names;
|
||||
}
|
||||
return {
|
||||
resolveFont,
|
||||
listFonts
|
||||
};
|
||||
}
|
||||
|
||||
//#endregion
|
||||
export { createUnifont, defaultResolveOptions, defineFontProvider, providers_exports as providers };
|
||||
Reference in New Issue
Block a user