UNPKG

vite-plugin-svg-symbols

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