UNPKG

vite-plugin-svg-symbols

Version:
302 lines (300 loc) 9.67 kB
import { join as A, extname as J, relative as x, sep as E, isAbsolute as Z, resolve as K, dirname as Q } from "node:path"; import { stat as H, readFile as tt, readdir as et, writeFile as st } from "node:fs/promises"; import { createHash as ot } from "node:crypto"; const nt = (e = [], r) => { const c = /* @__PURE__ */ new Map(), p = async (s) => { const m = await H(s); if (c.has(s)) { const [f, M] = c.get(s) || [0, []]; if (f === m.mtimeMs) { const [i] = M; return i; } } const u = await tt(s, { encoding: "utf8" }), h = await (typeof r == "function" ? r(u, s) : Promise.resolve(u)); return c.set(s, [m.mtimeMs, [h]]), h; }, o = async (s) => { const m = await H(s); if (c.has(s)) { const [f, M] = c.get(s) || [0, []]; if (f === m.mtimeMs) return M; } const u = [], h = await et(s, { withFileTypes: !0 }); for (const f of h) { if (f.isDirectory()) { const M = await o(A(s, f.name)); u.push(...M); } (e.length === 0 || e.includes(J(f.name))) && u.push(A(s, f.name)); } return c.set(s, [m.mtimeMs, u]), u; }; return { store: c, getDirContent: o, getFileContent: p }; }, rt = () => { const e = { isDevMode: !1, isTestMode: !1, root: "", base: "", assets: "", aliases: {} }, r = (o) => o.reduce((s, m) => (typeof m.find == "string" && (s[m.find] = m.replacement), s), {}); return { config: e, updateConfig: (o) => { e.isDevMode = o.command === "serve", e.isTestMode = o.mode === "plugintest", e.root = o.root, e.base = o.base, e.assets = o.build.assetsDir, e.aliases = { "@icons": A(e.root, "src", "icons"), ...r(o.resolve.alias) }; }, findAliasName: (o) => { const [s = []] = Object.entries(e.aliases).filter(([u, h]) => o.startsWith(h)).sort(([, u], [, h]) => h.length - u.length), [m = ""] = s; return m.replace("@", ""); } }; }, at = "\0virtual:", D = "\0virtual:symbol:", V = "\0virtual:svg:symbols", Y = ".svg?symbol", it = "svg:symbols@", C = "/@id/__vite-plugin-svg-symbols__.svg", ct = { "aria-hidden": "true", style: [ "position:absolute;", "top:0;", "left:0;", "width:1px;", "height:1px;", "visibility:hidden;", "opacity:0;" ].join("") }, lt = [ "fill", "stroke", "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin", "stroke-miterlimit", "stroke-opacity", "stroke-width" ], dt = `declare module 'svg:symbols@[alias]' { [content] const symbols: Record<string, string>; export default symbols; }`, mt = `/** * @see [see] */`, z = `/** * Vite plugin for creating SVG symbol sprite * @see [Documentation on Github](https://github.com/werty1001/vite-plugin-svg-symbols) */`, ut = `declare module '*.svg?symbol' { const src: string export default src }`, pt = (e, r) => x(e, r).split(E).join("_").replace(/[-_]([a-z0-9])/gi, (p, o) => o.toUpperCase()).replace(".svg", "").concat("Icon"), G = (e, r = 8) => ot("sha256").update(e).digest("hex").slice(0, r), ft = (e, r = ".svg") => e.replace(r, "").trim().concat(r), X = (e) => `<svg xmlns="http://www.w3.org/2000/svg">${e}</svg>`, q = (e) => { const [, r = ""] = e.split("#"); return r; }, gt = (e, r) => x(r, e).replace(".svg", "").replace("node_modules".concat(E), "").replace("src".concat(E), "").split(E).join("-"), ht = (e, r, c, p) => { const o = /<svg([^>]*)>([\s\S]*?)<\/svg>/, s = /([a-zA-Z-]+)\s*=\s*["']([^"']*)["']/g, [, m = "", u = ""] = e.match(o) || [], h = u.trim().replace(/>((\r\n|\n|\r)(\s+)?)</g, "><"), f = { viewBox: "0 0 24 24" }, M = m.matchAll(s); if (M) for (const _ of M) { const [, j, I] = _; f[j.trim()] = I.trim(); } const i = p.reduce((_, j) => { const I = f[j]; return I && _.push([j, `"${I}"`].join("=")), _; }, []).join(" "), N = i ? `<g ${i}>${h}</g>` : h, B = f.viewBox; return [`<symbol viewBox="${B}" id="${c}">${N}</symbol>`, { symbolId: c, symbolBody: N, symbolViewBox: B, svgBody: h, svgPath: r, svgAttrs: f }]; }, yt = (e, r) => { const c = new RegExp(r.concat("#([^'\"`]*)"), "gi"), p = Object.keys(e).reduce((o, s) => { if (s.endsWith(".js") || s.endsWith(".mjs")) { const u = e[s].code.match(c) || []; o.push(...u); } return o; }, []); return new Set(p); }, vt = (e, r, c) => { Object.keys(e).forEach((p) => { if (!p.endsWith(".js") && !p.endsWith(".mjs")) return; const o = e[p]; o.code && (o.code = o.code.replace(r, c)); }); }, wt = ct, bt = lt, St = (e) => { const { fileName: r = "sprite-[hash]", shouldInheritAttrs: c = bt, shouldInjectToHtml: p = !1, injectAttrs: o = wt, transformIcon: s = (t) => t, transformIconId: m = (t) => t, transformSymbol: u = (t) => t, transformSprite: h = (t) => t, transformImportName: f = (t) => t, transformImportComment: M = (t) => t } = e || {}, { config: i, updateConfig: N, findAliasName: B } = rt(), { getDirContent: _, getFileContent: j } = nt([".svg"], s), I = /* @__PURE__ */ new Map(), L = async (t) => { const n = i.isDevMode ? `?v=${Date.now()}` : "", a = await m( gt(t, i.root), t ); return [C.concat(n), a].join("#"); }, O = async (t) => { const [n, a] = await Promise.all([ j(t), L(t) ]), [d, l] = ht(n, t, q(a), c), y = await u(d, l); return I.set(t, y), a; }, k = async (t) => { const n = Array.from(I.keys()).sort((d, l) => { const y = x(i.root, d), v = x(i.root, l); return y > v ? 1 : -1; }); let a = ""; for (const d of n) { const l = I.get(d) || ""; if (!t) { a += l; continue; } const y = await L(d); t.has(y) && (a += l); } return h(a); }, F = /* @__PURE__ */ new Map(), W = async (t) => { const n = i.aliases[`@${t}`] || ""; if (!n) throw new Error([ `The alias @${t} is not defined.`, "Please add it to your vite.config.ts" ].join(" ")); const a = await _(n), d = [], l = /* @__PURE__ */ new Map(); let y = "", v = ""; for (const w of a) { const [g, b] = await Promise.all([ f(pt(n, w), w), O(w) ]), T = l.get(g) || 0, S = g.concat(T > 0 ? String(T + 1) : ""); y += `export const ${S} = '${b}'; `, v += `${S}, `, d.push([w, S, b]), l.set(g, T + 1); } return F.set(t, d), y.concat(`export default { ${v}};`); }, P = async () => { let t = ""; for (const [a, d] of F.entries()) { let l = ""; for (const [v, w, g] of d) { const b = x(i.root, v), T = mt.replace("[see]", `[${b}](file://${v})`), S = await M(T, v), R = `export const ${w}: '#${q(g)}'`; l += [S, R, ` `].join(` `); } const y = dt.replace("[alias]", a).replace("[content]", l); t += [z, y, ` `].join(` `); } t += [ [z, ut].join(` `) ].join(` `); const n = i.isTestMode ? A(i.root, "app", "client.d.ts") : A(i.root, "node_modules", "vite-plugin-svg-symbols", "client.d.ts"); return st(n, t); }; let $, U = ""; return { name: "vite-plugin-svg-symbols", enforce: "pre", closeWatcher() { clearTimeout($); }, configResolved(t) { N(t); }, configureServer({ watcher: t, middlewares: n, ws: a, moduleGraph: d }) { const l = /* @__PURE__ */ new Set(), y = () => { for (const w of l) { const g = V.concat("@", w), b = d.getModuleById(g); b && d.invalidateModule(b); } l.clear(), a.send({ type: "full-reload", path: "*" }); }, v = () => { clearTimeout($), $ = setTimeout(y, 50); }; t.on("all", (w, g) => { if (!g.endsWith(".svg")) return; const b = B(g); b && (l.add(b), v()); }), n.use(C, async (w, g) => { const b = await k(), T = X(b), S = G(T, 32), R = w.headers["if-none-match"]; if (S === R) { g.writeHead(304, { "Content-Length": 0 }).end(""); return; } g.writeHead(200, { "Content-Length": Buffer.byteLength(T), "Content-Type": "image/svg+xml", "Cache-Control": "no-cache", Etag: S }).end(T); }); }, resolveId(t, n = "") { if (t.endsWith(Y)) { const a = Z(t) ? t : K(Q(n), t); return D.concat(a.replace(Y, ".svg")); } return t.startsWith(it) ? at.concat(t) : null; }, async load(t) { if (t.startsWith(D)) { const n = t.replace(D, ""); return `export default '${await O(n)}';`; } if (t.startsWith(V)) { const [, n = "icons"] = t.split("@"), a = await W(n); return await P(), a; } return null; }, transformIndexHtml() { if (!(i.isDevMode || !p)) return [{ tag: "svg", attrs: o, children: U, injectTo: "body-prepend" }]; }, async generateBundle(t, n) { if (i.isDevMode) return; const a = yt(n, C), d = await k(a), l = X(d), y = G(l), v = ft(r.replace("[hash]", y)), w = p ? "" : A(i.base, i.assets, v), g = new RegExp(C, "g"); vt(n, g, w), p ? U = d : this.emitFile({ type: "asset", fileName: A(i.assets, v), source: l }); }, api: { async generateSymbolsTypes() { const t = Object.keys(i.aliases).map((n) => n.replace("@", "")).filter(Boolean); return await Promise.all(t.map(W)), P(); } } }; }; export { St as default, bt as defaultInheritAttrs, wt as defaultInjectAttrs };