UNPKG

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
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 };