Files
WAI_Project_UNIX/.cool/scripts/icon.js
haibiao_gu 33e59242c0 feat(icon): 支持读取iconfont图标前缀并生成对应ts文件
- 新增getIconPrefix函数读取图标前缀配置
- 在generateTypeScript函数中支持传入图标前缀参数
- 优化处理流程,当文件夹不是iconfont时读取其前缀
2025-11-03 09:49:18 +08:00

350 lines
9.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const fs = require("fs");
const path = require("path");
const AdmZip = require("adm-zip");
// 清理所有临时文件
function cleanupTempDir() {
const tempDir = path.join(".cool", "temp");
if (fs.existsSync(tempDir)) {
try {
fs.rmSync(tempDir, { recursive: true, force: true });
} catch (error) {
console.warn(`❌ 清理临时目录失败: ${ tempDir }`, error);
}
}
}
// 确保临时目录存在
function ensureTempDir() {
const tempDir = path.join(".cool", "temp");
if (!fs.existsSync(tempDir)) {
fs.mkdirSync(tempDir, { recursive: true });
}
}
// 创建icons目录和子目录
function ensureDistDir(folderName = "") {
const iconsPath = folderName ? path.join("icons", folderName) : "icons";
if (!fs.existsSync(iconsPath)) {
fs.mkdirSync(iconsPath, { recursive: true });
}
}
// 读取zip文件列表
function getZipFiles() {
const iconsDir = path.join(".cool", "icons");
if (!fs.existsSync(iconsDir)) {
console.error(`❌ 目录不存在: ${ iconsDir }`);
return [];
}
return fs.readdirSync(iconsDir).filter((item) => {
const filePath = path.join(iconsDir, item);
const stats = fs.statSync(filePath);
return stats.isFile() && item.endsWith(".zip");
});
}
// 解压zip文件到临时目录
function extractZipFile(zipPath, folderName) {
try {
const zip = new AdmZip(zipPath);
const tempDir = path.join(".cool", "temp", folderName);
// 确保临时目录存在
if (!fs.existsSync(tempDir)) {
fs.mkdirSync(tempDir, { recursive: true });
}
// 解压到临时目录
zip.extractAllTo(tempDir, true);
// 检查是否有额外的顶层文件夹
const extractedItems = fs.readdirSync(tempDir);
// 如果只有一个项目且是文件夹,则可能是额外的包装文件夹
if (extractedItems.length === 1) {
const singleItem = extractedItems[0];
const singleItemPath = path.join(tempDir, singleItem);
const stats = fs.statSync(singleItemPath);
if (stats.isDirectory()) {
// 检查这个文件夹是否包含我们需要的文件
const innerItems = fs.readdirSync(singleItemPath);
const hasIconFiles = innerItems.some(
(item) =>
item.endsWith(".ttf") || item.endsWith(".json") || item.endsWith(".css")
);
if (hasIconFiles) {
return singleItemPath;
}
}
}
return tempDir;
} catch (error) {
console.error(`❌ 解压失败: ${ zipPath }`, error);
return null;
}
}
// 将TTF文件转换为base64
function ttfToBase64(ttfPath) {
try {
const ttfBuffer = fs.readFileSync(ttfPath);
return ttfBuffer.toString("base64");
} catch (error) {
console.error(`❌ 读取TTF文件失败: ${ ttfPath }`, error);
return null;
}
}
// 生成TypeScript文件
function generateTypeScript(originalFolderName, camelCaseName, iconData, iconPrefix) {
const tsContent = `export const ${ camelCaseName } = {\n${ iconData
.map((item) => `\t"${ iconPrefix }${ item.name }": "${ item.unicode }"`)
.join(",\n") }\n};\n`;
const outputPath = path.join("icons", originalFolderName, "index.ts");
fs.writeFileSync(outputPath, tsContent);
}
// 生成SCSS文件
function generateSCSS(originalFolderName, base64Data) {
const scssContent = `@font-face {\n\tfont-family: "${ toCamelCase(originalFolderName) }";\n\tsrc: url("data:font/ttf;base64,${ base64Data }") format("woff");\n}\n`;
const outputPath = path.join("icons", originalFolderName, "index.scss");
fs.writeFileSync(outputPath, scssContent);
}
// 从CSS文件提取图标数据用于remixicon等
function extractIconsFromCSS(cssPath) {
try {
const cssContent = fs.readFileSync(cssPath, "utf8");
const iconData = [];
// 匹配CSS中的图标规则例如.ri-home-line:before { content: "\ee2b"; }
const regex = /\.ri-([^:]+):before\s*{\s*content:\s*"\\([^"]+)"/g;
let match;
while ((match = regex.exec(cssContent)) !== null) {
const iconName = match[1];
const unicode = match[2];
iconData.push({
name: iconName,
unicode: unicode
});
}
return iconData;
} catch (error) {
console.error(`❌ 读取CSS文件失败: ${ cssPath }`, error);
return [];
}
}
// 读取和处理图标数据
function processIconData(jsonPath) {
try {
const jsonData = JSON.parse(fs.readFileSync(jsonPath, "utf8"));
return jsonData.glyphs.map((item) => ({
name: item.font_class,
unicode: item.unicode
}));
} catch (error) {
console.error(`❌ 读取JSON文件失败: ${ jsonPath }`, error);
return [];
}
}
// 读取iconfont图标前缀
function getIconPrefix(jsonPath) {
try {
const jsonData = JSON.parse(fs.readFileSync(jsonPath, "utf8"));
return jsonData.css_prefix_text;
} catch (error) {
console.error(`❌ 读取JSON文件失败: ${jsonPath}`, error);
return [];
}
}
// 将连字符转换为驼峰命名的函数
function toCamelCase(str) {
return str.replace(/-([a-z])/g, (match, letter) => letter.toUpperCase());
}
// 处理单个zip文件
function processZipFile(zipFileName) {
const originalFolderName = path.basename(zipFileName, ".zip");
const folderName = toCamelCase(originalFolderName); // 转换为驼峰命名用于变量名
const zipPath = path.join(".cool", "icons", zipFileName);
// 解压zip文件 (使用原始文件夹名称)
const tempDir = extractZipFile(zipPath, originalFolderName);
if (!tempDir) {
return null;
}
// 图标库名称
const ptName = ["iconfont", "remixicon"];
// 获取文件路径
const getFilePath = (ext) => {
let filePath = null;
for (const name of ptName) {
const tempPath = path.join(tempDir, `${ name }.${ ext }`);
if (fs.existsSync(tempPath)) {
filePath = tempPath;
break;
}
}
return filePath;
};
// 在解压后的目录中查找文件
const jsonPath = getFilePath("json");
const cssPath = getFilePath("css");
const ttfPath = getFilePath("ttf");
if (!ttfPath) {
console.warn(`⚠️跳过 ${ folderName }: 缺少 TTF 文件`);
return null;
}
let iconData = [];
let iconPrefix = "";
// 优先使用JSON文件
if (jsonPath) {
iconData = processIconData(jsonPath);
if (originalFolderName !== "iconfont") {
iconPrefix = getIconPrefix(jsonPath);
}
}
// 如果没有则尝试CSS文件
else if (cssPath) {
iconData = extractIconsFromCSS(cssPath);
} else {
console.warn(`⚠️ 跳过 ${ folderName }: 缺少 ${ jsonPath }${ cssPath }`);
return null;
}
if (iconData.length === 0) {
console.warn(`⚠️ ${ folderName }: 没有找到图标数据`);
return null;
}
console.log(`${ zipFileName } 找到 ${ iconData.length } 个图标`);
// 转换TTF为base64
const base64Data = ttfToBase64(ttfPath);
if (!base64Data) {
console.error(`${ folderName }: TTF转换失败`);
return null;
}
// 为该文件夹创建icons子目录 (使用原始文件夹名称)
ensureDistDir(originalFolderName);
// 生成TypeScript文件 (使用驼峰命名作为变量名,原始名称作为路径)
generateTypeScript(originalFolderName, folderName, iconData, iconPrefix);
// 生成SCSS文件 (使用原始名称作为路径和字体名称)
generateSCSS(originalFolderName, base64Data);
return { originalName: originalFolderName, camelName: folderName };
}
// 生成主index.ts文件
function generateIndexTS(actualFolders) {
const imports = actualFolders
.map((folder) => {
const camelName = toCamelCase(folder);
return `import { ${ camelName } } from "./${ folder }";`;
})
.join("\n");
const exports = `export const icons = {\n${ actualFolders
.map((folder) => `\t${ toCamelCase(folder) }`)
.join(",\n") }\n};\n`;
const content = `${ imports }\n\n${ exports }`;
fs.writeFileSync("icons/index.ts", content);
}
// 生成主index.scss文件
function generateIndexSCSS(actualFolders) {
const imports = actualFolders.map((folder) => `@import "./${ folder }/index.scss";`).join("\n");
fs.writeFileSync("icons/index.scss", imports + "\n");
}
// 扫描icons目录下的实际文件夹
function getActualIconFolders() {
const iconsDir = "icons";
if (!fs.existsSync(iconsDir)) {
return [];
}
return fs.readdirSync(iconsDir).filter((item) => {
const itemPath = path.join(iconsDir, item);
const stats = fs.statSync(itemPath);
return stats.isDirectory();
});
}
// 主函数
function main() {
console.log("🚀 开始处理字体文件...\n");
// 确保临时目录存在
ensureTempDir();
// 确保icons目录存在
ensureDistDir();
try {
// 获取所有zip文件
const zipFiles = getZipFiles();
// 处理每个zip文件
const processedFolders = [];
for (const zipFile of zipFiles) {
const result = processZipFile(zipFile);
if (result) {
processedFolders.push(result);
}
}
// 扫描icons目录下的实际文件夹
const actualFolders = getActualIconFolders();
if (actualFolders.length > 0) {
// 生成主index文件
generateIndexTS(actualFolders);
generateIndexSCSS(actualFolders);
}
if (processedFolders.length > 0) {
const folderNames = processedFolders.map((f) =>
typeof f === "string" ? f : f.originalName
);
console.log(
`\n🎉 成功处理了 ${ processedFolders.length } 个字体包: ${ folderNames.join(", ") }`
);
}
} catch (error) {
console.error("❌ 脚本执行出错:", error);
} finally {
cleanupTempDir();
}
}
// 运行脚本
if (require.main === module) {
main();
}