UNPKG

fantasticon

Version:
945 lines (906 loc) 30.6 kB
var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __commonJS = (cb, mod) => function __require() { return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); // package.json var require_package = __commonJS({ "package.json"(exports2, module2) { module2.exports = { name: "fantasticon", version: "0.0.0", description: "Icon font generation tool", repository: { type: "git", url: "git+https://github.com/tancredi/fantasticon.git" }, keywords: [ "icons", "fonts", "iconfonts", "svg", "vector" ], author: "Tancredi Trugenberger", bugs: { url: "https://github.com/tancredi/fantasticon/issues" }, homepage: "https://github.com/tancredi/fantasticon#readme", type: "module", exports: { require: "./dist/index.cjs", import: "./dist/index.js" }, main: "dist/index.cjs", module: "dist/index.js", types: "dist/index.d.ts", bin: { fantasticon: "bin/fantasticon" }, license: "MIT", scripts: { test: "vitest run", "test:watch": "vitest dev", build: "rimraf dist && npm run ts", ts: "tsup --dts --format esm,cjs src/index.ts src/cli/index.ts", "build:package:cjs": `touch ./dist/cjs/package.json && echo '{"type": "commonjs"}' > ./dist/cjs/package.json`, "build:cjs": "tsc --project ./tsconfig.cjs.json && npm run build:package:cjs", "build:package:esm": `touch ./dist/esm/package.json && echo '{"type": "module"}' > ./dist/esm/package.json`, "build:esm": "tsc --project ./tsconfig.json && npm run build:package:esm", watch: "npm run ts -- --watch", typecheck: "tsc --noEmit --project tsconfig.test.json --noEmit", lint: "prettier --check .", fix: "prettier --write ." }, files: [ "bin/fantasticon", "dist/**/*", "!dist/**/{__mocks__,__tests__}/", "templates/*.hbs" ], prepublish: "build", dependencies: { case: "^1.6.3", "cli-color": "^2.0.4", commander: "^14.0.2", glob: "^13.0.0", handlebars: "^4.7.8", slugify: "^1.6.6", svg2ttf: "^6.0.3", svgicons2svgfont: "^15.0.1", ttf2eot: "^3.1.0", ttf2woff: "^3.0.0", ttf2woff2: "^8.0.0" }, devDependencies: { "@types/cli-color": "^2.0.6", "@types/glob": "^9.0.0", "@types/node": "^25.0.3", "@types/svg2ttf": "^5.0.3", "@types/svgicons2svgfont": "^14.0.0", "@types/ttf2eot": "^2.0.2", "@types/ttf2woff": "^2.0.4", "@types/ttf2woff2": "^6.0.0", prettier: "^3.7.4", rimraf: "^6.1.2", "semantic-release": "^25.0.2", tsup: "^8.5.1", typescript: "^5.9.3", vitest: "^4.0.16" }, jest: { preset: "ts-jest", testEnvironment: "node", rootDir: "./src", transform: { "^.+\\.tsx?$": [ "ts-jest", { tsconfig: "./tsconfig.test.json" } ] } }, engines: { node: ">= 22.0" } }; } }); // src/cli/index.ts var commander = __toESM(require("commander"), 1); // src/types/misc.ts var FontAssetType = /* @__PURE__ */ ((FontAssetType2) => { FontAssetType2["EOT"] = "eot"; FontAssetType2["WOFF2"] = "woff2"; FontAssetType2["WOFF"] = "woff"; FontAssetType2["TTF"] = "ttf"; FontAssetType2["SVG"] = "svg"; return FontAssetType2; })(FontAssetType || {}); var OtherAssetType = /* @__PURE__ */ ((OtherAssetType3) => { OtherAssetType3["CSS"] = "css"; OtherAssetType3["SCSS"] = "scss"; OtherAssetType3["SASS"] = "sass"; OtherAssetType3["HTML"] = "html"; OtherAssetType3["JSON"] = "json"; OtherAssetType3["TS"] = "ts"; return OtherAssetType3; })(OtherAssetType || {}); var ASSET_TYPES_WITH_TEMPLATE = [ "css" /* CSS */, "html" /* HTML */, "scss" /* SCSS */, "sass" /* SASS */ ]; var ASSET_TYPES = { ...FontAssetType, ...OtherAssetType }; // src/cli/config-loader.ts var import_path = require("path"); // src/utils/fs-async.ts var import_util = require("util"); var fs = __toESM(require("fs"), 1); var readFile2 = (0, import_util.promisify)(fs.readFile); var writeFile2 = (0, import_util.promisify)(fs.writeFile); var stat2 = (0, import_util.promisify)(fs.stat); var checkPath = async (filepath, type) => { try { const result = await stat2(filepath); if (type) { return type === "directory" ? result.isDirectory() : result.isFile(); } return true; } catch (err) { return false; } }; // src/cli/config-loader.ts var DEFAULT_FILEPATHS = [ ".fantasticonrc", "fantasticonrc", ".fantasticonrc.json", "fantasticonrc.json", ".fantasticonrc.js", "fantasticonrc.js" ]; var attemptLoading = async (filepath) => { if (await checkPath(filepath, "file")) { try { return require((0, import_path.join)(process.cwd(), filepath)); } catch (err) { } try { return JSON.parse(await readFile2(filepath, "utf8")); } catch (err) { } throw new Error(`Failed parsing configuration at '${filepath}'`); } }; var loadConfig = async (filepath) => { let loadedConfigPath = null; let loadedConfig = {}; if (filepath) { loadedConfig = await attemptLoading(filepath); loadedConfigPath = filepath; } else { for (const path of DEFAULT_FILEPATHS) { loadedConfig = await attemptLoading(path); if (loadedConfig) { loadedConfigPath = path; break; } } } return { loadedConfig, loadedConfigPath }; }; // src/constants.ts var import_path4 = require("path"); // src/utils/path.ts var import_fs = require("fs"); var import_path2 = require("path"); var import_url = require("url"); var import_meta = {}; var MAX_LOOKUP_DEPTH = 5; var getFileName = () => typeof __filename !== "undefined" ? __filename : (0, import_url.fileURLToPath)(import_meta.url); var getDirName = () => (0, import_path2.dirname)(getFileName()); var removeExtension = (path) => path.includes(".") ? path.split(".").slice(0, -1).join(".") : path; var splitSegments = (path) => (0, import_path2.normalize)(path).split(/\/|\\/).filter((part) => part && part !== "."); var getRoot = () => { let current = getDirName(); for (let i = 0; i < MAX_LOOKUP_DEPTH; i++) { const pkg = (0, import_path2.join)(current, "package.json"); if ((0, import_fs.existsSync)(pkg)) return (0, import_path2.resolve)(current); const parent = (0, import_path2.dirname)(current); current = parent; } throw new Error("Module root not found"); }; // src/utils/icon-id.ts var import_slugify = __toESM(require("slugify"), 1); var getIconId = ({ relativeFilePath }) => (0, import_slugify.default)(removeExtension(relativeFilePath).replace(/(\/|\\|\.)+/g, "-"), { replacement: "-", remove: /['"`]/g }); // src/constants.ts var TEMPLATES_DIR = (0, import_path4.resolve)(getRoot(), "templates"); var DEFAULT_OPTIONS = { name: "icons", fontTypes: ["eot" /* EOT */, "woff2" /* WOFF2 */, "woff" /* WOFF */], assetTypes: [ "css" /* CSS */, "html" /* HTML */, "json" /* JSON */, "ts" /* TS */ ], formatOptions: { json: { indent: 4 } }, pathOptions: {}, templates: {}, codepoints: {}, round: void 0, fontHeight: 300, descent: void 0, normalize: void 0, selector: null, tag: "i", prefix: "icon", fontsUrl: void 0, getIconId }; var DEFAULT_START_CODEPOINT = 61697; // src/utils/assets.ts var import_glob = require("glob"); var import_path6 = require("path"); var ASSETS_EXTENSION = "svg"; var loadPaths = async (dir) => { const globPath = (0, import_path6.join)(dir, `**/*.${ASSETS_EXTENSION}`); const files = await (0, import_glob.glob)(globPath, {}); if (!files.length) { throw new Error(`No SVGs found in ${dir}`); } return files; }; var failForConflictingId = ({ relativePath: pathA, id }, { relativePath: pathB }) => { throw new Error( `Conflicting result from 'getIconId': '${id}' - conflicting input files: ` + [pathA, pathB].map((fpath) => ` - ${fpath}`).join("\n") ); }; var loadAssets = async ({ inputDir, getIconId: getIconId2 }) => { const paths = await loadPaths(inputDir); const out = {}; let index = 0; for (const path of paths) { const relativePath = (0, import_path6.relative)((0, import_path6.resolve)(inputDir), (0, import_path6.resolve)(path)); const parts = splitSegments(relativePath); const basename = removeExtension(parts.pop()); const absolutePath = (0, import_path6.resolve)(path); const iconId = getIconId2({ basename, relativeDirPath: (0, import_path6.join)(...parts), absoluteFilePath: absolutePath, relativeFilePath: relativePath, index }); const result = { id: iconId, relativePath, absolutePath }; if (out[iconId]) { failForConflictingId(out[iconId], result); } out[iconId] = result; index++; } return out; }; var writeAssets = async (assets, { name, pathOptions = {}, outputDir }) => { const results = []; for (const ext of Object.keys(assets)) { const filename = [name, ext].join("."); const writePath = pathOptions[ext] || (0, import_path6.join)(outputDir, filename); results.push({ content: assets[ext], writePath }); await writeFile2(writePath, assets[ext]); } return results; }; // src/generators/generator-options.ts var import_path8 = require("path"); // src/utils/codepoints.ts var getCodepoints = (assets, predefined = {}, start = DEFAULT_START_CODEPOINT) => { const out = {}; const used = Object.values(predefined); let current = start; const getNextCodepoint = () => { while (used.includes(current)) { current++; } const res = current; current++; return res; }; for (const id of Object.keys(assets)) { if (!predefined[id]) { out[id] = getNextCodepoint(); } } return { ...predefined, ...out }; }; var getHexCodepoint = (decimalCodepoint) => decimalCodepoint.toString(16); // src/generators/generator-options.ts var getGeneratorOptions = (options, assets) => ({ ...options, codepoints: getCodepoints(assets, options.codepoints), formatOptions: prefillOptions( Object.values(ASSET_TYPES), options.formatOptions, (assetType) => DEFAULT_OPTIONS.formatOptions[assetType] || {} ), templates: prefillOptions( ASSET_TYPES_WITH_TEMPLATE, options.templates, (assetType) => (0, import_path8.join)(TEMPLATES_DIR, `${assetType}.hbs`) ), assets }); var prefillOptions = (keys, userOptions = {}, getDefault) => keys.reduce( (cur = {}, type) => ({ ...cur, [type]: mergeOptions(userOptions[type], getDefault(type)) }), {} ); var mergeOptions = (input, defaultVal) => { if (typeof defaultVal === "object") { return { ...defaultVal, ...input }; } return input || defaultVal; }; // src/utils/validation.ts var parseNumeric = (value) => { const out = Number(value); if (typeof value !== "number" && typeof value !== "string" || Number.isNaN(out)) { throw new Error(`${value} is not a valid number`); } return out; }; var parseString = (value) => { if (typeof value !== "string") { throw new Error(`${value} is not a string`); } return value; }; var parseFunction = (value) => { if (typeof value !== "function") { throw new Error(`${value} is not a function`); } return value; }; var listMembersParser = (allowedValues) => (values) => { for (const value of values) { if (!allowedValues.includes(value)) { throw new Error( [ `${value} is not valid`, `accepted values are: ${allowedValues.join(", ")}` ].join(" - ") ); } } return values; }; var removeUndefined = (object) => { for (const key of Object.keys(object)) { if (typeof object[key] === "undefined") { delete object[key]; } } return object; }; var parseBoolean = (val) => { if (typeof val === "string" && ["1", "0", "true", "false"].includes(val)) { return val === "true" || val === "1"; } else if (typeof val === "number" && [0, 1].includes(val)) { return val === 1; } else if (typeof val === "boolean") { return val; } throw new Error(`must be a boolean value`); }; var skipIfMatching = (match) => (fn) => (val) => val === match ? match : fn(val); var parseDir = async (dirname2) => { if (await checkPath(dirname2, "directory") === false) { throw new Error(`${dirname2} is not a directory`); } return dirname2; }; var nullable = skipIfMatching(null); var optional = skipIfMatching(void 0); // src/core/config-parser.ts var CONFIG_VALIDATORS = { inputDir: [optional(parseString), optional(parseDir)], outputDir: [optional(parseString), optional(parseDir)], name: [optional(parseString)], fontTypes: [listMembersParser(Object.values(FontAssetType))], assetTypes: [listMembersParser(Object.values(OtherAssetType))], formatOptions: [], pathOptions: [], templates: [], codepoints: [], fontHeight: [optional(parseNumeric)], descent: [optional(parseNumeric)], normalize: [optional(parseBoolean)], round: [optional(parseNumeric)], selector: [nullable(parseString)], tag: [parseString], prefix: [parseString], fontsUrl: [optional(parseString)], getIconId: [optional(parseFunction)] }; var parseConfig = async (input = {}) => { const options = { ...DEFAULT_OPTIONS, ...input }; const out = {}; const allkeys = [ .../* @__PURE__ */ new Set([...Object.keys(options), ...Object.keys(CONFIG_VALIDATORS)]) ]; for (const key of allkeys) { const validators = CONFIG_VALIDATORS[key]; if (!validators) { throw new Error(`The option '${key}' is not recognised`); } let val = options[key]; try { for (const fn of validators) { val = await fn(val, val); } } catch (err) { throw new Error(`Invalid option ${key}: ${err.message}`); } out[key] = val; } return out; }; // src/generators/asset-types/svg.ts var import_fs2 = require("fs"); var import_svgicons2svgfont = require("svgicons2svgfont"); var generator = { generate: ({ name: fontName, fontHeight, descent, normalize: normalize2, assets, codepoints, formatOptions: { svg } = {} }) => new Promise((resolve5) => { let font = Buffer.alloc(0); const fontStream = new import_svgicons2svgfont.SVGIcons2SVGFontStream({ fontName, fontHeight, descent, normalize: normalize2, // log: () => null, ...svg }).on("data", (data) => font = Buffer.concat([font, Buffer.from(data)])).on("end", () => resolve5(font.toString())); for (const { id, absolutePath } of Object.values(assets)) { const glyph = (0, import_fs2.createReadStream)(absolutePath); const unicode = String.fromCharCode(codepoints[id]); glyph.metadata = { name: id, unicode: [unicode] }; fontStream.write(glyph); } fontStream.end(); }) }; var svg_default = generator; // src/generators/asset-types/ttf.ts var import_svg2ttf = __toESM(require("svg2ttf"), 1); var generator2 = { dependsOn: "svg" /* SVG */, async generate({ formatOptions }, svg) { const font = (0, import_svg2ttf.default)(svg, { ts: 0, ...formatOptions?.ttf || {} }); return Buffer.from(font.buffer); } }; var ttf_default = generator2; // src/generators/asset-types/woff.ts var import_ttf2woff = __toESM(require("ttf2woff"), 1); var generator3 = { dependsOn: "ttf" /* TTF */, async generate({ formatOptions }, ttf) { const font = (0, import_ttf2woff.default)(new Uint8Array(ttf), formatOptions?.woff); return Buffer.from(font.buffer); } }; var woff_default = generator3; // src/generators/asset-types/woff2.ts var generator4 = { dependsOn: "ttf" /* TTF */, async generate(_options, ttf) { const font = (await import("ttf2woff2")).default(ttf); return Buffer.from(font.buffer); } }; var woff2_default = generator4; // src/generators/asset-types/eot.ts var import_ttf2eot = __toESM(require("ttf2eot"), 1); var generator5 = { dependsOn: "ttf" /* TTF */, async generate(_options, ttf) { const font = (0, import_ttf2eot.default)(new Uint8Array(ttf)); return Buffer.from(font.buffer); } }; var eot_default = generator5; // src/utils/template.ts var import_handlebars = __toESM(require("handlebars"), 1); var import_path9 = require("path"); var TEMPLATE_HELPERS = { codepoint: getHexCodepoint }; var renderTemplate = async (templatePath, context, options = {}) => { const absoluteTemplatePath = (0, import_path9.isAbsolute)(templatePath) ? templatePath : (0, import_path9.resolve)(process.cwd(), templatePath); const template = await readFile2(absoluteTemplatePath, "utf8"); return import_handlebars.default.compile(template)(context, { ...options, helpers: { ...TEMPLATE_HELPERS, ...options.helpers || {} } }); }; // src/utils/hash.ts var import_crypto = __toESM(require("crypto"), 1); var getHash = (...contents) => { const hash = import_crypto.default.createHash("md5"); for (const content of contents) { hash.update(content); } return hash.digest("hex"); }; // src/utils/css.ts var renderSrcOptions = { ["eot" /* EOT */]: { formatValue: "embedded-opentype", getSuffix: () => "#iefix" }, ["woff2" /* WOFF2 */]: { formatValue: "woff2" }, ["woff" /* WOFF */]: { formatValue: "woff" }, ["ttf" /* TTF */]: { formatValue: "truetype" }, ["svg" /* SVG */]: { formatValue: "svg", getSuffix: (name) => `#${name}` } }; var renderSrcAttribute = ({ name, fontTypes, fontsUrl }, font, { inline = false } = {}) => fontTypes.map((fontType) => { const { formatValue, getSuffix } = renderSrcOptions[fontType]; const hash = getHash(font.toString("utf8")); const suffix = getSuffix ? getSuffix(name) : ""; return `url("${fontsUrl || "."}/${name}.${fontType}?${hash}${suffix}") format("${formatValue}")`; }).join(inline ? ", " : ",\n"); // src/generators/asset-types/css.ts var generator6 = { dependsOn: "svg" /* SVG */, generate: (options, svg) => renderTemplate(options.templates.css, { ...options, fontSrc: renderSrcAttribute(options, svg) }) }; var css_default = generator6; // src/generators/asset-types/html.ts var generator7 = { generate: (options) => renderTemplate(options.templates.html, options) }; var html_default = generator7; // src/generators/asset-types/json.ts var generator8 = { generate: async ({ formatOptions: { json } = {}, codepoints }) => JSON.stringify(codepoints, null, json?.indent) }; var json_default = generator8; // src/generators/asset-types/ts.ts var import_case = __toESM(require("case"), 1); var generateEnumKeys = (assetKeys) => assetKeys.map((name) => { const enumName = import_case.default.pascal(name); const prefix = enumName.match(/^\d/) ? "i" : ""; return { [name]: `${prefix}${enumName}` }; }).reduce((prev, curr) => Object.assign(prev, curr), {}); var generateEnums = (enumName, enumKeys, quote = '"') => [ `export enum ${enumName} {`, ...Object.entries(enumKeys).map( ([enumValue, enumKey]) => ` ${enumKey} = ${quote}${enumValue}${quote},` ), "}\n" ].join("\n"); var generateConstant = ({ constantName, enumName, literalIdName, literalKeyName, enumKeys, codepoints, quote = '"', kind = {} }) => { let varType = ": Record<string, string>"; if (kind.enum) { varType = `: { [key in ${enumName}]: string }`; } else if (kind.literalId) { varType = `: { [key in ${literalIdName}]: string }`; } else if (kind.literalKey) { varType = `: { [key in ${literalKeyName}]: string }`; } return [ `export const ${constantName}${varType} = {`, Object.entries(enumKeys).map(([enumValue, enumKey]) => { const key = kind.enum ? `[${enumName}.${enumKey}]` : `${quote}${enumValue}${quote}`; return ` ${key}: ${quote}${codepoints[enumValue]}${quote},`; }).join("\n"), "};\n" ].join("\n"); }; var generateStringLiterals = (typeName, literals, quote = '"') => [ `export type ${typeName} =`, `${literals.map((key) => ` | ${quote}${key}${quote}`).join("\n")}; ` ].join("\n"); var generator9 = { generate: async ({ name, codepoints, assets, formatOptions: { ts } = {} }) => { const quote = Boolean(ts?.singleQuotes) ? "'" : '"'; const generateKind = (Boolean(ts?.types?.length) ? ts.types : ["enum", "constant", "literalId", "literalKey"]).map((kind) => ({ [kind]: true })).reduce((prev, curr) => Object.assign(prev, curr), {}); const enumName = ts?.enumName || import_case.default.pascal(name); const constantName = ts?.constantName || `${import_case.default.constant(name)}_CODEPOINTS`; const literalIdName = ts?.literalIdName || `${import_case.default.pascal(name)}Id`; const literalKeyName = ts?.literalKeyName || `${import_case.default.pascal(name)}Key`; const names = { enumName, constantName, literalIdName, literalKeyName }; const enumKeys = generateEnumKeys(Object.keys(assets)); const stringLiteralId = generateKind.literalId ? generateStringLiterals(literalIdName, Object.keys(enumKeys), quote) : null; const stringLiteralKey = generateKind.literalKey ? generateStringLiterals(literalKeyName, Object.values(enumKeys), quote) : null; const enums = generateKind.enum ? generateEnums(enumName, enumKeys, quote) : null; const constant = generateKind.constant ? generateConstant({ ...names, enumKeys, codepoints, quote, kind: generateKind }) : null; return [stringLiteralId, stringLiteralKey, enums, constant].filter(Boolean).join("\n"); } }; var ts_default = generator9; // src/generators/asset-types/sass.ts var generator10 = { dependsOn: "svg" /* SVG */, generate: (options, svg) => renderTemplate(options.templates.sass, { ...options, fontSrc: renderSrcAttribute(options, svg, { inline: true }) }) }; var sass_default = generator10; // src/generators/asset-types/scss.ts var generator11 = { dependsOn: "svg" /* SVG */, generate: (options, svg) => renderTemplate( options.templates.scss, { ...options, fontSrc: renderSrcAttribute(options, svg) }, { helpers: { codepoint: (str) => str.toString(16) } } ) }; var scss_default = generator11; // src/generators/asset-types/index.ts var generators = { ["svg" /* SVG */]: svg_default, ["ttf" /* TTF */]: ttf_default, ["woff" /* WOFF */]: woff_default, ["woff2" /* WOFF2 */]: woff2_default, ["eot" /* EOT */]: eot_default, ["css" /* CSS */]: css_default, ["html" /* HTML */]: html_default, ["json" /* JSON */]: json_default, ["ts" /* TS */]: ts_default, ["sass" /* SASS */]: sass_default, ["scss" /* SCSS */]: scss_default }; var asset_types_default = generators; // src/generators/generate-assets.ts var generateAssets = async (options) => { const generated = {}; const generateTypes = [...options.fontTypes, ...options.assetTypes]; const generateAsset = async (type) => { if (generated[type]) { return generated[type]; } const generator12 = asset_types_default[type]; const dependsOn = "dependsOn" in generator12 && generator12.dependsOn; if (dependsOn && !generated[dependsOn]) { generated[dependsOn] = await generateAsset(dependsOn); } return generated[type] = await generator12.generate( options, dependsOn ? generated[dependsOn] : null ); }; for (const type of generateTypes) { await generateAsset(type); } return generateTypes.reduce( (out, type) => ({ ...out, [type]: generated[type] }), {} ); }; // src/core/runner.ts var sanitiseOptions = (userOptions) => parseConfig({ ...DEFAULT_OPTIONS, ...userOptions }); var generateFonts = async (userOptions, mustWrite = false) => { const options = await sanitiseOptions(userOptions); const { outputDir, inputDir } = options; if (!inputDir) { throw new Error("You must specify an input directory"); } if (mustWrite && !outputDir) { throw new Error("You must specify an output directory"); } const assetsIn = await loadAssets(options); const generatorOptions = getGeneratorOptions(options, assetsIn); const assetsOut = await generateAssets(generatorOptions); const writeResults = outputDir ? await writeAssets(assetsOut, options) : []; const { codepoints } = generatorOptions; return { options, assetsIn, assetsOut, writeResults, codepoints }; }; // src/cli/logger.ts var import_cli_color = __toESM(require("cli-color"), 1); // src/utils/string.ts var pluralize = (word, amount) => amount === 1 ? word : `${word}s`; // src/cli/logger.ts var getLogger = (debug = false, silent = false) => ({ error(error) { const message = error instanceof Error && error.message || error; console.log(import_cli_color.default.red(message)); debug && error instanceof Error && console.log(error.stack); }, log(...values) { !silent && console.log(...values); }, start(loadedConfigPath = null) { this.log(import_cli_color.default.yellow(`Generating font kit...`)); if (loadedConfigPath) { this.log( import_cli_color.default.green( `\u2714 Using configuration file: ${import_cli_color.default.green.bold(loadedConfigPath)}` ) ); } }, results({ assetsIn, writeResults, options: { inputDir } }) { const iconsCount = Object.values(assetsIn).length; this.log( import_cli_color.default.white( `\u2714 ${iconsCount} ${pluralize("SVG", iconsCount)} found in ${inputDir}` ) ); for (const { writePath } of writeResults) { this.log(import_cli_color.default.blue(`\u2714 Generated`, import_cli_color.default.blueBright(writePath))); } this.log(import_cli_color.default.green.bold("Done")); } }); // src/utils/module.ts var getPackageInfo = () => { const { bin, name, version } = require_package(); return { bin, name, version }; }; // src/cli/index.ts var packageInfo = getPackageInfo(); var getCommandName = () => packageInfo.bin && Object.keys(packageInfo.bin)[0] || packageInfo.name; var cli = async () => { config(); const input = commander.program.parse(process.argv); const { debug, silent, config: configPath } = input.opts(); const logger = getLogger(debug, silent); try { const { loadedConfig, loadedConfigPath } = await loadConfig(configPath); const results = await run(await buildOptions(input, loadedConfig)); logger.start(loadedConfigPath); logger.results(results); } catch (error) { logger.error(error); process.exitCode = 1; } }; var printList = (available, defaults) => ` (default: ${defaults.join(", ")}, available: ${Object.values( available ).join(", ")})`; var printDefaultValue = (value) => { let printVal = String(value); if (typeof value === "undefined") { return ""; } return ` (default: ${printVal})`; }; var printDefaultOption = (key) => printDefaultValue(DEFAULT_OPTIONS[key]); var printConfigPaths = () => DEFAULT_FILEPATHS.join(" | "); var config = () => { commander.program.storeOptionsAsProperties(false).name(getCommandName()).version(packageInfo.version).arguments("[input-dir]").option( "-c, --config <value>", `custom config path (default: ${printConfigPaths()})` ).option("-o, --output <value>", "specify output directory").option( "-n, --name <value>", "base name of the font set used both as default asset name" + printDefaultOption("name") ).option( "-t, --font-types <value...>", `specify font formats to generate` + printList(FontAssetType, DEFAULT_OPTIONS.fontTypes) ).option( "-g --asset-types <value...>", `specify other asset types to generate` + printList(OtherAssetType, DEFAULT_OPTIONS.assetTypes) ).option( "-h, --font-height <value>", "the output font height (icons will be scaled so the highest has this height)" + printDefaultOption("fontHeight") ).option( "--descent <value>", "the font descent" + printDefaultOption("descent") ).option( "--normalize [bool]", "normalize icons by scaling them to the height of the highest icon" + printDefaultOption("normalize") ).option("-r, --round [bool]", "setup the SVG path rounding [10e12]").option( "--selector <value>", "use a CSS selector instead of 'tag + prefix'" + printDefaultOption("selector") ).option( "-p, --prefix <value>", "CSS class prefix" + printDefaultOption("prefix") ).option( "--tag <value>", "CSS base tag for icons" + printDefaultOption("tag") ).option( "-u, --fonts-url <value>", "public URL to the fonts directory (used in the generated CSS)" ).option("--debug", "display errors stack trace" + printDefaultValue(false)).option("--silent", "run with no logs" + printDefaultValue(false)); }; var buildOptions = async (cmd, loadedConfig = {}) => { const [inputDir] = cmd.args; const opts = cmd.opts(); return { ...loadedConfig, ...removeUndefined({ inputDir, outputDir: opts.output, name: opts.name, fontTypes: opts.fontTypes, assetTypes: opts.assetTypes, fontHeight: opts.fontHeight, descent: opts.descent, normalize: opts.normalize, round: opts.round, selector: opts.selector, tag: opts.tag, prefix: opts.prefix, fontsUrl: opts.fontsUrl }) }; }; var run = async (options) => await generateFonts(options, true); cli();