Skip to content

Commit c8ac8e5

Browse files
chore(fonts): classes (#14920)
1 parent 33333e8 commit c8ac8e5

32 files changed

+1056
-840
lines changed

packages/astro/src/assets/fonts/definitions.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export interface LocalProviderUrlResolver {
2828
resolve: (input: string) => string;
2929
}
3030

31-
interface ProxyData {
31+
export interface ProxyData {
3232
weight: unifont.FontFaceData['weight'];
3333
style: unifont.FontFaceData['style'];
3434
subset: NonNullable<unifont.FontFaceData['meta']>['subset'];
@@ -46,7 +46,7 @@ export interface UrlProxy {
4646

4747
export interface UrlResolver {
4848
resolve: (hash: string) => string;
49-
getCspResources: () => Array<string>;
49+
readonly cspResources: Array<string>;
5050
}
5151

5252
export interface UrlProxyContentResolver {
@@ -112,3 +112,10 @@ export interface UrlProxyHashResolver {
112112
export interface StringMatcher {
113113
getClosestMatch: (target: string, candidates: Array<string>) => string;
114114
}
115+
116+
export interface Storage {
117+
getItem: (key: string) => Promise<any | null>;
118+
getItemRaw: (key: string) => Promise<Buffer | null>;
119+
setItem: (key: string, value: any) => Promise<void>;
120+
setItemRaw: (key: string, value: any) => Promise<void>;
121+
}
Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
import type { RemoteFontProviderModResolver } from '../definitions.js';
22

3-
export function createBuildRemoteFontProviderModResolver(): RemoteFontProviderModResolver {
4-
return {
5-
resolve(id) {
6-
return import(id);
7-
},
8-
};
3+
export class BuildRemoteFontProviderModResolver implements RemoteFontProviderModResolver {
4+
async resolve(id: string): Promise<any> {
5+
return await import(id);
6+
}
97
}
Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,35 @@
1-
import type { Hasher, UrlProxyContentResolver, UrlProxyHashResolver } from '../definitions.js';
1+
import type {
2+
Hasher,
3+
ProxyData,
4+
UrlProxyContentResolver,
5+
UrlProxyHashResolver,
6+
} from '../definitions.js';
7+
import type { FontType } from '../types.js';
28

3-
export function createBuildUrlProxyHashResolver({
4-
hasher,
5-
contentResolver,
6-
}: {
7-
hasher: Hasher;
8-
contentResolver: UrlProxyContentResolver;
9-
}): UrlProxyHashResolver {
10-
return {
11-
resolve({ originalUrl, type }) {
12-
return `${hasher.hashString(contentResolver.resolve(originalUrl))}.${type}`;
13-
},
14-
};
9+
export class BuildUrlProxyHashResolver implements UrlProxyHashResolver {
10+
readonly #hasher: Hasher;
11+
readonly #contentResolver: UrlProxyContentResolver;
12+
13+
constructor({
14+
hasher,
15+
contentResolver,
16+
}: {
17+
hasher: Hasher;
18+
contentResolver: UrlProxyContentResolver;
19+
}) {
20+
this.#hasher = hasher;
21+
this.#contentResolver = contentResolver;
22+
}
23+
24+
resolve({
25+
originalUrl,
26+
type,
27+
}: {
28+
originalUrl: string;
29+
type: FontType;
30+
cssVariable: string;
31+
data: ProxyData;
32+
}): string {
33+
return `${this.#hasher.hashString(this.#contentResolver.resolve(originalUrl))}.${type}`;
34+
}
1535
}

packages/astro/src/assets/fonts/infra/build-url-resolver.ts

Lines changed: 43 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4,38 +4,49 @@ import { getAssetsPrefix } from '../../utils/getAssetsPrefix.js';
44
import { createPlaceholderURL, stringifyPlaceholderURL } from '../../utils/url.js';
55
import type { UrlResolver } from '../definitions.js';
66

7-
export function createBuildUrlResolver({
8-
base,
9-
assetsPrefix,
10-
searchParams,
11-
}: {
12-
base: string;
13-
assetsPrefix: AssetsPrefix;
14-
searchParams: URLSearchParams;
15-
}): UrlResolver {
16-
const resources = new Set<string>();
17-
return {
18-
resolve(hash) {
19-
const prefix = assetsPrefix ? getAssetsPrefix(fileExtension(hash), assetsPrefix) : undefined;
20-
let urlPath: string;
21-
if (prefix) {
22-
resources.add(prefix);
23-
urlPath = joinPaths(prefix, base, hash);
24-
} else {
25-
resources.add("'self'");
26-
urlPath = prependForwardSlash(joinPaths(base, hash));
27-
}
7+
export class BuildUrlResolver implements UrlResolver {
8+
readonly #resources = new Set<string>();
9+
readonly #base: string;
10+
readonly #assetsPrefix: AssetsPrefix;
11+
readonly #searchParams: URLSearchParams;
2812

29-
// Create URL object and append searchParams if available (for adapter-level tracking like skew protection)
30-
const url = createPlaceholderURL(urlPath);
31-
searchParams.forEach((value, key) => {
32-
url.searchParams.set(key, value);
33-
});
13+
constructor({
14+
base,
15+
assetsPrefix,
16+
searchParams,
17+
}: {
18+
base: string;
19+
assetsPrefix: AssetsPrefix;
20+
searchParams: URLSearchParams;
21+
}) {
22+
this.#base = base;
23+
this.#assetsPrefix = assetsPrefix;
24+
this.#searchParams = searchParams;
25+
}
3426

35-
return stringifyPlaceholderURL(url);
36-
},
37-
getCspResources() {
38-
return Array.from(resources);
39-
},
40-
};
27+
resolve(hash: string): string {
28+
const prefix = this.#assetsPrefix
29+
? getAssetsPrefix(fileExtension(hash), this.#assetsPrefix)
30+
: undefined;
31+
let urlPath: string;
32+
if (prefix) {
33+
this.#resources.add(prefix);
34+
urlPath = joinPaths(prefix, this.#base, hash);
35+
} else {
36+
this.#resources.add("'self'");
37+
urlPath = prependForwardSlash(joinPaths(this.#base, hash));
38+
}
39+
40+
// Create URL object and append searchParams if available (for adapter-level tracking like skew protection)
41+
const url = createPlaceholderURL(urlPath);
42+
this.#searchParams.forEach((value, key) => {
43+
url.searchParams.set(key, value);
44+
});
45+
46+
return stringifyPlaceholderURL(url);
47+
}
48+
49+
get cspResources(): Array<string> {
50+
return Array.from(this.#resources);
51+
}
4152
}
Lines changed: 42 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,48 @@
11
import { isAbsolute } from 'node:path';
2-
import type { Storage } from 'unstorage';
32
import { AstroError, AstroErrorData } from '../../../core/errors/index.js';
4-
import type { FontFetcher } from '../definitions.js';
3+
import type { FontFetcher, Storage } from '../definitions.js';
4+
import type { FontFileData } from '../types.js';
55
import { cache } from '../utils.js';
66

7-
export function createCachedFontFetcher({
8-
storage,
9-
fetch,
10-
readFile,
11-
}: {
12-
storage: Storage;
13-
fetch: (url: string, init?: RequestInit) => Promise<Response>;
14-
readFile: (url: string) => Promise<Buffer>;
15-
}): FontFetcher {
16-
return {
17-
async fetch({ hash, url, init }) {
18-
return await cache(storage, hash, async () => {
19-
try {
20-
if (isAbsolute(url)) {
21-
return await readFile(url);
22-
}
23-
const response = await fetch(url, init ?? undefined);
24-
if (!response.ok) {
25-
throw new Error(`Response was not successful, received status code ${response.status}`);
26-
}
27-
return Buffer.from(await response.arrayBuffer());
28-
} catch (cause) {
29-
throw new AstroError(
30-
{
31-
...AstroErrorData.CannotFetchFontFile,
32-
message: AstroErrorData.CannotFetchFontFile.message(url),
33-
},
34-
{ cause },
35-
);
7+
export class CachedFontFetcher implements FontFetcher {
8+
readonly #storage: Storage;
9+
readonly #fetch: (url: string, init?: RequestInit) => Promise<Response>;
10+
readonly #readFile: (url: string) => Promise<Buffer>;
11+
12+
constructor({
13+
storage,
14+
fetch,
15+
readFile,
16+
}: {
17+
storage: Storage;
18+
fetch: (url: string, init?: RequestInit) => Promise<Response>;
19+
readFile: (url: string) => Promise<Buffer>;
20+
}) {
21+
this.#storage = storage;
22+
this.#fetch = fetch;
23+
this.#readFile = readFile;
24+
}
25+
26+
async fetch({ hash, url, init }: FontFileData): Promise<Buffer> {
27+
return await cache(this.#storage, hash, async () => {
28+
try {
29+
if (isAbsolute(url)) {
30+
return await this.#readFile(url);
31+
}
32+
const response = await this.#fetch(url, init ?? undefined);
33+
if (!response.ok) {
34+
throw new Error(`Response was not successful, received status code ${response.status}`);
3635
}
37-
});
38-
},
39-
};
36+
return Buffer.from(await response.arrayBuffer());
37+
} catch (cause) {
38+
throw new AstroError(
39+
{
40+
...AstroErrorData.CannotFetchFontFile,
41+
message: AstroErrorData.CannotFetchFontFile.message(url),
42+
},
43+
{ cause },
44+
);
45+
}
46+
});
47+
}
4048
}

0 commit comments

Comments
 (0)