@ton-ai-core/vibecode-linter
Version:
Advanced TypeScript linter with Git integration, dependency analysis, and comprehensive error reporting
179 lines • 6.03 kB
JavaScript
// CHANGE: Extracted configuration loading from lint.ts
// WHY: Configuration loading logic should be in a separate module
// QUOTE(ТЗ): "Разбить lint.ts на подфайлы, каждый файл желательно должен быть не больше 300 строчек кода"
// REF: REQ-20250210-MODULAR-ARCH
// SOURCE: n/a
// CHANGE: Use node: protocol for Node.js built-in modules
// WHY: Biome lint rule requires explicit node: prefix for clarity
// REF: lint/style/useNodejsImportProtocol
// SOURCE: https://biomejs.dev/linter/rules/lint/style/useNodejsImportProtocol
import * as fs from "node:fs";
import * as path from "node:path";
/**
* Type guard to check if value is a JSON object.
*
* @param value Value to check
* @returns True if value is a non-null object
*/
function isJSONObject(value) {
return value !== null && typeof value === "object" && !Array.isArray(value);
}
/**
* Type guard to check if value is a string.
*
* @param value Value to check
* @returns True if value is a string
*/
function isString(value) {
return typeof value === "string";
}
/**
* Type guard to check if value is a number.
*
* @param value Value to check
* @returns True if value is a number
*/
function isNumber(value) {
return typeof value === "number";
}
/**
* Type guard to check if value is an array.
*
* @param value Value to check
* @returns True if value is an array
*/
function isArray(value) {
return Array.isArray(value);
}
/**
* Type guard to check if value is a PriorityLevelJSON object.
*
* @param value Value to check
* @returns True if value has required priority level fields
*/
function isPriorityLevelJSON(value) {
return (isJSONObject(value) &&
"level" in value &&
"name" in value &&
"rules" in value);
}
/**
* Валидирует и нормализует уровень приоритета.
*
* @param value Значение для валидации
* @returns Нормализованный уровень приоритета или null
*
* @invariant value должен быть объектом с полями level, name, rules
*/
function validatePriorityLevel(value) {
if (!isPriorityLevelJSON(value)) {
return null;
}
const level = value.level;
const name = value.name;
const rules = value.rules;
if (!isNumber(level) || !isString(name) || !isArray(rules)) {
return null;
}
const normalizedRules = rules
.filter((r) => isString(r))
.map((r) => r.toLowerCase());
return {
level,
name,
rules: normalizedRules,
};
}
/**
* Type guard to check if value is a ConfigJSON object.
*
* @param value Value to check
* @returns True if value has priorityLevels field
*/
function isConfigJSON(value) {
return isJSONObject(value) && "priorityLevels" in value;
}
export function loadLinterConfig(configPath = path.resolve(process.cwd(), "linter.config.json")) {
try {
const raw = fs.readFileSync(configPath, "utf8");
const parsed = JSON.parse(raw);
if (!isConfigJSON(parsed)) {
return null;
}
const priorityLevels = parsed.priorityLevels;
if (!isArray(priorityLevels)) {
return null;
}
const validatedLevels = [];
for (const level of priorityLevels) {
const validated = validatePriorityLevel(level);
if (validated) {
validatedLevels.push(validated);
}
}
if (validatedLevels.length === 0) {
return null;
}
return { priorityLevels: validatedLevels };
}
catch {
return null;
}
}
/**
* Извлекает идентификатор правила из сообщения линтера.
*
* @param m Сообщение линтера (может содержать ruleId, code, rule, category)
* @returns Идентификатор правила в нижнем регистре
*/
export function ruleIdOf(m) {
return String(m.ruleId ?? m.code ?? m.rule ?? m.category ?? "").toLowerCase();
}
/**
* Создает карту правил с их уровнями приоритета.
* Поддерживает специальное значение "all" для указания "все правила кроме явно перечисленных".
*
* @param cfg Конфигурация линтера
* @returns Объект с картой явных правил и уровнем "all"
*
* @example
* // Конфиг:
* {
* priorityLevels: [
* {level: 1, rules: ["error1", "error2"]},
* {level: 2, rules: ["all"]}, // Все кроме error1, error2, error3
* {level: 3, rules: ["error3"]}
* ]
* }
* // Результат:
* // error1 → level 1
* // error2 → level 1
* // error3 → level 3
* // все остальные → level 2 (from "all")
*/
export function makeRuleLevelMap(cfg) {
// CHANGE: Support "all" keyword to match all rules except explicitly listed ones
// WHY: User wants flexible priority system where "all" acts as catch-all for unlisted rules
// QUOTE(USER): "чем больше ошибок будет описано тем больше они не отображаются на уровне all"
// REF: user-request-all-keyword
// SOURCE: n/a
const explicitRules = new Map();
let allLevel = null;
// Collect all explicit rules and find "all" level
for (const pl of cfg.priorityLevels) {
for (const r of pl.rules) {
if (r === "all") {
// Remember first "all" occurrence
if (!allLevel) {
allLevel = { level: pl.level, name: pl.name };
}
}
else {
// Collect explicit rule
explicitRules.set(r, { level: pl.level, name: pl.name });
}
}
}
return { explicitRules, allLevel };
}
//# sourceMappingURL=loader.js.map