create-fomantic-icons
Version:
A simple CLI to generate the icon.variables and icon.html.eco files for FUI
349 lines (309 loc) • 11.5 kB
text/typescript
// node
import { resolve as resolvePath } from 'path';
// npm
import * as fse from 'fs-extra';
import { load as parseYAML } from 'js-yaml';
import chalk from 'chalk';
// tasks
import { PromptResults } from '../tasks/InitialPrompt';
import { PathResults } from '../tasks/DownloadAsset';
// utils
import Logger, { spinner } from '../util/Logger';
// modals
import Icon from '../modals/Icon';
import Category from '../modals/Category';
// enums
import IconType from '../enums/IconType';
// static
import aliases from '../static/aliases.json';
export interface IconMeta {
search: {
terms: string[];
};
terms?: string[];
styles: string[];
unicode: string;
private: boolean;
}
export interface CategoryMeta {
icons: string[];
label: string;
}
export interface ParseResults {
icons: {
solid: {
icons:Icon[];
aliases: object[];
}
outline: {
icons: Icon[];
aliases: object[];
}
thin: {
icons: Icon[];
aliases: object[];
}
brand: {
icons: Icon[];
aliases: object[];
}
duotone: {
icons: Icon[];
aliases: object[];
}
deprecated: {
icons: Icon[];
aliases: object[];
}
};
categories: Category[];
fontAssetsDirectory: string;
fontFileNames: { [key: string]: string };
}
export default function parse(results: PromptResults, paths: PathResults): Promise<ParseResults> {
return new Promise((resolve) => {
const {
asset,
} = results;
const parseSpinner = spinner()
.start('parsing icons');
const fontAwesomeDirectoryName = results.iconSet.name.toLowerCase()
.replace(/\d+\+* /g, '')
.replace(/\s/g, '-');
const fontAwesomeDirectory = `${fontAwesomeDirectoryName}-${asset.version}-web`;
const iconsMetadataFilePath = resolvePath(
paths.assetDirectoryPath,
fontAwesomeDirectory,
'metadata',
'icons.json',
);
const categoriesFilePath = resolvePath(
paths.assetDirectoryPath,
fontAwesomeDirectory,
'metadata',
'categories.yml',
);
fse.readFile(iconsMetadataFilePath, (iconsErr, iconsData) => {
if (!iconsErr) {
const iconMetadata = JSON.parse(iconsData.toString());
const iconNames = Object.keys(iconMetadata);
const deprecatedTester: RegExp = /(^|-)(in|out)(-|$)/;
let icons: Icon[] = [];
let categories: Category[] = [];
iconNames.forEach((iconName) => {
const iconMeta: IconMeta = iconMetadata[iconName];
if (!iconMeta.private) {
const searchTerms: string[] = iconMeta.search
? (iconMeta.search.terms || [])
: (iconMeta.terms || []);
// deprecated
if (deprecatedTester.test(iconName)) {
icons.push(new Icon({
name: iconName,
type: IconType.DEPRECATED,
unicode: iconMeta.unicode,
searchTerms,
}));
} else {
// solid
if (iconMeta.styles.includes('solid')) {
icons.push(new Icon({
name: iconName,
type: IconType.SOLID,
unicode: iconMeta.unicode,
searchTerms,
}));
}
// outline
if (iconMeta.styles.includes('regular')) {
icons.push(new Icon({
name: iconName,
type: IconType.OUTLINE,
unicode: iconMeta.unicode,
searchTerms,
}));
}
// thin
if (iconMeta.styles.includes('light')) {
icons.push(new Icon({
name: iconName,
type: IconType.THIN,
unicode: iconMeta.unicode,
searchTerms,
}));
}
// brand
if (iconMeta.styles.includes('brands')) {
icons.push(new Icon({
name: iconName,
type: IconType.BRAND,
unicode: iconMeta.unicode,
searchTerms,
}));
}
// duotone
if (iconMeta.styles.includes('duotone')) {
icons.push(new Icon({
name: iconName,
type: IconType.DUOTONE,
unicode: iconMeta.unicode,
searchTerms,
}));
}
}
}
});
const sortAz = (a: {[key: string]: any}, b: {[key: string]: any}) => {
if (a.name < b.name) return -1;
if (a.name > b.name) return 1;
return 0;
};
// eslint-disable-next-line max-len
const deprecatedAliases = aliases.filter((alias) => deprecatedTester.test(alias.rawName)).sort(sortAz);
icons = icons.sort(sortAz);
parseSpinner.text = 'parsing categories';
fse.readFile(categoriesFilePath, (categoriesErr, categoriesData) => {
if (!categoriesErr) {
const categoriesMetadata = parseYAML(categoriesData.toString());
// @ts-ignore
const categoryNames = Object.keys(categoriesMetadata);
categoryNames.forEach((categoryName) => {
// @ts-ignore
const categoryMeta: CategoryMeta = categoriesMetadata[categoryName];
const categoryIcons: Icon[] = [];
categoryMeta.icons.forEach((iconName) => {
icons.filter((i) => i.rawName === iconName)
.forEach((icon) => {
categoryIcons.push(icon);
});
});
categories.push(new Category({
name: categoryName,
label: categoryMeta.label,
icons: categoryIcons.sort(sortAz),
}));
});
categories = categories
.sort(sortAz);
categories.push(new Category({
name: 'brands',
label: 'Brands',
icons: icons
.filter((i) => i.type === IconType.BRAND)
.sort(sortAz),
}));
categories.push(new Category({
name: 'duotone',
label: 'Duotone',
icons: icons
.filter((i) => i.type === IconType.DUOTONE)
.sort(sortAz),
}));
const deprecatedCategoryIcons = icons.filter((i) => i.type === IconType.DEPRECATED);
deprecatedAliases.forEach((alias) => {
deprecatedCategoryIcons.push(new Icon({
name: alias.name,
type: IconType.DEPRECATED,
unicode: alias.unicode,
searchTerms: [],
}));
deprecatedCategoryIcons.sort(sortAz);
});
categories.push(new Category({
name: 'deprecated',
label: 'Deprecated',
icons: deprecatedCategoryIcons,
}));
const parseResults = {
icons: {
solid: icons.filter((i) => i.type === IconType.SOLID),
outline: icons.filter((i) => i.type === IconType.OUTLINE),
thin: icons.filter((i) => i.type === IconType.THIN),
brand: icons.filter((i) => i.type === IconType.BRAND),
duotone: icons.filter((i) => i.type === IconType.DUOTONE),
deprecated: icons.filter((i) => i.type === IconType.DEPRECATED),
},
categories,
};
const {
solid: solidIcons,
outline: outlineIcons,
thin: thinIcons,
brand: brandIcons,
duotone: duotoneIcons,
deprecated: deprecatedIcons
} = parseResults.icons;
const totalIcons = solidIcons.length + outlineIcons.length
+ thinIcons.length + brandIcons.length + duotoneIcons.length
+ deprecatedIcons.length + deprecatedAliases.length;
parseSpinner.succeed('icons & categories parsed');
Logger.log();
Logger.log(chalk.cyan(' Results:'));
Logger.log(` Solid: ${chalk.cyan(String(solidIcons.length))}`);
Logger.log(` Outline: ${chalk.cyan(String(outlineIcons.length))}`);
Logger.log(` Thin: ${chalk.cyan(String(thinIcons.length))}`);
Logger.log(` Brand: ${chalk.cyan(String(brandIcons.length))}`);
Logger.log(` Duotone: ${chalk.cyan(String(duotoneIcons.length))}`);
Logger.log(` Deprecated: ${chalk.cyan(String(deprecatedIcons.length + deprecatedAliases.length))}`);
Logger.log(` ----------------`);
Logger.log(` TOTAL: ${chalk.cyan(String(totalIcons))}`);
resolve({
icons: {
solid: {
icons: solidIcons,
aliases: aliases.filter((alias) => alias.type === 'solid' && !deprecatedTester.test(alias.rawName))
.sort(sortAz),
},
outline: {
icons: outlineIcons,
aliases: aliases.filter((alias) => alias.type === 'outline' && !deprecatedTester.test(alias.rawName))
.sort(sortAz),
},
thin: {
icons: thinIcons,
aliases: aliases.filter((alias) => alias.type === 'thin' && !deprecatedTester.test(alias.rawName))
.sort(sortAz),
},
brand: {
icons: brandIcons,
aliases: aliases.filter((alias) => alias.type === 'brand' && !deprecatedTester.test(alias.rawName))
.sort(sortAz),
},
duotone: {
icons: duotoneIcons,
aliases: aliases.filter((alias) => alias.type === 'duotone' && !deprecatedTester.test(alias.rawName))
.sort(sortAz),
},
deprecated: {
icons: deprecatedIcons,
aliases: deprecatedAliases,
},
},
categories,
fontAssetsDirectory: resolvePath(
paths.assetDirectoryPath,
fontAwesomeDirectory,
'webfonts',
),
fontFileNames: {
'fa-solid-900': 'icons',
'fa-regular-400': 'outline-icons',
'fa-light-300': 'thin-icons',
'fa-brands-400': 'brand-icons',
'fa-duotone-900': 'duotone-icons',
},
});
} else {
parseSpinner.stop();
Logger.error(categoriesErr);
process.exit(1);
}
});
} else {
parseSpinner.stop();
Logger.error(iconsErr);
process.exit(1);
}
});
});
}