diff --git a/scripts/commands/elixir-compile.js b/scripts/commands/elixir-compile.js index 0f7348e..267977e 100644 --- a/scripts/commands/elixir-compile.js +++ b/scripts/commands/elixir-compile.js @@ -5,7 +5,7 @@ * Compile Elixir projects with Elixir-specific improvements */ -const ElixirCommandRunner = require('../elixir/command-runner'); +const ElixirCommandRunner = require('../elixir/elixir-command-runner-refactored'); async function main() { const args = process.argv.slice(2); diff --git a/scripts/commands/elixir-deps.js b/scripts/commands/elixir-deps.js index 71957b1..bb7d000 100644 --- a/scripts/commands/elixir-deps.js +++ b/scripts/commands/elixir-deps.js @@ -5,7 +5,7 @@ * Manage Elixir dependencies with Mix, Hex, and security scanning */ -const ElixirCommandRunner = require('../elixir/command-runner'); +const ElixirCommandRunner = require('../elixir/elixir-command-runner-refactored'); const { defaultErrorHandler } = require('../lib/error-handler'); async function main() { @@ -137,7 +137,7 @@ async function runSecurityAudit(runner, _options) { dep.includes('plug') || dep.includes('crypto') || dep.includes('ssl') || - dep.includes('tls'), + dep.includes('tls') ); if (securityDeps.length > 0) { console.log(` ⚠️ ${securityDeps.length} security-related dependencies need updates`); @@ -169,7 +169,7 @@ async function runSecurityAudit(runner, _options) { if (auditResult.stdout) { const lines = auditResult.stdout.split('\n'); const vulnCount = lines.filter( - (line) => line.includes('Vulnerability') || line.includes('CVE') || line.includes('HIGH'), + (line) => line.includes('Vulnerability') || line.includes('CVE') || line.includes('HIGH') ).length; results.tools.mix_audit = { @@ -194,7 +194,7 @@ async function runSecurityAudit(runner, _options) { securityTools.push('hex.audit'); const lines = hexAuditResult.stdout.split('\n'); const issues = lines.filter( - (line) => line.includes('Vulnerability') || line.includes('found') || line.includes('⚠'), + (line) => line.includes('Vulnerability') || line.includes('found') || line.includes('⚠') ); results.tools.hex_audit = { diff --git a/scripts/commands/elixir-format.js b/scripts/commands/elixir-format.js index 705a1fc..65a50c9 100644 --- a/scripts/commands/elixir-format.js +++ b/scripts/commands/elixir-format.js @@ -5,7 +5,7 @@ * Format Elixir code with built-in formatter and Elixir-specific improvements */ -const ElixirCommandRunner = require('../elixir/command-runner'); +const ElixirCommandRunner = require('../elixir/elixir-command-runner-refactored'); async function main() { const args = process.argv.slice(2); diff --git a/scripts/commands/elixir-lint.js b/scripts/commands/elixir-lint.js index a7a0c41..40773b2 100644 --- a/scripts/commands/elixir-lint.js +++ b/scripts/commands/elixir-lint.js @@ -5,7 +5,7 @@ * Lint Elixir code with Credo and Elixir-specific improvements */ -const ElixirCommandRunner = require('../elixir/command-runner'); +const ElixirCommandRunner = require('../elixir/elixir-command-runner-refactored'); async function main() { const args = process.argv.slice(2); diff --git a/scripts/commands/elixir-test.js b/scripts/commands/elixir-test.js index b3aebc5..ca9101d 100644 --- a/scripts/commands/elixir-test.js +++ b/scripts/commands/elixir-test.js @@ -5,7 +5,7 @@ * Run Elixir tests with ExUnit and Elixir-specific improvements */ -const ElixirCommandRunner = require('../elixir/command-runner'); +const ElixirCommandRunner = require('../elixir/elixir-command-runner-refactored'); async function main() { const args = process.argv.slice(2); diff --git a/scripts/commands/elixir-typecheck.js b/scripts/commands/elixir-typecheck.js index 086525e..c277261 100644 --- a/scripts/commands/elixir-typecheck.js +++ b/scripts/commands/elixir-typecheck.js @@ -5,7 +5,7 @@ * Type check Elixir code with Dialyzer */ -const ElixirCommandRunner = require('../elixir/command-runner'); +const ElixirCommandRunner = require('../elixir/elixir-command-runner-refactored'); async function main() { const args = process.argv.slice(2); @@ -64,10 +64,7 @@ async function main() { console.error(`\n❌ Type checking failed: ${error.message}`); // Check if Dialyzer/dialyxir is not installed - if ( - error.message.includes('Dialyzer') || - error.message.includes('dialyxir') - ) { + if (error.message.includes('Dialyzer') || error.message.includes('dialyxir')) { console.log('\n💡 Dialyzer/dialyxir is not configured. Add to mix.exs:'); console.log(' {:dialyxir, "~> 1.4", only: [:dev], runtime: false}'); console.log('\nThen run:'); diff --git a/scripts/commands/js-build.js b/scripts/commands/js-build.js index 7ccd7c7..1c2f3c0 100644 --- a/scripts/commands/js-build.js +++ b/scripts/commands/js-build.js @@ -5,7 +5,7 @@ * Build JavaScript/TypeScript projects */ -const JSCommandRunner = require('../javascript/command-runner'); +const JSCommandRunner = require('../javascript/javascript-command-runner-refactored'); async function main() { try { diff --git a/scripts/commands/js-dev.js b/scripts/commands/js-dev.js index 4a73633..25a4c39 100644 --- a/scripts/commands/js-dev.js +++ b/scripts/commands/js-dev.js @@ -5,7 +5,7 @@ * Start development server for JavaScript/TypeScript projects */ -const JSCommandRunner = require('../javascript/command-runner'); +const JSCommandRunner = require('../javascript/javascript-command-runner-refactored'); async function main() { try { diff --git a/scripts/commands/js-lint.js b/scripts/commands/js-lint.js index 1ca965b..e540044 100644 --- a/scripts/commands/js-lint.js +++ b/scripts/commands/js-lint.js @@ -5,7 +5,7 @@ * Run linter for JavaScript/TypeScript projects */ -const JSCommandRunner = require('../javascript/command-runner'); +const JSCommandRunner = require('../javascript/javascript-command-runner-refactored'); async function main() { try { diff --git a/scripts/commands/js-test.js b/scripts/commands/js-test.js index b3175a4..da7dc31 100644 --- a/scripts/commands/js-test.js +++ b/scripts/commands/js-test.js @@ -5,7 +5,7 @@ * Run tests for JavaScript/TypeScript projects */ -const JSCommandRunner = require('../javascript/command-runner'); +const JSCommandRunner = require('../javascript/javascript-command-runner-refactored'); async function main() { try { diff --git a/scripts/commands/ts-build.js b/scripts/commands/ts-build.js index 3b04eac..d4bf5ce 100644 --- a/scripts/commands/ts-build.js +++ b/scripts/commands/ts-build.js @@ -5,7 +5,7 @@ * Compile TypeScript projects */ -const JSCommandRunner = require('../javascript/command-runner'); +const JSCommandRunner = require('../javascript/javascript-command-runner-refactored'); async function main() { try { diff --git a/scripts/commands/ts-typecheck.js b/scripts/commands/ts-typecheck.js index 3ef7bfe..4b18025 100644 --- a/scripts/commands/ts-typecheck.js +++ b/scripts/commands/ts-typecheck.js @@ -5,7 +5,7 @@ * Run TypeScript type checking for TypeScript projects */ -const JSCommandRunner = require('../javascript/command-runner'); +const JSCommandRunner = require('../javascript/javascript-command-runner-refactored'); async function main() { try { diff --git a/scripts/elixir/elixir-command-runner-modules/code-quality.js b/scripts/elixir/elixir-command-runner-modules/code-quality.js new file mode 100644 index 0000000..08726dc --- /dev/null +++ b/scripts/elixir/elixir-command-runner-modules/code-quality.js @@ -0,0 +1,167 @@ +class CodeQuality { + constructor(commandExecutor, loggingUtils) { + this.commandExecutor = commandExecutor; + this.loggingUtils = loggingUtils; + } + + async format(options = {}) { + const args = []; + + if (options.check) { + args.push('--check-formatted'); + } + + if (options.dryRun) { + args.push('--dry-run'); + } + + if (options.files) { + args.push(...options.files.split(',')); + } + + if (options.verbose) { + args.push('--verbose'); + } + + try { + return await this.commandExecutor.executeMixCommand('format', args, options); + } catch (error) { + this.suggestFormatFix(error.message); + throw error; + } + } + + async lint(options = {}) { + const args = []; + + if (options.strict) { + args.push('--strict'); + } + + if (options.all) { + args.push('--all'); + } + + if (options.allPriorities) { + args.push('--all-priorities'); + } + + if (options.format) { + args.push('--format', options.format); + } + + if (options.config) { + args.push('--config', options.config); + } + + if (options.files) { + args.push(...options.files.split(',')); + } + + if (options.verbose) { + args.push('--verbose'); + } + + try { + return await this.commandExecutor.executeMixCommand('credo', args, options); + } catch (error) { + this.suggestLintFix(error.message); + throw error; + } + } + + async typecheck(options = {}) { + const args = []; + + if (options.noCheck) { + args.push('--no-check'); + } + + if (options.warnings) { + args.push('--warnings', options.warnings); + } + + if (options.format) { + args.push('--format', options.format); + } + + if (options.verbose) { + args.push('--verbose'); + } + + try { + return await this.commandExecutor.executeMixCommand('dialyzer', args, options); + } catch (error) { + this.suggestTypecheckFix(error.message); + throw error; + } + } + + suggestFormatFix(errorMessage) { + this.loggingUtils.info('\n💡 Formatting Error Suggestions:'); + + if (errorMessage.includes('check-formatted')) { + this.loggingUtils.info(' • Files are not properly formatted'); + this.loggingUtils.info(' • Run mix format without --check-formatted to fix'); + this.loggingUtils.info(' • Check .formatter.exs configuration'); + } + + if (errorMessage.includes('syntax error')) { + this.loggingUtils.info(' • Fix syntax errors before formatting'); + this.loggingUtils.info(' • Check for missing commas, parentheses, or do/end blocks'); + this.loggingUtils.info(' • Verify Elixir version compatibility'); + } + + if (errorMessage.includes('file not found')) { + this.loggingUtils.info(' • Check file paths are correct'); + this.loggingUtils.info(' • Ensure files exist in project directory'); + this.loggingUtils.info(' • Use relative paths from project root'); + } + } + + suggestLintFix(errorMessage) { + this.loggingUtils.info('\n💡 Linting Error Suggestions:'); + + if (errorMessage.includes('Credo')) { + this.loggingUtils.info(' • Install Credo: mix archive.install hex credo'); + this.loggingUtils.info(' • Check .credo.exs configuration file'); + this.loggingUtils.info(' • Run mix credo --help for available options'); + } + + if (errorMessage.includes('warning')) { + this.loggingUtils.info(' • Review warnings in context'); + this.loggingUtils.info(' • Use --strict for stricter checks'); + this.loggingUtils.info(' • Consider fixing high-priority issues first'); + } + + if (errorMessage.includes('configuration')) { + this.loggingUtils.info(' • Check .credo.exs syntax'); + this.loggingUtils.info(' • Verify check configurations'); + this.loggingUtils.info(' • Ensure consistent code style rules'); + } + } + + suggestTypecheckFix(errorMessage) { + this.loggingUtils.info('\n💡 Type Checking Error Suggestions:'); + + if (errorMessage.includes('Dialyzer')) { + this.loggingUtils.info(' • Install Dialyzer: mix archive.install hex dialyxir'); + this.loggingUtils.info(' • Build PLT first: mix dialyzer --plt'); + this.loggingUtils.info(' • Check dialyzer.ignore-warnings file'); + } + + if (errorMessage.includes('type mismatch')) { + this.loggingUtils.info(' • Check function signatures match specifications'); + this.loggingUtils.info(' • Review @spec annotations'); + this.loggingUtils.info(' • Use dialyzer.ignore-warnings for false positives'); + } + + if (errorMessage.includes('undefined function')) { + this.loggingUtils.info(' • Ensure all functions are defined or imported'); + this.loggingUtils.info(' • Check module dependencies'); + this.loggingUtils.info(' • Verify function arity matches'); + } + } +} + +module.exports = CodeQuality; diff --git a/scripts/elixir/elixir-command-runner-modules/command-executor.js b/scripts/elixir/elixir-command-runner-modules/command-executor.js new file mode 100644 index 0000000..3d97715 --- /dev/null +++ b/scripts/elixir/elixir-command-runner-modules/command-executor.js @@ -0,0 +1,196 @@ +#!/usr/bin/env node +/** + * Elixir Command Executor Module + * + * Handles core Mix command execution with comprehensive error handling + */ + +const fs = require('fs'); +const path = require('path'); +const { exec } = require('child_process'); +const os = require('os'); +const { defaultErrorHandler } = require('../../lib/error-handler'); +const { FileUtils, LoggingUtils } = require('../../lib'); + +class ElixirCommandExecutor { + constructor(projectPath, platformDetector) { + this.projectPath = projectPath; + this.platformDetector = platformDetector; + } + + /** + * Find Elixir files in the project + */ + findElixirFiles(pattern = '**/*.{ex,exs}', excludePatterns = []) { + try { + return FileUtils.findFilesByPattern(this.projectPath, [pattern], { + exclude: excludePatterns, + language: 'elixir', + }); + } catch (error) { + LoggingUtils.warn('Failed to find Elixir files:', error.message); + return []; + } + } + + /** + * Get Elixir project metadata + */ + getElixirProjectInfo() { + try { + const info = { + hasMixExs: fs.existsSync(path.join(this.projectPath, 'mix.exs')), + hasMixLock: fs.existsSync(path.join(this.projectPath, 'mix.lock')), + hasConfig: fs.existsSync(path.join(this.projectPath, 'config')), + hasLib: fs.existsSync(path.join(this.projectPath, 'lib')), + hasTest: fs.existsSync(path.join(this.projectPath, 'test')), + elixirFiles: this.findElixirFiles().length, + }; + + return info; + } catch (error) { + LoggingUtils.debug('Failed to get Elixir project info:', error.message); + return null; + } + } + + /** + * Execute Mix command with Elixir-specific improvements + */ + async executeMixCommand(command, args = [], options = {}) { + return this._executeMixCommandWithErrorHandling(command, args, options); + } + + /** + * Internal method with comprehensive error handling + */ + async _executeMixCommandWithErrorHandling(command, args = [], options = {}) { + const context = { + tool: 'elixir', + command: `mix ${command} ${args.join(' ')}`.trim(), + platform: this.platformDetector.getPlatformName(), + cwd: this.projectPath, + }; + + try { + // Ensure critical Elixir environment variables are set + const elixirEnv = { + ...process.env, + MIX_ENV: options.env || process.env.MIX_ENV || 'dev', + MIX_QUIET: '1', + }; + + // Set HOME if not set + if (!elixirEnv.HOME) { + elixirEnv.HOME = os.homedir(); + } + + const defaultOptions = { + cwd: this.projectPath, + stdio: 'inherit', + env: elixirEnv, + timeout: 300000, // 5 minutes for Elixir commands + }; + + const finalOptions = { + ...defaultOptions, + ...options, + // Merge environment objects instead of overwriting + env: options.env ? { ...defaultOptions.env, ...options.env } : defaultOptions.env, + }; + + LoggingUtils.info(`🚀 Executing: mix ${command} ${args.join(' ')}`); + + return await new Promise((resolve, reject) => { + // Build the command string with dynamic path to mix + const mixPath = this.platformDetector.getToolPath('mix', { + required: true, + customLocations: [ + // Additional Elixir installation locations + '/usr/local/bin/mix', + '/usr/bin/mix', + 'C:\\Program Files\\Elixir\\bin\\mix.bat', + ], + }); + + const cmd = `${mixPath} ${command} ${args.join(' ')}`; + LoggingUtils.debug(`🔍 Executing: ${cmd}`); + LoggingUtils.debug(`🔍 CWD: ${finalOptions.cwd}`); + LoggingUtils.debug(`🔍 MIX_ENV: ${finalOptions.env?.MIX_ENV}`); + LoggingUtils.debug(`🔍 Platform: ${this.platformDetector.getPlatformName()}`); + + exec(cmd, finalOptions, (error, stdout, stderr) => { + if (error) { + // Enhance error with additional context + error.context = context; + error.command = cmd; + error.mixPath = mixPath; + reject(error); + } else { + resolve({ + success: error ? false : true, + code: error ? error.code : 0, + stdout, + stderr, + }); + } + }); + }); + } catch (error) { + // Handle error with comprehensive error handler + const errorInfo = defaultErrorHandler.handleError(error, context); + + // Log user-friendly message using LoggingUtils + LoggingUtils.error(errorInfo.userMessage); + + // Log recovery steps using LoggingUtils + if (errorInfo.recoverySteps && errorInfo.recoverySteps.length > 0) { + LoggingUtils.info('💡 Recovery steps:'); + errorInfo.recoverySteps.forEach((step, i) => { + LoggingUtils.info(` ${i + 1}. ${step}`); + }); + } + + // Re-throw with enhanced error information + const enhancedError = new Error(errorInfo.userMessage); + enhancedError.originalError = error; + enhancedError.errorInfo = errorInfo; + throw enhancedError; + } + } + + /** + * Check if an Elixir tool is available + */ + async checkElixirTool(toolName) { + try { + const toolPath = this.platformDetector.getToolPath(toolName, { + required: false, + customLocations: [ + `/usr/local/bin/${toolName}`, + `/usr/bin/${toolName}`, + `C:\\Program Files\\Elixir\\bin\\${toolName}.bat`, + ], + }); + + const result = await new Promise((resolve) => { + exec(`${toolPath} --version`, (error) => { + resolve(!error); + }); + }); + + if (!result) { + LoggingUtils.warn(`⚠️ Elixir tool '${toolName}' not found or not working`); + LoggingUtils.info(`Run /elixir-setup to install '${toolName}'`); + } + + return result; + } catch (error) { + LoggingUtils.error(`Elixir tool '${toolName}' check failed:`, error.message); + LoggingUtils.info(`Run /elixir-setup to install '${toolName}'`); + throw error; + } + } +} + +module.exports = ElixirCommandExecutor; diff --git a/scripts/elixir/elixir-command-runner-modules/compilation-manager.js b/scripts/elixir/elixir-command-runner-modules/compilation-manager.js new file mode 100644 index 0000000..08738ca --- /dev/null +++ b/scripts/elixir/elixir-command-runner-modules/compilation-manager.js @@ -0,0 +1,183 @@ +#!/usr/bin/env node +/** + * Elixir Compilation Manager Module + * + * Handles Elixir compilation operations and information display + */ + +const { runCommand } = require('../../lib/utils'); +const { FileUtils, LoggingUtils } = require('../../lib'); + +class ElixirCompilationManager { + constructor(projectPath, commandExecutor, elixirConfig, detectedTools) { + this.projectPath = projectPath; + this.commandExecutor = commandExecutor; + this.elixirConfig = elixirConfig; + this.detectedTools = detectedTools; + } + + /** + * Compile Elixir project with Elixir-specific improvements + */ + async compile(options = {}) { + const args = []; + + // Add compilation flags from config + if (this.elixirConfig.compile?.flags) { + args.push(...this.elixirConfig.compile.flags); + } + + // Add warnings as errors + if (options.warningsAsErrors) { + args.push('--warnings-as-errors'); + } + + // Add force compilation + if (options.force) { + args.push('--force'); + } + + // Add verbose output + if (options.verbose) { + args.push('--verbose'); + } + + // Add long compilation + if (options.longCompilation) { + args.push('--long-compilation'); + } + + // Add profile + if (options.profile) { + args.push('--profile'); + } + + try { + // Log compilation information + const projectInfo = this.commandExecutor.getElixirProjectInfo(); + if (projectInfo) { + LoggingUtils.debug(`Elixir files: ${projectInfo.elixirFiles}`); + LoggingUtils.debug(`Has mix.exs: ${projectInfo.hasMixExs}`); + } + + const result = await this.commandExecutor.executeMixCommand('compile', args, options); + + // Elixir-specific: Show compilation information + if (result.success) { + await this.showCompilationInfo(options); + } + + return result; + } catch (error) { + // Elixir-specific: Provide helpful compilation error suggestions + this.suggestCompilationFix(error.message); + throw error; + } + } + + /** + * Show compilation information + */ + async showCompilationInfo(options) { + try { + // Get Elixir version + const versionResult = runCommand('elixir --version', { cwd: this.projectPath }); + + // Get OTP version + const otpResult = runCommand( + 'erl -eval "erlang:display(erlang:system_info(otp_release)), halt()." -noshell', + { + cwd: this.projectPath, + } + ); + + // Get mix environment + const envResult = runCommand('mix env', { cwd: this.projectPath }); + + LoggingUtils.info('📦 Compilation Information:'); + + if (versionResult.stdout) { + const versionMatch = versionResult.stdout.match(/Elixir (\d+\.\d+\.\d+)/); + if (versionMatch) { + LoggingUtils.info(` • Elixir Version: ${versionMatch[1]}`); + } + } + + if (otpResult.stdout) { + const otpMatch = otpResult.stdout.match(/"(\d+)"/); + if (otpMatch) { + LoggingUtils.info(` • OTP Version: ${otpMatch[1]}`); + } + } + + if (envResult.stdout) { + LoggingUtils.info(` • Mix Environment: ${envResult.stdout.trim()}`); + } + + // Show project structure + const projectInfo = this.commandExecutor.getElixirProjectInfo(); + if (projectInfo) { + LoggingUtils.info(' • Project Structure:'); + if (projectInfo.hasMixExs) LoggingUtils.info(' ✓ mix.exs'); + if (projectInfo.hasMixLock) LoggingUtils.info(' ✓ mix.lock'); + if (projectInfo.hasConfig) LoggingUtils.info(' ✓ config/'); + if (projectInfo.hasLib) LoggingUtils.info(' ✓ lib/'); + if (projectInfo.hasTest) LoggingUtils.info(' ✓ test/'); + LoggingUtils.info(` • Elixir Files: ${projectInfo.elixirFiles}`); + } + + // Show detected tools + if (this.detectedTools) { + const availableTools = Object.entries(this.detectedTools) + .filter(([_, info]) => info.installed) + .map(([name, _]) => name); + + if (availableTools.length > 0) { + LoggingUtils.info(` • Available Tools: ${availableTools.join(', ')}`); + } + } + } catch (error) { + LoggingUtils.debug('Failed to show compilation info:', error.message); + } + } + + /** + * Suggest fixes for common compilation errors + */ + suggestCompilationFix(errorMessage) { + const suggestions = []; + + if (errorMessage.includes('module not found') || errorMessage.includes('undefined function')) { + suggestions.push('Run /elixir-deps get to download dependencies'); + suggestions.push('Check if mix.exs file exists and is valid'); + suggestions.push('Verify module names and imports'); + } + + if (errorMessage.includes('syntax error')) { + suggestions.push('Check for missing parentheses, commas, or do/end blocks'); + suggestions.push('Verify Elixir syntax in the problematic file'); + suggestions.push('Run /elixir-format to format code and catch syntax errors'); + } + + if (errorMessage.includes('compile error')) { + suggestions.push('Check function arities (number of arguments)'); + suggestions.push('Verify pattern matching in function definitions'); + suggestions.push('Look for type mismatches'); + } + + if (errorMessage.includes('dependency')) { + suggestions.push('Run /elixir-deps get to fetch dependencies'); + suggestions.push('Check hex.pm for package availability'); + suggestions.push('Verify dependency versions in mix.exs'); + } + + if (suggestions.length > 0) { + LoggingUtils.info('💡 Compilation error suggestions:'); + suggestions.forEach((suggestion, i) => { + LoggingUtils.info(` ${i + 1}. ${suggestion}`); + }); + } + } +} + +module.exports = ElixirCompilationManager; diff --git a/scripts/elixir/elixir-command-runner-modules/dependency-manager.js b/scripts/elixir/elixir-command-runner-modules/dependency-manager.js new file mode 100644 index 0000000..e4154a8 --- /dev/null +++ b/scripts/elixir/elixir-command-runner-modules/dependency-manager.js @@ -0,0 +1,86 @@ +class DependencyManager { + constructor(commandExecutor, loggingUtils) { + this.commandExecutor = commandExecutor; + this.loggingUtils = loggingUtils; + } + + async deps(command, options = {}) { + const args = []; + + let mixCommand = 'deps'; + + const subcommandsWithDot = ['get', 'update', 'clean', 'compile', 'unlock', 'tree']; + if (subcommandsWithDot.includes(command)) { + mixCommand = `deps.${command}`; + } else { + args.push(command); + } + + if (options.package) { + args.push(options.package); + } + + if (options.only) { + args.push('--only', options.only); + } + + if (options.lock) { + args.push('--lock'); + } + + if (options.unlock) { + args.push('--unlock'); + } + + if (options.checkUnlock) { + args.push('--check-unlock'); + } + + if (options.verbose) { + args.push('--verbose'); + } + + try { + return await this.commandExecutor.executeMixCommand(mixCommand, args, options); + } catch (error) { + this.suggestDepsFix(error.message); + throw error; + } + } + + suggestDepsFix(errorMessage) { + this.loggingUtils.info('\n💡 Dependency Error Suggestions:'); + + if (errorMessage.includes('mix.exs')) { + this.loggingUtils.info(' • Check mix.exs file syntax'); + this.loggingUtils.info(' • Verify dependency names and versions'); + this.loggingUtils.info(' • Ensure hex package registry is accessible'); + } + + if (errorMessage.includes('not found')) { + this.loggingUtils.info(' • Package may not exist on hex.pm'); + this.loggingUtils.info(' • Check package name spelling'); + this.loggingUtils.info(' • Try mix hex.search to find correct name'); + } + + if (errorMessage.includes('version conflict')) { + this.loggingUtils.info(' • Check mix.lock for version conflicts'); + this.loggingUtils.info(' • Run mix deps.update --all to update all dependencies'); + this.loggingUtils.info(' • Use mix deps.unlock to remove lock constraints'); + } + + if (errorMessage.includes('git')) { + this.loggingUtils.info(' • Git dependencies need proper git setup'); + this.loggingUtils.info(' • Check git repository URLs'); + this.loggingUtils.info(' • Ensure git is installed and accessible'); + } + + if (errorMessage.includes('hex')) { + this.loggingUtils.info(' • Hex package manager may need setup'); + this.loggingUtils.info(' • Run mix local.hex to install hex'); + this.loggingUtils.info(' • Check hex.pm connectivity'); + } + } +} + +module.exports = DependencyManager; diff --git a/scripts/elixir/elixir-command-runner-modules/test-runner.js b/scripts/elixir/elixir-command-runner-modules/test-runner.js new file mode 100644 index 0000000..39d9976 --- /dev/null +++ b/scripts/elixir/elixir-command-runner-modules/test-runner.js @@ -0,0 +1,103 @@ +class TestRunner { + constructor(commandExecutor, loggingUtils) { + this.commandExecutor = commandExecutor; + this.loggingUtils = loggingUtils; + } + + async test(options = {}) { + const args = []; + + if (options.file) { + args.push(options.file); + } else if (options.directory) { + args.push(options.directory); + } + + if (options.only) { + args.push('--only', options.only); + } + + if (options.exclude) { + args.push('--exclude', options.exclude); + } + + if (options.seed) { + args.push('--seed', options.seed); + } + + if (options.coverage) { + args.push('--cover'); + } + + if (options.trace) { + args.push('--trace'); + } + + if (options.maxFailures) { + args.push('--max-failures', options.maxFailures); + } + + if (options.timeout) { + args.push('--timeout', options.timeout); + } + + if (options.verbose) { + args.push('--verbose'); + } + + if (options.slowest) { + args.push('--slowest', options.slowest); + } + + try { + const result = await this.commandExecutor.executeMixCommand('test', args, options); + + if (result.success) { + await this.showTestSummary(options); + } + + return result; + } catch (error) { + this.suggestTestFix(error.message); + throw error; + } + } + + async showTestSummary(options) { + console.log('\n✅ Tests completed successfully!'); + + if (options.coverage) { + console.log('📊 Coverage report generated in cover/ directory'); + } + } + + suggestTestFix(errorMessage) { + this.loggingUtils.info('\n💡 Test Error Suggestions:'); + + if (errorMessage.includes('assert')) { + this.loggingUtils.info(' • Check assertion values match expected'); + this.loggingUtils.info(' • Use assert_in_delta for floating point comparisons'); + this.loggingUtils.info(' • Check test setup and teardown'); + } + + if (errorMessage.includes('timeout')) { + this.loggingUtils.info(' • Increase timeout with --timeout option'); + this.loggingUtils.info(' • Check for infinite loops or long-running operations'); + this.loggingUtils.info(' • Consider using async: false for integration tests'); + } + + if (errorMessage.includes('module not found')) { + this.loggingUtils.info(' • Ensure test files are in test/ directory'); + this.loggingUtils.info(' • Check test_helper.exs exists and is loaded'); + this.loggingUtils.info(' • Run mix test --no-start to skip application start'); + } + + if (errorMessage.includes('Compilation error')) { + this.loggingUtils.info(' • Run mix compile first'); + this.loggingUtils.info(' • Check for syntax errors in test files'); + this.loggingUtils.info(' • Ensure all dependencies are available'); + } + } +} + +module.exports = TestRunner; diff --git a/scripts/elixir/elixir-command-runner-modules/utility-runner.js b/scripts/elixir/elixir-command-runner-modules/utility-runner.js new file mode 100644 index 0000000..1998b00 --- /dev/null +++ b/scripts/elixir/elixir-command-runner-modules/utility-runner.js @@ -0,0 +1,104 @@ +class UtilityRunner { + constructor(commandExecutor, loggingUtils) { + this.commandExecutor = commandExecutor; + this.loggingUtils = loggingUtils; + } + + async clean(options = {}) { + const args = []; + + if (options.all) { + args.push('--all'); + } + + if (options.deps) { + args.push('--deps'); + } + + if (options.build) { + args.push('--build'); + } + + if (options.release) { + args.push('--release'); + } + + if (options.logs) { + args.push('--logs'); + } + + return await this.commandExecutor.executeMixCommand('clean', args, options); + } + + async run(task, args = [], options = {}) { + const allArgs = [task, ...args]; + return await this.commandExecutor.executeMixCommand('run', allArgs, options); + } + + async getProjectInfo() { + try { + const version = await this.commandExecutor.executeMixCommand('--version', [], { + stdio: 'pipe', + }); + const deps = await this.commandExecutor.executeMixCommand('deps', [], { stdio: 'pipe' }); + const compile = await this.commandExecutor.executeMixCommand('compile', [], { + stdio: 'pipe', + }); + + return { + version: version.stdout.trim(), + dependencies: deps.stdout, + compileStatus: compile.stdout, + }; + } catch (error) { + this.loggingUtils.warn(`Failed to get project info: ${error.message}`); + return { + version: 'Unknown', + dependencies: 'Failed to retrieve', + compileStatus: 'Failed to retrieve', + }; + } + } + + async showCompilationInfo(options) { + try { + const versionResult = await this.commandExecutor.executeMixCommand('--version', [], { + stdio: 'pipe', + }); + + const projectResult = await this.commandExecutor.executeMixCommand( + 'run', + ['-e', 'IO.puts("Project: #{Mix.Project.config()[:app]}")'], + { + stdio: 'pipe', + } + ); + + const envResult = await this.commandExecutor.executeMixCommand( + 'run', + ['-e', 'IO.puts("Environment: #{Mix.env()}")'], + { + stdio: 'pipe', + } + ); + + this.loggingUtils.info('\n📋 Compilation Information:'); + this.loggingUtils.info(` • ${versionResult.stdout.trim()}`); + this.loggingUtils.info(` • ${projectResult.stdout.trim()}`); + this.loggingUtils.info(` • ${envResult.stdout.trim()}`); + + if (options.verbose) { + const depsResult = await this.commandExecutor.executeMixCommand('deps', [], { + stdio: 'pipe', + }); + this.loggingUtils.info( + ` • Dependencies: ${depsResult.stdout.split('\n').length} packages` + ); + } + } catch (error) { + this.loggingUtils.warn(`Failed to show compilation info: ${error.message}`); + } + } +} + +module.exports = UtilityRunner; diff --git a/scripts/elixir/elixir-command-runner-refactored.js b/scripts/elixir/elixir-command-runner-refactored.js new file mode 100644 index 0000000..831f083 --- /dev/null +++ b/scripts/elixir/elixir-command-runner-refactored.js @@ -0,0 +1,297 @@ +#!/usr/bin/env node +/** + * Elixir Command Runner (Refactored) + * + * Execute Elixir commands with Elixir-specific improvements and error handling + * Modular architecture for better maintainability + */ + +const fs = require('fs'); +const path = require('path'); +const ConfigManager = require('../interactive/config-manager'); +const ElixirToolDetector = require('../../languages/elixir/tool-detector'); +const PlatformDetector = require('../lib/platform-detector'); +const { defaultErrorHandler } = require('../lib/error-handler'); + +// Import shared utilities +const { ConfigUtils, FileUtils, ProjectUtils, LoggingUtils } = require('../lib'); + +// Import modular components +const CommandExecutor = require('./elixir-command-runner-modules/command-executor'); +const CompilationManager = require('./elixir-command-runner-modules/compilation-manager'); +const TestRunner = require('./elixir-command-runner-modules/test-runner'); +const CodeQuality = require('./elixir-command-runner-modules/code-quality'); +const DependencyManager = require('./elixir-command-runner-modules/dependency-manager'); +const UtilityRunner = require('./elixir-command-runner-modules/utility-runner'); + +class ElixirCommandRunner { + constructor(projectPath = process.cwd()) { + this.projectPath = projectPath; + this.configManager = new ConfigManager(projectPath); + this.toolDetector = new ElixirToolDetector(); + this.platformDetector = new PlatformDetector(); + this.config = null; + this.elixirConfig = null; + this.detectedTools = null; + + // Initialize modular components + this.commandExecutor = new CommandExecutor(this, LoggingUtils); + this.compilationManager = new CompilationManager(this.commandExecutor, LoggingUtils); + this.testRunner = new TestRunner(this.commandExecutor, LoggingUtils); + this.codeQuality = new CodeQuality(this.commandExecutor, LoggingUtils); + this.dependencyManager = new DependencyManager(this.commandExecutor, LoggingUtils); + this.utilityRunner = new UtilityRunner(this.commandExecutor, LoggingUtils); + } + + /** + * Initialize command runner with Elixir-specific setup + */ + async initialize() { + try { + const projectInfo = ProjectUtils.detectProjectType(this.projectPath); + + if (projectInfo.type !== 'elixir' && projectInfo.confidence < 0.7) { + LoggingUtils.warn( + `Project detection: ${projectInfo.type} (confidence: ${projectInfo.confidence})` + ); + LoggingUtils.warn( + 'This may not be an Elixir project. Some features may not work correctly.' + ); + } else if (projectInfo.type === 'elixir') { + LoggingUtils.debug( + `Detected Elixir project: ${projectInfo.framework || 'standard Elixir'}` + ); + } + + if (projectInfo.languages && projectInfo.languages.length > 0) { + LoggingUtils.debug(`Detected languages: ${projectInfo.languages.join(', ')}`); + } + + this.config = await this.configManager.loadConfig(); + this.elixirConfig = this.config.elixir || {}; + + this.detectedTools = await this.toolDetector.detectTools(this.projectPath); + + if (this.detectedTools.elixir) { + LoggingUtils.debug(`Elixir version: ${this.detectedTools.elixir.version}`); + } + + if (this.detectedTools.mix) { + LoggingUtils.debug(`Mix version: ${this.detectedTools.mix.version}`); + } + + if (this.detectedTools.erlang) { + LoggingUtils.debug(`Erlang/OTP version: ${this.detectedTools.erlang.version}`); + } + + if (this.detectedTools.credo) { + LoggingUtils.debug(`Credo available: ${this.detectedTools.credo.version}`); + } + + if (this.detectedTools.dialyxir) { + LoggingUtils.debug(`Dialyxir available: ${this.detectedTools.dialyxir.version}`); + } + + return this.detectedTools; + } catch (error) { + LoggingUtils.error(`Failed to initialize Elixir command runner: ${error.message}`); + throw error; + } + } + + /** + * Check if a tool is available + */ + hasTool(toolName, required = false) { + if (!this.detectedTools) { + throw new Error('Command runner not initialized. Call initialize() first.'); + } + + const hasTool = !!this.detectedTools[toolName]; + + if (required && !hasTool) { + throw new Error(`Required tool '${toolName}' not found. Please install it.`); + } + + return hasTool; + } + + /** + * Compile Elixir code + */ + async compile(options = {}) { + if (!this.detectedTools) { + await this.initialize(); + } + + return await this.compilationManager.compile(options); + } + + /** + * Run tests with ExUnit and Elixir-specific improvements + */ + async test(options = {}) { + if (!this.detectedTools) { + await this.initialize(); + } + + return await this.testRunner.test(options); + } + + /** + * Format code with Elixir formatter + */ + async format(options = {}) { + if (!this.detectedTools) { + await this.initialize(); + } + + return await this.codeQuality.format(options); + } + + /** + * Lint code with Credo + */ + async lint(options = {}) { + if (!this.detectedTools) { + await this.initialize(); + } + + this.hasTool('credo', true); + + return await this.codeQuality.lint(options); + } + + /** + * Type check with Dialyzer + */ + async typecheck(options = {}) { + if (!this.detectedTools) { + await this.initialize(); + } + + this.hasTool('dialyxir', true); + + return await this.codeQuality.typecheck(options); + } + + /** + * Manage dependencies + */ + async deps(command, options = {}) { + if (!this.detectedTools) { + await this.initialize(); + } + + return await this.dependencyManager.deps(command, options); + } + + /** + * Clean build artifacts + */ + async clean(options = {}) { + if (!this.detectedTools) { + await this.initialize(); + } + + return await this.utilityRunner.clean(options); + } + + /** + * Run custom Mix task + */ + async run(task, args = [], options = {}) { + if (!this.detectedTools) { + await this.initialize(); + } + + return await this.utilityRunner.run(task, args, options); + } + + /** + * Get project information + */ + async getProjectInfo() { + if (!this.detectedTools) { + await this.initialize(); + } + + return await this.utilityRunner.getProjectInfo(); + } + + /** + * Show compilation information + */ + async showCompilationInfo(options) { + if (!this.detectedTools) { + await this.initialize(); + } + + return await this.utilityRunner.showCompilationInfo(options); + } + + /** + * Show test summary + */ + async showTestSummary(options) { + return await this.testRunner.showTestSummary(options); + } + + /** + * Suggest compilation fixes + */ + suggestCompilationFix(errorMessage) { + return this.compilationManager.suggestCompilationFix(errorMessage); + } + + /** + * Suggest test fixes + */ + suggestTestFix(errorMessage) { + return this.testRunner.suggestTestFix(errorMessage); + } + + /** + * Suggest formatting fixes + */ + suggestFormatFix(errorMessage) { + return this.codeQuality.suggestFormatFix(errorMessage); + } + + /** + * Suggest linting fixes + */ + suggestLintFix(errorMessage) { + return this.codeQuality.suggestLintFix(errorMessage); + } + + /** + * Suggest type checking fixes + */ + suggestTypecheckFix(errorMessage) { + return this.codeQuality.suggestTypecheckFix(errorMessage); + } + + /** + * Suggest dependency fixes + */ + suggestDepsFix(errorMessage) { + return this.dependencyManager.suggestDepsFix(errorMessage); + } + + /** + * Execute Mix command (public method for backward compatibility) + */ + async executeMixCommand(command, args = [], options = {}) { + return await this.commandExecutor.executeMixCommand(command, args, options); + } + + /** + * Execute Elixir command (public method for backward compatibility) + */ + async executeElixirCommand(args = [], options = {}) { + return await this.commandExecutor.executeElixirCommand(args, options); + } +} + +module.exports = ElixirCommandRunner; diff --git a/scripts/javascript/javascript-command-runner-modules/build-runner.js b/scripts/javascript/javascript-command-runner-modules/build-runner.js new file mode 100644 index 0000000..538d0de --- /dev/null +++ b/scripts/javascript/javascript-command-runner-modules/build-runner.js @@ -0,0 +1,186 @@ +const path = require('path'); +const fs = require('fs'); + +class BuildRunner { + constructor(commandExecutor, loggingUtils) { + this.commandExecutor = commandExecutor; + this.loggingUtils = loggingUtils; + } + + async build(args = [], options = {}) { + await this.commandExecutor.runner.initialize(); + + const projectInfo = this.commandExecutor.runner.getJSProjectInfo(); + const buildCommand = 'build'; + let buildArgs = args; + + if (projectInfo.hasTsConfig) { + buildArgs = ['tsc', ...buildArgs]; + } + + this.loggingUtils.info(`🔨 Building project...`); + + try { + const result = await this.commandExecutor.executeNpmCommand( + 'run', + [buildCommand, ...buildArgs], + options + ); + + this._showBuildInfo(projectInfo); + + return result; + } catch (error) { + this._suggestBuildFix(error.message); + throw error; + } + } + + async dev(args = [], options = {}) { + await this.commandExecutor.runner.initialize(); + + let devCommand = 'dev'; + const devArgs = args; + + const scripts = ['dev', 'start', 'develop']; + const packageJsonPath = path.join(this.commandExecutor.runner.projectPath, 'package.json'); + + try { + if (fs.existsSync(packageJsonPath)) { + const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); + + for (const script of scripts) { + if (packageJson.scripts && packageJson.scripts[script]) { + devCommand = script; + break; + } + } + } + } catch (error) {} + + this.loggingUtils.info(`🚀 Starting development server with ${devCommand}...`); + + try { + const result = await this.commandExecutor.executeNpmCommand( + 'run', + [devCommand, ...devArgs], + options + ); + + this.loggingUtils.info('✅ Development server started'); + + return result; + } catch (error) { + this._suggestDevFix(error.message); + throw error; + } + } + + _showBuildInfo(projectInfo) { + try { + const distPath = path.join(this.commandExecutor.runner.projectPath, 'dist'); + const buildPath = path.join(this.commandExecutor.runner.projectPath, 'build'); + + let outputDir = null; + if (fs.existsSync(distPath) && fs.statSync(distPath).isDirectory()) { + outputDir = distPath; + } else if (fs.existsSync(buildPath) && fs.statSync(buildPath).isDirectory()) { + outputDir = buildPath; + } + + if (outputDir) { + const files = fs.readdirSync(outputDir); + const size = this._getDirectorySize(outputDir); + + this.loggingUtils.info('\n📦 Build Information:'); + this.loggingUtils.info('='.repeat(40)); + this.loggingUtils.info(`Output directory: ${path.basename(outputDir)}`); + this.loggingUtils.info(`Files generated: ${files.length}`); + this.loggingUtils.info(`Total size: ${(size / 1024 / 1024).toFixed(2)} MB`); + this.loggingUtils.info('='.repeat(40)); + + if (projectInfo.hasTsConfig) { + this.loggingUtils.info('✅ TypeScript compilation successful'); + } + } + } catch (error) {} + } + + _getDirectorySize(dir) { + let size = 0; + const files = fs.readdirSync(dir); + + for (const file of files) { + const filePath = path.join(dir, file); + const stat = fs.statSync(filePath); + + if (stat.isDirectory()) { + size += this._getDirectorySize(filePath); + } else { + size += stat.size; + } + } + + return size; + } + + _suggestBuildFix(errorMessage) { + this.loggingUtils.info('\n💡 Build Error Suggestions:'); + + if (errorMessage.includes('TypeScript') || errorMessage.includes('tsc')) { + this.loggingUtils.info(' • Install TypeScript: npm install --save-dev typescript'); + this.loggingUtils.info(' • Create tsconfig.json configuration'); + this.loggingUtils.info(' • Check TypeScript compiler options'); + } + + if (errorMessage.includes('module') || errorMessage.includes('import')) { + this.loggingUtils.info(' • Check import/export statements'); + this.loggingUtils.info(' • Configure module resolution in tsconfig.json'); + this.loggingUtils.info(' • Install missing type definitions: @types/package-name'); + } + + if (errorMessage.includes('webpack') || errorMessage.includes('vite')) { + this.loggingUtils.info(' • Check webpack/vite configuration'); + this.loggingUtils.info(' • Install build tool: npm install --save-dev webpack'); + this.loggingUtils.info(' • Review build configuration files'); + } + + if (errorMessage.includes('babel')) { + this.loggingUtils.info( + ' • Install Babel: npm install --save-dev @babel/core @babel/preset-env' + ); + this.loggingUtils.info(' • Create .babelrc configuration'); + this.loggingUtils.info(' • Check Babel plugin compatibility'); + } + } + + _suggestDevFix(errorMessage) { + this.loggingUtils.info('\n💡 Development Server Error Suggestions:'); + + if (errorMessage.includes('port') || errorMessage.includes('EADDRINUSE')) { + this.loggingUtils.info(' • Change port: npm run dev -- --port 3001'); + this.loggingUtils.info(' • Kill process using port: lsof -ti:3000 | xargs kill'); + this.loggingUtils.info(' • Use different port in .env file'); + } + + if (errorMessage.includes('hot reload') || errorMessage.includes('HMR')) { + this.loggingUtils.info(' • Check webpack/vite configuration for HMR'); + this.loggingUtils.info(' • Ensure dev server supports hot module replacement'); + this.loggingUtils.info(' • Check browser console for HMR errors'); + } + + if (errorMessage.includes('proxy') || errorMessage.includes('CORS')) { + this.loggingUtils.info(' • Configure proxy in dev server config'); + this.loggingUtils.info(' • Add CORS headers to API responses'); + this.loggingUtils.info(' • Use middleware for development proxies'); + } + + if (errorMessage.includes('certificate') || errorMessage.includes('HTTPS')) { + this.loggingUtils.info(' • Generate SSL certificates for local development'); + this.loggingUtils.info(' • Configure dev server for HTTPS'); + this.loggingUtils.info(' • Trust self-signed certificates in browser'); + } + } +} + +module.exports = BuildRunner; diff --git a/scripts/javascript/javascript-command-runner-modules/code-quality.js b/scripts/javascript/javascript-command-runner-modules/code-quality.js new file mode 100644 index 0000000..56bfb74 --- /dev/null +++ b/scripts/javascript/javascript-command-runner-modules/code-quality.js @@ -0,0 +1,184 @@ +class CodeQuality { + constructor(commandExecutor, loggingUtils) { + this.commandExecutor = commandExecutor; + this.loggingUtils = loggingUtils; + } + + async lint(args = [], options = {}) { + await this.commandExecutor.runner.initialize(); + + const projectInfo = this.commandExecutor.runner.getJSProjectInfo(); + let lintCommand = 'lint'; + let lintArgs = args; + + if ( + projectInfo.hasEslintConfig || + this.commandExecutor.runner.detectedTools?.eslint?.installed + ) { + lintCommand = 'eslint'; + lintArgs = ['.', '--ext', '.js,.jsx,.ts,.tsx', ...lintArgs]; + } + + this.loggingUtils.info(`🔍 Running linter with ${lintCommand}...`); + + try { + const result = await this.commandExecutor.executeNpmCommand( + 'run', + [lintCommand, ...lintArgs], + options + ); + + this.loggingUtils.info('✅ Linting completed successfully'); + + return result; + } catch (error) { + this._suggestLintFix(error.message); + throw error; + } + } + + async format(args = [], options = {}) { + await this.commandExecutor.runner.initialize(); + + const projectInfo = this.commandExecutor.runner.getJSProjectInfo(); + let formatCommand = 'format'; + let formatArgs = args; + + if ( + projectInfo.hasPrettierConfig || + this.commandExecutor.runner.detectedTools?.prettier?.installed + ) { + formatCommand = 'prettier'; + formatArgs = ['--write', '.', ...formatArgs]; + } + + this.loggingUtils.info(`🎨 Formatting code with ${formatCommand}...`); + + try { + const result = await this.commandExecutor.executeNpmCommand( + 'run', + [formatCommand, ...formatArgs], + options + ); + + this.loggingUtils.info('✅ Code formatting completed'); + + return result; + } catch (error) { + this._suggestFormatFix(error.message); + throw error; + } + } + + async typecheck(args = [], options = {}) { + await this.commandExecutor.runner.initialize(); + + const projectInfo = this.commandExecutor.runner.getJSProjectInfo(); + if (!projectInfo.hasTsConfig) { + throw new Error('TypeScript configuration (tsconfig.json) not found.'); + } + + this.loggingUtils.info(`🔍 Running TypeScript type checking...`); + + try { + const result = await this.commandExecutor.executeNpmCommand( + 'run', + ['typecheck', ...args], + options + ); + + this.loggingUtils.info('✅ Type checking completed successfully'); + + return result; + } catch (error) { + this._suggestTypeCheckFix(error.message); + throw error; + } + } + + _suggestLintFix(errorMessage) { + this.loggingUtils.info('\n💡 Linting Error Suggestions:'); + + if (errorMessage.includes('ESLint') || errorMessage.includes('parser')) { + this.loggingUtils.info(' • Install ESLint: npm install --save-dev eslint'); + this.loggingUtils.info(' • Create .eslintrc.js configuration'); + this.loggingUtils.info(' • Install TypeScript ESLint if using TypeScript'); + } + + if (errorMessage.includes('rule') || errorMessage.includes('configuration')) { + this.loggingUtils.info(' • Check ESLint rule configuration'); + this.loggingUtils.info(' • Disable problematic rules with // eslint-disable-next-line'); + this.loggingUtils.info(' • Update ESLint to latest version'); + } + + if (errorMessage.includes('import') || errorMessage.includes('module')) { + this.loggingUtils.info(' • Configure module resolution in ESLint'); + this.loggingUtils.info(' • Install eslint-plugin-import'); + this.loggingUtils.info(' • Check import/export statements'); + } + + if (errorMessage.includes('TypeError') || errorMessage.includes('undefined')) { + this.loggingUtils.info(' • Check for undefined variables'); + this.loggingUtils.info(' • Add proper TypeScript types'); + this.loggingUtils.info(' • Use optional chaining (?.) for nested properties'); + } + } + + _suggestFormatFix(errorMessage) { + this.loggingUtils.info('\n💡 Formatting Error Suggestions:'); + + if (errorMessage.includes('Prettier') || errorMessage.includes('parser')) { + this.loggingUtils.info(' • Install Prettier: npm install --save-dev prettier'); + this.loggingUtils.info(' • Create .prettierrc configuration'); + this.loggingUtils.info(' • Add prettier script to package.json'); + } + + if (errorMessage.includes('syntax') || errorMessage.includes('parse')) { + this.loggingUtils.info(' • Fix syntax errors before formatting'); + this.loggingUtils.info(' • Check for missing brackets or parentheses'); + this.loggingUtils.info(' • Verify file encoding (UTF-8 recommended)'); + } + + if (errorMessage.includes('ignore') || errorMessage.includes('exclude')) { + this.loggingUtils.info(' • Create .prettierignore file'); + this.loggingUtils.info(' • Add node_modules, build, dist to ignore list'); + this.loggingUtils.info(' • Use --ignore-path option'); + } + + if (errorMessage.includes('line length') || errorMessage.includes('printWidth')) { + this.loggingUtils.info(' • Adjust printWidth in .prettierrc'); + this.loggingUtils.info(' • Consider breaking long lines'); + this.loggingUtils.info(' • Use Prettier ignore comments for specific cases'); + } + } + + _suggestTypeCheckFix(errorMessage) { + this.loggingUtils.info('\n💡 Type Checking Error Suggestions:'); + + if (errorMessage.includes('tsconfig') || errorMessage.includes('configuration')) { + this.loggingUtils.info(' • Check tsconfig.json syntax and structure'); + this.loggingUtils.info(' • Verify compiler options are valid'); + this.loggingUtils.info(' • Run tsc --init to create new config'); + } + + if (errorMessage.includes('type') || errorMessage.includes('interface')) { + this.loggingUtils.info(' • Add proper TypeScript type annotations'); + this.loggingUtils.info(' • Check interface definitions'); + this.loggingUtils.info(' • Use type guards for complex types'); + } + + if (errorMessage.includes('module') || errorMessage.includes('import')) { + this.loggingUtils.info(' • Install missing type definitions: @types/package-name'); + this.loggingUtils.info(' • Configure module resolution in tsconfig.json'); + this.loggingUtils.info(' • Check import statements for correct paths'); + } + + if (errorMessage.includes('strict') || errorMessage.includes('null')) { + this.loggingUtils.info(' • Use strict null checks: const value = maybeValue!'); + this.loggingUtils.info(' • Add optional chaining: obj?.prop?.nested'); + this.loggingUtils.info(' • Use type assertions when certain: value as Type'); + } + } +} + +module.exports = CodeQuality; diff --git a/scripts/javascript/javascript-command-runner-modules/command-executor.js b/scripts/javascript/javascript-command-runner-modules/command-executor.js new file mode 100644 index 0000000..3e765a4 --- /dev/null +++ b/scripts/javascript/javascript-command-runner-modules/command-executor.js @@ -0,0 +1,127 @@ +const { spawn } = require('child_process'); +const { defaultErrorHandler } = require('../../lib/error-handler'); + +class CommandExecutor { + constructor(runner, loggingUtils, platformDetector) { + this.runner = runner; + this.loggingUtils = loggingUtils; + this.platformDetector = platformDetector; + } + + async executeNpmCommand(command, args = [], options = {}) { + return this._executeNpmCommandWithErrorHandling(command, args, options); + } + + async _executeNpmCommandWithErrorHandling(command, args = [], options = {}) { + try { + await this.runner.initialize(); + + this.runner.checkTool('npm', true); + + const defaultOptions = { + cwd: this.runner.projectPath, + stdio: 'inherit', + shell: true, + env: process.env, + }; + + const finalOptions = { + ...defaultOptions, + ...options, + env: options.env ? { ...defaultOptions.env, ...options.env } : defaultOptions.env, + }; + + this.loggingUtils.info(`🚀 Executing: npm ${command} ${args.join(' ')}`); + + return await new Promise((resolve, reject) => { + const npmPath = this.platformDetector.getToolPath('npm', { + required: true, + }); + + const cmd = `${npmPath} ${command} ${args.join(' ')}`; + this.loggingUtils.debug(`🔍 Executing: ${cmd}`); + this.loggingUtils.debug(`🔍 CWD: ${finalOptions.cwd}`); + this.loggingUtils.debug(`🔍 Platform: ${this.platformDetector.getPlatformName()}`); + + const child = spawn(npmPath, [command, ...args], finalOptions); + + child.on('close', (code) => { + if (code === 0) { + resolve({ success: true, code: 0 }); + } else { + reject(new Error(`npm ${command} failed with exit code ${code}`)); + } + }); + + child.on('error', (error) => { + this.loggingUtils.debug(`🔍 Exec error: ${error.message}`); + reject(new Error(`Failed to execute npm ${command}: ${error.message}`)); + }); + }); + } catch (error) { + return this._handleNpmError(error, { command, args, options }); + } + } + + _handleNpmError(error, context = {}) { + const errorInfo = defaultErrorHandler.handleError(error, context); + + this.loggingUtils.error(errorInfo.userMessage); + + if (errorInfo.recoverySteps && errorInfo.recoverySteps.length > 0) { + this.loggingUtils.info('💡 Recovery steps:'); + errorInfo.recoverySteps.forEach((step, i) => { + this.loggingUtils.info(` ${i + 1}. ${step}`); + }); + } + + this._suggestNpmFix(error.message, context.command); + + const enhancedError = new Error(errorInfo.userMessage); + enhancedError.recoverySteps = errorInfo.recoverySteps; + enhancedError.originalError = error; + throw enhancedError; + } + + _suggestNpmFix(errorMessage, _command) { + this.loggingUtils.info('\n💡 JavaScript/TypeScript Error Suggestions:'); + + if (errorMessage.includes('ENOENT') || errorMessage.includes('not found')) { + this.loggingUtils.info(' • Run: npm install'); + this.loggingUtils.info(' • Check package.json for correct dependencies'); + this.loggingUtils.info(' • Verify node_modules directory exists'); + } + + if (errorMessage.includes('EACCES') || errorMessage.includes('permission')) { + this.loggingUtils.info(' • Fix permissions: sudo chown -R $USER node_modules'); + this.loggingUtils.info(' • Or use: npm install --unsafe-perm'); + this.loggingUtils.info(' • Consider using nvm for better permission management'); + } + + if (errorMessage.includes('version') || errorMessage.includes('incompatible')) { + this.loggingUtils.info(' • Update packages: npm update'); + this.loggingUtils.info(' • Check package.json version constraints'); + this.loggingUtils.info(' • Clear cache: npm cache clean --force'); + } + + if (errorMessage.includes('memory') || errorMessage.includes('heap')) { + this.loggingUtils.info(' • Increase memory: export NODE_OPTIONS=--max-old-space-size=4096'); + this.loggingUtils.info(' • Close other applications using memory'); + this.loggingUtils.info(' • Consider using --no-optional flag'); + } + + if (errorMessage.includes('script') || errorMessage.includes('missing')) { + this.loggingUtils.info(' • Check package.json scripts section'); + this.loggingUtils.info(' • Verify script name is correct'); + this.loggingUtils.info(' • Run npm run to see available scripts'); + } + + if (errorMessage.includes('typescript') || errorMessage.includes('tsc')) { + this.loggingUtils.info(' • Install TypeScript: npm install -D typescript'); + this.loggingUtils.info(' • Check tsconfig.json configuration'); + this.loggingUtils.info(' • Run tsc --init to create config if missing'); + } + } +} + +module.exports = CommandExecutor; diff --git a/scripts/javascript/javascript-command-runner-modules/dependency-manager.js b/scripts/javascript/javascript-command-runner-modules/dependency-manager.js new file mode 100644 index 0000000..a56cc57 --- /dev/null +++ b/scripts/javascript/javascript-command-runner-modules/dependency-manager.js @@ -0,0 +1,69 @@ +class DependencyManager { + constructor(commandExecutor, loggingUtils) { + this.commandExecutor = commandExecutor; + this.loggingUtils = loggingUtils; + } + + async install(args = [], options = {}) { + await this.commandExecutor.runner.initialize(); + + this.loggingUtils.info(`📦 Installing dependencies...`); + + try { + const result = await this.commandExecutor.executeNpmCommand('install', args, options); + + this.loggingUtils.info('✅ Dependencies installed successfully'); + + return result; + } catch (error) { + this._suggestInstallFix(error.message); + throw error; + } + } + + _suggestInstallFix(errorMessage) { + this.loggingUtils.info('\n💡 Installation Error Suggestions:'); + + if (errorMessage.includes('network') || errorMessage.includes('timeout')) { + this.loggingUtils.info(' • Check internet connection'); + this.loggingUtils.info( + ' • Use different registry: npm config set registry https://registry.npmjs.org/' + ); + this.loggingUtils.info(' • Clear npm cache: npm cache clean --force'); + } + + if (errorMessage.includes('peer') || errorMessage.includes('dependency')) { + this.loggingUtils.info(' • Install peer dependencies manually'); + this.loggingUtils.info(' • Use --legacy-peer-deps flag for npm 7+'); + this.loggingUtils.info(' • Check package.json for version conflicts'); + } + + if (errorMessage.includes('EACCES') || errorMessage.includes('permission')) { + this.loggingUtils.info(' • Fix permissions: sudo chown -R $USER node_modules'); + this.loggingUtils.info(' • Use npm install --unsafe-perm'); + this.loggingUtils.info(' • Install packages globally with sudo or use nvm'); + } + + if (errorMessage.includes('version') || errorMessage.includes('incompatible')) { + this.loggingUtils.info(' • Update Node.js to compatible version'); + this.loggingUtils.info(' • Check package.json engines field'); + this.loggingUtils.info(' • Use nvm to switch Node.js versions'); + } + + if (errorMessage.includes('git') || errorMessage.includes('repository')) { + this.loggingUtils.info(' • Check git repository URLs in package.json'); + this.loggingUtils.info(' • Ensure git is installed and accessible'); + this.loggingUtils.info(' • Use SSH keys for private repositories'); + } + + if (errorMessage.includes('checksum') || errorMessage.includes('integrity')) { + this.loggingUtils.info(' • Clear npm cache: npm cache clean --force'); + this.loggingUtils.info(' • Delete package-lock.json and node_modules'); + this.loggingUtils.info( + ' • Reinstall: rm -rf node_modules package-lock.json && npm install' + ); + } + } +} + +module.exports = DependencyManager; diff --git a/scripts/javascript/javascript-command-runner-modules/test-runner.js b/scripts/javascript/javascript-command-runner-modules/test-runner.js new file mode 100644 index 0000000..d6ff4c1 --- /dev/null +++ b/scripts/javascript/javascript-command-runner-modules/test-runner.js @@ -0,0 +1,105 @@ +const path = require('path'); +const fs = require('fs'); + +class TestRunner { + constructor(commandExecutor, loggingUtils) { + this.commandExecutor = commandExecutor; + this.loggingUtils = loggingUtils; + } + + async test(args = [], options = {}) { + await this.commandExecutor.runner.initialize(); + + const projectInfo = this.commandExecutor.runner.getJSProjectInfo(); + let testCommand = 'test'; + let testArgs = args; + + if (projectInfo.hasJestConfig) { + testCommand = 'jest'; + } else if (this.commandExecutor.runner.detectedTools?.vitest?.installed) { + testCommand = 'vitest'; + } else if (this.commandExecutor.runner.detectedTools?.mocha?.installed) { + testCommand = 'mocha'; + } + + if (projectInfo.hasTsConfig && testCommand === 'jest') { + testArgs = ['--preset', 'ts-jest', ...testArgs]; + } + + this.loggingUtils.info(`🧪 Running tests with ${testCommand}...`); + + try { + const result = await this.commandExecutor.executeNpmCommand( + 'run', + [testCommand, ...testArgs], + options + ); + + this._showTestSummary(projectInfo); + + return result; + } catch (error) { + this._suggestTestFix(error.message); + throw error; + } + } + + _showTestSummary(projectInfo) { + try { + const coveragePath = path.join( + this.commandExecutor.runner.projectPath, + 'coverage', + 'coverage-summary.json' + ); + + if (fs.existsSync(coveragePath)) { + const coverage = JSON.parse(fs.readFileSync(coveragePath, 'utf8')); + const total = coverage.total; + + this.loggingUtils.info('\n📊 Test Coverage Summary:'); + this.loggingUtils.info('='.repeat(40)); + this.loggingUtils.info(`Lines: ${total.lines.pct}%`); + this.loggingUtils.info(`Statements: ${total.statements.pct}%`); + this.loggingUtils.info(`Functions: ${total.functions.pct}%`); + this.loggingUtils.info(`Branches: ${total.branches.pct}%`); + this.loggingUtils.info('='.repeat(40)); + } + } catch (error) {} + } + + _suggestTestFix(errorMessage) { + this.loggingUtils.info('\n💡 Test Error Suggestions:'); + + if (errorMessage.includes('not found') || errorMessage.includes('command')) { + this.loggingUtils.info(' • Install test framework: npm install --save-dev jest'); + this.loggingUtils.info(' • Add test script to package.json'); + this.loggingUtils.info(' • Create test files in __tests__ or test directory'); + } + + if (errorMessage.includes('import') || errorMessage.includes('module')) { + this.loggingUtils.info(' • Check import statements in test files'); + this.loggingUtils.info(' • Configure module resolution in jest.config.js'); + this.loggingUtils.info(' • Use babel-jest for ES6+ syntax'); + } + + if (errorMessage.includes('timeout')) { + this.loggingUtils.info(' • Increase test timeout in jest.config.js'); + this.loggingUtils.info(' • Use --testTimeout flag for Jest'); + this.loggingUtils.info(' • Check for infinite loops in tests'); + } + + if (errorMessage.includes('snapshot')) { + this.loggingUtils.info(' • Update snapshots: npm test -- -u'); + this.loggingUtils.info(' • Review snapshot changes before committing'); + this.loggingUtils.info(' • Consider using inline snapshots for smaller tests'); + } + + if (errorMessage.includes('TypeError') || errorMessage.includes('undefined')) { + this.loggingUtils.info(' • Check test setup and teardown'); + this.loggingUtils.info(' • Mock external dependencies properly'); + this.loggingUtils.info(' • Use beforeEach/afterEach for test isolation'); + } + } +} + +module.exports = TestRunner; diff --git a/scripts/javascript/javascript-command-runner-modules/utility-runner.js b/scripts/javascript/javascript-command-runner-modules/utility-runner.js new file mode 100644 index 0000000..b6b36dd --- /dev/null +++ b/scripts/javascript/javascript-command-runner-modules/utility-runner.js @@ -0,0 +1,58 @@ +class UtilityRunner { + constructor(commandExecutor, loggingUtils) { + this.commandExecutor = commandExecutor; + this.loggingUtils = loggingUtils; + } + + async clean(options = {}) { + const args = []; + + if (options.all) { + args.push('--all'); + } + + if (options.deps) { + args.push('--deps'); + } + + if (options.build) { + args.push('--build'); + } + + return await this.commandExecutor.executeNpmCommand('run', ['clean', ...args], options); + } + + async run(script, args = [], options = {}) { + const allArgs = [script, ...args]; + return await this.commandExecutor.executeNpmCommand('run', allArgs, options); + } + + async getProjectInfo() { + try { + const version = await this.commandExecutor.executeNpmCommand('--version', [], { + stdio: 'pipe', + }); + const deps = await this.commandExecutor.executeNpmCommand('list', [], { stdio: 'pipe' }); + + return { + npmVersion: version.stdout?.trim() || 'unknown', + dependencies: deps.stdout || 'No dependencies listed', + }; + } catch (error) { + return { error: error.message }; + } + } + + _showBuildInfo(projectInfo) { + this.loggingUtils.info('\n📊 Build Information:'); + this.loggingUtils.info('='.repeat(40)); + this.loggingUtils.info(`JavaScript files: ${projectInfo.jsFiles}`); + this.loggingUtils.info(`TypeScript files: ${projectInfo.tsFiles}`); + this.loggingUtils.info(`Has TypeScript: ${projectInfo.hasTsConfig ? 'Yes' : 'No'}`); + this.loggingUtils.info(`Has ESLint: ${projectInfo.hasEslintConfig ? 'Yes' : 'No'}`); + this.loggingUtils.info(`Has Prettier: ${projectInfo.hasPrettierConfig ? 'Yes' : 'No'}`); + this.loggingUtils.info('='.repeat(40)); + } +} + +module.exports = UtilityRunner; diff --git a/scripts/javascript/javascript-command-runner-refactored.js b/scripts/javascript/javascript-command-runner-refactored.js new file mode 100644 index 0000000..4d15883 --- /dev/null +++ b/scripts/javascript/javascript-command-runner-refactored.js @@ -0,0 +1,296 @@ +/** + * JavaScript/TypeScript Command Runner (Refactored) + * + * Execute JavaScript/TypeScript commands with project-specific improvements + * Modular architecture for better maintainability + */ + +const path = require('path'); +const ConfigManager = require('../interactive/config-manager'); +const JSToolDetector = require('../../languages/javascript/tool-detector'); +const PlatformDetector = require('../lib/platform-detector'); + +// Import shared utilities +const { ConfigUtils, FileUtils, ProjectUtils, LoggingUtils } = require('../lib'); + +// Import modular components +const CommandExecutor = require('./javascript-command-runner-modules/command-executor'); +const TestRunner = require('./javascript-command-runner-modules/test-runner'); +const BuildRunner = require('./javascript-command-runner-modules/build-runner'); +const CodeQuality = require('./javascript-command-runner-modules/code-quality'); +const DependencyManager = require('./javascript-command-runner-modules/dependency-manager'); +const UtilityRunner = require('./javascript-command-runner-modules/utility-runner'); + +class JSCommandRunner { + constructor(projectPath = process.cwd()) { + this.projectPath = projectPath; + this.configManager = new ConfigManager(projectPath); + this.toolDetector = new JSToolDetector(); + this.platformDetector = new PlatformDetector(); + this.config = null; + this.jsConfig = null; + this.detectedTools = null; + + // Initialize modular components + this.commandExecutor = new CommandExecutor(this, LoggingUtils, this.platformDetector); + this.testRunner = new TestRunner(this.commandExecutor, LoggingUtils); + this.buildRunner = new BuildRunner(this.commandExecutor, LoggingUtils); + this.codeQuality = new CodeQuality(this.commandExecutor, LoggingUtils); + this.dependencyManager = new DependencyManager(this.commandExecutor, LoggingUtils); + this.utilityRunner = new UtilityRunner(this.commandExecutor, LoggingUtils); + } + + /** + * Initialize command runner with JavaScript/TypeScript setup + */ + async initialize() { + try { + const projectInfo = ProjectUtils.detectProjectType(this.projectPath); + + if (projectInfo.type !== 'javascript' && projectInfo.confidence < 0.7) { + LoggingUtils.warn( + `Project detection: ${projectInfo.type} (confidence: ${projectInfo.confidence})` + ); + LoggingUtils.warn( + 'This may not be a JavaScript project. Some features may not work correctly.' + ); + } else if (projectInfo.type === 'javascript') { + LoggingUtils.debug( + `Detected JavaScript project: ${projectInfo.framework || 'standard JavaScript'}` + ); + } + + if (projectInfo.languages && projectInfo.languages.length > 0) { + LoggingUtils.debug(`Detected languages: ${projectInfo.languages.join(', ')}`); + } + + this.config = await this.configManager.loadConfig(); + this.jsConfig = this.config.javascript || {}; + + this.detectedTools = await this.toolDetector.detectTools(this.projectPath); + + if (this.detectedTools.npm) { + LoggingUtils.debug(`npm version: ${this.detectedTools.npm.version}`); + } + + if (this.detectedTools.node) { + LoggingUtils.debug(`Node.js version: ${this.detectedTools.node.version}`); + } + + if (this.detectedTools.typescript) { + LoggingUtils.debug(`TypeScript available: ${this.detectedTools.typescript.version}`); + } + + if (this.detectedTools.eslint) { + LoggingUtils.debug(`ESLint available: ${this.detectedTools.eslint.version}`); + } + + if (this.detectedTools.prettier) { + LoggingUtils.debug(`Prettier available: ${this.detectedTools.prettier.version}`); + } + + return this.detectedTools; + } catch (error) { + LoggingUtils.error(`Failed to initialize JavaScript command runner: ${error.message}`); + throw error; + } + } + + /** + * Check if a tool is available + */ + checkTool(toolName, required = true) { + if (!this.detectedTools) { + throw new Error('Command runner not initialized. Call initialize() first.'); + } + + const isInstalled = ConfigUtils.checkToolInstalled(this.jsConfig, toolName, required); + + if (required && !isInstalled) { + throw new Error(`Required tool '${toolName}' not found. Please install it.`); + } + + return isInstalled; + } + + /** + * Get JavaScript project information + */ + getJSProjectInfo() { + try { + const fs = require('fs'); + const packageJsonPath = path.join(this.projectPath, 'package.json'); + const tsConfigPath = path.join(this.projectPath, 'tsconfig.json'); + const eslintConfigPath = path.join(this.projectPath, '.eslintrc.js'); + const prettierConfigPath = path.join(this.projectPath, '.prettierrc'); + + let packageJson = {}; + if (fs.existsSync(packageJsonPath)) { + packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); + } + + const jsFiles = FileUtils.findFiles(this.projectPath, '*.js').length; + const tsFiles = FileUtils.findFiles(this.projectPath, '*.ts').length; + + return { + hasPackageJson: fs.existsSync(packageJsonPath), + hasTsConfig: fs.existsSync(tsConfigPath), + hasEslintConfig: fs.existsSync(eslintConfigPath), + hasPrettierConfig: fs.existsSync(prettierConfigPath), + hasJestConfig: + !!packageJson.jest || fs.existsSync(path.join(this.projectPath, 'jest.config.js')), + jsFiles, + tsFiles, + packageJson, + }; + } catch (error) { + LoggingUtils.debug('Failed to get JavaScript project info:', error.message); + return null; + } + } + + /** + * Run tests + */ + async test(args = [], options = {}) { + await this.initialize(); + return await this.testRunner.test(args, options); + } + + /** + * Run linter + */ + async lint(args = [], options = {}) { + await this.initialize(); + return await this.codeQuality.lint(args, options); + } + + /** + * Format code + */ + async format(args = [], options = {}) { + await this.initialize(); + return await this.codeQuality.format(args, options); + } + + /** + * Build project + */ + async build(args = [], options = {}) { + await this.initialize(); + return await this.buildRunner.build(args, options); + } + + /** + * Start development server + */ + async dev(args = [], options = {}) { + await this.initialize(); + return await this.buildRunner.dev(args, options); + } + + /** + * TypeScript type checking + */ + async typecheck(args = [], options = {}) { + await this.initialize(); + return await this.codeQuality.typecheck(args, options); + } + + /** + * Install dependencies + */ + async install(args = [], options = {}) { + await this.initialize(); + return await this.dependencyManager.install(args, options); + } + + /** + * Clean build artifacts + */ + async clean(options = {}) { + await this.initialize(); + return await this.utilityRunner.clean(options); + } + + /** + * Run custom npm script + */ + async run(script, args = [], options = {}) { + await this.initialize(); + return await this.utilityRunner.run(script, args, options); + } + + /** + * Get project information + */ + async getProjectInfo() { + await this.initialize(); + return await this.utilityRunner.getProjectInfo(); + } + + /** + * Show build information + */ + _showBuildInfo(projectInfo) { + return this.utilityRunner._showBuildInfo(projectInfo); + } + + /** + * Execute npm command (public method for backward compatibility) + */ + async executeNpmCommand(command, args = [], options = {}) { + return await this.commandExecutor.executeNpmCommand(command, args, options); + } + + /** + * Suggest test fixes (public method for backward compatibility) + */ + _suggestTestFix(errorMessage) { + return this.testRunner._suggestTestFix(errorMessage); + } + + /** + * Suggest lint fixes (public method for backward compatibility) + */ + _suggestLintFix(errorMessage) { + return this.codeQuality._suggestLintFix(errorMessage); + } + + /** + * Suggest format fixes (public method for backward compatibility) + */ + _suggestFormatFix(errorMessage) { + return this.codeQuality._suggestFormatFix(errorMessage); + } + + /** + * Suggest build fixes (public method for backward compatibility) + */ + _suggestBuildFix(errorMessage) { + return this.buildRunner._suggestBuildFix(errorMessage); + } + + /** + * Suggest dev server fixes (public method for backward compatibility) + */ + _suggestDevFix(errorMessage) { + return this.buildRunner._suggestDevFix(errorMessage); + } + + /** + * Suggest type check fixes (public method for backward compatibility) + */ + _suggestTypeCheckFix(errorMessage) { + return this.codeQuality._suggestTypeCheckFix(errorMessage); + } + + /** + * Suggest installation fixes (public method for backward compatibility) + */ + _suggestInstallFix(errorMessage) { + return this.dependencyManager._suggestInstallFix(errorMessage); + } +} + +module.exports = JSCommandRunner; diff --git a/scripts/test-integration.js b/scripts/test-integration.js index 7222078..3ac33bf 100644 --- a/scripts/test-integration.js +++ b/scripts/test-integration.js @@ -51,12 +51,15 @@ try { // Test Elixir command runner console.log(' Testing Elixir command runner...'); try { - const ElixirCommandRunner = require('./elixir/command-runner'); + const ElixirCommandRunner = require('./elixir/elixir-command-runner-refactored'); new ElixirCommandRunner(); console.log(' ✅ Elixir command runner loaded'); // Check if error handler is integrated - const source = fs.readFileSync(path.join(__dirname, 'elixir/command-runner.js'), 'utf8'); + const source = fs.readFileSync( + path.join(__dirname, 'elixir/elixir-command-runner-refactored.js'), + 'utf8' + ); if (source.includes('defaultErrorHandler')) { console.log(' ✅ Error handler integrated in Elixir command runner'); } else { @@ -173,7 +176,7 @@ if (allFilesExist) { console.log(' 5. ✅ Enhanced Go deps with security scanning (gosec, govulncheck)'); console.log(' 6. ✅ Enhanced Python deps with security scanning (safety, pip-audit, bandit)'); console.log( - ' 7. ✅ Enhanced Elixir deps with security scanning (hex.audit, mix_audit, sobelow)', + ' 7. ✅ Enhanced Elixir deps with security scanning (hex.audit, mix_audit, sobelow)' ); console.log(' 8. ✅ Added user-friendly error messages with recovery steps'); console.log(' 9. ✅ Added comprehensive security reports with exit codes'); diff --git a/tests/languages/javascript.test.js b/tests/languages/javascript.test.js index 0519ab7..d0cc32b 100644 --- a/tests/languages/javascript.test.js +++ b/tests/languages/javascript.test.js @@ -12,7 +12,7 @@ const os = require('os'); // Import the modules const JSConfigWizard = require('../../languages/javascript/config-wizard'); const JSToolDetector = require('../../languages/javascript/tool-detector'); -const JSCommandRunner = require('../../scripts/javascript/command-runner'); +const JSCommandRunner = require('../../scripts/javascript/javascript-command-runner-refactored'); // Test helper function test(name, fn) {