import { readFileSync } from 'fs'; import { fileURLToPath } from 'url'; import { dirname, join } from 'path'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); export function iconifyPlugin() { const iconCache = new Map(); const collections = new Map(); function loadCollection(prefix) { if (collections.has(prefix)) { return collections.get(prefix); } try { const collectionPath = join(__dirname, 'node_modules', '@iconify-json', prefix, 'icons.json'); const data = JSON.parse(readFileSync(collectionPath, 'utf8')); collections.set(prefix, data); return data; } catch (e) { return null; } } function getIconCSS(iconData, selector) { const { body, width, height } = iconData; const viewBox = `0 0 ${width || 24} ${height || 24}`; // Create SVG data URI const svg = `${body}`; const dataUri = `data:image/svg+xml;base64,${Buffer.from(svg).toString('base64')}`; return `${selector} { display: inline-block; width: 1em; height: 1em; background-color: currentColor; -webkit-mask-image: url("${dataUri}"); mask-image: url("${dataUri}"); -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat; -webkit-mask-size: 100% 100%; mask-size: 100% 100%; }`; } return { name: 'vite-plugin-iconify', transform(code, id) { // Only process files that might contain icon classes if (!id.endsWith('.tsx') && !id.endsWith('.jsx') && !id.endsWith('.ts') && !id.endsWith('.js')) { return null; } // Find all icon-[...] patterns const iconPattern = /icon-\[([^\]]+)\]/g; const matches = [...code.matchAll(iconPattern)]; if (matches.length === 0) { return null; } // Extract unique icons const icons = new Set(matches.map(m => m[1])); // Generate CSS for each icon for (const iconName of icons) { if (iconCache.has(iconName)) { continue; } try { // Parse icon name (e.g., "solar--add-square-bold-duotone") const parts = iconName.split('--'); if (parts.length !== 2) continue; const [prefix, name] = parts; // Load collection const collection = loadCollection(prefix); if (!collection || !collection.icons || !collection.icons[name]) { continue; } // Get icon data const iconData = collection.icons[name]; // Generate CSS const selector = `.icon-\\[${iconName}\\]`; const iconCSS = getIconCSS(iconData, selector); iconCache.set(iconName, iconCSS); } catch (e) { // Silently skip failed icons } } return null; }, resolveId(id) { if (id === '/@iconify-css') { return id; } }, load(id) { if (id === '/@iconify-css') { const allCSS = Array.from(iconCache.values()).join('\n'); return allCSS; } }, transformIndexHtml() { // Inject icon CSS into HTML const allCSS = Array.from(iconCache.values()).join('\n'); if (allCSS) { return [ { tag: 'style', attrs: { type: 'text/css' }, children: allCSS, injectTo: 'head' } ]; } } }; }