vite-awesome-svg-loader
Version:
A universal Vite SVG loader. Imports SVGs as source code, base64 and data URI. Preserves stroke width. Replaces colors with currentColor or custom colors. Creates SVG sprites. Optimizes SVGs.
355 lines (354 loc) • 10.4 kB
JavaScript
import { readFile as ce, writeFile as ue, rm as fe } from "node:fs/promises";
import j from "path";
import { optimize as pe } from "svgo";
import { querySelectorAll as me, matches as he } from "svgo/lib/xast.js";
import de from "imurmurhash";
import { i as K } from "../common-utils/index-YJ0rTXLH.js";
import * as g from "css-tree";
function X(e) {
return e = e.replaceAll("\\", "/"), e.endsWith("/") && (e = e.substring(0, e.length - 1)), e;
}
function Y(e) {
const n = String.fromCodePoint(...new TextEncoder().encode(e));
return btoa(n);
}
function Z(e) {
return e.replaceAll("`", "\\`");
}
function ge(e) {
return re(e.queryValue) || q(e);
}
function re(e) {
return !!e && e.toLowerCase() !== "false";
}
function q(e) {
const n = [j.basename(e.relativePath), e.relativePath], r = K(e.matchers);
return r.length ? r.some((o) => {
switch (typeof o) {
case "string":
return n.some((a) => a === o);
case "function":
return o({ fullPath: e.fullPath, relativePath: e.relativePath });
}
return n.some((a) => o.test(a));
}) : !1;
}
function ee(e) {
return e.replaceAll(/\s+/g, " ").trim();
}
function ve(e) {
const n = K(e.selectors), r = [];
for (const o of n) {
if (typeof o == "string") {
r.push(ee(o));
continue;
}
if (q({ ...e, matchers: o.files }))
for (const a of o.selectors)
r.push(ee(a));
}
return r;
}
const se = he;
function V(e, n) {
for (const r of n)
if (se(e, r))
return !0;
return !1;
}
function G(e, n) {
return e ? n.replacements[e.toLowerCase()] || n.default || e : n.default || "";
}
function _e(e) {
return Array.isArray(e?.files);
}
const be = {
circle: !0,
ellipse: !0,
foreignObject: !0,
image: !0,
line: !0,
path: !0,
polygon: !0,
polyline: !0,
rect: !0,
text: !0,
textPath: !0,
tspan: !0,
use: !0
};
function ye(e, n) {
if (!be[e.name])
return;
const r = e.attributes["vector-effect"];
r && r !== "non-scaling-stroke" ? console.warn(
`"${n}": Element "${e.name}" already contains "vector-effect" property. Please remove it, so it can scale correctly. This element will not be transformed.`
) : e.attributes["vector-effect"] = "non-scaling-stroke";
}
const ne = {
fill: !0,
stroke: !0,
"stop-color": !0
}, F = {
none: !0,
transparent: !0,
currentColor: !0
}, we = ["url", "source", "source-data-uri", "base64", "base64-data-uri"];
function te(e, n, r, o = !1) {
if (!e || typeof e != "string")
return "";
let a = "stylesheet";
o && (e = `{${e}}`, a = "block");
const i = !o && r.length;
let c = [], p = [], _ = !1;
const S = g.parse(e, { context: a });
return g.walk(S, {
// Ignore because of broken types in csstree:
// @ts-ignore
visit: i ? void 0 : "Declaration",
enter: function(u) {
if (u.__SKIP_SVG_LOADER__ || this.rule?.__SKIP_SVG_LOADER__)
return;
if (i) {
if (u.type === "SelectorList") {
c = [], p = [], _ = !1;
return;
}
if (u.type === "Selector") {
const m = g.generate(u);
let s = !1;
for (const f of r)
if (se(f, m)) {
s = !0, u.__ORIG_COLOR__ = !0;
break;
}
(s ? c : p).push(m);
return;
}
}
if (u.type !== "Declaration" || !ne[u.property])
return;
const C = u.value?.children?.first, P = C?.value || C?.name;
if (!(!P || F[P])) {
if (i && !_ && this.rule?.prelude.type === "SelectorList") {
const m = g.clone(this.rule);
m.__SKIP_SVG_LOADER__ = !0;
const s = new g.List(), f = this.rule.prelude.children;
f.forEach((y, h) => {
y.__ORIG_COLOR__ && (f.remove(h), s.push(y));
}), m.prelude.children = s;
const b = this.atrule?.block?.children || this.stylesheet?.children;
let l;
b?.some((y, h) => y === this.rule ? (l = h, !0) : !1), l ? b?.insertData(m, l) : b?.push(m), _ = !0;
}
u.value = g.parse(G(g.generate(u.value), n), {
context: "value"
});
}
}
}), g.generate(S);
}
const Le = {
circle: !0,
ellipse: !0,
path: !0,
polygon: !0,
polyline: !0,
rect: !0,
text: !0,
textPath: !0,
tref: !0,
tspan: !0
}, oe = { ...ne };
delete oe.fill;
function xe(e, n, r, o) {
if (e.name === "style") {
const c = e.children[0], p = te(c?.value, r, o, !1);
p && (c.value = p);
} else {
const c = te(e.attributes.style, r, o, !0);
c && (e.attributes.style = c);
}
const a = e.name === "svg", i = e.attributes.fill;
a && i && (n = !0), (a && n || !a && !n && Le[e.name]) && !F[i] && (e.attributes.fill = G(i, r));
for (const c in oe) {
const p = e.attributes[c];
p && !F[p] && (e.attributes[c] = G(p, r));
}
return n;
}
function Oe(e = {}) {
const { urlImportsInLibraryMode: n = "source-data-uri" } = e;
let r = e.tempDir || ".temp";
if (r.startsWith("/") || r.startsWith("./") || r.indexOf(":/") !== -1)
throw new Error(
`"tempDir" option must be in format "path/to/temp/dir",i.e. it shouldn't be an absolute path, or start with "./".It'll be resolved to the project's root by the plugin.`
);
r.endsWith("/") && (r = r.substring(0, r.length - 1)), r = "/" + r;
let o = !1, a = !1, i = "", c = "";
const p = K(e.replaceColorsList || []), _ = [], S = [], u = {
files: /.*/,
replacements: {},
default: ""
};
let C = !1;
const P = (s) => {
switch (typeof s) {
case "string":
case "function":
return !0;
}
return s instanceof RegExp;
};
for (const s of p) {
if (_e(s)) {
_.push(s);
continue;
}
if (P(s)) {
S.push({
files: s,
replacements: {},
default: "currentColor"
});
continue;
}
for (const f in s)
C = !0, u.replacements[f] = s[f];
}
const m = [..._, ...S];
return C && m.push(u), {
name: "vite-awesome-svg-loader",
enforce: "pre",
config(s, { command: f }) {
o = f === "build";
},
configResolved(s) {
a = !!s.build.lib, i = X(s.root), c = X(s.base);
},
configureServer(s) {
s.httpServer?.on("close", async () => {
o || await fe(i + r, { force: !0, recursive: !0 });
});
},
async load(s) {
const f = ".svg", b = s.indexOf(f);
if (b === -1)
return null;
let l = s.substring(0, b + f.length).replaceAll("\\", "/");
l.startsWith(i) && (l = l.substring(i.length)), l.startsWith("/") || (l = "/" + l);
const y = (s.split("?", 2)[1] || "").split("&"), h = {};
for (const t of y) {
const [x, T] = t.split("=");
h[x.toLowerCase()] = T || "1";
}
const R = {
fullPath: (i.endsWith("/") ? i.substring(i.length) : i) + l,
relativePath: l
}, k = (t) => ge({
...R,
matchers: t.matchers || [],
queryValue: h[t.param]
}), $ = (t) => ve({ ...R, selectors: t || [] });
if (k({ param: "skip-awesome-svg-loader", matchers: e.skipFilesList }))
return null;
const I = k({
param: "skip-transforms",
matchers: e.skipTransformsList
}), E = !I && k({ param: "preserve-line-width", matchers: e.preserveLineWidthList }) && !k({ matchers: e.skipPreserveLineWidthList }), N = E ? $(e.skipPreserveLineWidthSelectors) : [];
let w = !1;
const v = {
replacements: {},
// @ts-ignore
default: void 0
};
if (!I && !k({ matchers: e.skipReplaceColorsList })) {
if (re(h["set-current-color"]))
v.default = "currentColor", w = !0;
else
for (const t of m)
if (q({ ...R, matchers: t.files })) {
w = !0, v.default === void 0 && t.default !== void 0 && (v.default = t.default);
for (const x in t.replacements)
v.replacements[x] ||= t.replacements[x];
}
}
v.default ??= "currentColor";
const W = w ? $(e.skipReplaceColorsSelectors) : [], D = I ? [] : $(e.skipTransformsSelectors), A = [l];
for (const t of [N, W, D])
A.push(t.join(","));
for (const t of [I, E, w])
A.push(t ? "1" : "0");
w && A.push(JSON.stringify(v));
const ie = new de(A.join("__")).result(), U = `${j.basename(l).split(".")[0]}-${ie}`, z = U + ".svg", le = j.dirname(l) + "/" + z, J = i + l;
let d = (await ce(J)).toString(), M = !1;
const B = [], H = U + "__";
let Q = !1;
d = pe(d, {
multipass: !0,
plugins: [
{
name: "prefixIds",
params: {
prefixIds: !0,
prefixClassNames: !0,
prefix: H,
delim: ""
}
},
{
name: "awesome-svg-loader",
fn: () => Q ? null : (Q = !0, {
root: {
enter: (t) => {
for (const x of [W, D])
for (const T of x)
B.push(...me(t, T));
}
},
element: {
enter: (t) => {
V(t, D) || (E && !V(t, N) && ye(t, J), w && !V(t, W) && (M = xe(t, M, v, B)));
}
}
})
}
]
}).data;
let O = e.defaultImport || "source";
for (const t of we)
h[t] && (O = t);
a && O === "url" && n !== "emit-files" && (O = n);
const L = (t) => [
`export const src = ${t};`,
`export const prefix = "${H}"`,
"export default src"
].join(`
`);
switch (O) {
case "source":
return L("`" + Z(d) + "`");
case "source-data-uri":
return L("`data:image/svg+xml," + encodeURIComponent(d) + "`");
case "base64":
return L("`" + Z(Y(d)) + "`");
case "base64-data-uri":
return L("`data:image/svg+xml;base64," + encodeURIComponent(Y(d)) + "`");
}
if (!o) {
const t = r + le;
return await ue(i + t, d), L(`"${c + t}"`);
}
const ae = this.emitFile({
type: "asset",
name: z,
source: d
});
return L(`"__VITE_ASSET__${ae}__"`);
}
};
}
export {
Oe as A
};
//# sourceMappingURL=index-DQbV_Mhq.js.map