monkeytype/frontend/scripts/fontawesome.mjs

157 lines
3.9 KiB
JavaScript

import * as fs from "fs";
import * as path from "path";
const iconSet = {
solid: parseIcons("solid"),
regular: parseIcons("regular"),
brands: parseIcons("brands"),
};
/**
* Map containing reserved classes by module
*/
const modules2 = {
animated: ["spin", "pulse"],
"bordererd-pulled": ["border", "pull-left", "pull-right"],
"fixed-width": ["fw"],
larger: [
"lg",
"xs",
"sm",
"1x",
"2x",
"3x",
"4x",
"5x",
"6x",
"7x",
"8x",
"9x",
"10x",
],
"rotated-flipped": [
"rotate-90",
"rotate-180",
"rotate-270",
"flip-horizontal",
"flip-vertical",
"flip-both",
],
stacked: ["stack", "stack-1x", "stack-2x", "inverse"],
};
/**
* fontawesome icon config
* @typedef {Object} FontawesomeConfig
* @property {string[]} solid - used solid icons without `fa-` prefix
* @property {string[]} brands - used brands icons without `fa-` prefix
*/
/**
* Detect used fontawesome icons in the directories `src/**` and `static/**{.html|.css}`
* @param {boolean} debug - Enable debug output
* @returns {FontawesomeConfig} - used icons
*/
export function getFontawesomeConfig(debug = false) {
const time = Date.now();
const srcFiles = findAllFiles(
"./src",
(filename) =>
!filename.endsWith("fontawesome-5.scss") &&
!filename.endsWith("fontawesome-6.scss") //ignore our own css
);
const staticFiles = findAllFiles(
"./static",
(filename) => filename.endsWith(".html") || filename.endsWith(".css")
);
const allFiles = [...srcFiles, ...staticFiles];
const usedClassesSet = new Set();
const regex = /\bfa-[a-z0-9-]+\b/g;
for (const file of allFiles) {
const fileContent = fs.readFileSync("./" + file).toString();
const matches = fileContent.match(regex);
if (matches) {
matches.forEach((match) => {
const [icon] = match.split(" ");
usedClassesSet.add(icon.substring(3));
});
}
}
const usedClasses = new Array(...usedClassesSet).sort();
const allModuleClasses = Object.values(modules2).flatMap((it) => it);
const icons = usedClasses.filter((it) => !allModuleClasses.includes(it));
const solid = icons.filter((it) => iconSet.solid.includes(it));
const regular = icons.filter((it) => iconSet.regular.includes(it));
const brands = usedClasses.filter((it) => iconSet.brands.includes(it));
const leftOvers = icons.filter(
(it) => !(solid.includes(it) || regular.includes(it) || brands.includes(it))
);
if (leftOvers.length !== 0) {
throw new Error("unknown icons: " + leftOvers);
}
if (debug === true) {
console.debug(
"Make sure fontawesome modules are active: ",
Object.entries(modules2)
.filter((it) => usedClasses.filter((c) => it[1].includes(c)).length > 0)
.map((it) => it[0])
.filter((it) => it !== "brands")
.join(", ")
);
console.debug(
"Here is your config: \n",
JSON.stringify({
regular,
solid,
brands,
})
);
console.debug("Detected fontawesome classes in", Date.now() - time, "ms");
}
return {
regular,
solid,
brands,
};
}
//detect if we run this as a main
if (import.meta.url.endsWith(process.argv[1])) {
getFontawesomeConfig(true);
}
function toFileAndDir(dir, file) {
const name = path.join(dir, file);
return { name, isDirectory: fs.statSync(name).isDirectory() };
}
function findAllFiles(dir, filter = (filename) => true) {
return fs
.readdirSync(dir)
.map((it) => toFileAndDir(dir, it))
.filter((file) => file.isDirectory || filter(file.name))
.reduce((files, file) => {
return file.isDirectory
? [...files, ...findAllFiles(file.name, filter)]
: [...files, file.name];
}, []);
}
function parseIcons(iconSet) {
const file = fs
.readFileSync(`node_modules/@fortawesome/fontawesome-free/js/${iconSet}.js`)
.toString();
return file
.match(/\"(.*)\"\: \[.*\],/g)
.map((it) => it.substring(1, it.indexOf(":") - 1));
}