/** * This is an advanced example for creating icon bundles for Iconify SVG Framework. * * It creates a bundle from: * - All SVG files in a directory. * - Custom JSON files. * - Iconify icon sets. * - SVG framework. * * This example uses Iconify Tools to import and clean up icons. * For Iconify Tools documentation visit https://docs.iconify.design/tools/tools2/ */ import { promises as fs } from 'node:fs'; import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; import { createRequire } from 'node:module'; // Installation: npm install --save-dev @iconify/tools @iconify/utils @iconify/json @iconify/iconify import { cleanupSVG, importDirectory, isEmptyColor, parseColors, runSVGO } from '@iconify/tools'; import { getIcons, getIconsCSS, stringToIcon } from '@iconify/utils'; const require = createRequire(import.meta.url); async function generateIconsCSS() { const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const sources = { json: [ // Iconify JSON file (@iconify/json is a package name, /json/ is directory where files are, then filename) require.resolve('@iconify/json/json/tabler.json'), // Custom file with only few icons /* { filename: require.resolve('@iconify/json/json/line-md.json'), icons: ['home-twotone-alt', 'github', 'document-list', 'document-code', 'image-twotone'] } */ // Custom JSON file // 'json/gg.json' ], icons: [ 'bx-basket', 'bi-airplane-engines', 'ri-anchor-line', 'uit-adobe-alt', // 'fa6-regular-comment', 'twemoji-auto-rickshaw' ], svg: [ /* { dir: 'src/assets/iconify-icons/svg', monotone: false, prefix: 'custom' } */ /* { dir: 'src/assets/iconify-icons/emojis', monotone: false, prefix: 'emoji' } */ ] }; // File to save bundle to const target = join(__dirname, 'generated-icons.css'); /** * Do stuff! */ // Create directory for output if missing const dir = dirname(target); try { await fs.mkdir(dir, { recursive: true }); } catch { // } const allIcons = []; /** * Convert sources.icons to sources.json */ if (sources.icons) { const sourcesJSON = sources.json ? sources.json : (sources.json = []); // Sort icons by prefix const organizedList = organizeIconsList(sources.icons); for (const prefix in organizedList) { const filename = require.resolve(`@iconify/json/json/${prefix}.json`); sourcesJSON.push({ filename, icons: organizedList[prefix] }); } } /** * Bundle JSON files and collect icons */ if (sources.json) { for (let i = 0; i < sources.json.length; i++) { const item = sources.json[i]; // Load icon set const filename = typeof item === 'string' ? item : item.filename; const content = JSON.parse(await fs.readFile(filename, 'utf8')); // Filter icons if (typeof item !== 'string' && item.icons?.length) { const filteredContent = getIcons(content, item.icons); if (!filteredContent) throw new Error(`Cannot find required icons in ${filename}`); // Collect filtered icons allIcons.push(filteredContent); } else { // Collect all icons from the JSON file allIcons.push(content); } } } /** * Bundle custom SVG icons and collect icons */ if (sources.svg) { for (let i = 0; i < sources.svg.length; i++) { const source = sources.svg[i]; // Import icons const iconSet = await importDirectory(source.dir, { prefix: source.prefix }); // Validate, clean up, fix palette, etc. await iconSet.forEach(async (name, type) => { if (type !== 'icon') return; // Get SVG instance for parsing const svg = iconSet.toSVG(name); if (!svg) { // Invalid icon iconSet.remove(name); return; } // Clean up and optimise icons try { // Clean up icon code await cleanupSVG(svg); if (source.monotone) { // Replace color with currentColor, add if missing // If icon is not monotone, remove this code await parseColors(svg, { defaultColor: 'currentColor', callback: (attr, colorStr, color) => { return !color || isEmptyColor(color) ? colorStr : 'currentColor'; } }); } // Optimise await runSVGO(svg); } catch (err) { // Invalid icon console.error(`Error parsing ${name} from ${source.dir}:`, err); iconSet.remove(name); return; } // Update icon from SVG instance iconSet.fromSVG(name, svg); }); // Collect the SVG icon allIcons.push(iconSet.export()); } } // Generate CSS from collected icons const cssContent = allIcons .map(iconSet => getIconsCSS(iconSet, Object.keys(iconSet.icons), { iconSelector: '.{prefix}-{name}' })) .join('\n'); // Save the CSS to a file await fs.writeFile(target, cssContent, 'utf8'); console.log(`Saved CSS to ${target}!`); } generateIconsCSS().catch(err => { console.error(err); }); /** * Sort icon names by prefix */ function organizeIconsList(icons) { const sorted = Object.create(null); icons.forEach(icon => { const item = stringToIcon(icon); if (!item) return; const prefix = item.prefix; const prefixList = sorted[prefix] ? sorted[prefix] : (sorted[prefix] = []); const name = item.name; if (!prefixList.includes(name)) prefixList.push(name); }); return sorted; }