import fs from 'fs';
import path from 'path';
import { getAdapterByName, adapters } from './index.js';
export interface DetectionResult {
environment: string;
confidence: number;
suggestedAdapter: string;
reasoning: string;
}
export class EnvironmentDetector {
/**
* Detect the runtime environment based on file content and extension
*/
detectEnvironment(filePath: string, fileContent?: string): DetectionResult {
const ext = path.extname(filePath).toLowerCase();
const content = fileContent || this.readFileContent(filePath);
// Priority 1: File extension
const extensionResult = this.detectByExtension(ext, content);
if (extensionResult.confidence > 0.8) {
return extensionResult;
}
// Priority 2: Framework markers
const frameworkResult = this.detectByFramework(content);
if (frameworkResult.confidence > 0.7) {
return frameworkResult;
}
// Priority 3: Global objects/imports
const globalResult = this.detectByGlobals(content);
if (globalResult.confidence > 0.6) {
return globalResult;
}
// Default to browser for .js/.html files
if (['.js', '.jsx', '.ts', '.tsx', '.html'].includes(ext)) {
return {
environment: 'browser',
confidence: 0.5,
suggestedAdapter: 'browser',
reasoning: 'Defaulted to browser environment for web files'
};
}
// Default to node for .js files in server contexts
if (['.js', '.ts'].includes(ext) && this.looksLikeServerFile(filePath, content)) {
return {
environment: 'node',
confidence: 0.6,
suggestedAdapter: 'node',
reasoning: 'Detected server-side JavaScript file'
};
}
return {
environment: 'unknown',
confidence: 0.0,
suggestedAdapter: 'browser',
reasoning: 'Unable to detect environment, defaulting to browser'
};
}
private readFileContent(filePath: string): string {
try {
return fs.readFileSync(filePath, 'utf8');
} catch {
return '';
}
}
private detectByExtension(ext: string, content: string): DetectionResult {
switch (ext) {
case '.php':
return {
environment: 'php',
confidence: 0.95,
suggestedAdapter: 'php',
reasoning: 'Detected PHP file extension'
};
case '.py':
return {
environment: 'python',
confidence: 0.95,
suggestedAdapter: 'python',
reasoning: 'Detected Python file extension'
};
case '.java':
return {
environment: 'java',
confidence: 0.95,
suggestedAdapter: 'java',
reasoning: 'Detected Java file extension'
};
case '.kt':
return {
environment: 'kotlin',
confidence: 0.95,
suggestedAdapter: 'kotlin',
reasoning: 'Detected Kotlin file extension'
};
case '.m':
case '.mm':
return {
environment: 'objective-c',
confidence: 0.95,
suggestedAdapter: 'objective-c',
reasoning: 'Detected Objective-C file extension'
};
case '.h':
// Could be C, C++, or Objective-C header
if (content.includes('#import <UIKit/UIKit.h>') ||
content.includes('#import <AppKit/AppKit.h>') ||
content.includes('@interface') ||
content.includes('@implementation')) {
return {
environment: 'objective-c',
confidence: 0.9,
suggestedAdapter: 'objective-c',
reasoning: 'Detected Objective-C header file'
};
}
return {
environment: 'unknown',
confidence: 0.3,
suggestedAdapter: 'browser',
reasoning: 'Detected header file, but cannot determine language'
};
case '.wxml':
case '.wxss':
return {
environment: 'wechat-miniprogram',
confidence: 0.9,
suggestedAdapter: 'wechat-miniprogram',
reasoning: 'Detected WeChat Mini Program file'
};
case '.jsx':
case '.tsx':
return {
environment: 'browser',
confidence: 0.7,
suggestedAdapter: 'browser',
reasoning: 'Detected React/JSX file, likely browser environment'
};
default:
return {
environment: 'unknown',
confidence: 0,
suggestedAdapter: 'browser',
reasoning: 'No specific environment detected from extension'
};
}
}
private detectByFramework(content: string): DetectionResult {
// Electron detection
if (content.includes('require(\'electron\')') || content.includes('from \'electron\'')) {
if (content.includes('ipcRenderer') || content.includes('remote')) {
return {
environment: 'electron-renderer',
confidence: 0.9,
suggestedAdapter: 'electron-renderer',
reasoning: 'Detected Electron renderer process (ipcRenderer usage)'
};
}
return {
environment: 'electron-main',
confidence: 0.9,
suggestedAdapter: 'electron-main',
reasoning: 'Detected Electron main process'
};
}
// React Native detection
if (content.includes('from \'react-native\'') ||
content.includes('require(\'react-native\')') ||
content.includes('ReactNative')) {
return {
environment: 'react-native',
confidence: 0.85,
suggestedAdapter: 'react-native',
reasoning: 'Detected React Native imports'
};
}
// WeChat Mini Program detection
if (content.includes('wx.') || content.includes('swan.') || content.includes('my.')) {
return {
environment: 'wechat-miniprogram',
confidence: 0.9,
suggestedAdapter: 'wechat-miniprogram',
reasoning: 'Detected mini program SDK (wx/swan/my)'
};
}
// Android detection
if (content.includes('import android.') ||
content.includes('extends Activity') ||
content.includes('extends Fragment') ||
content.includes('android.os.Looper') ||
content.includes('import androidx.')) {
return {
environment: 'android',
confidence: 0.9,
suggestedAdapter: 'android',
reasoning: 'Detected Android framework imports'
};
}
// Standard Java detection
if (content.includes('import java.') &&
!content.includes('import android.') &&
(content.includes('public static void main') ||
content.includes('public class') ||
content.includes('@SpringBootApplication') ||
content.includes('javax.servlet'))) {
return {
environment: 'java',
confidence: 0.85,
suggestedAdapter: 'java',
reasoning: 'Detected standard Java server-side code'
};
}
// Kotlin detection
if (content.includes('fun main') ||
content.includes('import kotlin.') ||
content.includes('data class') ||
content.includes('suspend fun')) {
return {
environment: 'kotlin',
confidence: 0.9,
suggestedAdapter: 'kotlin',
reasoning: 'Detected Kotlin language features'
};
}
// iOS detection
if (content.includes('#import <UIKit/UIKit.h>') ||
content.includes('UIViewController') ||
content.includes('UIView') ||
content.includes('@IBAction')) {
return {
environment: 'objective-c',
confidence: 0.9,
suggestedAdapter: 'objective-c',
reasoning: 'Detected iOS UIKit framework'
};
}
// macOS detection
if (content.includes('#import <AppKit/AppKit.h>') ||
content.includes('NSApplication') ||
content.includes('NSWindow')) {
return {
environment: 'objective-c',
confidence: 0.9,
suggestedAdapter: 'objective-c',
reasoning: 'Detected macOS AppKit framework'
};
}
return {
environment: 'unknown',
confidence: 0,
suggestedAdapter: 'browser',
reasoning: 'No framework-specific patterns detected'
};
}
private detectByGlobals(content: string): DetectionResult {
// Browser-specific globals
if (content.includes('window.') || content.includes('document.') || content.includes('navigator.')) {
// Make sure it's not Node.js code that happens to have these strings
if (!content.includes('process.') && !content.includes('require(')) {
return {
environment: 'browser',
confidence: 0.8,
suggestedAdapter: 'browser',
reasoning: 'Detected browser-specific globals (window/document)'
};
}
}
// Node.js detection
if (content.includes('process.') || content.includes('__dirname') || content.includes('__filename')) {
// Check Node version to decide between fetch and legacy http
if (content.includes('require(\'http\')') || content.includes('require(\'https\')')) {
return {
environment: 'node-legacy',
confidence: 0.85,
suggestedAdapter: 'node-legacy',
reasoning: 'Detected Node.js with http module (likely legacy version)'
};
}
return {
environment: 'node',
confidence: 0.85,
suggestedAdapter: 'node',
reasoning: 'Detected Node.js environment (process globals)'
};
}
return {
environment: 'unknown',
confidence: 0,
suggestedAdapter: 'browser',
reasoning: 'No global objects detected'
};
}
private looksLikeServerFile(filePath: string, content: string): boolean {
const lowerPath = filePath.toLowerCase();
// Check path patterns
const serverPatterns = [
'/server/', '/api/', '/backend/', '/src/server/',
'server.js', 'server.ts', 'api.js', 'api.ts',
'controller', 'service', 'model', 'repository'
];
if (serverPatterns.some(pattern => lowerPath.includes(pattern))) {
return true;
}
// Check for Express/Koa/Fastify imports
const serverFrameworks = [
'express', 'koa', 'fastify', 'nest', 'hapi', 'restana'
];
return serverFrameworks.some(fw => content.includes(fw));
}
/**
* Get list of all supported environments
*/
getSupportedEnvironments(): string[] {
return adapters.map(adapter => adapter.name);
}
/**
* Generate debug code using detected or specified environment
*/
generateDebugCode(
filePath: string,
fileContent: string | undefined,
options: {
logMessage: string;
variables: string[];
level?: 'info' | 'error' | 'debug' | 'warn';
environment?: string;
projectPath?: string;
}
): string {
let adapter = getAdapterByName(options.environment || '');
if (!adapter) {
const detection = this.detectEnvironment(filePath, fileContent);
adapter = getAdapterByName(detection.suggestedAdapter);
}
if (!adapter) {
// Fallback to browser adapter
adapter = adapters.find(a => a.name === 'browser')!;
}
const result = adapter.generateDebugCode({
logMessage: options.logMessage,
variables: options.variables,
level: options.level,
projectPath: options.projectPath
});
return result.code;
}
}