mirror of
https://github.com/StepanovPlaton/AboutMe.git
synced 2026-07-28 19:35:51 +04:00
Cut iconify
This commit is contained in:
135
scripts/collect-icons.mjs
Normal file
135
scripts/collect-icons.mjs
Normal file
@@ -0,0 +1,135 @@
|
||||
import { readFileSync, readdirSync, statSync, writeFileSync, mkdirSync } from "fs";
|
||||
import { join, relative } from "path";
|
||||
|
||||
const ROOT = join(import.meta.dirname, "..");
|
||||
const SRC = join(ROOT, "src");
|
||||
const OUT_DIR = join(SRC, "generated");
|
||||
const OUT_FILE = join(OUT_DIR, "icon-inventory.json");
|
||||
const OUT_INCLUDE = join(OUT_DIR, "icon-include.mjs");
|
||||
|
||||
/** @type {Record<string, string>} */
|
||||
const ICON_ALIASES = {
|
||||
"eos-icons:loading": "material-symbols:progress-activity",
|
||||
};
|
||||
|
||||
const KNOWN_PREFIXES = new Set([
|
||||
"material-symbols",
|
||||
"fa6-brands",
|
||||
"fa6-regular",
|
||||
"fa6-solid",
|
||||
"mdi",
|
||||
"logos",
|
||||
"devicon",
|
||||
"hugeicons",
|
||||
"mingcute",
|
||||
"skill-icons",
|
||||
"vscode-icons",
|
||||
"material-icon-theme",
|
||||
"game-icons",
|
||||
"iconoir",
|
||||
]);
|
||||
|
||||
const ICON_PATTERN =
|
||||
/(?:name|icon)=["'{]([a-z0-9][a-z0-9-]*:[a-z0-9][a-z0-9._-]*)/gi;
|
||||
|
||||
const JSON_ICON_PATTERN = /"icon"\s*:\s*"([^"]+)"/g;
|
||||
const YAML_ICON_PATTERN = /\bicon:\s*["']([^"']+)["']/g;
|
||||
const TS_ICON_PATTERN = /\bicon\s*:\s*["']([^"']+)["']/g;
|
||||
const RETURN_ICON_PATTERN =
|
||||
/return\s+["']([a-z0-9][a-z0-9-]*:[a-z0-9][a-z0-9._-]*)["']/g;
|
||||
|
||||
function walk(dir, files = []) {
|
||||
for (const entry of readdirSync(dir)) {
|
||||
const full = join(dir, entry);
|
||||
if (statSync(full).isDirectory()) {
|
||||
walk(full, files);
|
||||
} else {
|
||||
files.push(full);
|
||||
}
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
function normalizeIcon(name) {
|
||||
const trimmed = name.trim();
|
||||
return ICON_ALIASES[trimmed] ?? trimmed;
|
||||
}
|
||||
|
||||
function addIcon(icons, raw) {
|
||||
const name = normalizeIcon(raw);
|
||||
if (!name.includes(":")) return;
|
||||
const [prefix, iconName] = name.split(":");
|
||||
if (!prefix || !iconName || !KNOWN_PREFIXES.has(prefix)) return;
|
||||
if (!icons[prefix]) icons[prefix] = new Set();
|
||||
icons[prefix].add(iconName);
|
||||
}
|
||||
|
||||
function scanFile(path, icons) {
|
||||
const content = readFileSync(path, "utf8");
|
||||
const rel = relative(ROOT, path).replace(/\\/g, "/");
|
||||
|
||||
if (rel.endsWith(".json")) {
|
||||
for (const match of content.matchAll(JSON_ICON_PATTERN)) {
|
||||
addIcon(icons, match[1]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (rel.endsWith(".yaml") || rel.endsWith(".yml")) {
|
||||
for (const match of content.matchAll(YAML_ICON_PATTERN)) {
|
||||
addIcon(icons, match[1]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!/\.(astro|svelte|ts|tsx|js|mjs)$/.test(rel)) return;
|
||||
|
||||
for (const match of content.matchAll(ICON_PATTERN)) {
|
||||
addIcon(icons, match[1]);
|
||||
}
|
||||
for (const match of content.matchAll(TS_ICON_PATTERN)) {
|
||||
addIcon(icons, match[1]);
|
||||
}
|
||||
for (const match of content.matchAll(RETURN_ICON_PATTERN)) {
|
||||
addIcon(icons, match[1]);
|
||||
}
|
||||
}
|
||||
|
||||
function main() {
|
||||
/** @type {Record<string, Set<string>>} */
|
||||
const icons = {};
|
||||
|
||||
const files = walk(SRC);
|
||||
for (const file of files) {
|
||||
if (file.includes("generated")) continue;
|
||||
scanFile(file, icons);
|
||||
}
|
||||
|
||||
const extraConfigFiles = [join(ROOT, "twilight.config.yaml")];
|
||||
for (const file of extraConfigFiles) {
|
||||
try {
|
||||
scanFile(file, icons);
|
||||
} catch {
|
||||
/* optional config */
|
||||
}
|
||||
}
|
||||
|
||||
/** @type {Record<string, string[]>} */
|
||||
const inventory = {};
|
||||
for (const [prefix, names] of Object.entries(icons)) {
|
||||
inventory[prefix] = [...names].sort();
|
||||
}
|
||||
|
||||
mkdirSync(OUT_DIR, { recursive: true });
|
||||
writeFileSync(OUT_FILE, `${JSON.stringify(inventory, null, 2)}\n`, "utf8");
|
||||
writeFileSync(
|
||||
OUT_INCLUDE,
|
||||
`// AUTO-GENERATED by scripts/collect-icons.mjs — do not edit\nexport default ${JSON.stringify(inventory, null, 4)};\n`,
|
||||
"utf8",
|
||||
);
|
||||
|
||||
const total = Object.values(inventory).reduce((n, arr) => n + arr.length, 0);
|
||||
console.log(`Collected ${total} icons in ${Object.keys(inventory).length} sets -> ${relative(ROOT, OUT_FILE)}`);
|
||||
}
|
||||
|
||||
main();
|
||||
@@ -1,9 +0,0 @@
|
||||
const { copyFileSync, mkdirSync } = require("fs");
|
||||
const { dirname, join } = require("path");
|
||||
|
||||
const src = join(__dirname, "../node_modules/iconify-icon/dist/iconify-icon.min.js");
|
||||
const dest = join(__dirname, "../public/assets/iconify/iconify-icon.min.js");
|
||||
|
||||
mkdirSync(dirname(dest), { recursive: true });
|
||||
copyFileSync(src, dest);
|
||||
console.log(`Copied iconify-icon.min.js to ${dest}`);
|
||||
92
scripts/generate-site-icons.mjs
Normal file
92
scripts/generate-site-icons.mjs
Normal file
@@ -0,0 +1,92 @@
|
||||
import { readFileSync, writeFileSync, mkdirSync } from "fs";
|
||||
import { join } from "path";
|
||||
import { createRequire } from "module";
|
||||
import { getIconData, iconToSVG } from "@iconify/utils";
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
const ROOT = join(import.meta.dirname, "..");
|
||||
const INVENTORY_FILE = join(ROOT, "src/generated/icon-inventory.json");
|
||||
const OUT_DIR = join(ROOT, "src/generated");
|
||||
const OUT_FILE = join(OUT_DIR, "site-icon-svgs.ts");
|
||||
|
||||
const PACKAGE_BY_PREFIX = {
|
||||
"fa6-brands": "@iconify-json/fa6-brands",
|
||||
"fa6-regular": "@iconify-json/fa6-regular",
|
||||
"fa6-solid": "@iconify-json/fa6-solid",
|
||||
mdi: "@iconify-json/mdi",
|
||||
"material-symbols": "@iconify-json/material-symbols",
|
||||
logos: "@iconify-json/logos",
|
||||
devicon: "@iconify-json/devicon",
|
||||
hugeicons: "@iconify-json/hugeicons",
|
||||
mingcute: "@iconify-json/mingcute",
|
||||
"skill-icons": "@iconify-json/skill-icons",
|
||||
"vscode-icons": "@iconify-json/vscode-icons",
|
||||
"material-icon-theme": "@iconify-json/material-icon-theme",
|
||||
"game-icons": "@iconify-json/game-icons",
|
||||
iconoir: "@iconify-json/iconoir",
|
||||
};
|
||||
|
||||
function loadIconSet(prefix) {
|
||||
const pkg = PACKAGE_BY_PREFIX[prefix];
|
||||
if (!pkg) return null;
|
||||
return require(`${pkg}/icons.json`);
|
||||
}
|
||||
|
||||
function buildSvg(iconName) {
|
||||
const [prefix, name] = iconName.split(":");
|
||||
const iconSet = loadIconSet(prefix);
|
||||
if (!iconSet) {
|
||||
console.warn(`Unknown icon set: ${prefix} (${iconName})`);
|
||||
return null;
|
||||
}
|
||||
const data = getIconData(iconSet, name);
|
||||
if (!data) {
|
||||
console.warn(`Icon not found: ${iconName}`);
|
||||
return null;
|
||||
}
|
||||
const { body, attributes } = iconToSVG(data, { height: "1em" });
|
||||
const attrs = Object.entries(attributes)
|
||||
.map(([k, v]) => `${k}="${v}"`)
|
||||
.join(" ");
|
||||
return `<svg xmlns="http://www.w3.org/2000/svg" ${attrs}>${body}</svg>`;
|
||||
}
|
||||
|
||||
function main() {
|
||||
const inventory = JSON.parse(readFileSync(INVENTORY_FILE, "utf8"));
|
||||
/** @type {Record<string, string>} */
|
||||
const svgs = {};
|
||||
|
||||
for (const [prefix, names] of Object.entries(inventory)) {
|
||||
for (const name of names) {
|
||||
const fullName = `${prefix}:${name}`;
|
||||
const svg = buildSvg(fullName);
|
||||
if (svg) svgs[fullName] = svg;
|
||||
}
|
||||
}
|
||||
|
||||
const fallback =
|
||||
svgs["material-symbols:help-outline"] ??
|
||||
svgs["material-symbols:info"] ??
|
||||
Object.values(svgs)[0] ??
|
||||
"";
|
||||
|
||||
const lines = [
|
||||
"// Generated by scripts/generate-site-icons.mjs — do not edit manually",
|
||||
"",
|
||||
`export const siteIconFallback = ${JSON.stringify(fallback)};`,
|
||||
"",
|
||||
"export const siteIconSvgs: Record<string, string> = {",
|
||||
];
|
||||
|
||||
for (const [name, svg] of Object.entries(svgs).sort(([a], [b]) => a.localeCompare(b))) {
|
||||
lines.push(` ${JSON.stringify(name)}: ${JSON.stringify(svg)},`);
|
||||
}
|
||||
|
||||
lines.push("};", "");
|
||||
|
||||
mkdirSync(OUT_DIR, { recursive: true });
|
||||
writeFileSync(OUT_FILE, `${lines.join("\n")}\n`, "utf8");
|
||||
console.log(`Generated ${Object.keys(svgs).length} SVGs -> ${OUT_FILE}`);
|
||||
}
|
||||
|
||||
main();
|
||||
17
scripts/icon-inventory-include.mjs
Normal file
17
scripts/icon-inventory-include.mjs
Normal file
@@ -0,0 +1,17 @@
|
||||
import { readFileSync } from "fs";
|
||||
import { join } from "path";
|
||||
|
||||
const inventoryPath = join(import.meta.dirname, "../src/generated/icon-inventory.json");
|
||||
|
||||
export function loadIconInventory() {
|
||||
return JSON.parse(readFileSync(inventoryPath, "utf8"));
|
||||
}
|
||||
|
||||
export function toAstroIconInclude(inventory) {
|
||||
/** @type {Record<string, string[]>} */
|
||||
const include = {};
|
||||
for (const [prefix, icons] of Object.entries(inventory)) {
|
||||
include[prefix] = icons;
|
||||
}
|
||||
return include;
|
||||
}
|
||||
Reference in New Issue
Block a user