path-icons
Version:
A utility for generating C# and CSS with SVG path-based icons
198 lines (174 loc) • 7.44 kB
JavaScript
import { promises as fs } from 'fs';
import { resolve, dirname, basename, extname, join, normalize } from 'path';
import { fileURLToPath } from 'url';
import { prepareJson, createCSS, createHtml, createCSharp } from '../build/pi-processor.mjs';
import { createRequire } from 'module';
const require = createRequire(import.meta.url);
const { version } = require('../package.json');
const __dirname = dirname(fileURLToPath(import.meta.url));
const PI_JSON_FILE_PATH = resolve(__dirname, '../dist/path-icons.json');
const BI_JSON_FILE_PATH = join(__dirname, '../src/bi.json');
const BI_INPUT_JSON_FILE_PATH = join(__dirname, '../src/bi-override.json');
const DEFAULT_CONFIG_FILE = 'path-icons.config.json';
const DEFAULT_CSHARP_FILE = 'BootstrapSymbol.cs';
const DEFAULT_OUT_DIR = 'dist';
// Function to load and parse config file
async function loadConfig(configPath, inputArg) {
try {
const data = await fs.readFile(configPath, 'utf8');
return JSON.parse(data);
} catch (error) {
if (error.code === 'ENOENT') {
// If file not found and inputArg is provided, return default config
if (inputArg) {
return {
outDir: 'dist',
html: true,
css: true,
csharp: true
};
}
return {}; // File not found, return empty config
}
throw new Error(`Failed to load config file ${configPath}: ${error.message}`);
}
}
// Function to build options from parsed args and config
function buildOptions({ config, inputArg, verboseArg, rebuildArg }) {
function prepareFileName(configValue, subDir, defaultName) {
var fileName = typeof configValue === 'string'
? configValue
: configValue === true
? defaultName
: null;
if (fileName) {
fileName = normalize(fileName);
return dirname(fileName) !== '.' ? fileName : join(subDir, fileName);
}
return fileName;
}
// Validate required input
const inputFile = rebuildArg ? BI_INPUT_JSON_FILE_PATH : (inputArg || config.input);
if (!inputFile) {
throw new Error(
'Input file is required. Specify it via --input or in path-icons.config.json'
);
}
// Validate output file types
for (const key of ['json', 'css', 'html', 'csharp']) {
if (config[key] && config[key] !== true && config[key] !== false && typeof config[key] !== 'string') {
throw new Error(`Config error: "${key}" must be a boolean or string`);
}
}
const inputBaseName = basename(inputFile, extname(inputFile));
const outDir = config.outDir || DEFAULT_OUT_DIR;
let opts = {};
if (rebuildArg) {
opts = {
input: inputFile,
source: BI_JSON_FILE_PATH,
outDir: outDir,
json: prepareFileName('path-icons.json', outDir, null),
jsonOptions: { addSource: true }
};
} else {
// Merge config with CLI arguments
opts = {
input: inputFile,
source: PI_JSON_FILE_PATH,
outDir: outDir,
verbose: verboseArg || config.verbose || false,
json: prepareFileName(config.json, outDir, `${inputBaseName}.json`),
jsonOptions: config.jsonOptions,
css: prepareFileName(config.css, outDir, `${inputBaseName}.css`),
html: prepareFileName(config.html, outDir, `${inputBaseName}.html`),
csharp: prepareFileName(config.csharp, outDir, DEFAULT_CSHARP_FILE),
csharpOptions: config.csharpOptions || {},
};
}
return opts;
}
// Main logic
(async () => {
try {
const args = process.argv.slice(2);
// Show version and exit
if (args.includes('--version') || args.includes('-v')) {
console.log(`path-icons v${version}`);
process.exit(0);
}
// Parse arguments
const CONFIG_INDEX = args.indexOf('--config');
const INPUT_INDEX = args.indexOf('--input');
const inputArg = INPUT_INDEX !== -1 && INPUT_INDEX + 1 < args.length ? args[INPUT_INDEX + 1] : null;
const configArg =
CONFIG_INDEX !== -1 && CONFIG_INDEX + 1 < args.length ? args[CONFIG_INDEX + 1] : null;
const verboseArg = args.includes('--verbose');
const rebuildArg = args.includes('--rebuild');
// Load config
let config = {};
if (rebuildArg) {
} else if (configArg) {
// Load user-specified config file
const configPath = resolve(configArg);
config = await loadConfig(configPath, inputArg);
} else {
// Try to load default config file
config = await loadConfig(resolve(DEFAULT_CONFIG_FILE), inputArg);
}
// Build options
const opts = buildOptions({ config, inputArg, verboseArg, rebuildArg });
if (opts.verbose) {
console.log('Starting processing...');
console.time('Processing completed');
}
// Merge icon data
if (opts.verbose) {
console.log('\nStarting JSON merge...');
}
const mergedData = await prepareJson(resolve(opts.input), opts.source, opts);
// Log merged icons
if (opts.verbose) {
const iconNames = Object.keys(mergedData);
iconNames.forEach(name => console.log(`- ${name}`));
}
if (opts.verbose) {
console.log(`Success! Generated JSON for ${Object.keys(mergedData).length} icon${Object.keys(mergedData).length === 1 ? '' : 's'}`);
}
// Write merged JSON data
if (opts.json) {
const jsonFilePath = resolve(opts.json);
await fs.mkdir(dirname(jsonFilePath), { recursive: true });
await fs.writeFile(jsonFilePath, JSON.stringify(mergedData, null, 2), 'utf8');
console.log(`Generated JSON saved at ${jsonFilePath}`);
}
// Generate CSS if requested
if (opts.css) {
const cssFilePath = resolve(opts.css);
await fs.mkdir(dirname(cssFilePath), { recursive: true });
await createCSS(mergedData, cssFilePath);
console.log(`Generated CSS at ${cssFilePath}`);
}
// Generate HTML if requested
if (opts.html) {
const htmlFilePath = resolve(opts.html);
await fs.mkdir(dirname(htmlFilePath), { recursive: true });
await createHtml(mergedData, htmlFilePath);
console.log(`Generated HTML at ${htmlFilePath}`);
}
// Generate C# if requested
if (opts.csharp) {
const csharpPath = resolve(opts.csharp);
await fs.mkdir(dirname(csharpPath), { recursive: true });
await createCSharp(mergedData, csharpPath, opts);
console.log(`Generated C# at ${csharpPath}`);
}
if (opts.verbose) {
console.timeEnd('Processing completed');
}
} catch (error) {
console.error('Error:', error.message);
process.exit(1);
}
})();