@neurolint/cli
Version:
NeuroLint CLI - Deterministic code fixing for TypeScript, JavaScript, React, and Next.js with 8-layer architecture including Security Forensics, Next.js 16, React Compiler, and Turbopack support
339 lines (302 loc) • 8.19 kB
JavaScript
/**
* NeuroLint - Unified Configuration Manager
*
* Handles configuration across CLI, VS Code, and Web App platforms
* with support for project-specific, team, and global settings.
*
* Copyright (c) 2025 NeuroLint
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const fs = require('fs').promises;
const path = require('path');
class ConfigManager {
constructor() {
this.config = {
enabledLayers: [1, 2, 3, 4, 5, 6, 7],
include: ['**/*.{ts,tsx,js,jsx,json}'],
exclude: ['**/node_modules/**', '**/dist/**', '**/.next/**'],
verbose: false,
backup: true,
format: 'console',
apiUrl: 'https://app.neurolint.dev/api',
timeout: 30000,
teamPrefs: {
syncRules: true,
sharedHistory: true
}
};
this.configPath = '.neurolintrc';
this.globalConfigPath = this.getGlobalConfigPath();
}
/**
* Get global configuration path
*/
getGlobalConfigPath() {
const home = process.env.HOME || process.env.USERPROFILE;
return path.join(home, '.neurolintrc');
}
/**
* Load configuration from file
*/
async loadConfig(configPath = null) {
const paths = [
configPath,
path.join(process.cwd(), this.configPath),
this.globalConfigPath
].filter(Boolean);
for (const configFile of paths) {
try {
const data = await fs.readFile(configFile, 'utf8');
const fileConfig = JSON.parse(data);
this.config = { ...this.config, ...fileConfig };
return this.config;
} catch (error) {
// Continue to next config file
continue;
}
}
return this.config;
}
/**
* Save configuration to file
*/
async saveConfig(config = null, configPath = null) {
const configToSave = config || this.config;
const savePath = configPath || path.join(process.cwd(), this.configPath);
try {
await fs.writeFile(savePath, JSON.stringify(configToSave, null, 2));
return true;
} catch (error) {
throw new Error(`Failed to save configuration: ${error.message}`);
}
}
/**
* Get configuration value
*/
get(key, defaultValue = null) {
return this.config[key] !== undefined ? this.config[key] : defaultValue;
}
/**
* Set configuration value
*/
set(key, value) {
this.config[key] = value;
}
/**
* Get enabled layers for analysis
*/
getEnabledLayers() {
return this.config.enabledLayers || [1, 2, 3, 4, 5, 6, 7];
}
/**
* Check if layer is enabled
*/
isLayerEnabled(layerId) {
return this.config.enabledLayers.includes(layerId);
}
/**
* Get file patterns to include
*/
getIncludePatterns() {
return this.config.include || ['**/*.{ts,tsx,js,jsx,json}'];
}
/**
* Get file patterns to exclude
*/
getExcludePatterns() {
return this.config.exclude || ['**/node_modules/**', '**/dist/**', '**/.next/**'];
}
/**
* Get team preferences
*/
getTeamPrefs() {
return this.config.teamPrefs || {
syncRules: true,
sharedHistory: true
};
}
/**
* Validate configuration
*/
validateConfig(config = null) {
const configToValidate = config || this.config;
const errors = [];
// Validate enabled layers
if (!Array.isArray(configToValidate.enabledLayers)) {
errors.push('enabledLayers must be an array');
} else {
for (const layer of configToValidate.enabledLayers) {
if (typeof layer !== 'number' || layer < 1 || layer > 6) {
errors.push(`Invalid layer ID: ${layer}`);
}
}
}
// Validate include patterns
if (!Array.isArray(configToValidate.include)) {
errors.push('include must be an array');
}
// Validate exclude patterns
if (!Array.isArray(configToValidate.exclude)) {
errors.push('exclude must be an array');
}
// Validate timeout
if (configToValidate.timeout && typeof configToValidate.timeout !== 'number') {
errors.push('timeout must be a number');
}
return {
valid: errors.length === 0,
errors
};
}
/**
* Get configuration for specific platform
*/
getPlatformConfig(platform) {
const baseConfig = { ...this.config };
switch (platform) {
case 'cli':
return {
...baseConfig,
format: baseConfig.format || 'console'
};
case 'vscode':
return {
...baseConfig,
format: 'diagnostics',
realTime: true
};
case 'web':
return {
...baseConfig,
format: 'json',
apiEnabled: true
};
default:
return baseConfig;
}
}
/**
* Merge configurations from multiple sources
*/
mergeConfigs(configs) {
return configs.reduce((merged, config) => {
return { ...merged, ...config };
}, {});
}
/**
* Get configuration schema for validation
*/
getConfigSchema() {
return {
type: 'object',
properties: {
enabledLayers: {
type: 'array',
items: { type: 'number', minimum: 1, maximum: 6 },
description: 'Array of layer IDs to enable'
},
include: {
type: 'array',
items: { type: 'string' },
description: 'File patterns to include'
},
exclude: {
type: 'array',
items: { type: 'string' },
description: 'File patterns to exclude'
},
verbose: {
type: 'boolean',
description: 'Enable detailed output'
},
backup: {
type: 'boolean',
description: 'Create backups before applying fixes'
},
format: {
type: 'string',
enum: ['console', 'json', 'html'],
description: 'Output format'
},
apiUrl: {
type: 'string',
description: 'API endpoint URL'
},
timeout: {
type: 'number',
minimum: 1000,
maximum: 300000,
description: 'Request timeout in milliseconds'
},
teamPrefs: {
type: 'object',
properties: {
syncRules: { type: 'boolean' },
sharedHistory: { type: 'boolean' }
}
}
}
};
}
/**
* Create default configuration
*/
createDefaultConfig() {
return {
enabledLayers: [1, 2, 3, 4, 5, 6],
include: ['**/*.{ts,tsx,js,jsx,json}'],
exclude: ['**/node_modules/**', '**/dist/**', '**/.next/**'],
verbose: false,
backup: true,
format: 'console',
apiUrl: 'https://app.neurolint.dev/api',
timeout: 30000,
teamPrefs: {
syncRules: true,
sharedHistory: true
}
};
}
/**
* Export configuration for sharing
*/
exportConfig() {
return {
version: '1.2.1',
timestamp: new Date().toISOString(),
config: this.config
};
}
/**
* Import configuration from external source
*/
async importConfig(configData) {
if (typeof configData === 'string') {
configData = JSON.parse(configData);
}
if (configData.config) {
this.config = { ...this.config, ...configData.config };
} else {
this.config = { ...this.config, ...configData };
}
// Validate imported configuration
const validation = this.validateConfig();
if (!validation.valid) {
throw new Error(`Invalid configuration: ${validation.errors.join(', ')}`);
}
return this.config;
}
}
// Create and export singleton instance
const configManager = new ConfigManager();
module.exports = configManager;