parse_with_acorn($file_path); if (empty($violations)) { return; } // Process violations foreach ($violations as $violation) { $this->add_violation( $file_path, $violation['line'], $violation['message'], $violation['codeSnippet'], $violation['remediation'], $this->get_default_severity() ); } } /** * Use Node.js with acorn to parse JavaScript and find violations * Uses external parser script stored in resources directory */ private function parse_with_acorn(string $file_path): array { // Setup cache directory $cache_dir = storage_path('rsx-tmp/cache/code-quality/js-this'); if (!is_dir($cache_dir)) { mkdir($cache_dir, 0755, true); } // Cache based on file modification time $cache_key = md5($file_path) . '-' . filemtime($file_path); $cache_file = $cache_dir . '/' . $cache_key . '.json'; // Check cache first if (file_exists($cache_file)) { $cached = json_decode(file_get_contents($cache_file), true); if ($cached !== null) { return $cached; } } // Clean old cache files for this source file $pattern = $cache_dir . '/' . md5($file_path) . '-*.json'; foreach (glob($pattern) as $old_cache) { if ($old_cache !== $cache_file) { unlink($old_cache); } } // Path to the parser script $parser_script = __DIR__ . '/resource/this-usage-parser.js'; if (!file_exists($parser_script)) { // Parser script missing - fatal error throw new \RuntimeException("JS-THIS parser script missing: {$parser_script}"); } // Run Node.js parser with the external script $output = shell_exec("cd /tmp && node " . escapeshellarg($parser_script) . " " . escapeshellarg($file_path) . " 2>&1"); if (!$output) { return []; } $result = json_decode($output, true); if (!$result) { return []; } // Check for errors from the parser if (isset($result['error'])) { // Parser encountered an error but it's not fatal for the rule return []; } $violations = $result['violations'] ?? []; // Cache the result file_put_contents($cache_file, json_encode($violations)); return $violations; } }