UNPKG

graphql-lint-clint-platform

Version:

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

214 lines 8.8 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ClintUsageAnalyzer = void 0; const fs_1 = __importDefault(require("fs")); const glob_1 = require("glob"); class ClintUsageAnalyzer { constructor(actionPatterns) { this.actionPatterns = {}; this.sourceFiles = []; this.actionPatterns = actionPatterns; } async findSourceFiles(sourcePath) { const tsFiles = await (0, glob_1.glob)('**/*.{ts,tsx,js,jsx}', { cwd: sourcePath, ignore: ['node_modules/**', 'dist/**', 'build/**', '.git/**'], absolute: true }); return tsFiles; } async analyzeUsageInPath(sourcePath, verbose = false) { if (verbose) { console.log('📋 Iniciando análise DETALHADA de uso dos actions da Clint...\n'); console.log(`🎯 Total de actions detectadas: ${Object.keys(this.actionPatterns).length}`); } // Encontrar arquivos fonte this.sourceFiles = await this.findSourceFiles(sourcePath); if (verbose) { console.log(`📁 Total de arquivos para análise: ${this.sourceFiles.length}\n`); } const unusedActions = []; const usedActions = {}; // Buscar por todos os patterns de todas as actions for (const [actionName, patterns] of Object.entries(this.actionPatterns)) { if (verbose) { console.log(`🔍 Analisando action: ${actionName}`); console.log(` 🎨 Padrões gerados (${patterns.length}): ${patterns.join(', ')}`); } let hasUsage = false; const actionUsages = []; // Testar cada padrão com logging detalhado for (const pattern of patterns) { if (verbose) { console.log(` 🔎 Testando padrão: "${pattern}"`); } const usages = await this.findUsageInFiles(pattern); if (usages.length > 0) { hasUsage = true; actionUsages.push(...usages); if (verbose) { console.log(` ✅ ${usages.length} uso(s) encontrado(s) para "${pattern}"`); } } else { if (verbose) { console.log(` ❌ Nenhum uso para "${pattern}"`); } } } if (hasUsage) { usedActions[actionName] = actionUsages; if (verbose) { console.log(` ✅ USADO - Total: ${actionUsages.length} ocorrência(s)\n`); } } else { unusedActions.push(actionName); if (verbose) { console.log(` ❌ NÃO USADO - Nenhuma ocorrência encontrada\n`); } } } if (verbose) { console.log(`📊 RESUMO DA ANÁLISE:`); console.log(` ✅ Actions usadas: ${Object.keys(usedActions).length}`); console.log(` ❌ Actions não usadas: ${unusedActions.length}`); console.log(` 📈 Total: ${Object.keys(this.actionPatterns).length}\n`); } return { unusedActions, usedActions, totalActions: Object.keys(this.actionPatterns).length }; } async findUsageInFiles(pattern) { const allUsages = []; for (const filePath of this.sourceFiles) { const usages = await this.findUsageInFile(filePath, pattern); allUsages.push(...usages); } return allUsages; } async findUsageInFile(filePath, pattern) { try { const content = fs_1.default.readFileSync(filePath, 'utf-8'); const lines = content.split('\n'); const usages = []; for (let i = 0; i < lines.length; i++) { const line = lines[i]; const lineNumber = i + 1; // MÚLTIPLAS ESTRATÉGIAS DE BUSCA: // 1. Busca exata pelo padrão if (line.includes(pattern)) { usages.push({ file: filePath, line: lineNumber, code: line.trim() }); continue; } // 2. Busca por regex - padrões com pontos if (pattern.includes('.')) { // Escapar pontos para regex const regexPattern = pattern.replace(/\./g, '\\.'); const regex = new RegExp(`\\b${regexPattern}\\b`); if (regex.test(line)) { usages.push({ file: filePath, line: lineNumber, code: line.trim() }); continue; } } // 3. Busca por partes do padrão (mais flexível) const patternParts = pattern.split('.'); if (patternParts.length >= 2) { // Verificar se todas as partes estão na linha const allPartsPresent = patternParts.every(part => line.toLowerCase().includes(part.toLowerCase())); if (allPartsPresent) { // Verificação adicional: deve ser código, não comentário const trimmedLine = line.trim(); if (!trimmedLine.startsWith('//') && !trimmedLine.startsWith('/*') && !trimmedLine.startsWith('*')) { usages.push({ file: filePath, line: lineNumber, code: line.trim() }); } } } // 4. Busca por snake_case original (fallback) const snakeCasePattern = pattern.replace(/\./g, '_').replace(/([A-Z])/g, '_$1').toLowerCase().replace(/^_/, ''); if (line.toLowerCase().includes(snakeCasePattern)) { usages.push({ file: filePath, line: lineNumber, code: line.trim() }); } } if (usages.length > 0) { console.log(` ✅ Padrão "${pattern}" encontrado ${usages.length}x em ${filePath}`); } 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, filePath) { const usages = []; 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) { 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 */ clintPatternToAction(pattern) { 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}`; } } exports.ClintUsageAnalyzer = ClintUsageAnalyzer; //# sourceMappingURL=clintAnalyzer.js.map