vite-plugin-single-image-format
Version:
vite-plugin-single-image-format is a Vite/Rollup plugin that converts every raster asset in your build to a single output format – webp, png or avif. It can optionally re‑compress images that are already in the target format and automatically rewrites all
109 lines (108 loc) • 4.88 kB
JavaScript
import d from "sharp";
function L(S = {}) {
const { format: m = "webp", reencode: B = !1, webp: O = {}, png: N = {}, avif: E = {}, htmlSizeMode: y = "add-only" } = S, R = { quality: 88, alphaQuality: 90, smartSubsample: !0, ...O }, k = { quality: 80, compressionLevel: 9, palette: !0, adaptiveFiltering: !0, ...N }, q = { quality: 60, lossless: !1, speed: 5, ...E }, w = /\.(png|jpe?g|webp|gif|avif|heif|heic|tiff?|bmp|jp2)$/i, $ = [".html", ".css", ".js", ".mjs", ".ts", ".jsx", ".tsx"], b = "imgfmt=keep", g = /* @__PURE__ */ new Map();
function j(h, s) {
return new RegExp(`\\b${s}\\s*=`, "i").test(h);
}
function v(h) {
return h.replace(/\s+(?:width|height)\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)/gi, "");
}
function z(h, s, f, p, u, e) {
const i = (function(l) {
const c = (function(o) {
return o.split(/[?#]/)[0];
})(l);
for (const o of g.keys()) if (c.endsWith(o) || c.endsWith("./" + o) || c.endsWith("/" + o)) return o;
return null;
})(p);
if (!i) return h;
const t = g.get(i);
if (!t) return h;
if (y === "overwrite")
return `<img ${v(s)}src=${f}${p}${f}${v(u)} width="${t.width}" height="${t.height}"${e}>`;
const n = j(s + u, "width"), a = j(s + u, "height");
return n && a ? h : `<img ${s}src=${f}${p}${f}${u}${n ? "" : ` width="${t.width}"`}${a ? "" : ` height="${t.height}"`}${e}>`;
}
function W(h) {
return h.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
function M(h) {
const [s, f = ""] = h.split("#", 2), p = s.startsWith("?"), u = p ? s.slice(1) : s;
if (!u) return (p ? "?" : "") + (f ? "#" + f : "");
const e = u.split("&").filter(Boolean).filter((i) => {
const [t, n = ""] = i.split("=", 2);
return !(t === "imgfmt" && n === "keep");
});
return (e.length ? "?" + e.join("&") : "") + (f ? "#" + f : "");
}
return { name: "vite-plugin-single-image-format", apply: "build", enforce: "post", async generateBundle(h, s) {
const f = /* @__PURE__ */ new Map(), p = /* @__PURE__ */ new Set(), u = [];
for (const e of Object.values(s)) e.type === "asset" && $.some((i) => e.fileName.endsWith(i)) && u.push(e.source.toString());
if (u.length > 0) for (const [e, i] of Object.entries(s)) {
if (i.type !== "asset" || !w.test(e)) continue;
const t = `${e}?${b}`;
u.some((n) => n.includes(t)) && p.add(e);
}
for (const [e, i] of Object.entries(s)) {
if (i.type !== "asset" || !w.test(e)) continue;
const t = (i.source, Buffer.from(i.source));
if (p.has(e)) {
try {
const r = await d(t).metadata();
r.width && r.height && g.set(e, { width: r.width, height: r.height });
} catch {
process;
}
continue;
}
const n = e.toLowerCase().endsWith(`.${m}`);
if (n && !B) {
try {
const r = await d(t).metadata();
r.width && r.height && g.set(e, { width: r.width, height: r.height });
} catch {
process;
}
continue;
}
const a = m === "webp" ? await d(t).webp(R).toBuffer() : m === "png" ? await d(t).png(k).toBuffer() : await d(t).avif(q).toBuffer(), l = await d(a).metadata(), c = l.width && l.height ? { width: l.width, height: l.height } : void 0;
if (n) {
i.source = a, c && g.set(e, c);
continue;
}
const o = e.replace(w, `.${m}`);
s[o] ? (c && g.set(e, c), i.source = a) : (this.emitFile({ type: "asset", fileName: o, source: a }), f.set(e, o), c && g.set(o, c), delete s[e]);
}
if (f.size > 0) for (const e of Object.values(s)) {
if (e.type !== "asset" || !$.some((t) => e.fileName.endsWith(t))) continue;
let i = e.source.toString();
for (const [t, n] of f) {
const a = t.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
i = i.replace(new RegExp(`([./]*)${a}`, "g"), (l, c) => `${c}${n}`);
}
e.source = i;
}
{
const e = Array.from(p);
if (e.length > 0) for (const i of Object.values(s)) {
if (i.type !== "asset" || !$.some((n) => i.fileName.endsWith(n))) continue;
let t = i.source.toString();
for (const n of e) {
const a = new RegExp(`(${W(n)})(\\?[^"'\\s)><#]*?)?(#[^"'\\s)><]*)?`, "g");
t = t.replace(a, (c, o, r, x) => r ? o + M((r || "") + (x || "")) : o + (x ?? ""));
const l = new RegExp(`(${W(n)})\\?${b}(?![^"'\\s)><#])`, "g");
t = t.replace(l, "$1");
}
i.source = t;
}
}
if (y !== "off" && g.size > 0) for (const e of Object.values(s)) {
if (e.type !== "asset" || !e.fileName.endsWith(".html")) continue;
const i = /<img\s+([^>]*?)src=(["'])([^"']+)\2([^>]*?)(\/?)>/gi, t = e.source.toString().replace(i, (n, a, l, c, o, r) => z(n, a, l, c, o, r));
e.source = t;
}
} };
}
export {
L as default
};