mirror of
https://github.com/withastro/astro.git
synced 2025-03-10 23:01:26 -05:00
feat(fonts): fallbacks (#13331)
* feat(fonts): fallbacks * feat: local * fix: test * feat: isGenericFontFamily test * feat: generateFallbackCSS test * feat: docs * feat: simplify * fix * feat: improve schema * Discard changes to examples/basics/astro.config.mjs * feat: address reviews
This commit is contained in:
parent
08eff8cd9e
commit
4eef143486
10 changed files with 532 additions and 42 deletions
|
@ -145,6 +145,7 @@
|
|||
"esbuild": "^0.25.0",
|
||||
"estree-walker": "^3.0.3",
|
||||
"flattie": "^1.1.1",
|
||||
"fontaine": "^0.5.0",
|
||||
"github-slugger": "^2.0.0",
|
||||
"html-escaper": "3.0.3",
|
||||
"http-cache-semantics": "^4.1.1",
|
||||
|
|
|
@ -8,7 +8,6 @@ export const DEFAULTS: ResolveFontOptions = {
|
|||
weights: ['400'],
|
||||
styles: ['normal', 'italic'],
|
||||
subsets: ['cyrillic-ext', 'cyrillic', 'greek-ext', 'greek', 'vietnamese', 'latin-ext', 'latin'],
|
||||
fallbacks: undefined,
|
||||
};
|
||||
|
||||
export const VIRTUAL_MODULE_ID = 'virtual:astro:assets/fonts/internal';
|
||||
|
@ -19,3 +18,20 @@ export const URL_PREFIX = '/_astro/fonts/';
|
|||
export const CACHE_DIR = './fonts/';
|
||||
|
||||
export const FONT_TYPES = ['woff2', 'woff', 'otf', 'ttf', 'eot'] as const;
|
||||
|
||||
// Source: https://github.com/nuxt/fonts/blob/3a3eb6dfecc472242b3011b25f3fcbae237d0acc/src/module.ts#L55-L75
|
||||
export const DEFAULT_FALLBACKS: Record<string, Array<string>> = {
|
||||
serif: ['Times New Roman'],
|
||||
'sans-serif': ['Arial'],
|
||||
monospace: ['Courier New'],
|
||||
cursive: [],
|
||||
fantasy: [],
|
||||
'system-ui': ['BlinkMacSystemFont', 'Segoe UI', 'Roboto', 'Helvetica Neue', 'Arial'],
|
||||
'ui-serif': ['Times New Roman'],
|
||||
'ui-sans-serif': ['Arial'],
|
||||
'ui-monospace': ['Courier New'],
|
||||
'ui-rounded': [],
|
||||
emoji: [],
|
||||
math: [],
|
||||
fangsong: [],
|
||||
};
|
||||
|
|
|
@ -28,15 +28,18 @@ export function resolveLocalFont(
|
|||
for (const src of family.src) {
|
||||
for (const weight of src.weights ?? DEFAULTS.weights) {
|
||||
for (const style of src.styles ?? DEFAULTS.styles) {
|
||||
// TODO: handle fallbacks?
|
||||
// TODO: handle subset
|
||||
fonts.push({
|
||||
weight,
|
||||
style,
|
||||
src: src.paths.map((path) => ({
|
||||
url: proxyURL(fileURLToPath(new URL(path, root))),
|
||||
format: extractFontType(path),
|
||||
})),
|
||||
src: src.paths.map((path) => {
|
||||
const originalURL = fileURLToPath(new URL(path, root));
|
||||
return {
|
||||
originalURL,
|
||||
url: proxyURL(originalURL),
|
||||
format: extractFontType(path),
|
||||
};
|
||||
}),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,9 +25,9 @@ interface FontFamilyAttributes extends Partial<ResolveFontOptions> {
|
|||
provider: string;
|
||||
}
|
||||
|
||||
export interface LocalFontFamily extends Pick<FontFamilyAttributes, 'name'> {
|
||||
export interface LocalFontFamily extends Pick<FontFamilyAttributes, 'name' | 'fallbacks'> {
|
||||
provider: LocalProviderName;
|
||||
src: Array<Partial<ResolveFontOptions> & { paths: Array<string> }>;
|
||||
src: Array<Partial<Omit<ResolveFontOptions, 'fallbacks'>> & { paths: Array<string> }>;
|
||||
}
|
||||
|
||||
interface CommonFontFamily<TProvider extends string>
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import type * as unifont from 'unifont';
|
||||
import type { FontType } from './types.js';
|
||||
import { extname } from 'node:path';
|
||||
import { FONT_TYPES } from './constants.js';
|
||||
import { DEFAULT_FALLBACKS, FONT_TYPES } from './constants.js';
|
||||
import type { Storage } from 'unstorage';
|
||||
import type * as fontaine from 'fontaine';
|
||||
|
||||
// TODO: expose all relevant options in config
|
||||
// Source: https://github.com/nuxt/fonts/blob/main/src/css/render.ts#L7-L21
|
||||
|
@ -81,7 +82,7 @@ export interface ProxyURLOptions {
|
|||
value: string;
|
||||
/**
|
||||
* Specifies how the hash is computed. Can be based on the value,
|
||||
* a specific string for testing etc
|
||||
* a specific string for testing etc
|
||||
*/
|
||||
hashString: (value: string) => string;
|
||||
/**
|
||||
|
@ -111,3 +112,76 @@ export function proxyURL({ value, hashString, collect }: ProxyURLOptions): strin
|
|||
// Now that we collected the original url, we return our proxy so the consumer can override it
|
||||
return url;
|
||||
}
|
||||
|
||||
export function isGenericFontFamily(str: string): str is keyof typeof DEFAULT_FALLBACKS {
|
||||
return Object.keys(DEFAULT_FALLBACKS).includes(str);
|
||||
}
|
||||
|
||||
type FontFaceMetrics = Parameters<typeof fontaine.generateFontFace>[0];
|
||||
|
||||
/**
|
||||
* Generates CSS for a given family fallbacks if possible.
|
||||
*
|
||||
* It works by trying to get metrics (using fontaine) of the provided font family.
|
||||
* If some can be computed, they will be applied to the eligible fallbacks to match
|
||||
* the original font shape as close as possible.
|
||||
*/
|
||||
export async function generateFallbacksCSS({
|
||||
family,
|
||||
fallbacks: _fallbacks,
|
||||
fontURL,
|
||||
getMetricsForFamily,
|
||||
// eslint-disable-next-line @typescript-eslint/no-shadow
|
||||
generateFontFace,
|
||||
}: {
|
||||
/** The family name */
|
||||
family: string;
|
||||
/** The family fallbacks */
|
||||
fallbacks: Array<string>;
|
||||
/** A remote url or local filepath to a font file. Used if metrics can't be resolved purely from the family name */
|
||||
fontURL: string | null;
|
||||
getMetricsForFamily: (family: string, fontURL: string | null) => Promise<null | FontFaceMetrics>;
|
||||
generateFontFace: typeof fontaine.generateFontFace;
|
||||
}): Promise<null | { css: string; fallbacks: Array<string> }> {
|
||||
// We avoid mutating the original array
|
||||
let fallbacks = [..._fallbacks];
|
||||
if (fallbacks.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let css = '';
|
||||
|
||||
// The last element of the fallbacks is usually a generic family name (eg. serif)
|
||||
const lastFallback = fallbacks[fallbacks.length - 1];
|
||||
// If it's not a generic family name, we can't infer local fonts to be used as fallbacks
|
||||
if (!isGenericFontFamily(lastFallback)) {
|
||||
return { css, fallbacks };
|
||||
}
|
||||
|
||||
// If it's a generic family name, we get the associated local fonts (eg. Arial)
|
||||
const localFonts = DEFAULT_FALLBACKS[lastFallback];
|
||||
// Some generic families do not have associated local fonts so we abort early
|
||||
if (localFonts.length === 0) {
|
||||
return { css, fallbacks };
|
||||
}
|
||||
|
||||
const metrics = await getMetricsForFamily(family, fontURL);
|
||||
if (!metrics) {
|
||||
// If there are no metrics, we can't generate useful fallbacks
|
||||
return { css, fallbacks };
|
||||
}
|
||||
|
||||
// We prepend the fallbacks with the local fonts and we dedupe in case a local font is already provided
|
||||
fallbacks = [...new Set([...localFonts, ...fallbacks])];
|
||||
|
||||
for (const fallback of localFonts) {
|
||||
css += generateFontFace(metrics, {
|
||||
font: fallback,
|
||||
// TODO: support family.as
|
||||
name: `${family} fallback: ${fallback}`,
|
||||
metrics: (await getMetricsForFamily(fallback, null)) ?? undefined,
|
||||
});
|
||||
}
|
||||
|
||||
return { css, fallbacks };
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ import {
|
|||
proxyURL,
|
||||
extractFontType,
|
||||
type ProxyURLOptions,
|
||||
generateFallbacksCSS,
|
||||
} from './utils.js';
|
||||
import {
|
||||
DEFAULTS,
|
||||
|
@ -31,6 +32,7 @@ import { readFile } from 'node:fs/promises';
|
|||
import { createStorage } from 'unstorage';
|
||||
import fsLiteDriver from 'unstorage/drivers/fs-lite';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import * as fontaine from 'fontaine';
|
||||
|
||||
interface Options {
|
||||
settings: AstroSettings;
|
||||
|
@ -153,6 +155,7 @@ export function fontsPlugin({ settings, sync, logger }: Options): Plugin {
|
|||
let isBuild: boolean;
|
||||
let cache: CacheHandler | null = null;
|
||||
|
||||
// TODO: refactor to allow testing
|
||||
async function initialize({ resolveMod, base }: { resolveMod: ResolveMod; base: URL }) {
|
||||
const { h64ToString } = await xxhash();
|
||||
|
||||
|
@ -194,19 +197,19 @@ export function fontsPlugin({ settings, sync, logger }: Options): Plugin {
|
|||
return url;
|
||||
};
|
||||
|
||||
// TODO: investigate using fontaine for fallbacks
|
||||
// TODO: refactor to avoid repetition
|
||||
for (const family of families) {
|
||||
// Reset
|
||||
preloadData.length = 0;
|
||||
css = '';
|
||||
|
||||
if (family.provider === LOCAL_PROVIDER_NAME) {
|
||||
const { fonts, fallbacks } = resolveLocalFont(family, {
|
||||
const { fonts } = resolveLocalFont(family, {
|
||||
proxyURL: (value) => {
|
||||
return proxyURL({
|
||||
value,
|
||||
// We hash based on the filepath and the contents, since the user could replace
|
||||
// a given font file with completely different contents.
|
||||
// a given font file with completely different contents.
|
||||
hashString: (v) => {
|
||||
let content: string;
|
||||
try {
|
||||
|
@ -224,15 +227,39 @@ export function fontsPlugin({ settings, sync, logger }: Options): Plugin {
|
|||
for (const data of fonts) {
|
||||
css += generateFontFace(family.name, data);
|
||||
}
|
||||
const urls = fonts
|
||||
.flatMap((font) => font.src.map((src) => ('originalURL' in src ? src.originalURL : null)))
|
||||
.filter(Boolean);
|
||||
|
||||
const fallbackData = await generateFallbacksCSS({
|
||||
family: family.name,
|
||||
fallbacks: family.fallbacks ?? [],
|
||||
fontURL: urls.at(0) ?? null,
|
||||
getMetricsForFamily: async (name, fontURL) => {
|
||||
let metrics = await fontaine.getMetricsForFamily(name);
|
||||
if (fontURL && !metrics) {
|
||||
// TODO: investigate in using capsize directly (fromBlob) to be able to cache
|
||||
metrics = await fontaine.readMetrics(fontURL);
|
||||
}
|
||||
return metrics;
|
||||
},
|
||||
generateFontFace: fontaine.generateFontFace,
|
||||
});
|
||||
|
||||
if (fallbackData) {
|
||||
css += fallbackData.css;
|
||||
// TODO: generate css var
|
||||
}
|
||||
} else {
|
||||
const { fonts, fallbacks } = await resolveFont(
|
||||
const { fonts } = 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,
|
||||
// No default fallback to be used here
|
||||
fallbacks: family.fallbacks,
|
||||
},
|
||||
// By default, fontaine goes through all providers. We use a different approach
|
||||
// where we specify a provider per font (default to google)
|
||||
|
@ -244,6 +271,7 @@ export function fontsPlugin({ settings, sync, logger }: Options): Plugin {
|
|||
if ('name' in source) {
|
||||
continue;
|
||||
}
|
||||
source.originalURL = source.url;
|
||||
source.url = proxyURL({
|
||||
value: source.url,
|
||||
// We only use the url for hashing since the service returns urls with a hash already
|
||||
|
@ -254,6 +282,30 @@ export function fontsPlugin({ settings, sync, logger }: Options): Plugin {
|
|||
// TODO: support optional as prop
|
||||
css += generateFontFace(family.name, data);
|
||||
}
|
||||
|
||||
const urls = fonts
|
||||
.map((font) => font.src.map((src) => ('originalURL' in src ? src.originalURL : null)))
|
||||
.flat()
|
||||
.filter((url) => typeof url === 'string');
|
||||
|
||||
const fallbackData = await generateFallbacksCSS({
|
||||
family: family.name,
|
||||
fallbacks: family.fallbacks ?? [],
|
||||
fontURL: urls.at(0) ?? null,
|
||||
getMetricsForFamily: async (name, fontURL) => {
|
||||
let metrics = await fontaine.getMetricsForFamily(name);
|
||||
if (fontURL && !metrics) {
|
||||
metrics = await fontaine.readMetrics(fontURL);
|
||||
}
|
||||
return metrics;
|
||||
},
|
||||
generateFontFace: fontaine.generateFontFace,
|
||||
});
|
||||
|
||||
if (fallbackData) {
|
||||
css += fallbackData.css;
|
||||
// TODO: generate css var
|
||||
}
|
||||
}
|
||||
resolvedMap.set(family.name, { preloadData: [...preloadData], css });
|
||||
}
|
||||
|
@ -285,7 +337,7 @@ export function fontsPlugin({ settings, sync, logger }: Options): Plugin {
|
|||
// as well as local paths for the local provider. We filter them to only keep the filepaths
|
||||
paths: [...hashToUrlMap!.values()].filter((url) => isAbsolute(url)),
|
||||
// Whenever a local font file is updated, we restart the server so the user always has an up to date
|
||||
// version of the font file
|
||||
// version of the font file
|
||||
update: () => {
|
||||
logger.info('assets', 'Font file updated');
|
||||
server.restart();
|
||||
|
@ -356,18 +408,20 @@ export function fontsPlugin({ settings, sync, logger }: Options): Plugin {
|
|||
} catch (e) {
|
||||
throw new AstroError(AstroErrorData.UnknownFilesystemError, { cause: e });
|
||||
}
|
||||
await Promise.all(
|
||||
Array.from(hashToUrlMap!.entries()).map(async ([hash, url]) => {
|
||||
logManager.add(hash);
|
||||
const { cached, data } = await cache!(hash, () => fetchFont(url));
|
||||
logManager.remove(hash, cached);
|
||||
try {
|
||||
writeFileSync(new URL(hash, fontsDir), data);
|
||||
} catch (e) {
|
||||
throw new AstroError(AstroErrorData.UnknownFilesystemError, { cause: e });
|
||||
}
|
||||
}),
|
||||
);
|
||||
if (hashToUrlMap) {
|
||||
await Promise.all(
|
||||
Array.from(hashToUrlMap.entries()).map(async ([hash, url]) => {
|
||||
logManager.add(hash);
|
||||
const { cached, data } = await cache!(hash, () => fetchFont(url));
|
||||
logManager.remove(hash, cached);
|
||||
try {
|
||||
writeFileSync(new URL(hash, fontsDir), data);
|
||||
} catch (e) {
|
||||
throw new AstroError(AstroErrorData.UnknownFilesystemError, { cause: e });
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
hashToUrlMap = null;
|
||||
cache = null;
|
||||
|
|
|
@ -600,6 +600,8 @@ export const AstroConfigSchema = z.object({
|
|||
}
|
||||
return svgConfig;
|
||||
}),
|
||||
|
||||
// TODO: properly test everything
|
||||
fonts: z
|
||||
.object({
|
||||
providers: z
|
||||
|
@ -635,10 +637,11 @@ export const AstroConfigSchema = z.object({
|
|||
.object({
|
||||
paths: z.array(z.string()).nonempty(),
|
||||
})
|
||||
.merge(resolveFontOptionsSchema.partial())
|
||||
.merge(resolveFontOptionsSchema.omit({ fallbacks: true }).partial())
|
||||
.strict(),
|
||||
),
|
||||
})
|
||||
.merge(resolveFontOptionsSchema.pick({ fallbacks: true }).partial())
|
||||
.strict(),
|
||||
z
|
||||
.object({
|
||||
|
|
|
@ -106,16 +106,32 @@ describe('fonts providers', () => {
|
|||
weight: '400',
|
||||
style: 'normal',
|
||||
src: [
|
||||
{ url: '/_astro/fonts/foo.woff2', format: 'woff2' },
|
||||
{ url: '/_astro/fonts/foo.ttf', format: 'ttf' },
|
||||
{
|
||||
originalURL: fileURLToPath(new URL('./src/fonts/foo.woff2', import.meta.url)),
|
||||
url: '/_astro/fonts/foo.woff2',
|
||||
format: 'woff2',
|
||||
},
|
||||
{
|
||||
originalURL: fileURLToPath(new URL('./src/fonts/foo.ttf', import.meta.url)),
|
||||
url: '/_astro/fonts/foo.ttf',
|
||||
format: 'ttf',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
weight: '400',
|
||||
style: 'italic',
|
||||
src: [
|
||||
{ url: '/_astro/fonts/foo.woff2', format: 'woff2' },
|
||||
{ url: '/_astro/fonts/foo.ttf', format: 'ttf' },
|
||||
{
|
||||
originalURL: fileURLToPath(new URL('./src/fonts/foo.woff2', import.meta.url)),
|
||||
url: '/_astro/fonts/foo.woff2',
|
||||
format: 'woff2',
|
||||
},
|
||||
{
|
||||
originalURL: fileURLToPath(new URL('./src/fonts/foo.ttf', import.meta.url)),
|
||||
url: '/_astro/fonts/foo.ttf',
|
||||
format: 'ttf',
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
@ -143,12 +159,24 @@ describe('fonts providers', () => {
|
|||
{
|
||||
weight: '600',
|
||||
style: 'oblique',
|
||||
src: [{ url: '/_astro/fonts/bar.eot', format: 'eot' }],
|
||||
src: [
|
||||
{
|
||||
originalURL: fileURLToPath(new URL('./src/fonts/bar.eot', import.meta.url)),
|
||||
url: '/_astro/fonts/bar.eot',
|
||||
format: 'eot',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
weight: '700',
|
||||
style: 'oblique',
|
||||
src: [{ url: '/_astro/fonts/bar.eot', format: 'eot' }],
|
||||
src: [
|
||||
{
|
||||
originalURL: fileURLToPath(new URL('./src/fonts/bar.eot', import.meta.url)),
|
||||
url: '/_astro/fonts/bar.eot',
|
||||
format: 'eot',
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
assert.deepStrictEqual(values, [fileURLToPath(new URL('./src/fonts/bar.eot', root))]);
|
||||
|
|
|
@ -6,6 +6,8 @@ import {
|
|||
extractFontType,
|
||||
createCache,
|
||||
proxyURL,
|
||||
isGenericFontFamily,
|
||||
generateFallbacksCSS,
|
||||
} from '../../../../dist/assets/fonts/utils.js';
|
||||
|
||||
function createSpyCache() {
|
||||
|
@ -143,4 +145,156 @@ describe('fonts utils', () => {
|
|||
value: '/home/documents/project/font.ttf',
|
||||
});
|
||||
});
|
||||
|
||||
it('isGenericFontFamily()', () => {
|
||||
assert.equal(isGenericFontFamily('serif'), true);
|
||||
assert.equal(isGenericFontFamily('sans-serif'), true);
|
||||
assert.equal(isGenericFontFamily('monospace'), true);
|
||||
assert.equal(isGenericFontFamily('cursive'), true);
|
||||
assert.equal(isGenericFontFamily('fantasy'), true);
|
||||
assert.equal(isGenericFontFamily('system-ui'), true);
|
||||
assert.equal(isGenericFontFamily('ui-serif'), true);
|
||||
assert.equal(isGenericFontFamily('ui-sans-serif'), true);
|
||||
assert.equal(isGenericFontFamily('ui-monospace'), true);
|
||||
assert.equal(isGenericFontFamily('ui-rounded'), true);
|
||||
assert.equal(isGenericFontFamily('emoji'), true);
|
||||
assert.equal(isGenericFontFamily('math'), true);
|
||||
assert.equal(isGenericFontFamily('fangsong'), true);
|
||||
assert.equal(isGenericFontFamily(''), false);
|
||||
});
|
||||
|
||||
describe('generateFallbacksCSS()', () => {
|
||||
it('should return null if there are no fallbacks', async () => {
|
||||
assert.equal(
|
||||
await generateFallbacksCSS({
|
||||
family: 'Roboto',
|
||||
fallbacks: [],
|
||||
fontURL: null,
|
||||
getMetricsForFamily: async () => null,
|
||||
generateFontFace: () => '',
|
||||
}),
|
||||
null,
|
||||
);
|
||||
});
|
||||
|
||||
it('should return fallbacks if there are no metrics', async () => {
|
||||
assert.deepStrictEqual(
|
||||
await generateFallbacksCSS({
|
||||
family: 'Roboto',
|
||||
fallbacks: ['foo'],
|
||||
fontURL: null,
|
||||
getMetricsForFamily: async () => null,
|
||||
generateFontFace: () => '',
|
||||
}),
|
||||
{
|
||||
css: '',
|
||||
fallbacks: ['foo'],
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it('should return fallbacks if there are metrics but no generic font family', async () => {
|
||||
assert.deepStrictEqual(
|
||||
await generateFallbacksCSS({
|
||||
family: 'Roboto',
|
||||
fallbacks: ['foo'],
|
||||
fontURL: null,
|
||||
getMetricsForFamily: async () => ({
|
||||
ascent: 0,
|
||||
descent: 0,
|
||||
lineGap: 0,
|
||||
unitsPerEm: 0,
|
||||
xWidthAvg: 0,
|
||||
}),
|
||||
generateFontFace: () => '',
|
||||
}),
|
||||
{
|
||||
css: '',
|
||||
fallbacks: ['foo'],
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it('shold return fallbacks if the generic font family does not have fonts associated', async () => {
|
||||
assert.deepStrictEqual(
|
||||
await generateFallbacksCSS({
|
||||
family: 'Roboto',
|
||||
fallbacks: ['emoji'],
|
||||
fontURL: null,
|
||||
getMetricsForFamily: async () => ({
|
||||
ascent: 0,
|
||||
descent: 0,
|
||||
lineGap: 0,
|
||||
unitsPerEm: 0,
|
||||
xWidthAvg: 0,
|
||||
}),
|
||||
generateFontFace: () => '',
|
||||
}),
|
||||
{
|
||||
css: '',
|
||||
fallbacks: ['emoji'],
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it('resolves fallbacks correctly', async () => {
|
||||
assert.deepStrictEqual(
|
||||
await generateFallbacksCSS({
|
||||
family: 'Roboto',
|
||||
fallbacks: ['foo', 'bar'],
|
||||
fontURL: null,
|
||||
getMetricsForFamily: async () => ({
|
||||
ascent: 0,
|
||||
descent: 0,
|
||||
lineGap: 0,
|
||||
unitsPerEm: 0,
|
||||
xWidthAvg: 0,
|
||||
}),
|
||||
generateFontFace: (_metrics, fallback) => `[${fallback.font},${fallback.name}]`,
|
||||
}),
|
||||
{
|
||||
css: '',
|
||||
fallbacks: ['foo', 'bar'],
|
||||
},
|
||||
);
|
||||
assert.deepStrictEqual(
|
||||
await generateFallbacksCSS({
|
||||
family: 'Roboto',
|
||||
fallbacks: ['sans-serif', 'foo'],
|
||||
fontURL: null,
|
||||
getMetricsForFamily: async () => ({
|
||||
ascent: 0,
|
||||
descent: 0,
|
||||
lineGap: 0,
|
||||
unitsPerEm: 0,
|
||||
xWidthAvg: 0,
|
||||
}),
|
||||
generateFontFace: (_metrics, fallback) => `[${fallback.font},${fallback.name}]`,
|
||||
}),
|
||||
{
|
||||
css: '',
|
||||
fallbacks: ['sans-serif', 'foo'],
|
||||
},
|
||||
);
|
||||
assert.deepStrictEqual(
|
||||
await generateFallbacksCSS({
|
||||
family: 'Roboto',
|
||||
fallbacks: ['foo', 'sans-serif'],
|
||||
fontURL: null,
|
||||
getMetricsForFamily: async () => ({
|
||||
ascent: 0,
|
||||
descent: 0,
|
||||
lineGap: 0,
|
||||
unitsPerEm: 0,
|
||||
xWidthAvg: 0,
|
||||
}),
|
||||
generateFontFace: (_metrics, fallback) => `[${fallback.font},${fallback.name}]`,
|
||||
}),
|
||||
{
|
||||
css: `[Arial,Roboto fallback: Arial]`,
|
||||
fallbacks: ['Arial', 'foo', 'sans-serif'],
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
171
pnpm-lock.yaml
generated
171
pnpm-lock.yaml
generated
|
@ -538,6 +538,9 @@ importers:
|
|||
flattie:
|
||||
specifier: ^1.1.1
|
||||
version: 1.1.1
|
||||
fontaine:
|
||||
specifier: ^0.5.0
|
||||
version: 0.5.0
|
||||
github-slugger:
|
||||
specifier: ^2.0.0
|
||||
version: 2.0.0
|
||||
|
@ -6588,6 +6591,12 @@ packages:
|
|||
resolution: {integrity: sha512-v9f+ueUOKkZCDKiCm0yxKtYgYNLD9zlKarNux0NSXOvNm94QEYL3RlMpGKgD2hq44pbF2qWqEmHnCvmk56kPJw==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
'@capsizecss/metrics@2.2.0':
|
||||
resolution: {integrity: sha512-DkFIser1KbGxWyG2hhQQeCit72TnOQDx5pr9bkA7+XlIy7qv+4lYtslH3bidVxm2qkY2guAgypSIPYuQQuk70A==}
|
||||
|
||||
'@capsizecss/unpack@2.3.0':
|
||||
resolution: {integrity: sha512-qkf9IoFIVTOkkpr8oZtCNSmubyWFCuPU4EOWO6J/rFPP5Ks2b1k1EHDSQRLwfokh6nCd7mJgBT2lhcuDCE6w4w==}
|
||||
|
||||
'@changesets/apply-release-plan@7.0.8':
|
||||
resolution: {integrity: sha512-qjMUj4DYQ1Z6qHawsn7S71SujrExJ+nceyKKyI9iB+M5p9lCL55afuEd6uLBPRpLGWQwkwvWegDHtwHJb1UjpA==}
|
||||
|
||||
|
@ -8049,6 +8058,9 @@ packages:
|
|||
svelte: ^5.0.0
|
||||
vite: ^6.0.0
|
||||
|
||||
'@swc/helpers@0.5.15':
|
||||
resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==}
|
||||
|
||||
'@tailwindcss/node@4.0.6':
|
||||
resolution: {integrity: sha512-jb6E0WeSq7OQbVYcIJ6LxnZTeC4HjMvbzFBMCrQff4R50HBlo/obmYNk6V2GCUXDeqiXtvtrQgcIbT+/boB03Q==}
|
||||
|
||||
|
@ -8744,6 +8756,9 @@ packages:
|
|||
blake3-wasm@2.1.5:
|
||||
resolution: {integrity: sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==}
|
||||
|
||||
blob-to-buffer@1.2.9:
|
||||
resolution: {integrity: sha512-BF033y5fN6OCofD3vgHmNtwZWRcq9NLyyxyILx9hfMy1sXYy4ojFl765hJ2lP0YaN2fuxPaLO2Vzzoxy0FLFFA==}
|
||||
|
||||
body-parser@1.20.3:
|
||||
resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==}
|
||||
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
|
||||
|
@ -8765,6 +8780,9 @@ packages:
|
|||
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
brotli@1.3.3:
|
||||
resolution: {integrity: sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==}
|
||||
|
||||
browserslist@4.24.0:
|
||||
resolution: {integrity: sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A==}
|
||||
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
|
||||
|
@ -8902,6 +8920,10 @@ packages:
|
|||
resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
clone@2.1.2:
|
||||
resolution: {integrity: sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==}
|
||||
engines: {node: '>=0.8'}
|
||||
|
||||
clsx@2.1.1:
|
||||
resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
|
||||
engines: {node: '>=6'}
|
||||
|
@ -9004,6 +9026,9 @@ packages:
|
|||
cross-argv@2.0.0:
|
||||
resolution: {integrity: sha512-YIaY9TR5Nxeb8SMdtrU8asWVM4jqJDNDYlKV21LxtYcfNJhp1kEsgSa6qXwXgzN0WQWGODps0+TlGp2xQSHwOg==}
|
||||
|
||||
cross-fetch@3.2.0:
|
||||
resolution: {integrity: sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==}
|
||||
|
||||
cross-spawn@7.0.6:
|
||||
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
|
||||
engines: {node: '>= 8'}
|
||||
|
@ -9199,6 +9224,9 @@ packages:
|
|||
devlop@1.1.0:
|
||||
resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==}
|
||||
|
||||
dfa@1.2.0:
|
||||
resolution: {integrity: sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q==}
|
||||
|
||||
didyoumean@1.2.2:
|
||||
resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
|
||||
|
||||
|
@ -9667,6 +9695,12 @@ packages:
|
|||
debug:
|
||||
optional: true
|
||||
|
||||
fontaine@0.5.0:
|
||||
resolution: {integrity: sha512-vPDSWKhVAfTx4hRKT777+N6Szh2pAosAuzLpbppZ6O3UdD/1m6OlHjNcC3vIbgkRTIcLjzySLHXzPeLO2rE8cA==}
|
||||
|
||||
fontkit@2.0.4:
|
||||
resolution: {integrity: sha512-syetQadaUEDNdxdugga9CpEYVaQIxOwk7GlwZWWZ19//qW4zE5bknOKeMBDYAASwnpaSHKJITRLMF9m1fp3s6g==}
|
||||
|
||||
foreground-child@3.3.0:
|
||||
resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==}
|
||||
engines: {node: '>=14'}
|
||||
|
@ -10348,6 +10382,9 @@ packages:
|
|||
resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==}
|
||||
hasBin: true
|
||||
|
||||
magic-regexp@0.8.0:
|
||||
resolution: {integrity: sha512-lOSLWdE156csDYwCTIGiAymOLN7Epu/TU5e/oAnISZfU6qP+pgjkE+xbVjVn3yLPKN8n1G2yIAYTAM5KRk6/ow==}
|
||||
|
||||
magic-string@0.25.9:
|
||||
resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==}
|
||||
|
||||
|
@ -10912,6 +10949,9 @@ packages:
|
|||
package-manager-detector@0.2.8:
|
||||
resolution: {integrity: sha512-ts9KSdroZisdvKMWVAVCXiKqnqNfXz4+IbrBG8/BWx/TR5le+jfenvoBuIZ6UWM9nz47W7AbD9qYfAwfWMIwzA==}
|
||||
|
||||
pako@0.2.9:
|
||||
resolution: {integrity: sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==}
|
||||
|
||||
pako@1.0.11:
|
||||
resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==}
|
||||
|
||||
|
@ -11431,6 +11471,10 @@ packages:
|
|||
resolution: {integrity: sha512-sZuz1dYW/ZsfG17WSAG7eS85r5a0dDsvg+7BiiYR5o6lKCAtUrEwdmRmaGF6rwVj3LcmAeYkOWKEPlbPzN3Y3A==}
|
||||
engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
|
||||
|
||||
regexp-tree@0.1.27:
|
||||
resolution: {integrity: sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==}
|
||||
hasBin: true
|
||||
|
||||
rehype-autolink-headings@7.1.0:
|
||||
resolution: {integrity: sha512-rItO/pSdvnvsP4QRB1pmPiNHUskikqtPojZKJPPPAVx9Hj8i8TwMBhofrrAYRhYOOBZH9tgmG5lPqDLuIWPWmw==}
|
||||
|
||||
|
@ -11537,6 +11581,9 @@ packages:
|
|||
resolution: {integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
|
||||
restructure@3.0.2:
|
||||
resolution: {integrity: sha512-gSfoiOEA0VPE6Tukkrr7I0RBdE0s7H1eFCDBk05l1KIQT1UIKNc5JZy6jdyW6eYH3aR3g5b3PuL77rq0hvwtAw==}
|
||||
|
||||
retext-latin@4.0.0:
|
||||
resolution: {integrity: sha512-hv9woG7Fy0M9IlRQloq/N6atV82NxLGveq+3H2WOi79dtIYWN8OaxogDm77f8YnVXJL2VD3bbqowu5E3EMhBYA==}
|
||||
|
||||
|
@ -11972,6 +12019,9 @@ packages:
|
|||
resolution: {integrity: sha512-wMctrWD2HZZLuIlchlkE2dfXJh7J2KDI9Dwl+2abPYg0mswQHfOAyQW3jJg1pY5VfttSINZuKcXoB3FGypVklA==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
tiny-inflate@1.0.3:
|
||||
resolution: {integrity: sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==}
|
||||
|
||||
tinybench@2.9.0:
|
||||
resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==}
|
||||
|
||||
|
@ -12052,8 +12102,8 @@ packages:
|
|||
tslib@2.1.0:
|
||||
resolution: {integrity: sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==}
|
||||
|
||||
tslib@2.6.2:
|
||||
resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==}
|
||||
tslib@2.8.1:
|
||||
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
|
||||
|
||||
turbo-darwin-64@2.4.1:
|
||||
resolution: {integrity: sha512-oos3Gz5N6ol2/7+ys0wPENhl7ZzeVKIumn2BR7X2oE5dEPxNPDMOpKBwreU9ToCxM94e+uFTzKgjcUJpBqpTHA==}
|
||||
|
@ -12105,6 +12155,9 @@ packages:
|
|||
resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
type-level-regexp@0.1.17:
|
||||
resolution: {integrity: sha512-wTk4DH3cxwk196uGLK/E9pE45aLfeKJacKmcEgEOA/q5dnPGNxXt0cfYdFxb57L+sEpf1oJH4Dnx/pnRcku9jg==}
|
||||
|
||||
types-react-dom@19.0.0-alpha.3:
|
||||
resolution: {integrity: sha512-foCg3VSAoTLKBpU6FKgtHjOzqZVo7UVXfG/JnKM8imXq/+TvSGebj+KJlAVG6H1n+hiQtqpjHc+hk5FmZOJCqw==}
|
||||
|
||||
|
@ -12165,6 +12218,12 @@ packages:
|
|||
unenv@2.0.0-rc.1:
|
||||
resolution: {integrity: sha512-PU5fb40H8X149s117aB4ytbORcCvlASdtF97tfls4BPIyj4PeVxvpSuy1jAptqYHqB0vb2w2sHvzM0XWcp2OKg==}
|
||||
|
||||
unicode-properties@1.4.1:
|
||||
resolution: {integrity: sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==}
|
||||
|
||||
unicode-trie@2.0.0:
|
||||
resolution: {integrity: sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==}
|
||||
|
||||
unicorn-magic@0.3.0:
|
||||
resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==}
|
||||
engines: {node: '>=18'}
|
||||
|
@ -12242,6 +12301,10 @@ packages:
|
|||
resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
unplugin@1.16.1:
|
||||
resolution: {integrity: sha512-4/u/j4FrCKdi17jaxuJA0jClGxB1AvU2hw/IuayPc4ay1XGaJs/rbb4v5WKwAjNifjmXK9PIFyuPiaK8azyR9w==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
|
||||
unstorage@1.14.4:
|
||||
resolution: {integrity: sha512-1SYeamwuYeQJtJ/USE1x4l17LkmQBzg7deBJ+U9qOBoHo15d1cDxG4jM31zKRgF7pG0kirZy4wVMX6WL6Zoscg==}
|
||||
peerDependencies:
|
||||
|
@ -12615,6 +12678,9 @@ packages:
|
|||
resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
webpack-virtual-modules@0.6.2:
|
||||
resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==}
|
||||
|
||||
whatwg-encoding@3.1.1:
|
||||
resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==}
|
||||
engines: {node: '>=18'}
|
||||
|
@ -13186,6 +13252,16 @@ snapshots:
|
|||
dependencies:
|
||||
tar: 6.2.1
|
||||
|
||||
'@capsizecss/metrics@2.2.0': {}
|
||||
|
||||
'@capsizecss/unpack@2.3.0':
|
||||
dependencies:
|
||||
blob-to-buffer: 1.2.9
|
||||
cross-fetch: 3.2.0
|
||||
fontkit: 2.0.4
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
|
||||
'@changesets/apply-release-plan@7.0.8':
|
||||
dependencies:
|
||||
'@changesets/config': 3.0.5
|
||||
|
@ -13676,7 +13752,7 @@ snapshots:
|
|||
|
||||
'@emnapi/runtime@1.3.1':
|
||||
dependencies:
|
||||
tslib: 2.6.2
|
||||
tslib: 2.8.1
|
||||
optional: true
|
||||
|
||||
'@esbuild-plugins/node-globals-polyfill@0.2.3(esbuild@0.17.19)':
|
||||
|
@ -14499,6 +14575,10 @@ snapshots:
|
|||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@swc/helpers@0.5.15':
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
|
||||
'@tailwindcss/node@4.0.6':
|
||||
dependencies:
|
||||
enhanced-resolve: 5.18.1
|
||||
|
@ -15314,6 +15394,8 @@ snapshots:
|
|||
|
||||
blake3-wasm@2.1.5: {}
|
||||
|
||||
blob-to-buffer@1.2.9: {}
|
||||
|
||||
body-parser@1.20.3:
|
||||
dependencies:
|
||||
bytes: 3.1.2
|
||||
|
@ -15357,6 +15439,10 @@ snapshots:
|
|||
dependencies:
|
||||
fill-range: 7.1.1
|
||||
|
||||
brotli@1.3.3:
|
||||
dependencies:
|
||||
base64-js: 1.5.1
|
||||
|
||||
browserslist@4.24.0:
|
||||
dependencies:
|
||||
caniuse-lite: 1.0.30001667
|
||||
|
@ -15501,6 +15587,8 @@ snapshots:
|
|||
strip-ansi: 6.0.1
|
||||
wrap-ansi: 7.0.0
|
||||
|
||||
clone@2.1.2: {}
|
||||
|
||||
clsx@2.1.1: {}
|
||||
|
||||
collapse-white-space@2.1.0: {}
|
||||
|
@ -15576,6 +15664,12 @@ snapshots:
|
|||
|
||||
cross-argv@2.0.0: {}
|
||||
|
||||
cross-fetch@3.2.0:
|
||||
dependencies:
|
||||
node-fetch: 2.7.0
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
|
||||
cross-spawn@7.0.6:
|
||||
dependencies:
|
||||
path-key: 3.1.1
|
||||
|
@ -15725,6 +15819,8 @@ snapshots:
|
|||
dependencies:
|
||||
dequal: 2.0.3
|
||||
|
||||
dfa@1.2.0: {}
|
||||
|
||||
didyoumean@1.2.2: {}
|
||||
|
||||
diff@5.2.0: {}
|
||||
|
@ -16234,6 +16330,30 @@ snapshots:
|
|||
|
||||
follow-redirects@1.15.9: {}
|
||||
|
||||
fontaine@0.5.0:
|
||||
dependencies:
|
||||
'@capsizecss/metrics': 2.2.0
|
||||
'@capsizecss/unpack': 2.3.0
|
||||
magic-regexp: 0.8.0
|
||||
magic-string: 0.30.17
|
||||
pathe: 1.1.2
|
||||
ufo: 1.5.4
|
||||
unplugin: 1.16.1
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
|
||||
fontkit@2.0.4:
|
||||
dependencies:
|
||||
'@swc/helpers': 0.5.15
|
||||
brotli: 1.3.3
|
||||
clone: 2.1.2
|
||||
dfa: 1.2.0
|
||||
fast-deep-equal: 3.1.3
|
||||
restructure: 3.0.2
|
||||
tiny-inflate: 1.0.3
|
||||
unicode-properties: 1.4.1
|
||||
unicode-trie: 2.0.0
|
||||
|
||||
foreground-child@3.3.0:
|
||||
dependencies:
|
||||
cross-spawn: 7.0.6
|
||||
|
@ -17000,7 +17120,7 @@ snapshots:
|
|||
|
||||
lower-case@2.0.2:
|
||||
dependencies:
|
||||
tslib: 2.6.2
|
||||
tslib: 2.8.1
|
||||
|
||||
lru-cache@10.4.3: {}
|
||||
|
||||
|
@ -17010,6 +17130,16 @@ snapshots:
|
|||
|
||||
lz-string@1.5.0: {}
|
||||
|
||||
magic-regexp@0.8.0:
|
||||
dependencies:
|
||||
estree-walker: 3.0.3
|
||||
magic-string: 0.30.17
|
||||
mlly: 1.7.4
|
||||
regexp-tree: 0.1.27
|
||||
type-level-regexp: 0.1.17
|
||||
ufo: 1.5.4
|
||||
unplugin: 1.16.1
|
||||
|
||||
magic-string@0.25.9:
|
||||
dependencies:
|
||||
sourcemap-codec: 1.4.8
|
||||
|
@ -17654,7 +17784,7 @@ snapshots:
|
|||
no-case@3.0.4:
|
||||
dependencies:
|
||||
lower-case: 2.0.2
|
||||
tslib: 2.6.2
|
||||
tslib: 2.8.1
|
||||
|
||||
node-addon-api@7.1.1:
|
||||
optional: true
|
||||
|
@ -17829,6 +17959,8 @@ snapshots:
|
|||
|
||||
package-manager-detector@0.2.8: {}
|
||||
|
||||
pako@0.2.9: {}
|
||||
|
||||
pako@1.0.11: {}
|
||||
|
||||
parent-module@1.0.1:
|
||||
|
@ -17879,7 +18011,7 @@ snapshots:
|
|||
pascal-case@3.1.2:
|
||||
dependencies:
|
||||
no-case: 3.0.4
|
||||
tslib: 2.6.2
|
||||
tslib: 2.8.1
|
||||
|
||||
path-browserify@1.0.1: {}
|
||||
|
||||
|
@ -18381,6 +18513,8 @@ snapshots:
|
|||
'@eslint-community/regexpp': 4.12.1
|
||||
refa: 0.12.1
|
||||
|
||||
regexp-tree@0.1.27: {}
|
||||
|
||||
rehype-autolink-headings@7.1.0:
|
||||
dependencies:
|
||||
'@types/hast': 3.0.4
|
||||
|
@ -18571,6 +18705,8 @@ snapshots:
|
|||
onetime: 5.1.2
|
||||
signal-exit: 3.0.7
|
||||
|
||||
restructure@3.0.2: {}
|
||||
|
||||
retext-latin@4.0.0:
|
||||
dependencies:
|
||||
'@types/nlcst': 2.0.3
|
||||
|
@ -19145,6 +19281,8 @@ snapshots:
|
|||
|
||||
timestring@6.0.0: {}
|
||||
|
||||
tiny-inflate@1.0.3: {}
|
||||
|
||||
tinybench@2.9.0: {}
|
||||
|
||||
tinyexec@0.3.2: {}
|
||||
|
@ -19203,7 +19341,7 @@ snapshots:
|
|||
|
||||
tslib@2.1.0: {}
|
||||
|
||||
tslib@2.6.2: {}
|
||||
tslib@2.8.1: {}
|
||||
|
||||
turbo-darwin-64@2.4.1:
|
||||
optional: true
|
||||
|
@ -19245,6 +19383,8 @@ snapshots:
|
|||
media-typer: 0.3.0
|
||||
mime-types: 2.1.35
|
||||
|
||||
type-level-regexp@0.1.17: {}
|
||||
|
||||
types-react-dom@19.0.0-alpha.3:
|
||||
dependencies:
|
||||
'@types/react': 18.3.18
|
||||
|
@ -19307,6 +19447,16 @@ snapshots:
|
|||
pathe: 1.1.2
|
||||
ufo: 1.5.4
|
||||
|
||||
unicode-properties@1.4.1:
|
||||
dependencies:
|
||||
base64-js: 1.5.1
|
||||
unicode-trie: 2.0.0
|
||||
|
||||
unicode-trie@2.0.0:
|
||||
dependencies:
|
||||
pako: 0.2.9
|
||||
tiny-inflate: 1.0.3
|
||||
|
||||
unicorn-magic@0.3.0: {}
|
||||
|
||||
unified@11.0.5:
|
||||
|
@ -19408,6 +19558,11 @@ snapshots:
|
|||
|
||||
unpipe@1.0.0: {}
|
||||
|
||||
unplugin@1.16.1:
|
||||
dependencies:
|
||||
acorn: 8.14.0
|
||||
webpack-virtual-modules: 0.6.2
|
||||
|
||||
unstorage@1.14.4(@netlify/blobs@8.1.0):
|
||||
dependencies:
|
||||
anymatch: 3.1.3
|
||||
|
@ -19760,6 +19915,8 @@ snapshots:
|
|||
|
||||
webidl-conversions@7.0.0: {}
|
||||
|
||||
webpack-virtual-modules@0.6.2: {}
|
||||
|
||||
whatwg-encoding@3.1.1:
|
||||
dependencies:
|
||||
iconv-lite: 0.6.3
|
||||
|
|
Loading…
Add table
Reference in a new issue