@tianyio/quality-helper
Version:
A comprehensive quality helper tool for project scaffolding and management
1 lines • 28.3 kB
Source Map (JSON)
{"version":3,"file":"core-discovery-DoUt3gJ3.cjs","sources":["../src/core/Discovery.ts"],"sourcesContent":["import { join, dirname } from 'path'\nimport { FileOperations } from './FileOperations.ts'\nimport type { ConfigFileInfo } from '../services/ConfigService.ts'\nimport type { ArchitectureType, ProjectType, UpdateConfigOptions } from '../types.ts'\n\ninterface DirInfo {\n dir: string\n targetDir: string\n priority: number\n}\n\n/**\n * 配置发现服务类\n * 负责发现和扫描项目中的配置文件\n */\nexport class Discovery {\n /**\n * 获取可用的配置文件列表\n */\n static async getAvailableConfigFiles(\n templatesDir: string,\n projectRoot: string,\n options?: UpdateConfigOptions\n ): Promise<ConfigFileInfo[]> {\n const configs: ConfigFileInfo[] = []\n\n await Discovery.scanTemplateDirectories(templatesDir, projectRoot, configs, options)\n\n return configs\n }\n\n /**\n * 应用过滤选项\n */\n static applyFilters(dirInfos: DirInfo[], options?: UpdateConfigOptions): DirInfo[] {\n const { includeOnly = [], excludeGitHooks, excludeStaticFiles, excludeScripts } = options ?? {}\n const include = includeOnly.length > 0\n if (include || excludeGitHooks || excludeStaticFiles || excludeScripts) {\n return dirInfos.filter(({ dir }) => {\n const [, configType] = dir.split('/') // 获取配置类型,如 'build-tools', 'env' 等\n if (include) {\n return includeOnly.includes(configType as string)\n }\n if (excludeGitHooks && configType === 'git-hooks') {\n return false\n }\n if (excludeStaticFiles && configType === 'static-files') {\n return false\n }\n if (excludeScripts && configType === 'scripts') {\n return false\n }\n return true\n })\n } else {\n return dirInfos\n }\n }\n\n /**\n * 扫描模板目录\n */\n static async scanTemplateDirectories(\n templatesDir: string,\n projectRoot: string,\n configs: ConfigFileInfo[],\n options?: UpdateConfigOptions\n ): Promise<void> {\n const templateDirs = [\n { dir: 'configs/rules', targetDir: 'rules', priority: 11 },\n { dir: 'configs/env', targetDir: 'env', priority: 10 },\n { dir: 'configs/static-files', targetDir: '', priority: 9 },\n { dir: 'configs/types', targetDir: 'types', priority: 8 },\n { dir: 'configs/develop-tools', targetDir: '', priority: 7 },\n { dir: `configs/build-tools`, targetDir: '', priority: 6 },\n { dir: `configs/scripts`, targetDir: 'scripts', priority: 5 },\n { dir: 'configs/npm', targetDir: '', priority: 4 },\n { dir: 'configs/git-hooks', targetDir: '.husky', priority: 3 },\n { dir: 'configs/git-config', targetDir: '', priority: 2 },\n { dir: 'configs/docs', targetDir: '', priority: 1 },\n ]\n\n // 过滤模板目录\n const filteredTemplateDirs = Discovery.applyFilters(templateDirs, options)\n\n for (const { dir, targetDir, priority } of filteredTemplateDirs) {\n const templateSubDir = join(templatesDir, dir)\n\n try {\n const files = await FileOperations.readDir(templateSubDir)\n await Discovery.processTemplateFiles(\n files,\n templateSubDir,\n targetDir,\n priority,\n dir,\n projectRoot,\n configs,\n options\n )\n } catch {\n // 目录不存在,跳过\n continue\n }\n }\n }\n\n /**\n * 处理模板文件\n */\n static async processTemplateFiles(\n files: string[],\n templateSubDir: string,\n targetDir: string,\n priority: number,\n dir: string,\n projectRoot: string,\n configs: ConfigFileInfo[],\n options?: UpdateConfigOptions\n ): Promise<void> {\n for (const file of files) {\n // 跳过特定文件\n if (Discovery.shouldSkipFile(file, options)) {\n continue\n }\n const templatePath = join(templateSubDir, file)\n // 特殊处理路径和文件名\n const { finalTargetPath, displayName } = Discovery.resolveFilePaths(\n file,\n targetDir,\n projectRoot\n )\n\n // 检查是否已经在配置列表中\n const exists = configs.some(config => config.name === displayName)\n\n if (!exists) {\n const group = Discovery.determineFileGroup(dir)\n const [commentPrefix, commentSuffix] = Discovery.getCommentFix(displayName)\n const configInfo: ConfigFileInfo = {\n name: displayName,\n path: finalTargetPath,\n templatePath,\n description: Discovery.getFileDescription(displayName),\n commentPrefix,\n commentSuffix,\n group,\n priority,\n }\n\n configs.push(configInfo)\n }\n }\n }\n\n /**\n * 判断是否应该跳过文件\n */\n static shouldSkipFile(file: string, options?: UpdateConfigOptions): boolean {\n // 跳过VS Code配置目录本身\n if (file === '.vscode') {\n return true\n }\n if (options?.excludeNpmrc && file === 'npmrc') {\n return true\n }\n\n // 跳过系统文件和临时文件\n const skipPatterns = [\n /^\\.DS_Store$/,\n /^Thumbs\\.db$/,\n /^node_modules$/,\n /^dist$/,\n /^build$/,\n /^\\.cache/,\n /^\\.temp/,\n /^\\.tmp/,\n /~$/,\n /\\.bak$/,\n /\\.swp$/,\n /\\.swo$/,\n ]\n if (skipPatterns.some(pattern => pattern.test(file))) return true\n\n // 定义特殊处理文件\n const specialFiles = ['apps', 'libs', 'single', 'monorepo']\n return specialFiles.includes(file)\n }\n\n /**\n * 解析文件路径\n */\n static resolveFilePaths(\n file: string,\n targetDir: string,\n projectRoot: string\n ): {\n finalTargetPath: string\n displayName: string\n } {\n let finalTargetPath: string\n let displayName: string\n\n const needAddDot = [\n 'browserslistrc',\n 'editorconfig',\n 'lintstagedrc',\n 'prettierignore',\n 'gitattributes',\n 'gitconfig',\n 'gitignore',\n 'npmrc',\n 'pnpmrc',\n ]\n\n if (file === 'vite.svg') {\n finalTargetPath = join(projectRoot, 'public', file)\n displayName = file\n } else if (needAddDot.includes(file)) {\n finalTargetPath = join(projectRoot, `.${file}`)\n displayName = `.${file}`\n } else {\n finalTargetPath = targetDir ? join(projectRoot, targetDir, file) : join(projectRoot, file)\n displayName = file\n }\n\n return { finalTargetPath, displayName }\n }\n\n /**\n * 确定文件分组\n */\n static determineFileGroup(dir: string): string {\n if (dir === 'configs/types') return 'types'\n if (dir === 'configs/rules') return 'rules'\n if (dir === 'configs/env') return 'env'\n if (dir === 'configs/git-hooks') return 'git-hooks'\n if (dir === 'configs/npm') return 'npm'\n if (dir === 'configs/develop-tools') return 'develop-tools'\n if (dir === 'configs/git-config') return 'git-config'\n if (dir.includes('configs/build-tools')) return 'build-tools'\n if (dir.includes('configs/docs')) return 'docs'\n if (dir === 'configs/static-files') return 'static-files'\n if (dir === 'configs/scripts') return 'scripts'\n return 'other'\n }\n\n /**\n * 获取文件描述\n */\n static getFileDescription(fileName: string): string {\n const descriptions: Record<string, string> = {\n eslint: 'ESLint 代码质量检查配置',\n prettier: 'Prettier 代码格式化配置',\n '.editorconfig': 'EditorConfig 编辑器配置',\n '.gitignore': 'Git 忽略文件配置',\n '.gitattributes': 'Git 属性配置',\n 'package.json': 'Node.js 项目配置文件',\n 'tsconfig.json': 'TypeScript 编译配置',\n 'vite.config': 'Vite 构建工具配置',\n '.npmrc': 'npm 包管理器配置',\n '.pnpmrc': 'pnpm 包管理器配置',\n '.env': '环境变量配置文件',\n 'base.d.ts': 'TypeScript 环境变量类型定义',\n 'pre-commit': 'Git 提交前钩子',\n 'commit-msg': 'Git 提交信息钩子',\n 'vite.svg': 'Vite 项目图标',\n 'babel.config': 'Babel编译配置(预设、插件)',\n '.browserslistrc': '浏览器兼容性配置文件',\n 'commitlint.config': 'CommitLint 规范提交信息',\n lintstage: 'LintStaged 提交前代码检查',\n }\n\n for (const key in descriptions) {\n if (fileName.includes(key)) {\n return descriptions[key] as string\n }\n }\n\n return `${fileName} 配置文件`\n }\n\n /**\n * 获取注释前缀\n */\n static getCommentFix(fileName: string): [string, string] {\n // JavaScript/TypeScript 文件\n if (\n fileName.endsWith('.js') ||\n fileName.endsWith('.ts') ||\n fileName.endsWith('.jsx') ||\n fileName.endsWith('.tsx')\n ) {\n return ['//', '']\n }\n\n // CSS 相关文件\n if (\n fileName.endsWith('.css') ||\n fileName.endsWith('.scss') ||\n fileName.endsWith('.sass') ||\n fileName.endsWith('.less')\n ) {\n return ['/*', '*/']\n }\n\n // HTML/XML/SVG 文件\n if (\n fileName.endsWith('.html') ||\n fileName.endsWith('.xml') ||\n fileName.endsWith('.svg') ||\n fileName.endsWith('.vue')\n ) {\n return ['<!--', '-->']\n }\n\n // Shell 脚本\n if (fileName.endsWith('.sh') || fileName.endsWith('.bash') || fileName.endsWith('.zsh')) {\n return ['#', '']\n }\n\n // Python, Ruby, PHP\n if (fileName.endsWith('.py') || fileName.endsWith('.rb') || fileName.endsWith('.php')) {\n return ['#', '']\n }\n\n // YAML 文件\n if (fileName.endsWith('.yaml') || fileName.endsWith('.yml')) {\n return ['#', '']\n }\n\n const commentSupportedFiles = [\n '.browserslistrc',\n '.editorconfig',\n '.gitattributes',\n '.gitconfig',\n '.gitignore',\n '.lintstagedrc',\n '.npmrc',\n '.pnpmrc',\n '.prettierignore',\n ]\n // 环境变量和配置文件\n if (fileName.startsWith('.env') || commentSupportedFiles.includes(fileName)) {\n return ['#', '']\n }\n\n return ['//', '']\n }\n\n /**\n * 检查是否为 monorepo 根目录\n */\n static async isMonorepoRoot(projectRoot: string): Promise<boolean> {\n try {\n // 检查是否存在 monorepo 标识文件\n const workspaceFiles = ['pnpm-workspace.yaml', 'lerna.json', 'rush.json']\n for (const file of workspaceFiles) {\n if (await FileOperations.exists(join(projectRoot, file))) {\n return true\n }\n }\n\n // 检查 package.json 中的 workspaces 字段\n const packageJsonPath = join(projectRoot, 'package.json')\n const packageJsonContent = await FileOperations.readFile(packageJsonPath)\n const packageJson = JSON.parse(packageJsonContent)\n\n if (packageJson.workspaces) {\n return true\n }\n\n return false\n } catch {\n return false\n }\n }\n\n /**\n * 查找项目根目录信息对象\n * 返回包含项目根目录和顶层根目录的对象\n * - projectRoot: 根据项目类型返回适当的根目录\n * - 如果项目是monorepo架构的子项目,返回子项目的根目录\n * - 如果项目是monorepo项目非子项目,返回monorepo项目根目录\n * - 如果是single架构的项目,返回项目根目录\n * - rootPath: 总是返回最顶层的项目根目录\n * - 如果项目是monorepo架构的子项目,返回monorepo项目的根目录\n * - 如果项目是monorepo项目非子项目,返回monorepo项目根目录\n * - 如果是single架构的项目,返回项目根目录\n */\n static async findProjectRoot(startDir = process.cwd()): Promise<{\n projectPath: string | null\n rootPath: string | null\n }> {\n let currentDir = startDir\n let projectPath: string | null = null\n let rootPath: string | null = null\n\n // 向上查找,直到文件系统根目录\n while (currentDir !== dirname(currentDir)) {\n const packageJsonPath = join(currentDir, 'package.json')\n\n if (await FileOperations.exists(packageJsonPath)) {\n // 记录第一个找到的 package.json 目录(最可能的项目根目录)\n projectPath ??= currentDir\n rootPath = currentDir\n\n // 检查是否为 monorepo 根目录\n const isMonorepoRoot = await Discovery.isMonorepoRoot(currentDir)\n if (isMonorepoRoot) {\n // 如果当前目录是monorepo 根目录,直接返回\n return {\n projectPath,\n rootPath: currentDir,\n }\n }\n }\n // 继续向上查找\n currentDir = dirname(currentDir)\n }\n\n // 如果是 monorepo 子项目,返回子项目根目录和 monorepo 根目录\n // 如果是 monorepo 根目录或 single 项目,返回找到的第一个 package.json 目录\n return {\n projectPath,\n rootPath,\n }\n }\n\n /**\n * 查找指定目录下的所有package.json文件\n */\n static async findPackageJsonFiles(dir: string): Promise<string[]> {\n const packageJsonFiles: string[] = []\n\n const searchDirectory = async (currentDir: string): Promise<void> => {\n const entries = await FileOperations.readDirWithFileTypes(currentDir)\n\n for (const entry of entries) {\n const fullPath = join(currentDir, entry.name)\n\n if (entry.name === 'package.json' && entry.isFile()) {\n packageJsonFiles.push(fullPath)\n } else if (\n entry.isDirectory() &&\n !entry.name.startsWith('.') &&\n entry.name !== 'node_modules'\n ) {\n await searchDirectory(fullPath)\n }\n }\n }\n\n await searchDirectory(dir)\n return packageJsonFiles\n }\n\n /**\n * 检测项目架构类型\n */\n static async detectArchitectureType(root: string): Promise<ArchitectureType> {\n return (await Discovery.isMonorepoRoot(root)) ? 'monorepo' : 'single'\n }\n\n /**\n * 检测项目类型(应用项目或库项目)\n * 基于npm库项目的典型特征进行判断\n */\n static async detectProjectType(projectRoot: string): Promise<ProjectType> {\n try {\n const packageJsonPath = join(projectRoot, 'package.json')\n\n // 检查 package.json 是否存在\n if (!(await FileOperations.exists(packageJsonPath))) {\n return 'app' // 默认返回应用项目\n }\n\n // 检测项目架构类型\n const architectureType = await Discovery.detectArchitectureType(projectRoot)\n\n // 如果是 monorepo 架构根目录,则直接返回 'root'\n if (architectureType === 'monorepo') {\n // 再次确认当前目录是否为 monorepo 的根目录\n const isMonorepoRoot = await Discovery.isMonorepoRoot(projectRoot)\n if (isMonorepoRoot) {\n return 'root'\n }\n }\n\n const packageJsonContent = await FileOperations.readFile(packageJsonPath)\n const packageJson = JSON.parse(packageJsonContent)\n\n // 库项目特征计分\n let libScore = 0\n\n // 1. 检查是否有导出相关的字段 (exports, main, module, types/typings, files)\n const hasExports = !!packageJson.exports\n const hasMain = !!packageJson.main\n const hasModule = !!packageJson.module\n const hasTypes = !!(packageJson.types ?? packageJson.typings)\n const hasFiles = !!packageJson.files\n\n if (hasExports) libScore += 3 // exports是现代库的标准\n if (hasMain) libScore += 2\n if (hasModule) libScore += 2\n if (hasTypes) libScore += 2 // TypeScript库的典型特征\n if (hasFiles) libScore += 1 // 明确定义要发布的文件是库的特征\n\n // 2. 检查是否有库相关的关键词\n const keywords = packageJson.keywords ?? []\n const libKeywords = [\n 'library',\n 'component',\n 'util',\n 'tool',\n 'helper',\n 'sdk',\n 'api',\n 'framework',\n 'module',\n 'package',\n 'plugin',\n 'extension',\n 'widget',\n 'ui',\n 'esm',\n 'cjs',\n ]\n const hasLibKeywords = keywords.some((keyword: string) =>\n libKeywords.includes(keyword.toLowerCase())\n )\n\n if (hasLibKeywords) libScore += 2\n\n // 3. 检查构建和开发相关的依赖\n const devDependencies = packageJson.devDependencies ?? {}\n const libDevDependencies = ['jest', 'vitest', 'mocha', 'karma', 'vite-plugin-dts']\n\n const hasLibDevDeps = Object.keys(devDependencies).some(dep =>\n libDevDependencies.some(libDep => dep.includes(libDep))\n )\n\n if (hasLibDevDeps) libScore += 2\n\n // 4. 检查是否有发布配置\n const hasPublishConfig = !!packageJson.publishConfig\n if (hasPublishConfig) libScore += 1\n\n // 如果库特征分数大于等于5,认为是库项目\n // 这个阈值考虑了库项目通常具有多个特征的情况\n if (libScore >= 5) {\n return 'lib'\n }\n\n // 默认认为是应用项目\n return 'app'\n } catch (error) {\n // 出现错误时默认返回应用项目\n console.warn('⚠️ 项目类型检测失败,使用默认类型:', error)\n return 'app'\n }\n }\n\n /**\n * 更新package.json脚本\n */\n static async updatePackageJsonScripts(projectRoot: string): Promise<void> {\n const packageJsonPath = join(projectRoot, 'package.json')\n\n try {\n const packageJson = JSON.parse(await FileOperations.readFile(packageJsonPath))\n\n // 添加或更新基础脚本\n const defaultScripts = {\n build: 'vue-tsc --noEmit && vite build',\n preview: 'vite preview',\n }\n\n // 根据环境变量文件生成构建脚本\n const envBuildScripts = await Discovery.generateEnvBuildScripts(projectRoot)\n\n packageJson.scripts = {\n ...defaultScripts,\n ...packageJson.scripts,\n ...envBuildScripts,\n }\n\n await FileOperations.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2))\n // package.json脚本已更新\n } catch (error) {\n console.warn('⚠️ package.json脚本更新失败:', error)\n }\n }\n\n /**\n * 根据环境变量文件生成构建脚本\n */\n static async generateEnvBuildScripts(projectRoot: string): Promise<Record<string, string>> {\n const envBuildScripts: Record<string, string> = {}\n\n try {\n const envDir = join(projectRoot, 'env')\n\n // 检查env目录是否存在\n // 扫描env目录/项目根目录 中的环境变量文件\n const envFiles = await Discovery.scanEnvFilesInDirectory(\n (await FileOperations.exists(envDir)) ? envDir : projectRoot\n )\n\n for (const envFile of envFiles) {\n const mode = Discovery.extractModeFromEnvFile(envFile)\n if (mode) {\n envBuildScripts[`build:${mode}`] = `vite build --mode ${mode}`\n }\n }\n } catch (error) {\n console.warn('⚠️ 生成环境构建脚本失败:', error)\n }\n\n return envBuildScripts\n }\n\n /**\n * 扫描指定目录中的环境变量文件\n */\n static async scanEnvFilesInDirectory(directory: string): Promise<string[]> {\n try {\n const files = await FileOperations.readDir(directory)\n return files.filter(\n file => file.startsWith('.env.') && !file.endsWith('.example') && !file.endsWith('.local')\n )\n } catch {\n return []\n }\n }\n\n /**\n * 从环境变量文件名中提取模式名称\n */\n static extractModeFromEnvFile(envFile: string): string | null {\n // 移除.env.前缀\n const mode = envFile.replace(/^\\.env\\./, '')\n\n // 验证模式名称是否有效(只包含字母、数字、点和连字符)\n if (/^[a-zA-Z0-9.-]+$/.test(mode) && mode.length > 0) {\n return mode\n }\n\n return null\n }\n}\n"],"names":["Discovery","getAvailableConfigFiles","templatesDir","projectRoot","options","configs","scanTemplateDirectories","applyFilters","dirInfos","includeOnly","excludeGitHooks","excludeStaticFiles","excludeScripts","include","length","filter","dir","configType","split","includes","templateDirs","targetDir","priority","filteredTemplateDirs","templateSubDir","join","files","FileOperations","readDir","processTemplateFiles","file","shouldSkipFile","templatePath","finalTargetPath","displayName","resolveFilePaths","some","config","name","group","determineFileGroup","commentPrefix","commentSuffix","getCommentFix","configInfo","path","description","getFileDescription","push","excludeNpmrc","pattern","test","fileName","descriptions","eslint","prettier","lintstage","key","endsWith","startsWith","isMonorepoRoot","workspaceFiles","exists","packageJsonPath","packageJsonContent","readFile","JSON","parse","workspaces","findProjectRoot","startDir","process","cwd","currentDir","projectPath","rootPath","dirname","findPackageJsonFiles","packageJsonFiles","searchDirectory","async","entries","readDirWithFileTypes","entry","fullPath","isFile","isDirectory","detectArchitectureType","root","detectProjectType","packageJson","libScore","hasExports","exports","hasMain","main","hasModule","module","hasTypes","types","typings","hasFiles","keywords","libKeywords","keyword","toLowerCase","devDependencies","libDevDependencies","Object","keys","dep","libDep","publishConfig","error","console","warn","updatePackageJsonScripts","defaultScripts","build","preview","envBuildScripts","generateEnvBuildScripts","scripts","writeFile","stringify","envDir","envFiles","scanEnvFilesInDirectory","envFile","mode","extractModeFromEnvFile","directory","replace"],"mappings":"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"}