code-auditor-mcp
Version:
TypeScript/JavaScript code quality auditor with MCP server - Analyze code for SOLID principles, DRY violations, security patterns, and more
140 lines • 4.76 kB
JavaScript
/**
* Configuration Loader (Functional)
* Handles loading and merging configuration from multiple sources
*/
import { promises as fs } from 'fs';
import path from 'path';
import { getDefaultConfig, DEFAULT_CODE_INDEX_CONFIG } from './defaults.js';
/**
* Load configuration from multiple sources
*/
export async function loadConfig(options) {
// Start with defaults
let config = getDefaultConfig();
// Add code index defaults
config.codeIndex = DEFAULT_CODE_INDEX_CONFIG;
// Load from config file if specified
if (options?.configPath) {
config = await loadFromFile(config, options.configPath);
}
// Load from environment variables
if (options?.environmentPrefix) {
config = loadFromEnvironment(config, options.environmentPrefix);
}
// Apply CLI arguments (highest priority)
if (options?.cliArgs) {
config = { ...config, ...options.cliArgs };
}
// Normalize paths
config = normalizePaths(config);
return config;
}
/**
* Load configuration from a JSON file
*/
async function loadFromFile(baseConfig, configPath) {
try {
const content = await fs.readFile(configPath, 'utf-8');
const fileConfig = JSON.parse(content);
return mergeConfig(baseConfig, fileConfig);
}
catch (error) {
console.warn(`Failed to load config from ${configPath}:`, error);
return baseConfig;
}
}
/**
* Load configuration from environment variables
*/
function loadFromEnvironment(baseConfig, prefix) {
const env = process.env;
const envConfig = {};
// Map environment variables to config properties
if (env[`${prefix}_MIN_SEVERITY`]) {
envConfig.minSeverity = env[`${prefix}_MIN_SEVERITY`];
}
if (env[`${prefix}_OUTPUT_DIR`]) {
envConfig.outputDirectory = env[`${prefix}_OUTPUT_DIR`];
}
if (env[`${prefix}_FAIL_ON_CRITICAL`]) {
envConfig.failOnCritical = env[`${prefix}_FAIL_ON_CRITICAL`] === 'true';
}
if (env[`${prefix}_ANALYZERS`]) {
envConfig.enabledAnalyzers = env[`${prefix}_ANALYZERS`].split(',');
}
// Code index environment variables
const codeIndexConfig = {};
if (env[`${prefix}_CODE_INDEX_DB_PATH`]) {
codeIndexConfig.databasePath = env[`${prefix}_CODE_INDEX_DB_PATH`];
}
if (env[`${prefix}_CODE_INDEX_BATCH_SIZE`]) {
codeIndexConfig.maxBatchSize = parseInt(env[`${prefix}_CODE_INDEX_BATCH_SIZE`], 10);
}
if (env[`${prefix}_CODE_INDEX_SEARCH_LIMIT`]) {
codeIndexConfig.searchResultLimit = parseInt(env[`${prefix}_CODE_INDEX_SEARCH_LIMIT`], 10);
}
if (Object.keys(codeIndexConfig).length > 0) {
envConfig.codeIndex = codeIndexConfig;
}
return mergeConfig(baseConfig, envConfig);
}
/**
* Deep merge two configuration objects
*/
function mergeConfig(base, override) {
const result = { ...base };
for (const key in override) {
const value = override[key];
if (value !== undefined) {
if (typeof value === 'object' && !Array.isArray(value)) {
const baseValue = base[key];
result[key] = {
...(typeof baseValue === 'object' ? baseValue : {}),
...value
};
}
else {
result[key] = value;
}
}
}
return result;
}
/**
* Normalize file paths in configuration
*/
function normalizePaths(config) {
const normalized = { ...config };
// Normalize output directory
if (normalized.outputDirectory) {
normalized.outputDirectory = path.resolve(normalized.outputDirectory);
}
// Normalize include/exclude paths
if (normalized.includePaths) {
normalized.includePaths = normalized.includePaths.map(p => path.isAbsolute(p) ? p : path.join(process.cwd(), p));
}
if (normalized.excludePaths) {
normalized.excludePaths = normalized.excludePaths.map(p => path.isAbsolute(p) ? p : path.join(process.cwd(), p));
}
return normalized;
}
/**
* Validate configuration
*/
export function validateConfig(config) {
const errors = [];
// Validate severity
if (config.minSeverity && !['critical', 'warning', 'suggestion'].includes(config.minSeverity)) {
errors.push(`Invalid severity: ${config.minSeverity}`);
}
// Validate analyzers
const validAnalyzers = ['solid', 'dry', 'security', 'component', 'data-access'];
if (config.enabledAnalyzers) {
const invalid = config.enabledAnalyzers.filter(a => !validAnalyzers.includes(a));
if (invalid.length > 0) {
errors.push(`Invalid analyzers: ${invalid.join(', ')}`);
}
}
return errors;
}
//# sourceMappingURL=configLoader.js.map