Skip to content

Commit b3d62fb

Browse files
committed
refactor: integrate shared utilities into Elixir command runner
- Fix syntax error: remove duplicate hasTool method - Fix method signature: use ConfigUtils.checkToolInstalled correctly - Update inconsistent logging: replace console.log with LoggingUtils - Fix Go command runner bug: correct checkTool method signature - Add shared utilities imports: ConfigUtils, FileUtils, ProjectUtils, LoggingUtils - Enhance initialization with project detection and better error handling - Add utility methods: findElixirFiles and getElixirProjectInfo
1 parent cd920b5 commit b3d62fb

2 files changed

Lines changed: 193 additions & 75 deletions

File tree

scripts/elixir/command-runner.js

Lines changed: 188 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,15 @@ const ElixirToolDetector = require("../../languages/elixir/tool-detector");
1414
const PlatformDetector = require("../lib/platform-detector");
1515
const { defaultErrorHandler } = require("../lib/error-handler");
1616

17+
// Import shared utilities
18+
const {
19+
ConfigUtils,
20+
FileUtils,
21+
ProjectUtils,
22+
LoggingUtils,
23+
ensureDir,
24+
} = require("../lib");
25+
1726
class ElixirCommandRunner {
1827
constructor(projectPath = process.cwd()) {
1928
this.projectPath = projectPath;
@@ -29,42 +38,132 @@ class ElixirCommandRunner {
2938
* Initialize command runner with Elixir-specific setup
3039
*/
3140
async initialize() {
32-
// Load configuration
33-
this.config = this.configManager.loadConfig();
34-
if (!this.config) {
35-
throw new Error("Project not configured. Run /elixir-setup first.");
36-
}
41+
// First, validate that we're in an Elixir project using ProjectUtils
42+
try {
43+
const projectInfo = ProjectUtils.detectProjectType(this.projectPath);
3744

38-
// Get Elixir configuration
39-
this.elixirConfig = this.config.elixir;
40-
if (!this.elixirConfig) {
41-
throw new Error(
42-
"Elixir configuration not found. Run /elixir-setup first.",
43-
);
45+
if (projectInfo.type !== "elixir" && projectInfo.confidence < 0.7) {
46+
LoggingUtils.warn(
47+
`Project detection: ${projectInfo.type} (confidence: ${projectInfo.confidence})`,
48+
);
49+
LoggingUtils.warn(
50+
"This may not be an Elixir project. Some features may not work correctly.",
51+
);
52+
} else if (projectInfo.type === "elixir") {
53+
LoggingUtils.debug(
54+
`Detected Elixir project: ${projectInfo.framework || "standard Elixir"}`,
55+
);
56+
}
57+
58+
// Log detected languages if available
59+
if (projectInfo.languages && projectInfo.languages.length > 0) {
60+
LoggingUtils.debug(
61+
`Detected languages: ${projectInfo.languages.join(", ")}`,
62+
);
63+
}
64+
} catch (error) {
65+
LoggingUtils.debug("Project detection failed:", error.message);
4466
}
4567

46-
// Detect tools
47-
this.detectedTools = await this.toolDetector.detectTools();
68+
// Load configuration using ConfigUtils
69+
try {
70+
this.config = ConfigUtils.loadConfig(this.projectPath);
71+
if (!this.config) {
72+
throw new Error("Project not configured. Run /elixir-setup first.");
73+
}
74+
75+
// Get Elixir configuration
76+
this.elixirConfig = this.config.elixir;
77+
if (!this.elixirConfig) {
78+
throw new Error(
79+
"Elixir configuration not found. Run /elixir-setup first.",
80+
);
81+
}
82+
83+
// Validate Elixir configuration schema
84+
ConfigUtils.validateConfig(this.elixirConfig, "elixir");
4885

49-
return true;
86+
// Detect tools
87+
this.detectedTools = await this.toolDetector.detectTools();
88+
89+
return true;
90+
} catch (error) {
91+
// Use LoggingUtils for better error display
92+
LoggingUtils.error(
93+
"Failed to initialize Elixir command runner:",
94+
error.message,
95+
);
96+
LoggingUtils.info("Run /elixir-setup to configure your Elixir project");
97+
throw error;
98+
}
5099
}
51100

52101
/**
53102
* Check if a specific tool is available
54103
*/
55104
hasTool(toolName, required = true) {
56-
const toolInfo = this.elixirConfig.tools?.[toolName];
105+
try {
106+
// Use ConfigUtils to check if tool is installed
107+
const isInstalled = ConfigUtils.checkToolInstalled(
108+
this.elixirConfig,
109+
toolName,
110+
required,
111+
);
57112

58-
if (!toolInfo || !toolInfo.installed) {
59-
if (required) {
113+
if (!isInstalled && required) {
60114
throw new Error(
61115
`Required Elixir tool '${toolName}' is not installed. Run /elixir-setup to install it.`,
62116
);
63117
}
64-
return false;
118+
119+
return isInstalled;
120+
} catch (error) {
121+
// Use LoggingUtils for better error display
122+
if (required) {
123+
LoggingUtils.error(
124+
`Elixir tool '${toolName}' check failed:`,
125+
error.message,
126+
);
127+
LoggingUtils.info(`Run /elixir-setup to install '${toolName}'`);
128+
}
129+
throw error;
65130
}
131+
}
66132

67-
return true;
133+
/**
134+
* Find Elixir files in the project
135+
*/
136+
findElixirFiles(pattern = "**/*.{ex,exs}", excludePatterns = []) {
137+
try {
138+
return FileUtils.findFilesByPattern(this.projectPath, [pattern], {
139+
exclude: excludePatterns,
140+
language: "elixir",
141+
});
142+
} catch (error) {
143+
LoggingUtils.warn("Failed to find Elixir files:", error.message);
144+
return [];
145+
}
146+
}
147+
148+
/**
149+
* Get Elixir project metadata
150+
*/
151+
getElixirProjectInfo() {
152+
try {
153+
const info = {
154+
hasMixExs: fs.existsSync(path.join(this.projectPath, "mix.exs")),
155+
hasMixLock: fs.existsSync(path.join(this.projectPath, "mix.lock")),
156+
hasConfig: fs.existsSync(path.join(this.projectPath, "config")),
157+
hasLib: fs.existsSync(path.join(this.projectPath, "lib")),
158+
hasTest: fs.existsSync(path.join(this.projectPath, "test")),
159+
elixirFiles: this.findElixirFiles().length,
160+
};
161+
162+
return info;
163+
} catch (error) {
164+
LoggingUtils.debug("Failed to get Elixir project info:", error.message);
165+
return null;
166+
}
68167
}
69168

70169
/**
@@ -107,7 +206,7 @@ class ElixirCommandRunner {
107206
: defaultOptions.env,
108207
};
109208

110-
console.log(`🚀 Executing: mix ${command} ${args.join(" ")}`);
209+
LoggingUtils.info(`🚀 Executing: mix ${command} ${args.join(" ")}`);
111210

112211
return await new Promise((resolve, reject) => {
113212
const { exec } = require("child_process");
@@ -124,13 +223,15 @@ class ElixirCommandRunner {
124223
});
125224

126225
const cmd = `${mixPath} ${command} ${args.join(" ")}`;
127-
console.log(`🔍 Executing: ${cmd}`);
128-
console.log(`🔍 CWD: ${finalOptions.cwd}`);
129-
console.log(`🔍 Platform: ${this.platformDetector.getPlatformName()}`);
226+
LoggingUtils.debug(`🔍 Executing: ${cmd}`);
227+
LoggingUtils.debug(`🔍 CWD: ${finalOptions.cwd}`);
228+
LoggingUtils.debug(
229+
`🔍 Platform: ${this.platformDetector.getPlatformName()}`,
230+
);
130231

131232
exec(cmd, finalOptions, (error, stdout, stderr) => {
132233
if (error) {
133-
console.log(`🔍 Exec error: ${error.message}`);
234+
LoggingUtils.debug(`🔍 Exec error: ${error.message}`);
134235
reject(
135236
new Error(`Failed to execute mix ${command}: ${error.message}`),
136237
);
@@ -156,12 +257,16 @@ class ElixirCommandRunner {
156257

157258
const errorInfo = defaultErrorHandler.handleError(error, context);
158259

159-
// Log user-friendly error message
160-
console.error("\n" + errorInfo.userMessage);
161-
console.error("\n💡 Recovery steps:");
162-
errorInfo.recoverySteps.forEach((step, i) => {
163-
console.error(` ${i + 1}. ${step}`);
164-
});
260+
// Log user-friendly error message using LoggingUtils
261+
LoggingUtils.error(errorInfo.userMessage);
262+
263+
// Log recovery steps using LoggingUtils
264+
if (errorInfo.recoverySteps && errorInfo.recoverySteps.length > 0) {
265+
LoggingUtils.info("💡 Recovery steps:");
266+
errorInfo.recoverySteps.forEach((step, i) => {
267+
LoggingUtils.info(` ${i + 1}. ${step}`);
268+
});
269+
}
165270

166271
// Re-throw enhanced error
167272
const enhancedError = new Error(errorInfo.userMessage);
@@ -214,6 +319,13 @@ class ElixirCommandRunner {
214319
}
215320

216321
try {
322+
// Log compilation information
323+
const projectInfo = this.getElixirProjectInfo();
324+
if (projectInfo) {
325+
LoggingUtils.debug(`Elixir files: ${projectInfo.elixirFiles}`);
326+
LoggingUtils.debug(`Has mix.exs: ${projectInfo.hasMixExs}`);
327+
}
328+
217329
const result = await this.executeMixCommand("compile", args, options);
218330

219331
// Elixir-specific: Show compilation information
@@ -536,12 +648,12 @@ class ElixirCommandRunner {
536648
},
537649
);
538650

539-
console.log("\n📊 Compilation Information:");
540-
console.log("=".repeat(50));
541-
console.log(`Elixir: ${versionResult.stdout.trim()}`);
542-
console.log(`Project: ${projectResult.stdout.trim()}`);
543-
console.log(`Environment: ${options.env || "dev"}`);
544-
console.log("=".repeat(50));
651+
LoggingUtils.info("\n📊 Compilation Information:");
652+
LoggingUtils.info("=".repeat(50));
653+
LoggingUtils.info(`Elixir: ${versionResult.stdout.trim()}`);
654+
LoggingUtils.info(`Project: ${projectResult.stdout.trim()}`);
655+
LoggingUtils.info(`Environment: ${options.env || "dev"}`);
656+
LoggingUtils.info("=".repeat(50));
545657
} catch (error) {
546658
// Silently fail - this is just informational
547659
}
@@ -562,101 +674,107 @@ class ElixirCommandRunner {
562674
* Suggest compilation fixes
563675
*/
564676
suggestCompilationFix(errorMessage) {
565-
console.log("\n💡 Compilation Error Suggestions:");
677+
LoggingUtils.info("\n💡 Compilation Error Suggestions:");
566678

567679
if (errorMessage.includes("Dependency")) {
568-
console.log(" • Run 'mix deps.get' to fetch dependencies");
569-
console.log(" • Check mix.exs for correct dependency versions");
680+
LoggingUtils.info(" • Run 'mix deps.get' to fetch dependencies");
681+
LoggingUtils.info(" • Check mix.exs for correct dependency versions");
570682
}
571683

572684
if (errorMessage.includes("undefined function")) {
573-
console.log(" • Check function name and arity");
574-
console.log(" • Ensure module is compiled and available");
575-
console.log(" • Check imports and aliases");
685+
LoggingUtils.info(" • Check function name and arity");
686+
LoggingUtils.info(" • Ensure module is compiled and available");
687+
LoggingUtils.info(" • Check imports and aliases");
576688
}
577689

578690
if (errorMessage.includes("module not found")) {
579-
console.log(" • Check module name spelling");
580-
console.log(" • Ensure file exists in lib/ directory");
581-
console.log(" • Check file extension (.ex vs .exs)");
691+
LoggingUtils.info(" • Check module name spelling");
692+
LoggingUtils.info(" • Ensure file exists in lib/ directory");
693+
LoggingUtils.info(" • Check file extension (.ex vs .exs)");
582694
}
583695
}
584696

585697
/**
586698
* Suggest test fixes
587699
*/
588700
suggestTestFix(errorMessage) {
589-
console.log("\n💡 Test Error Suggestions:");
701+
LoggingUtils.info("\n💡 Test Error Suggestions:");
590702

591703
if (errorMessage.includes("assert")) {
592-
console.log(" • Check assertion values match expected");
593-
console.log(" • Use assert_in_delta for floating point comparisons");
594-
console.log(" • Check test setup and teardown");
704+
LoggingUtils.info(" • Check assertion values match expected");
705+
LoggingUtils.info(
706+
" • Use assert_in_delta for floating point comparisons",
707+
);
708+
LoggingUtils.info(" • Check test setup and teardown");
595709
}
596710

597711
if (errorMessage.includes("timeout")) {
598-
console.log(" • Increase timeout with --timeout option");
599-
console.log(" • Check for infinite loops or long-running operations");
600-
console.log(" • Consider using async: false for integration tests");
712+
LoggingUtils.info(" • Increase timeout with --timeout option");
713+
LoggingUtils.info(
714+
" • Check for infinite loops or long-running operations",
715+
);
716+
LoggingUtils.info(
717+
" • Consider using async: false for integration tests",
718+
);
601719
}
602720
}
603721

604722
/**
605723
* Suggest formatting fixes
606724
*/
607725
suggestFormatFix(errorMessage) {
608-
console.log("\n💡 Formatting Error Suggestions:");
726+
LoggingUtils.info("\n💡 Formatting Error Suggestions:");
609727

610728
if (errorMessage.includes("not formatted")) {
611-
console.log(" • Run 'mix format' to fix formatting");
612-
console.log(" • Check .formatter.exs configuration");
613-
console.log(" • Ensure line length is within limits");
729+
LoggingUtils.info(" • Run 'mix format' to fix formatting");
730+
LoggingUtils.info(" • Check .formatter.exs configuration");
731+
LoggingUtils.info(" • Ensure line length is within limits");
614732
}
615733
}
616734

617735
/**
618736
* Suggest linting fixes
619737
*/
620738
suggestLintFix(errorMessage) {
621-
console.log("\n💡 Linting Error Suggestions:");
739+
LoggingUtils.info("\n💡 Linting Error Suggestions:");
622740

623741
if (errorMessage.includes("Credo")) {
624-
console.log(" • Install Credo: mix archive.install hex credo");
625-
console.log(" • Check .credo.exs configuration");
626-
console.log(" • Run 'mix credo --strict' for detailed analysis");
742+
LoggingUtils.info(" • Install Credo: mix archive.install hex credo");
743+
LoggingUtils.info(" • Check .credo.exs configuration");
744+
LoggingUtils.info(" • Run 'mix credo --strict' for detailed analysis");
627745
}
628746
}
629747

630748
/**
631749
* Suggest type checking fixes
632750
*/
633751
suggestTypeCheckFix(errorMessage) {
634-
console.log("\n💡 Type Checking Error Suggestions:");
752+
LoggingUtils.info("\n💡 Type Checking Error Suggestions:");
635753

636754
if (errorMessage.includes("Dialyzer")) {
637-
console.log(
755+
LoggingUtils.info(
638756
' • Add dialyxir to mix.exs: {:dialyxir, "~> 1.4", only: [:dev]}',
639757
);
640-
console.log(" • Run 'mix dialyzer --plt' to build PLT");
641-
console.log(" • Check type specifications with @spec");
758+
LoggingUtils.info(" • Run 'mix dialyzer --plt' to build PLT");
759+
LoggingUtils.info(" • Check type specifications with @spec");
642760
}
643761
}
644762

645763
/**
646764
* Suggest dependency fixes
647765
*/
648766
suggestDepsFix(errorMessage) {
649-
console.log("\n💡 Dependency Error Suggestions:");
767+
LoggingUtils.info("\n💡 Dependency Error Suggestions:");
650768

651769
if (errorMessage.includes("Hex")) {
652-
console.log(" • Install Hex: mix local.hex");
653-
console.log(" • Check internet connection for Hex.pm");
654-
console.log(" • Verify package name and version in mix.exs");
770+
LoggingUtils.info(" • Install Hex: mix local.hex");
771+
LoggingUtils.info(" • Check internet connection for Hex.pm");
772+
LoggingUtils.info(" • Verify package name and version in mix.exs");
655773
}
656774

657775
if (errorMessage.includes("lock")) {
658-
console.log(" • Run 'mix deps.unlock --all' to clear lock");
659-
console.log(" • Run 'mix deps.get' to refetch dependencies");
776+
LoggingUtils.info(" • Run 'mix deps.unlock --all' to clear lock");
777+
LoggingUtils.info(" • Run 'mix deps.get' to refetch dependencies");
660778
}
661779
}
662780

0 commit comments

Comments
 (0)