unplugin-fonts
Version:
Universal Webfont loader
136 lines (135 loc) • 3.95 kB
JavaScript
import { basename, extname, join, relative } from "pathe";
import fg from "fast-glob";
//#region src/loaders/custom.ts
function resolveUserOption(options) {
let { families = [], preload = true, prefetchPrefix = "", prefetch = false, injectTo = "head-prepend", display = "auto", stripPrefix = "public/", linkFilter } = options;
if (!Array.isArray(families)) families = Object.entries(families).map(([name, family]) => Array.isArray(family) || typeof family === "string" ? {
name,
src: family
} : {
name,
...family
});
return {
families,
preload,
prefetchPrefix,
prefetch,
injectTo,
display,
stripPrefix,
linkFilter
};
}
function customVirtualModule(userOptions, root) {
const options = resolveUserOption(userOptions);
const css = [];
for (const family of options.families) {
const faces = resolveFontFiles(family, options, root);
for (const face of faces) css.push(generateFontCSS(face));
}
return css.join("\n");
}
function resolveFontFiles(family, options, root) {
const sources = Array.isArray(family.src) ? family.src : [family.src];
const facesMap = {};
for (const source of sources) {
const results = fg.sync(join(root, source), {
absolute: true,
cwd: root,
onlyFiles: true
});
for (const file of results) {
const ext = extname(file);
const basename$1 = basename(file, ext);
let format = "";
switch (ext) {
case ".woff":
format = "woff";
break;
case ".woff2":
format = "woff2";
break;
case ".ttf":
format = "truetype";
break;
case ".otf":
format = "opentype";
break;
case ".svg":
format = "svg";
break;
default: format = ext.replace(".", "");
}
facesMap[basename$1] ||= {
source,
name: family.name,
basename: basename$1,
weight: extractWeight(basename$1),
style: extractStyle(basename$1),
local: family.local,
display: options.display,
files: []
};
facesMap[basename$1].files.push({
src: file,
path: join("/", relative(root, file.replace(options.stripPrefix, ""))),
format,
ext
});
}
}
const faces = [];
for (const face of Object.values(facesMap)) {
if (!family.transform) {
faces.push(face);
continue;
}
const transformed = family.transform(face);
if (transformed) faces.push(transformed);
}
return faces;
}
function extractWeight(filename) {
if (!filename) return 400;
filename = filename.toLowerCase();
if (filename.includes("thin")) return 100;
if (filename.includes("extralight")) return 200;
if (filename.includes("ultralight")) return 200;
if (filename.includes("light")) return 300;
if (filename.includes("normal")) return 400;
if (filename.includes("medium")) return 500;
if (filename.includes("semibold")) return 600;
if (filename.includes("demibold")) return 600;
if (filename.includes("extrabold")) return 800;
if (filename.includes("ultrabold")) return 800;
if (filename.includes("bold")) return 700;
if (filename.includes("black")) return 900;
if (filename.includes("heavy")) return 900;
return 400;
}
function extractStyle(filename) {
if (!filename) return "normal";
filename = filename.toLowerCase();
if (filename.includes("normal")) return "normal";
if (filename.includes("italic")) return "italic";
if (filename.includes("oblique")) return "oblique";
return "normal";
}
function generateFontCSS(face) {
const srcs = face.files.map((file) => {
return `url('${file.path}') format('${file.format}')`;
}).join(",\n ");
const locals = (Array.isArray(face.local) ? face.local : [face.local]).filter(Boolean).map((x) => `local('${x}')`).join(", ");
return [
"@font-face {",
` font-family: '${face.name}';`,
` src: ${[srcs, locals].filter(Boolean).join(",")};`,
` font-weight: ${face.weight};`,
` font-style: ${face.style};`,
` font-display: ${face.display};`,
"}"
].join("\n");
}
//#endregion
export { customVirtualModule, resolveFontFiles, resolveUserOption };