// 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();