import { xml2js } from "xml-js";
import { promises as fs } from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
export async function buildIcons(outputPath) {
const iconPacksJson = await fs.readFile(path.resolve(__dirname, "./icon-packs.json"));
const iconPacks = JSON.parse(iconPacksJson);
const allIcons = [];
for (const packName in iconPacks) {
console.info(`Building icon pack "${packName}"`);
const packConfig = iconPacks[packName];
const prefix = packConfig["prefix"];
const folder = packConfig["folder"];
if (!prefix) {
throw new Error(`"prefix" is not defined for icon pack "${packName}"`);
}
if (!folder) {
throw new Error(`"folder" is not defined for icon pack "${packName}"`);
}
const folderPath = path.resolve(__dirname, folder);
const files = await fs.readdir(folderPath);
for (const fileName of files) {
const icon = await loadIcon(folderPath, fileName, prefix);
allIcons.push(icon);
}
}
console.info(`Saving generated Icon Pack to: "${outputPath}"`);
const allIconsJson = JSON.stringify(allIcons);
await fs.writeFile(path.resolve(outputPath), allIconsJson);
const md = makeMarkdown(allIcons);
await fs.writeFile(path.resolve(__dirname, "all-icons.md"), md);
console.info("Icons built successfully");
}
async function loadIcon(folderPath, fileName, prefix) {
const segments = fileName.split(".");
const type = segments[1];
const name = segments[0];
if (segments.length !== 3 || !["regular", "solid", "duotone"].includes(type) || !segments[2] === "svg") {
throw new Error(
`Failed to load icon "${fileName}". ` +
'Icon files must be in the following format: "`name`.`type`.svg", where type is `regular`, `solid` or `duotone`',
);
}
const fullPrefix = prefix + type[0];
console.info(` - loading icon "${fullPrefix} ${name}" from file "${fileName}"`);
const filePath = path.join(folderPath, fileName);
const fileXML = await fs.readFile(filePath);
const file = xml2js(fileXML);
const svg = file.elements.find((e) => e.name === "svg");
// icon without path / paths
const icon = [parseInt(svg.attributes.width), parseInt(svg.attributes.height), [], ""];
const paths = findAllPaths(svg);
// duotone want array of paths, the others just a single path
if (type === "duotone") {
icon.push(paths);
} else {
icon.push(paths[0]);
}
return {
iconName: name,
prefix: fullPrefix,
icon,
};
}
function findAllPaths(element) {
const childPaths = element.elements?.reduce((accumulator, e) => [...accumulator, ...findAllPaths(e)], []) ?? [];
if (element.name === "path") {
return [...childPaths, element.attributes.d];
} else {
return childPaths;
}
}
function makeMarkdown(iconDefinitions) {
let content = "# Icons\n\n";
content += "Galaxy custom icons\n\n";
content += "Auto-generated by `build_icons.js`\n\n";
content += "## Usage\n\n";
content += "```vue\n";
content += "\n\n";
content += "\n";
content += ' \n';
content += "\n";
content += "```\n\n";
content += "---\n\n";
iconDefinitions.forEach((icon) => {
content += `- **${icon.iconName}**: \`import { ${icon.iconName} } from "@/components/icons/galaxyIcons";\`\n`;
});
content += "\n---\n";
return content;
}