vite-font-unicode-range
Version:
[](https://www.npmjs.com/package/vite-font-unicode-range) [](https://github.com/yourusername/vite-font-unicode-range/blob/m
146 lines (144 loc) • 4.49 kB
JavaScript
import { createFilter as E } from "vite";
import { parse as b } from "css-tree";
import k from "subset-font";
import P from "path";
import m from "fs";
const w = [];
function G(e = {}) {
const {
include: n = /\.(css|scss|sass|less|styl|stylus)$/,
exclude: a,
fontExtensions: t = /\.(woff2?|ttf|eot|otf)$/i
} = e, r = E(n, a), o = /* @__PURE__ */ new Map();
let i = "", F = [];
const S = /* @__PURE__ */ new Set();
return {
name: "vite-plugin-font-unicode-range",
enforce: "pre",
apply: "build",
configResolved(s) {
i = s.cacheDir, F = s.resolve.alias;
},
generateBundle() {
if (!w.length) return;
const s = Math.max(...w.map(({ fileName: c }) => c.length));
console.log(
`
✨ [vite-plugin-font-unicode-range] - optimized:
` + w.map(
({ fileName: c, rate: x }) => `\x1B[34m${c.padEnd(s + 4)}\x1B[0m\x1B[90m-${x}\x1B[0m`
).join(`
`)
);
},
async resolveId(s) {
if (S.has(s) || !r(s) || /node_modules/.test(s))
return;
S.add(s);
const c = await this.resolve(s);
if (!c) return;
s = c.id;
const x = await m.promises.readFile(s, "utf8");
try {
const y = b(x);
await Promise.all(
y.children.map(async (f) => {
if (f.type === "Atrule" && f.name === "font-face") {
const U = $(f, "font-family"), A = $(f, "src"), B = $(f, "unicode-range");
if (!U || !A || !B) return;
const D = j(B);
if (D.length === 0) return;
await Promise.all(
R(A).map(async (l) => {
if (t.test(l) && !F.find((u) => u.find === l)) {
const u = await this.resolve(l);
if (!u) return;
const d = N(l);
let p = o.get(d);
if (!p) {
const g = await m.promises.readFile(u.id), z = K(u.id), h = await k(
g,
M(D),
{
targetFormat: z
}
), C = 100 - h.length * 100 / g.length;
m.existsSync(i) || await m.promises.mkdir(i);
const v = `${i}/subset-${d}`;
h.length < g.length ? (p = v, await m.promises.writeFile(v, h), o.set(d, v), w.push({
fileName: d,
rate: `${C.toFixed(0)}%`.padEnd(8) + `${I(g.length)} → ${I(h.length)}`
})) : p = "skip";
}
p !== "skip" && F.push({
find: l,
replacement: p
});
}
})
);
}
})
);
} catch (y) {
console.error("Error analyzing CSS:", y);
}
}
};
}
function I(e) {
return (e / 1024).toFixed(1) + "KB";
}
function R(e) {
return e.split(",").map((n) => n.trim());
}
function M(e) {
const n = [];
return e.forEach(({ start: a, end: t }) => {
for (let r = a; r <= t; r++)
n.push(r);
}), String.fromCharCode(...new Set(n));
}
function N(e) {
return (e.split("/").pop() || "").replace(/\..*?([a-z]+\d?)$/, ".$1");
}
function $(e, n) {
const a = Array.from(e.block?.children || []).find((t) => t.type === "Declaration" && t.property === n);
return a && // @ts-ignore
Array.from(a.value?.children || []).filter((t) => t.value).map((t) => t.value).join(" ") || null;
}
function j(e) {
const n = [], a = e.split(/\s*,\s*/);
for (const t of a)
if (t.includes("?")) {
const r = t.match(/U\+([0-9A-Fa-f?]+)/)?.[1] || "", o = parseInt(r.replace(/\?/g, "0"), 16), i = parseInt(r.replace(/\?/g, "F"), 16);
n.push({ start: o, end: i });
} else if (t.includes("-")) {
const r = t.match(/U\+([0-9A-Fa-f]+)-([0-9A-Fa-f]+)/);
r && n.push({
start: parseInt(r[1], 16),
end: parseInt(r[2], 16)
});
} else {
const r = t.match(/U\+([0-9A-Fa-f]+)/)?.[1] || "0", o = parseInt(r, 16);
n.push({ start: o, end: o });
}
return n;
}
function K(e) {
switch (P.extname(e).toLowerCase()) {
case ".woff":
return "woff";
case ".woff2":
return "woff2";
case ".sfnt":
return "sfnt";
case ".ttf":
return "truetype";
default:
return "woff2";
}
}
export {
G as default
};