Initial commit: Re-connected to Gitea
This commit is contained in:
157
packages/ui/scripts/generate-index-css.ts
Normal file
157
packages/ui/scripts/generate-index-css.ts
Normal file
@@ -0,0 +1,157 @@
|
||||
// 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();
|
||||
148
packages/ui/scripts/generate-index.ts
Normal file
148
packages/ui/scripts/generate-index.ts
Normal file
@@ -0,0 +1,148 @@
|
||||
// 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.scss';
|
||||
|
||||
${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();
|
||||
Reference in New Issue
Block a user