UNPKG

graphql-lint-clint-platform

Version:

GraphQL unused fields linter for Clint platform - Custom patterns and actions.graphql support

164 lines (136 loc) 4.59 kB
import { GraphQLQuery, GraphQLField } from "@graphql-lint/core"; import fs from "fs"; import path from "path"; import { glob } from "glob"; interface ClintAction { name: string; pattern: string; // clint.entity.method file: string; line: number; } interface ClintUsageResult { name: string; pattern: string; file: string; line: number; used: boolean; usageLocations?: Array<{ file: string; line: number; code: string; }>; } export class ClintUsageAnalyzer { async analyzeUsageInPath( targetPath: string, actions: ClintAction[], results: ClintUsageResult[], includePatterns: string[], excludePatterns: string[] ): Promise<void> { // Encontrar arquivos para analisar const files = await this.findFilesToAnalyze(targetPath, includePatterns, excludePatterns); // Para cada action, verificar se está sendo usada for (const action of actions) { const usageResult: ClintUsageResult = { ...action, used: false, usageLocations: [] }; // Procurar uso da action nos arquivos for (const file of files) { const usages = await this.findUsageInFile(file, action.pattern); if (usages.length > 0) { usageResult.used = true; usageResult.usageLocations!.push(...usages); } } results.push(usageResult); } } private async findFilesToAnalyze(targetPath: string, includePatterns: string[], excludePatterns: string[]): Promise<string[]> { const stats = fs.statSync(targetPath); if (stats.isFile()) { return [targetPath]; } // Buscar arquivos TypeScript/JavaScript const tsFiles = await glob("**/*.{ts,tsx,js,jsx}", { cwd: targetPath, ignore: excludePatterns, absolute: true }); return tsFiles; } private async findUsageInFile(filePath: string, pattern: string): Promise<Array<{file: string, line: number, code: string}>> { try { const content = fs.readFileSync(filePath, 'utf-8'); const lines = content.split('\n'); const usages: Array<{file: string, line: number, code: string}> = []; for (let i = 0; i < lines.length; i++) { const line = lines[i]; const lineNumber = i + 1; // Procurar pelo padrão clint.entity.method if (line.includes(pattern)) { usages.push({ file: filePath, line: lineNumber, code: line.trim() }); } } return usages; } catch (error) { console.warn(`⚠️ Erro ao analisar arquivo ${filePath}:`, error); return []; } } /** * Analisa uso de actions customizadas da Clint no código * Detecta padrões como: clint.owner.getName, clint.user.createAccount */ analyzeClintUsage(sourceCode: string, filePath: string): Array<{pattern: string, line: number, column: number}> { const usages: Array<{pattern: string, line: number, column: number}> = []; const lines = sourceCode.split('\n'); for (let i = 0; i < lines.length; i++) { const line = lines[i]; const lineNumber = i + 1; // Detectar padrões clint.*.* const clintMatches = line.matchAll(/clint\.([a-zA-Z_]+)\.([a-zA-Z_]+)/g); for (const match of clintMatches) { const fullPattern = match[0]; // clint.owner.getName const column = match.index || 0; usages.push({ pattern: fullPattern, line: lineNumber, column: column }); } } return usages; } /** * Mapeia uso de padrões Clint para actions do Hasura */ mapClintUsageToActions(clintUsages: Array<{pattern: string, line: number, column: number}>): Array<{actionName: string, line: number, column: number}> { return clintUsages.map(usage => ({ actionName: this.clintPatternToAction(usage.pattern), line: usage.line, column: usage.column })); } /** * Converte padrão Clint para nome de action do Hasura * Exemplo: clint.owner.getName -> owner_get_name */ private clintPatternToAction(pattern: string): string { const match = pattern.match(/^clint\.([^.]+)\.([^.]+)$/); if (!match) { return pattern.replace('clint.', ''); } const [, entity, method] = match; // Converter camelCase para snake_case const actionPart = method.replace(/([A-Z])/g, '_$1').toLowerCase(); return `${entity}_${actionPart}`; } } export { ClintAction, ClintUsageResult };