UNPKG

mira-consciousness

Version:

Memory & Intelligence Retention Archive - Preserving The Spark

446 lines 19.9 kB
/** * Environment Analyzer for Cross-Platform Compatibility * Analyzes environment variables, dependencies, and system requirements */ import fs from 'fs-extra'; import * as path from 'path'; import { glob } from 'glob'; export class EnvironmentAnalyzer { projectRoot; constructor(projectRoot) { this.projectRoot = projectRoot; } isTestFile(filePath) { const testPatterns = [ /\.test\.[jt]sx?$/, /\.spec\.[jt]sx?$/, /\/__tests__\//, /\/tests?\//, /\/e2e\//, /test-utils/ ]; return testPatterns.some(pattern => pattern.test(filePath)); } isAnalyzerFile(filePath) { return /\/analyzers?\//.test(filePath); } async analyze(issues) { const environment = { nodeVersionCompatibility: [], dependencies: [], environmentVariables: [], shellCompatibility: { bashScripts: [], powershellScripts: [], batchScripts: [], crossPlatformIssues: [] }, fileSystemRequirements: { pathSeparatorIssues: [], caseSensitivityIssues: [], reservedNameIssues: [], longPathIssues: [], permissionIssues: [] } }; await Promise.all([ this.analyzeNodeVersion(environment), this.analyzeDependencies(issues, environment), this.analyzeEnvironmentVariables(issues, environment), this.analyzeSystemAPIs(issues), this.analyzeNetworkInterfaces(issues), this.analyzeCharacterEncoding(issues), this.analyzeLineEndings(issues) ]); return environment; } async analyzeNodeVersion(environment) { try { const packageJsonPath = path.join(this.projectRoot, 'package.json'); if (await fs.pathExists(packageJsonPath)) { const packageJson = await fs.readJson(packageJsonPath); if (packageJson.engines?.node) { environment.nodeVersionCompatibility.push(packageJson.engines.node); } } } catch (error) { // Skip if package.json can't be read } } async analyzeDependencies(issues, environment) { try { const packageJsonPath = path.join(this.projectRoot, 'package.json'); if (await fs.pathExists(packageJsonPath)) { const packageJson = await fs.readJson(packageJsonPath); const allDeps = { ...packageJson.dependencies, ...packageJson.devDependencies }; for (const [name, version] of Object.entries(allDeps)) { const depAnalysis = { name, version: version, platforms: ['all'], issues: [], nativeModules: false }; // Check for known native modules const nativeModules = [ 'node-sass', 'canvas', 'sharp', 'sqlite3', 'bcrypt', 'fsevents', 'node-gyp', 'leveldown', 'serialport', 'usb', 'node-hid', 'cpu-features', 'microtime', 'node-expat', 'node-rdkafka', 'grpc', '@grpc/grpc-js', 'deasync', 'fibers', 'node-addon-api' ]; if (nativeModules.includes(name)) { depAnalysis.nativeModules = true; depAnalysis.issues.push('Requires native compilation'); issues.push({ file: 'package.json', severity: 'medium', category: 'Dependencies', type: 'native_module', message: `Native module "${name}" requires compilation`, affectedPlatforms: ['all'], recommendation: 'Ensure build tools are available on all target platforms' }); } // Platform-specific dependencies if (name === 'fsevents') { depAnalysis.platforms = ['macOS']; depAnalysis.issues.push('macOS only - file system events'); } if (name.includes('win32') || name.includes('windows')) { depAnalysis.platforms = ['windows']; depAnalysis.issues.push('Windows-specific dependency'); } // Check for optional dependencies if (packageJson.optionalDependencies && name in packageJson.optionalDependencies) { depAnalysis.issues.push('Optional dependency - may not install on all platforms'); } environment.dependencies.push(depAnalysis); } // Check for platform-specific scripts if (packageJson.scripts) { for (const [scriptName, scriptValue] of Object.entries(packageJson.scripts)) { if (typeof scriptValue === 'string') { if (scriptValue.includes('node-gyp')) { issues.push({ file: 'package.json', severity: 'low', category: 'Build', type: 'native_build', message: `Script "${scriptName}" uses node-gyp for native compilation`, affectedPlatforms: ['all'], recommendation: 'Ensure Python and build tools are available' }); } } } } } } catch (error) { // Skip if package.json can't be read } } async analyzeEnvironmentVariables(issues, environment) { const envPatterns = [ { pattern: /process\.env\.HOME/g, variable: 'HOME', message: 'HOME environment variable - not set on Windows', platforms: ['linux', 'macOS', 'unix'], recommendation: 'Use os.homedir() or check both HOME and USERPROFILE' }, { pattern: /process\.env\.USERPROFILE/g, variable: 'USERPROFILE', message: 'USERPROFILE environment variable - Windows specific', platforms: ['windows'], recommendation: 'Use os.homedir() for cross-platform compatibility' }, { pattern: /process\.env\.TEMP/g, variable: 'TEMP', message: 'TEMP environment variable - Windows specific', platforms: ['windows'], recommendation: 'Use os.tmpdir() for cross-platform temp directory' }, { pattern: /process\.env\.TMPDIR/g, variable: 'TMPDIR', message: 'TMPDIR environment variable - Unix specific', platforms: ['linux', 'macOS', 'unix'], recommendation: 'Use os.tmpdir() for cross-platform temp directory' }, { pattern: /process\.env\.USER/g, variable: 'USER', message: 'USER environment variable - not set on Windows', platforms: ['linux', 'macOS', 'unix'], recommendation: 'Use os.userInfo().username for cross-platform' }, { pattern: /process\.env\.USERNAME/g, variable: 'USERNAME', message: 'USERNAME environment variable - Windows specific', platforms: ['windows'], recommendation: 'Use os.userInfo().username for cross-platform' }, { pattern: /process\.env\.SHELL/g, variable: 'SHELL', message: 'SHELL environment variable - not set on Windows', platforms: ['linux', 'macOS', 'unix'], recommendation: 'Check platform before relying on SHELL variable' }, { pattern: /process\.env\.COMSPEC/g, variable: 'COMSPEC', message: 'COMSPEC environment variable - Windows command interpreter', platforms: ['windows'], recommendation: 'Check platform before using shell-specific variables' } ]; const foundVars = new Map(); try { const files = await glob('**/*.{js,ts,jsx,tsx}', { cwd: this.projectRoot, ignore: ['node_modules/**', '.git/**', 'dist/**', 'build/**'] }); for (const file of files) { const filePath = path.join(this.projectRoot, file); const isTest = this.isTestFile(file); const isAnalyzer = this.isAnalyzerFile(file); try { const content = await fs.readFile(filePath, 'utf-8'); const lines = content.split('\n'); lines.forEach((line, index) => { for (const pattern of envPatterns) { if (pattern.pattern.test(line)) { // Skip test files with intentional bad patterns if (isTest) { const context = lines.slice(Math.max(0, index - 3), Math.min(lines.length, index + 3)).join('\n'); if (context.includes('cross-platform issues for testing') || context.includes('// File with') && context.includes('issues')) { return; } } // Skip analyzer example patterns if (isAnalyzer && (line.includes('bad:') || line.includes('const home = process.env.HOME;'))) { return; } if (!foundVars.has(pattern.variable)) { foundVars.set(pattern.variable, { variable: pattern.variable, usage: [], platformSpecific: pattern.platforms[0] !== 'all', recommendations: [pattern.recommendation] }); } foundVars.get(pattern.variable).usage.push(`${file}:${index + 1}`); issues.push({ file, line: index + 1, severity: 'medium', category: 'Environment', type: 'env_variable', message: pattern.message, affectedPlatforms: pattern.platforms, recommendation: pattern.recommendation, codeSnippet: line.trim() }); } } }); } catch (error) { // Skip files that can't be read } } } catch (error) { // Skip if glob fails } environment.environmentVariables = Array.from(foundVars.values()); } async analyzeSystemAPIs(issues) { const apiPatterns = [ { pattern: /require\(['"]child_process['"]\)\.exec/g, type: 'exec_usage', message: 'exec() spawns shell - behavior differs across platforms', severity: 'medium', recommendation: 'Consider using spawn() with shell: false for consistency' }, { pattern: /os\.platform\(\)/g, type: 'platform_detection', message: 'Platform detection - good practice', severity: 'low', recommendation: 'Continue using platform detection for compatibility' }, { pattern: /os\.EOL/g, type: 'eol_usage', message: 'Using os.EOL for line endings - good practice', severity: 'low', recommendation: 'Continue using os.EOL for cross-platform line endings' }, { pattern: /\\r\\n|\\n\\r/g, type: 'hardcoded_line_endings', message: 'Hardcoded line endings detected', severity: 'low', recommendation: 'Use os.EOL for cross-platform line endings' }, { pattern: /os\.arch\(\)/g, type: 'architecture_check', message: 'Architecture detection - ensure handling all cases', severity: 'low', recommendation: 'Handle x64, arm64, ia32, and other architectures' } ]; await this.scanWithPatterns(issues, apiPatterns, 'System APIs'); } async analyzeNetworkInterfaces(issues) { const networkPatterns = [ { pattern: /127\.0\.0\.1|localhost/g, type: 'localhost_reference', message: 'Localhost reference - generally cross-platform', severity: 'low', recommendation: 'Consider IPv6 ::1 for complete compatibility' }, { pattern: /0\.0\.0\.0/g, type: 'bind_all_interfaces', message: 'Binding to all interfaces - behavior may vary', severity: 'low', recommendation: 'Be aware of platform-specific network configurations' }, { pattern: /\:\:1/g, type: 'ipv6_localhost', message: 'IPv6 localhost - ensure IPv6 is enabled', severity: 'low', recommendation: 'Check IPv6 availability on target platforms' } ]; await this.scanWithPatterns(issues, networkPatterns, 'Network'); } async analyzeCharacterEncoding(issues) { const encodingPatterns = [ { pattern: /encoding:\s*['"](?!utf-?8)/gi, type: 'non_utf8_encoding', message: 'Non-UTF8 encoding specified', severity: 'medium', recommendation: 'Use UTF-8 for maximum compatibility' }, { pattern: /Buffer\.from\([^,]+\)(?!.*,\s*['"]utf-?8)/g, type: 'buffer_default_encoding', message: 'Buffer without explicit encoding - uses UTF-8 by default', severity: 'low', recommendation: 'Explicitly specify encoding for clarity' }, { pattern: /readFile[^(]*\([^)]+\)(?!.*,\s*['"]utf-?8)/g, type: 'file_read_binary', message: 'File read without encoding - returns Buffer', severity: 'low', recommendation: 'Specify encoding if text file is expected' } ]; await this.scanWithPatterns(issues, encodingPatterns, 'Character Encoding'); } async analyzeLineEndings(issues) { try { const textFiles = await glob('**/*.{js,ts,jsx,tsx,json,md,txt,yml,yaml}', { cwd: this.projectRoot, ignore: ['node_modules/**', '.git/**', 'dist/**', 'build/**'] }); for (const file of textFiles) { const filePath = path.join(this.projectRoot, file); try { const content = await fs.readFile(filePath, 'utf-8'); const hasCRLF = content.includes('\r\n'); const hasLF = content.includes('\n') && !content.includes('\r\n'); if (hasCRLF && hasLF) { issues.push({ file, severity: 'medium', category: 'Line Endings', type: 'mixed_line_endings', message: 'File has mixed line endings (CRLF and LF)', affectedPlatforms: ['all'], recommendation: 'Use consistent line endings, preferably LF' }); } } catch (error) { // Skip files that can't be read } } // Check for .gitattributes const gitattributesPath = path.join(this.projectRoot, '.gitattributes'); if (!(await fs.pathExists(gitattributesPath))) { issues.push({ file: '.gitattributes', severity: 'low', category: 'Configuration', type: 'missing_gitattributes', message: 'No .gitattributes file for line ending configuration', affectedPlatforms: ['all'], recommendation: 'Add .gitattributes with "* text=auto" for consistent line endings' }); } } catch (error) { // Skip if analysis fails } } async scanWithPatterns(issues, patterns, category) { try { const files = await glob('**/*.{js,ts,jsx,tsx}', { cwd: this.projectRoot, ignore: ['node_modules/**', '.git/**', 'dist/**', 'build/**'] }); for (const file of files) { const filePath = path.join(this.projectRoot, file); try { const content = await fs.readFile(filePath, 'utf-8'); const lines = content.split('\n'); lines.forEach((line, index) => { for (const pattern of patterns) { if (pattern.pattern.test(line)) { issues.push({ file, line: index + 1, severity: pattern.severity, category, type: pattern.type, message: pattern.message, affectedPlatforms: ['all'], recommendation: pattern.recommendation, codeSnippet: line.trim() }); } } }); } catch (error) { // Skip files that can't be read } } } catch (error) { // Skip if glob fails } } } //# sourceMappingURL=EnvironmentAnalyzer.js.map