@svgd/cli
Version:
Command-line utility for generating constants from SVG assets
243 lines (233 loc) • 7.73 kB
JavaScript
// 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 }) => `|  | ${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: ``,
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);