UNPKG

@svgd/cli

Version:

Command-line utility for generating constants from SVG assets

243 lines (233 loc) 7.73 kB
#!/usr/bin/env node // src/index.ts import fs from "fs"; import path2 from "path"; // src/generate.ts import path from "path"; import { parseSvg, generateConstantName, generateFileName, getSvgFileNames, getPng, getSvg, defaultConfig, getSvgoConfig } from "@svgd/utils"; import { readFileSync } from "fs"; // src/templates.ts var jsRowTemplate = ({ name, d, quote }) => `export const ${name} = ${quote}${d}${quote}; `; var jsRowTemplateWithJSDoc = ({ name, d, image, quote, filePath, ts }) => `/** * @filepath ${filePath} * @return ${image} */ export const ${name} = ${quote}${d}${quote}${ts ? ` as "${name}"` : ""}; `; var dtsRowTemplate = ({ name, image, filePath }) => `/** * @filepath ${filePath} * @return ${image} */ export const ${name}: string; `; var mdRowTemplate = ({ name, filePath, relativePath }) => `| ![](${relativePath}) | ${name} | ${filePath} |`; var mdFileTemplate = (rows) => `# List of icons | Source | Name | Path | |---|---|---| ${rows} `; var htmlRowTemplate = ({ name, filePath, svg }) => `<tr><td>${svg}</td><td>${name}</td><td>${filePath}</td></tr>`; var htmlFileTemplate = (rows) => `<!DOCTYPE html> <html lang=""> <head> <style> table { border-collapse: collapse; } th { text-align: center; border: 1px solid darkgray; padding: 4px 8px; } td { text-align: left; border: 1px solid darkgray; padding: 4px 8px; } </style> </head> <body> <table> <tr><th>Icon</th><th>Name</th><th>Path</th></tr> ${rows} </table> </body> </html> `; // src/generate.ts var defoultOptions = { input: "src/assets/icons", output: "src/components/Icon/paths.js", quote: false, template: "", format: "camelCase", colors: false, size: 24 }; async function generateSvgConstants(options) { const root = process.cwd(); const filledOptions = { ...defoultOptions, ...options }; const baseDir = path.resolve(root, filledOptions.input); const svgoConfig = getSvgoConfig({ ...defaultConfig, colors: filledOptions.colors, resize: { targetViewBox: { minX: 0, minY: 0, width: filledOptions.size ?? 24, height: filledOptions.size ?? 24 } } }); const svgFiles = getSvgFileNames(baseDir); const singleQuote = filledOptions.quote; const quote = singleQuote ? "'" : '"'; const outputs = /* @__PURE__ */ new Map(); let md; if (filledOptions.md) { md = { rows: [], fileTemplate: mdFileTemplate, rowTemplate: mdRowTemplate, path: path.resolve(root, filledOptions.md) }; outputs.set(md.path, md); } let html; if (filledOptions.html) { html = { rows: [], fileTemplate: htmlFileTemplate, rowTemplate: htmlRowTemplate, path: path.resolve(root, filledOptions.html) }; outputs.set(html.path, html); } await Promise.all(svgFiles.map(async (file, index) => { try { const getRelativePath = getRelativePathFactory(file); const constantName = generateConstantName( file, baseDir, filledOptions.template, filledOptions.format ); const outputFileName = generateFileName(file, baseDir, filledOptions.output); const outputFilePath = path.resolve(root, outputFileName); let constants = outputs.get(outputFilePath); if (!constants) { constants = { rows: [], path: outputFilePath, rowTemplate: filledOptions.dts ? jsRowTemplate : jsRowTemplateWithJSDoc }; outputs.set(outputFilePath, constants); } let dts; if (filledOptions.dts) { const dtsOutputFilePath = outputFilePath.replace(/\.js|\.ts$/, ".d.ts"); dts = outputs.get(dtsOutputFilePath); if (!dts) { dts = { rows: [], path: dtsOutputFilePath, rowTemplate: dtsRowTemplate }; outputs.set(dtsOutputFilePath, dts); } } const d = parseSvg(readFileSync(file, "utf8"), svgoConfig); const svg = getSvg(d); const png = await getPng(svg); const templateProps = { name: constantName, d, quote, svg, image: `![](data:image/png;base64,${png})`, filePath: path.relative(baseDir, file).split("\\").join("/"), ts: /\.ts$/.test(outputFilePath) }; const orderedRowItem = (code) => ({ order: constantName, code }); constants.rows.push(orderedRowItem(constants.rowTemplate({ ...templateProps, relativePath: getRelativePath(outputFilePath) }))); dts?.rows.push(orderedRowItem(dts.rowTemplate({ ...templateProps, relativePath: getRelativePath(dts.path) }))); md?.rows.push(orderedRowItem(md.rowTemplate({ ...templateProps, relativePath: getRelativePath(md.path) }))); html?.rows.push(orderedRowItem(html.rowTemplate({ ...templateProps, relativePath: getRelativePath(html.path) }))); } catch (error) { console.error(`Error processing svg file ${file}:`, error); } if ((index + 1) % 100 === 0) { console.log(`Processed svg files: ${index + 1}/${svgFiles.length}`); } })); return [...outputs.entries()].map(([outPath, rawData]) => { const content = rawData.rows.sort(({ order: a }, { order: b }) => a.localeCompare(b)).map(({ code }) => code).join("\n"); return { path: outPath, content: rawData.fileTemplate ? rawData.fileTemplate(content) : content }; }); } var getRelativePathFactory = (file) => (absolutePath) => path.relative(path.dirname(absolutePath), file).split("\\").join("/"); // src/parseCliArgs.ts import { createCommand, InvalidArgumentError } from "commander"; function commanderParseInt(value) { const parsedValue = parseInt(value, 10); if (isNaN(parsedValue)) { throw new InvalidArgumentError("Not a number."); } return parsedValue; } function parseCliArgs(argv) { const program = createCommand(); program.version("1.0.5").description("CLI tool to generate constants from SVG files").option("-i, --input <directory>", "Input directory containing SVG files", "src/assets/icons").option("-o, --output <file>", "Output file path or pattern", "src/components/Icon/paths.js").option("-c, --colors", "Keep colors", false).option("-s, --size <number>", "Icon Size", commanderParseInt, 24).option("-q, --quote", "Use single quotes in the output", false).option("-t, --template <string>", "Template string for naming convention", "").option("-m, --md <string>", "Path to the output MD file", "").option("-h, --html <string>", "Path to the output HTML file", "").option("-d, --dts", "Path to the output HTML file", false).option( "-f, --format <format>", "Naming format: camelCase, PascalCase, snake_case, SCREAMING_SNAKE_CASE, or material", "camelCase" ).parse(argv); return program.opts(); } // src/index.ts async function runCLI(argv) { const options = parseCliArgs(argv); const generatedFiles = await generateSvgConstants(options); generatedFiles.forEach(({ path: outputFilePath, content }) => { ensureDirectoryExistence(outputFilePath); fs.writeFileSync(outputFilePath, content, "utf8"); console.log(`Constants file successfully created: ${outputFilePath}`); }); } function ensureDirectoryExistence(filePath) { const dirname = path2.dirname(filePath); if (!fs.existsSync(dirname)) { fs.mkdirSync(dirname, { recursive: true }); } } // src/cli.ts await runCLI(process.argv);