Files
defgov/packages/ui/scripts/generate-index.ts
2026-03-23 03:30:31 +08:00

149 lines
3.9 KiB
TypeScript
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.
// scripts/generate-index.ts
import { writeFileSync } from "fs";
import { extname, relative, resolve } from "path";
import { globSync } from "glob";
interface Config {
targetDirs: string[];
includeExtensions: string[];
excludeKeywords: {
dirs: string[];
fileSuffixes: string[];
filePatterns: RegExp[];
};
}
const CONFIG: Config = {
targetDirs: ["src"],
includeExtensions: [".ts", ".tsx", ".vue"],
excludeKeywords: {
dirs: ["__tests__", "tests", "story", "stories", "types"],
fileSuffixes: [".d.ts"],
filePatterns: [
/^index\.(ts|tsx|js|jsx)$/, // index文件
/\.(test|spec)\./, // 测试文件 .test.xxx / .spec.xxx
/\.(story|stories)\./, // Storybook文件 .story.xxx / .stories.xxx
],
},
};
/**
* 统一路径分隔符(兼容 Windows/Linux
* @param path 原始路径
* @returns 标准化路径(全部转为 /
*/
const normalizePath = (path: string): string => path.replace(/\\/g, "/");
/**
* 检查文件是否在排除目录中
* @param filePath 文件路径
* @returns 是否在排除目录
*/
const isInExcludeDir = (filePath: string): boolean => {
const normalizedPath = normalizePath(filePath);
return CONFIG.excludeKeywords.dirs.some((dir) =>
normalizedPath.includes(`/${dir}/`),
);
};
/**
* 检查文件是否匹配排除正则
* @param fileName 文件名
* @returns 是否匹配排除规则
*/
const isMatchExcludePattern = (fileName: string): boolean => {
return CONFIG.excludeKeywords.filePatterns.some((pattern) =>
pattern.test(fileName),
);
};
/**
* 检查文件是否为排除后缀(如 .d.ts
* @param filePath 文件路径
* @returns 是否为排除后缀
*/
const isExcludeSuffix = (filePath: string): boolean => {
return CONFIG.excludeKeywords.fileSuffixes.some((suffix) =>
filePath.endsWith(suffix),
);
};
function isValidFile(filePath: string): boolean {
const fileName = filePath.split(/[\\/]/).pop()!;
// 按优先级过滤:目录 > 后缀 > 文件名规则 > 扩展名
if (isInExcludeDir(filePath)) return false;
if (isExcludeSuffix(filePath)) return false;
if (isMatchExcludePattern(fileName)) return false;
const ext = extname(filePath);
const isValidExt = CONFIG.includeExtensions.includes(ext);
return isValidExt;
}
// ========== 5. 生成索引文件(错误处理 + 语法规范) ==========
function generateIndexFile(dirPath: string): void {
const searchPattern = resolve(dirPath, "**", "*.*");
const allFiles = globSync(searchPattern, {
nodir: true,
absolute: true,
windowsPathsNoEscape: true,
dot: false,
follow: true,
});
if (allFiles.length === 0) {
return;
}
const validFiles = allFiles.filter(isValidFile);
if (validFiles.length === 0) {
return;
}
validFiles.sort();
// 生成导出语句
const exportStatements = validFiles.map((file) => {
const relPath = relative(dirPath, file);
const importPath = `./${relPath.replace(/\.[^.]+$/, "").replace(/\\/g, "/")}`;
return `export * from '${importPath}';`;
});
// 生成索引文件内容(模板字符串格式化)
const indexContent = `
import './index.css';
${exportStatements.join("\n")}
`;
const indexFilePath = resolve(dirPath, "index.ts");
writeFileSync(indexFilePath, indexContent, "utf-8");
}
// ========== 6. 主函数(完善错误处理 + 类型安全) ==========
function main(): void {
const [targetDir] = CONFIG.targetDirs;
if (!targetDir) {
console.error(`❌ 未配置目标扫描目录,请检查 CONFIG.targetDirs`);
process.exit(1);
}
const absTargetDir = resolve(process.cwd(), targetDir);
try {
generateIndexFile(absTargetDir);
} catch (error) {
// 处理 unknown 类型错误TypeScript 最佳实践)
const errorMsg = error instanceof Error ? error.message : String(error);
console.error(`❌ 生成 index.ts 失败: ${errorMsg}`);
process.exit(1);
}
}
// 执行主函数
main();