UNPKG

@adambh/vue-fontawesome-autogen

Version:

Automates the importing of used fontawesome icons.

269 lines (218 loc) 7.7 kB
const path = require("path"); const fs = require("fs"); const src = path.resolve(__dirname, "../../..", "src"); const node_modules = path.resolve(__dirname, "../.."); // const src = "./example/src"; // const node_modules = "./example/node_modules"; const iconTypes = [ { prefix: "fas", libary: "solid", type: "" }, { prefix: "far", libary: "regular", type: "" }, { prefix: "fal", libary: "light", type: "" }, { prefix: "fab", libary: "brands", type: "" }, { prefix: "fad", libary: "duotone", type: "" }, { prefix: "fat", libary: "thin", type: "" }, ]; const prefixes = /fas|far|fal|fab|fad|fat/; const faModule = "@fortawesome"; const outputFolder = `${src}/plugins`; const outputFile = `${outputFolder}/fontawesome-autogen.js`; const componentCheck = /[Vue\.component\("]([a-z0-9-]+)["],[ ]{0,1}FontAwesomeIcon\)/g; const componentName = getComponentName(); // console.log(componentName); // Edit this per requirements const matches = [ /(?<=icon:\s"+)fa.*?(?=",)/g, // Get icons within props or data /(?<=default:\s"+)fa.*?(?=",)/g, // Get default icon values within props ///(?<=<fa\s+icon="+).*?(?=")/g // Get icons within components // /icon=['"]([a-z-]+)['"]|:icon=\"\[['"](fa[a-z])['"],.*['"]([a-z-]+)['"]\]/g, new RegExp( `<(?:fa|${componentName})\\s+icon=['"]([a-z-]+)['"]|<${componentName}\\s+:icon=['"]\\[['"](fa[a-z])['"],[ ]{0,1}['"]([a-z-]+)['"]\\]|<FontAwesomeIcon\\s+icon=['"]([a-z-]+)['"]|<FontAwesomeIcon\\s+icon={\\[['"](fa[a-z])['"],[ ]{0,1}['"]([a-z-]+)['"]\\]}`, "g" ), ]; String.prototype.replaceAt = function (index, replacement) { return ( this.substr(0, index) + replacement + this.substr(index + replacement.length) ); }; String.prototype.removeAt = function removeAt(index) { return this.slice(0, index) + this.slice(index + 1); }; function getLibraries() { const libs = { pro: [], free: [] }; const pathing = `${node_modules}${path.sep}${faModule}`; // Get all installed libraries for (const name of fs.readdirSync(pathing)) { if (!fs.lstatSync(`${pathing}${path.sep}${name}`).isDirectory()) continue; const lib = name.match(/(?=pro-|free-).*?(?=-svg-icons)/g); if (lib === null) continue; const libSpl = lib[0].split("-"); libs[libSpl[0]].push(libSpl[1]); } // Take only pro libraries instead of free ones const filtered = []; for (const [key, value] of Object.entries(libs)) { for (const val of value) { if (!filtered.map((e) => e.split("-")[1]).includes(val)) { filtered.push(`${key}-${val}`); } } } // Populate them into our libs object // the reason this is in its own loop is because the prior loop will fill the array out of order, it doesn't matter much anyway for (const l of filtered) { const spl = l.split("-"); const index = iconTypes.indexOf(iconTypes.find((e) => e.libary === spl[1])); iconTypes[index].type = spl[0]; } } var files = []; function getFiles(path) { // Recursive ((!)) for (const name of fs.readdirSync(path)) { const entry = `${path}/${name}`; fs.lstatSync(entry).isDirectory() ? getFiles(entry) : files.push(entry); } // Filter all non-vue files files = files.filter( (file) => file.indexOf(".vue") > -1 || file.indexOf(".jsx") > -1 || file.indexOf(".js") > -1 ); } function getComponentName() { var files = []; getfiles(src); function getfiles(path) { // Recursive ((!)) for (const name of fs.readdirSync(path)) { const entry = `${path}/${name}`; fs.lstatSync(entry).isDirectory() ? getfiles(entry) : files.push(entry); } // Filter all non-vue files files = files.filter((file) => file.indexOf(".js") > -1); } for (const f of files) { const file = fs.readFileSync(f); const compo = componentCheck.exec(file); if (compo === null) return "font-awesome-icon"; return compo[1]; } } function getIcons(files) { const icons = []; for (const f of files) { const file = fs.readFileSync(f); for (const r of matches) { // console.log(r); while (null != (icon = r.exec(file))) { // console.log(icon); icon = icon.filter((e) => e != null); icon.shift(); if (!prefixes.test(icon)) { icon = "fas-" + icon; } else if (icon.length == 1) { icon = icon[0]; } else if (icon.length == 2) { icon = icon[0] + "-" + icon[1]; } icons.push(icon); } } } // Distinct values (Remove duplicates) return [...new Set(icons)]; } function sortIcons(icons) { const ficons = []; for (const icon of icons) { const spl = icon.split("-"); // fa(r|s|l|b|d) const prefix = spl[0]; // thumbs-up -> fathumbs-up let iconName = `fa${spl.slice(1).join("-")}`; // fathumbs-up -> faThumbs-up iconName = iconName.replaceAt(2, iconName[2].toUpperCase()); // faThumbs-up while (true) { let pos = iconName.indexOf("-"); if (pos === -1) break; // faThumbs-up -> faThumbs-Up -> faThumbsUp iconName = iconName .replaceAt(pos + 1, iconName[pos + 1].toUpperCase()) .removeAt(pos); } ficons.push({ prefix: prefix, icon: iconName }); } return ficons; } function getListing(sortedIcons) { let result = `// Auto generated @ ${new Date()}`; // Filter out icons from not installed libraries sortedIcons = sortedIcons.filter( (el) => iconTypes.find((e) => e.prefix === el.prefix).type !== "" ); for (const type of iconTypes) { if (sortedIcons.filter((el) => el.prefix === type.prefix).length === 0) continue; // ignore unused libraries result += `\n\n//${type.prefix}\nimport {\n${sortedIcons .filter((el) => el.prefix === type.prefix) .map((e) => `\t${e.icon} as ${e.prefix}${e.icon.slice(2)}`) .join(",\n")}\n} from '${faModule}/${type.type}-${ type.libary }-svg-icons';`; } return (result += `\n\nimport { library } from "@fortawesome/fontawesome-svg-core";\nlibrary.add(\n${sortedIcons .map((e) => `\t${e.prefix}${e.icon.slice(2)}`) .join(",\n")}\n);`); } const t0 = Date.now(); // Fetch installed libraries and only prioritize pro versions if even free ones are installed getLibraries(); // Find all vue files within folders getFiles(src); // console.log(files); // parse icons within found files const icons = getIcons(files); // console.log(icons); // Fix up icon list const sortedIcons = sortIcons(icons); // Generate code const listing = getListing(sortedIcons); const previousListing = [ ...new Set( [ ...(fs.existsSync(outputFile) && fs.readFileSync(outputFile)) .toString() .matchAll(/fa[a-z][A-Z]\w+/g), ].map(([icon]) => icon) ), ].sort(); const currentListing = [ ...new Set( [...listing.toString().matchAll(/fa[a-z][A-Z]\w+/g)].map(([icon]) => icon) ), ].sort(); if (JSON.stringify(previousListing) === JSON.stringify(currentListing)) { console.log( `- Fontawesome treeshaking list already up-to-date. (took ${ Date.now() - t0 } ms)` ); return; } else { // Write to file fs.mkdir(outputFolder, { recursive: true }, (err) => { if (err) throw err; fs.writeFile(outputFile, listing, function (err) { if (err) return console.log(err); console.log( `- Fontawesome treeshaking list generated. (took ${Date.now() - t0} ms)` ); }); }); }