fantasticon
Version:
Icon font generation tool
945 lines (906 loc) • 30.6 kB
JavaScript
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();