Skip to content

Commit 79ef233

Browse files
committed
feat: ⚡ Download fonts on cache and put it on public folder
1 parent ab6c02f commit 79ef233

File tree

8 files changed

+487
-168
lines changed

8 files changed

+487
-168
lines changed

.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
node_modules
1+
node_modules
2+
**/_fonts

lib/cache.ts

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { createStorage } from "unstorage";
2+
import fsDriver from "unstorage/drivers/fs";
3+
4+
export const cacheBase = "node_modules/.cache/fontless/fonts/meta";
5+
6+
export const storage = createStorage({
7+
driver: fsDriver({ base: cacheBase }),
8+
});

lib/css/assets.ts

+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import fsp from "node:fs/promises";
2+
import { hash } from "ohash";
3+
import { extname, join } from "pathe";
4+
import { filename } from "pathe/utils";
5+
import { hasProtocol, joinRelativeURL, joinURL } from "ufo";
6+
import type { FontFaceData } from "unifont";
7+
import { storage } from "../cache";
8+
import type { Options, RawFontFaceData } from "../types";
9+
import { formatToExtension, parseFont } from "./render";
10+
11+
const renderedFontURLs = new Map<string, string>();
12+
13+
export async function setupPublicAssetStrategy(options: Options) {
14+
const { module } = options;
15+
16+
const assetsBaseURL = module.assets.prefix || "/fonts";
17+
18+
function normalizeFontData(
19+
faces: RawFontFaceData | FontFaceData[]
20+
): FontFaceData[] {
21+
const data: FontFaceData[] = [];
22+
for (const face of Array.isArray(faces) ? faces : [faces]) {
23+
data.push({
24+
...face,
25+
unicodeRange:
26+
face.unicodeRange === undefined || Array.isArray(face.unicodeRange)
27+
? face.unicodeRange
28+
: [face.unicodeRange],
29+
src: (Array.isArray(face.src) ? face.src : [face.src]).map((src) => {
30+
const source = typeof src === "string" ? parseFont(src) : src;
31+
if (
32+
"url" in source &&
33+
hasProtocol(source.url, { acceptRelative: true })
34+
) {
35+
source.url = source.url.replace(/^\/\//, "https://");
36+
const file = [
37+
// TODO: investigate why negative ignore pattern below is being ignored
38+
filename(source.url.replace(/\?.*/, "")).replace(/^-+/, ""),
39+
hash(source) +
40+
(extname(source.url) || formatToExtension(source.format) || ""),
41+
]
42+
.filter(Boolean)
43+
.join("-");
44+
45+
renderedFontURLs.set(file, source.url);
46+
source.originalURL = source.url;
47+
48+
source.url = options.fontless.dev
49+
? joinRelativeURL(assetsBaseURL, file)
50+
: joinURL(assetsBaseURL, file);
51+
}
52+
53+
return source;
54+
}),
55+
});
56+
}
57+
return data;
58+
}
59+
60+
const cacheDir = join(options.fontless.buildDir, "cache", "fonts");
61+
62+
if (!options.fontless.dev) {
63+
await fsp.mkdir(cacheDir, { recursive: true });
64+
}
65+
66+
const rollupBefore = async () => {
67+
if (options.fontless.dev) {
68+
await fsp.mkdir(join(options.fontless.baseURL, assetsBaseURL), {
69+
recursive: true,
70+
});
71+
}
72+
for (const [filename, url] of renderedFontURLs) {
73+
const key = "data:fonts:" + filename;
74+
// Use storage to cache the font data between builds
75+
let res = await storage.getItemRaw(key);
76+
if (!res) {
77+
res = await fetch(url)
78+
.then((r) => r.arrayBuffer())
79+
.then((r) => Buffer.from(r));
80+
81+
await storage.setItemRaw(key, res);
82+
}
83+
84+
await fsp.writeFile(join(cacheDir, filename), res);
85+
86+
if (options.fontless.dev) {
87+
await fsp.writeFile(
88+
joinRelativeURL(options.fontless.baseURL, assetsBaseURL, filename),
89+
res
90+
);
91+
}
92+
}
93+
};
94+
95+
options.hooks["rollup:before"] = rollupBefore;
96+
97+
return {
98+
normalizeFontData,
99+
};
100+
}

0 commit comments

Comments
 (0)