UNPKG

vite-font-unicode-range

Version:

[![npm version](https://img.shields.io/npm/v/vite-font-unicode-range)](https://www.npmjs.com/package/vite-font-unicode-range) [![license](https://img.shields.io/npm/l/vite-font-unicode-range)](https://github.com/yourusername/vite-font-unicode-range/blob/m

146 lines (144 loc) 4.49 kB
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 };