graphql-lint-clint-platform
Version:
GraphQL unused fields linter for Clint platform - Custom patterns and actions.graphql support
235 lines • 8.93 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.ClintGraphQLExtractor = void 0;
exports.actionToClintPattern = actionToClintPattern;
exports.clintPatternToAction = clintPatternToAction;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const graphql_1 = require("graphql");
class ClintGraphQLExtractor {
constructor() {
this.actionsQueries = new Map();
}
/**
* Detecta queries customizadas da Clint baseadas em actions.graphql
*/
async extractClintQueries(projectPath) {
const queries = [];
// Extrair queries do actions.graphql da Clint
const actionsQueries = await this.extractFromActionsGraphQL(projectPath);
queries.push(...actionsQueries);
return queries;
}
/**
* Extrai queries do arquivo actions.graphql da Clint
*/
async extractFromActionsGraphQL(projectPath) {
const actionsFiles = this.findActionsFiles(projectPath);
const queries = [];
for (const actionsFile of actionsFiles) {
try {
const content = fs.readFileSync(actionsFile, 'utf-8');
const document = (0, graphql_1.parse)(content);
const actionsQueries = this.parseActionsDocument(document, actionsFile);
queries.push(...actionsQueries);
// Armazenar para mapeamento posterior
actionsQueries.forEach(query => {
this.actionsQueries.set(query.name, query);
});
}
catch (error) {
console.warn(`⚠️ Erro ao processar actions.graphql em ${actionsFile}:`, error);
}
}
return queries;
}
/**
* Procura arquivos actions.graphql no projeto
*/
findActionsFiles(projectPath) {
const actionsFiles = [];
const searchPaths = [
path.join(projectPath, 'actions.graphql'),
path.join(projectPath, 'hasura', 'actions.graphql'),
path.join(projectPath, 'graphql', 'actions.graphql'),
path.join(projectPath, 'schema', 'actions.graphql'),
];
// Busca recursiva por actions.graphql
const findRecursive = (dir) => {
try {
const items = fs.readdirSync(dir);
for (const item of items) {
const fullPath = path.join(dir, item);
const stat = fs.statSync(fullPath);
if (stat.isDirectory() && !item.includes('node_modules')) {
findRecursive(fullPath);
}
else if (item === 'actions.graphql') {
actionsFiles.push(fullPath);
}
}
}
catch (error) {
// Ignorar diretórios inacessíveis
}
};
// Verificar caminhos conhecidos
searchPaths.forEach(filePath => {
if (fs.existsSync(filePath)) {
actionsFiles.push(filePath);
}
});
// Busca recursiva a partir da raiz
findRecursive(projectPath);
return [...new Set(actionsFiles)]; // Remove duplicatas
}
/**
* Parseia o documento GraphQL do actions.graphql
*/
parseActionsDocument(document, filePath) {
const queries = [];
for (const definition of document.definitions) {
if (definition.kind === 'ObjectTypeDefinition' && definition.name.value === 'Query') {
// Processar campos da Query type
if (definition.fields) {
for (const field of definition.fields) {
const query = this.parseActionField(field, filePath);
if (query) {
queries.push(query);
}
}
}
}
}
return queries;
}
/**
* Converte um campo de action em GraphQLQuery
* owner_get_name(id: uuid!) -> owner_get_name query
*/
parseActionField(field, filePath) {
try {
const queryName = field.name.value;
// Extrair campos do tipo de retorno (se disponível)
const fields = this.extractFieldsFromActionReturnType(field, queryName);
return {
name: queryName,
fields: fields,
rawQuery: `# Action: ${queryName}`,
location: {
file: filePath,
line: field.loc?.startToken?.line || 0,
column: field.loc?.startToken?.column || 0
}
};
}
catch (error) {
console.warn(`⚠️ Erro ao processar action field ${field.name.value}:`, error);
return null;
}
}
/**
* Extrai campos baseados no tipo de retorno da action
*/
extractFieldsFromActionReturnType(field, queryName) {
// Por enquanto, criar campos sintéticos baseados no nome da action
// Em uma implementação mais completa, poderia analisar o schema completo
const fields = [];
// Padrão: owner_get_name -> campos relacionados a owner
const entityMatch = queryName.match(/^(\w+)_/);
if (entityMatch) {
const entity = entityMatch[1];
// Campos comuns baseados na entidade
const commonFields = this.getCommonFieldsForEntity(entity);
fields.push(...commonFields);
}
// Adicionar campo de status padrão para actions
fields.push({
name: 'status',
path: ['status'],
line: field.loc?.startToken?.line || 0,
column: field.loc?.startToken?.column || 0
});
return fields;
}
/**
* Gera campos comuns para uma entidade
*/
getCommonFieldsForEntity(entity) {
const commonFieldsByEntity = {
owner: ['id', 'name', 'email', 'type'],
user: ['id', 'name', 'email', 'avatar'],
payment: ['id', 'amount', 'status', 'method'],
order: ['id', 'total', 'status', 'items'],
product: ['id', 'name', 'price', 'category'],
};
const fieldNames = commonFieldsByEntity[entity] || ['id', 'name'];
return fieldNames.map((fieldName, index) => ({
name: fieldName,
path: [fieldName],
line: 0,
column: index * 10
}));
}
}
exports.ClintGraphQLExtractor = ClintGraphQLExtractor;
/**
* Converte nome de action para padrão Clint
* owner_get_name -> clint.owner.getName
*/
function actionToClintPattern(actionName) {
const match = actionName.match(/^(\w+)_(.+)$/);
if (!match)
return null;
const [, entity, action] = match;
// Converter action para camelCase
const camelAction = action.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
return `clint.${entity}.${camelAction}`;
}
/**
* Converte padrão Clint para nome de action
* clint.owner.getName -> owner_get_name
*/
function clintPatternToAction(clintPattern) {
const match = clintPattern.match(/^clint\.(\w+)\.(.+)$/);
if (!match)
return null;
const [, entity, method] = match;
// Converter camelCase para snake_case
const snakeAction = method.replace(/[A-Z]/g, letter => `_${letter.toLowerCase()}`);
return `${entity}_${snakeAction}`;
}
//# sourceMappingURL=clintExtractor-old.js.map