UNPKG

@eventcatalog/linter

Version:

A linter for EventCatalog to validate frontmatter and resource references

155 lines 6.71 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.reportErrors = exports.groupErrorsByFile = exports.formatParseError = exports.formatError = void 0; const chalk_1 = __importDefault(require("chalk")); const formatError = (error, showFilename = true) => { const lineInfo = error.line ? chalk_1.default.dim(`:${error.line}:1`) : ''; const filename = showFilename ? `${chalk_1.default.dim(error.file)}${lineInfo}` : ''; const isWarning = error.severity === 'warning'; const severity = isWarning ? chalk_1.default.yellow('warning') : chalk_1.default.red('error'); const icon = isWarning ? chalk_1.default.yellow('⚠') : chalk_1.default.red('✖'); const errorCode = getErrorCode(error); const field = error.field ? chalk_1.default.dim(`[${error.field}]`) : ''; const parts = []; if (filename) parts.push(filename); parts.push(icon, severity, error.message, field, chalk_1.default.dim(errorCode)); return parts.filter(Boolean).join(' '); }; exports.formatError = formatError; const getErrorCode = (error) => { if (error.rule) { return `(${error.rule})`; } // Fallback to generic error codes if (error.type === 'schema') { if (error.field) { if (error.message.includes('Required')) return '(@eventcatalog/required-field)'; if (error.message.includes('Expected')) return '(@eventcatalog/invalid-type)'; return '(@eventcatalog/schema-validation)'; } return '(@eventcatalog/schema)'; } if (error.type === 'reference') { return '(@eventcatalog/invalid-reference)'; } return '(@eventcatalog/unknown)'; }; const formatParseError = (error, showFilename = true) => { const filename = showFilename ? chalk_1.default.dim(error.file.relativePath) : ''; const severity = chalk_1.default.red('error'); const message = `Parse error: ${error.error.message}`; const errorCode = chalk_1.default.dim('(@eventcatalog/parse-error)'); const parts = []; if (filename) parts.push(filename); parts.push(chalk_1.default.red('✖'), severity, message, errorCode); return parts.filter(Boolean).join(' '); }; exports.formatParseError = formatParseError; const groupErrorsByFile = (errors) => { const grouped = new Map(); for (const error of errors) { if (!grouped.has(error.file)) { grouped.set(error.file, []); } grouped.get(error.file).push(error); } return grouped; }; exports.groupErrorsByFile = groupErrorsByFile; const reportErrors = (validationErrors, parseErrors, verbose = false) => { const allErrors = [ ...validationErrors, ...parseErrors.map((pe) => ({ type: 'parse', resource: pe.file.resourceType || 'unknown', message: pe.error.message, file: pe.file.relativePath, line: undefined, })), ]; const schemaErrors = validationErrors.filter((e) => e.type === 'schema'); const referenceErrors = validationErrors.filter((e) => e.type === 'reference'); const warnings = validationErrors.filter((e) => e.severity === 'warning'); const errors = [...validationErrors.filter((e) => e.severity !== 'warning'), ...parseErrors]; const totalErrors = errors.length; const totalWarnings = warnings.length; if (totalErrors === 0 && totalWarnings === 0) { console.log(chalk_1.default.green('✔ No problems found!')); return { totalErrors: 0, totalWarnings: 0, schemaErrors: 0, referenceErrors: 0, parseErrors: 0, filesChecked: 0, filesWithErrors: 0, }; } const grouped = (0, exports.groupErrorsByFile)(validationErrors); const parseErrorsGrouped = groupParseErrorsByFile(parseErrors); const allFiles = new Set([...grouped.keys(), ...parseErrorsGrouped.keys()]); console.log(); // Empty line // Report by file for better readability for (const file of Array.from(allFiles).sort()) { const fileErrors = grouped.get(file) || []; const fileParseErrors = parseErrorsGrouped.get(file) || []; const fileErrorCount = fileErrors.length + fileParseErrors.length; if (fileErrorCount === 0) continue; // File header (ESLint-style) console.log(chalk_1.default.underline(file)); // Parse errors first for (const error of fileParseErrors) { console.log(` ${(0, exports.formatParseError)(error, false)}`); } // Then validation errors for (const error of fileErrors) { console.log(` ${(0, exports.formatError)(error, false)}`); } // File summary const fileWarnings = fileErrors.filter((e) => e.severity === 'warning').length; const fileActualErrors = fileErrorCount - fileWarnings; const problemText = fileErrorCount === 1 ? 'problem' : 'problems'; const summaryColor = fileActualErrors > 0 ? chalk_1.default.red : chalk_1.default.yellow; const summaryIcon = fileActualErrors > 0 ? '✖' : '⚠'; console.log(summaryColor(`\n${summaryIcon} ${fileErrorCount} ${problemText}\n`)); } // Overall summary (ESLint-style) const filesWithErrors = allFiles.size; const totalProblems = totalErrors + totalWarnings; const problemText = totalProblems === 1 ? 'problem' : 'problems'; const fileText = filesWithErrors === 1 ? 'file' : 'files'; const summaryColor = totalErrors > 0 ? chalk_1.default.red.bold : chalk_1.default.yellow.bold; const summaryIcon = totalErrors > 0 ? '✖' : '⚠'; console.log(summaryColor(`${summaryIcon} ${totalProblems} ${problemText} (${totalErrors} errors, ${totalWarnings} warnings)`)); console.log(chalk_1.default.dim(` ${filesWithErrors} ${fileText} checked`)); return { totalErrors, totalWarnings, schemaErrors: schemaErrors.length, referenceErrors: referenceErrors.length, parseErrors: parseErrors.length, filesChecked: allFiles.size, filesWithErrors, }; }; exports.reportErrors = reportErrors; const groupParseErrorsByFile = (errors) => { const grouped = new Map(); for (const error of errors) { const file = error.file.relativePath; if (!grouped.has(file)) { grouped.set(file, []); } grouped.get(file).push(error); } return grouped; }; //# sourceMappingURL=index.js.map