Skip to content
5 changes: 5 additions & 0 deletions .changeset/cruel-pants-lead.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'astro': patch
---

Improves handling of font URLs not ending with a file extension when using the experimental fonts API
17 changes: 9 additions & 8 deletions packages/astro/src/assets/fonts/constants.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Defaults } from './types.js';
import type { Defaults, FontType } from './types.js';

export const LOCAL_PROVIDER_NAME = 'local';

Expand All @@ -19,13 +19,14 @@ export const URL_PREFIX = '/_astro/fonts/';
export const CACHE_DIR = './fonts/';

export const FONT_TYPES = ['woff2', 'woff', 'otf', 'ttf', 'eot'] as const;
export const FONT_FORMAT_MAP: Record<(typeof FONT_TYPES)[number], string> = {
woff2: 'woff2',
woff: 'woff',
otf: 'opentype',
ttf: 'truetype',
eot: 'embedded-opentype',
};

export const FONT_FORMATS: Array<{ type: FontType; format: string }> = [
{ type: 'woff2', format: 'woff2' },
{ type: 'woff', format: 'woff' },
{ type: 'otf', format: 'opentype' },
{ type: 'ttf', format: 'truetype' },
{ type: 'eot', format: 'embedded-opentype' },
];

export const GENERIC_FALLBACK_NAMES = [
'serif',
Expand Down
1 change: 1 addition & 0 deletions packages/astro/src/assets/fonts/definitions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export interface ErrorHandler {
export interface UrlProxy {
proxy: (
input: Pick<FontFileData, 'url' | 'init'> & {
type: FontType;
collectPreload: boolean;
data: Partial<unifont.FontFaceData>;
},
Expand Down
13 changes: 2 additions & 11 deletions packages/astro/src/assets/fonts/implementations/url-proxy.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,18 @@
import type {
DataCollector,
FontTypeExtractor,
Hasher,
UrlProxy,
UrlProxyContentResolver,
} from '../definitions.js';
import type { DataCollector, Hasher, UrlProxy, UrlProxyContentResolver } from '../definitions.js';

export function createUrlProxy({
base,
contentResolver,
hasher,
dataCollector,
fontTypeExtractor,
}: {
base: string;
contentResolver: UrlProxyContentResolver;
hasher: Hasher;
dataCollector: DataCollector;
fontTypeExtractor: FontTypeExtractor;
}): UrlProxy {
return {
proxy({ url: originalUrl, data, collectPreload, init }) {
const type = fontTypeExtractor.extract(originalUrl);
proxy({ url: originalUrl, type, data, collectPreload, init }) {
const hash = `${hasher.hashString(contentResolver.resolve(originalUrl))}.${type}`;
const url = base + hash;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import type * as unifont from 'unifont';
import type { UrlProxy } from '../definitions.js';
import type { FontTypeExtractor, UrlProxy } from '../definitions.js';
import { FONT_FORMATS } from '../constants.js';

export function normalizeRemoteFontFaces({
fonts,
urlProxy,
fontTypeExtractor,
}: {
fonts: Array<unifont.FontFaceData>;
urlProxy: UrlProxy;
fontTypeExtractor: FontTypeExtractor;
}): Array<unifont.FontFaceData> {
return (
fonts
Expand All @@ -31,6 +34,9 @@ export function normalizeRemoteFontFaces({
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,
Expand Down
2 changes: 1 addition & 1 deletion packages/astro/src/assets/fonts/orchestrate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ export async function orchestrate({
);
}
// The data returned by the remote provider contains original URLs. We proxy them.
fonts = normalizeRemoteFontFaces({ fonts: result.fonts, urlProxy });
fonts = normalizeRemoteFontFaces({ fonts: result.fonts, urlProxy, fontTypeExtractor });
}

for (const data of fonts) {
Expand Down
7 changes: 5 additions & 2 deletions packages/astro/src/assets/fonts/providers/local.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type * as unifont from 'unifont';
import { FONT_FORMAT_MAP } from '../constants.js';
import { FONT_FORMATS } from '../constants.js';
import type { FontFileReader, FontTypeExtractor, UrlProxy } from '../definitions.js';
import type { ResolvedLocalFontFamily } from '../types.js';

Expand Down Expand Up @@ -44,10 +44,13 @@ export function resolveLocalFont({
if (variant.style === undefined) 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,
Expand All @@ -57,7 +60,7 @@ export function resolveLocalFont({
},
init: null,
}),
format: FONT_FORMAT_MAP[fontTypeExtractor.extract(source.url)],
format: FONT_FORMATS.find((e) => e.type === type)?.format,
tech: source.tech,
};
});
Expand Down
1 change: 0 additions & 1 deletion packages/astro/src/assets/fonts/vite-plugin-fonts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,6 @@ export function fontsPlugin({ settings, sync, logger }: Options): Plugin {
contentResolver,
hasher,
dataCollector,
fontTypeExtractor: fontTypeExtractor!,
});
},
defaults: DEFAULTS,
Expand Down
Loading