Files
defgov/packages/ui/scripts/generate-index-css.ts

158 lines
5.3 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-use.ts
import { writeFileSync, readFileSync } from "fs";
import { extname, relative, resolve, basename } from "path";
import { globSync } from "glob";
interface Config {
targetDirs: string[];
includeExtensions: string[];
importSyntax: "@import";
importSyntaxTail?: string;
excludeFilePattern: RegExp[];
excludeDirs: string[];
warnDuplicateTailwindImport: boolean;
indexFileName: string;
}
// 配置项
const srcDirConfig: Config = {
targetDirs: ["src"], // 包含的文件夹
includeExtensions: [".css"], // 目标文件后缀
importSyntax: "@import", // 导入时使用的语法
excludeFilePattern: [/index\.css/, /index\.scss/, /\.(test|spec)\./], // 正则若匹配则排除
// 排除的文件夹
excludeDirs: [
"__tests__",
"tests",
"story",
"stories",
"types",
"node_modules",
"dist",
"build",
],
warnDuplicateTailwindImport: true,
indexFileName: "index.css", // 生成的入口文件名称
};
// ======================================================================
/**
* 判断是否是有效 CSS 文件
* @param filePath 文件绝对路径
* @returns 是否为有效文件
*/
function isValidFile(filePath: string, config: Config): boolean {
// 过滤排除的文件
const filenameWithExt = filePath.split(/[\\/]/).pop()!; // “\\” 匹配反斜杠 “\”,“/” 匹配正斜杠 “/”pop 返回最后一个 “/” 后面的内容
const shouldExcludeFile = config.excludeFilePattern.some((pattern) =>
pattern.test(filenameWithExt),
);
if (shouldExcludeFile) {
return false; // 如果匹配到排除规则,跳过当前文件
}
// 过滤排除的文件夹
const normalizedFilePath = filePath.replace(/\\/g, "/"); // “\\” 匹配反斜杠 “\”,“/” 匹配正斜杠 “/”
const shouldExcludeDir = config.excludeDirs.some(
(dir) => normalizedFilePath.includes(`/${dir}/`), // 前后都有“/” 是为了避免误匹配到文件名(如 test.js 不会被排除)
);
if (shouldExcludeDir) return false; // 如果匹配到排除规则,跳过当前文件
// 过滤非目标扩展名,只有指定的才通过
const ext = extname(filePath);
const isValidExt = config.includeExtensions.includes(ext);
if (!isValidExt) {
return false; // 如果不是指定扩展名,跳过当前文件
}
// @import "tailwindcss" 重复警告,生成的入口文件会自动加一条,其他文件无需重复导入
if (config.warnDuplicateTailwindImport) {
try {
const fileContent = readFileSync(filePath, "utf-8");
if (
fileContent.includes('@import "tailwindcss"') ||
fileContent.includes("@import 'tailwindcss'")
) {
console.warn(
`${filePath} 中含有重复的 @import "tailwindcss" 导入,应手动删除`,
);
}
} catch (e) {
const errorMsg = e instanceof Error ? e.message : String(e);
console.warn(`⚠️ 读取文件内容失败(跳过过滤): ${filePath}`, errorMsg);
}
}
return true;
}
// ======================================================================
/**
* 生成 “@import” 导入语句
*/
function generateIndexFile(config: Config) {
const [targetDir] = config.targetDirs;
const dirPath = resolve(process.cwd(), targetDir); //目标文件夹绝对路径, 通常是 /project/src
const searchPattern = resolve(dirPath, "**", "*.*"); // 搜索路径 “dirPath/**/*”
const allFiles = globSync(searchPattern, {
nodir: true, // 不匹配文件夹,只匹配文件
absolute: true, // 返回从根目录开始的绝对路径
windowsPathsNoEscape: true, // 禁用 Windows 路径的反斜杠转义
dot: false, // 不配隐藏文件或目录(以 . 开头的文件)
follow: true, // 跟踪符号链接symlinks继续解析并返回链接的目标文件或目录
});
const validFiles = allFiles.filter((value) => isValidFile(value, config)); // 过滤出要导入的有效 CSS 文件
console.log(`✅ 有效 CSS 文件数量: ${validFiles.length}`);
validFiles.sort(); // 排序
const importStatements = validFiles.map((file) => {
const relPath = relative(dirPath, file); // 算出 src 与 css文件 的相对路径
let importPath = "./" + relPath.replace(/\\/g, "/"); // 给相对路径前面加上 “./”,并将 “\\” 替换成 “/”
// 拼接 @import 语句
if (config.importSyntaxTail !== undefined) {
return `${config.importSyntax} '${importPath}' ${config.importSyntaxTail};`;
} else {
return `${config.importSyntax} '${importPath}';`;
}
});
// index.css 文件的顶部内容
const indexContent = `
@import "tailwindcss";
${importStatements.join("\n")}
`.trim(); // 使用 trim 移除开头多余的换行
const indexFilePath = resolve(dirPath, config.indexFileName);
// 创建和写入 index.css 文件
writeFileSync(indexFilePath, indexContent, "utf-8");
console.log(`✅ 成功生成 ${config.indexFileName}: ${indexFilePath}`);
}
// ======================================================================
/**
* 主函数
*/
function main() {
try {
generateIndexFile(srcDirConfig);
} catch (e) {
const errorMsg = e instanceof Error ? e.message : String(e);
console.error(
`❌ 文件夹 ${srcDirConfig.targetDirs} 扫描和生成 ${srcDirConfig.indexFileName} 失败:`,
errorMsg,
);
process.exit(1);
}
}
main();