UNPKG

vite-vanjs-svg

Version:

Vite plugin to transform SVGs into VanJS components

108 lines (93 loc) 3.33 kB
import fs from "node:fs"; import path from "node:path"; import { createFilter } from "@rollup/pluginutils"; import { htmlToVanCode } from "./htmlToVanCode.mjs"; import process from "node:process"; const cwd = process.cwd(); /** @typedef {import("vanjs-core").PropsWithKnownKeys<SVGSVGElement>} PropsWithKnownKeys */ /** @typedef {import("vite").ResolvedConfig} ResolvedConfig */ /** @typedef {typeof import("./types").VitePluginVanSVG} VitePluginVanSVG */ /** @typedef {import("vite").BuildAppHook} BuildAppHook */ /** @typedef {ThisParameterType<BuildAppHook>} PluginContext */ /** * Compiles SVGs to VanJS component code * @param {string} svgCode * @returns {string} the compiled code */ function transformSvgToVanJS(svgCode) { // Convert the SVG string directly to VanJS code using htmlToVanCode const vanCode = htmlToVanCode(svgCode); // Wrap the converted code in a component const componentCode = ` import van from 'vanjs-core'; export default ({ children, ...rest }) => { const { ${vanCode.tags.join(", ")} } = van.tags("http://www.w3.org/2000/svg"); const props = Object.fromEntries(Object.entries(rest).filter(([_, val]) => val)); return ${vanCode.code} } `.trim(); return componentCode; } /** @type {VitePluginVanSVG} */ export default function vitePluginSvgVan(options = {}) { const { esbuildOptions, oxcOptions, include = ["**/*.svg?van"], exclude, } = options; const filter = createFilter(include, exclude); const postfixRE = /[?#].*$/s; /** @type {Partial<ResolvedConfig>} */ let config; let isOxc = true; return { name: "vanjs-svg", enforce: "pre", buildStart() { const { viteVersion } = this.meta; isOxc = Number(viteVersion[0]) >= 8; }, // istanbul ignore next - impossible to test outside of vite runtime configResolved(cfg) { config = cfg; }, async load(id) { if (filter(id)) { const file = id.replace(postfixRE, ""); // Resolve the file path /* istanbul ignore next @preserve - we cannot test this outside the vite runtime */ const filePath = !file.startsWith(cwd) && file.startsWith("/") && config?.publicDir ? path.resolve(config.publicDir, file.slice(1)) : file; // Read the SVG file const svgCode = await fs.promises.readFile(filePath, "utf8"); // Transform SVG to VanJS component const componentCode = transformSvgToVanJS(svgCode); const vite = await import("vite"); const transformer = isOxc ? "transformWithOxc" : "transformWithEsbuild"; const langProp = isOxc ? "lang" : "loader"; const options = (isOxc ? oxcOptions : esbuildOptions) || {}; // const mapProp = isOxc ? "source_map" : "sourcemap"; // Transform the component code using esbuild/oxc const result = await vite[transformer](componentCode, id, { [langProp]: "jsx", sourcemap: true, ...options, }); return { code: result.code, map: result.map ? ( typeof result.map === "string" ? JSON.parse(result.map) : result.map ) : /* istanbul ignore next @preserve */ null, }; } return null; }, }; }