Fix bin/publish: use correct .env path for rspade_system Fix bin/publish script: prevent grep exit code 1 from terminating script 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
160 lines
4.9 KiB
PHP
Executable File
160 lines
4.9 KiB
PHP
Executable File
<?php
|
|
|
|
namespace App\RSpade\CodeQuality\Rules\JavaScript;
|
|
|
|
use App\RSpade\CodeQuality\Rules\CodeQualityRule_Abstract;
|
|
|
|
/**
|
|
* JavaScript 'this' Usage Rule
|
|
*
|
|
* PHILOSOPHY: Remove ambiguity about what 'this' refers to in all contexts.
|
|
*
|
|
* RULES:
|
|
* 1. Anonymous functions: Can use 'const $var = $(this)' as first line (jQuery pattern)
|
|
* 2. Instance methods: Must use 'const that = this' as first line (constructors exempt)
|
|
* 3. Static methods: Use Class_Name OR 'const CurrentClass = this' for polymorphism
|
|
* 4. Arrow functions: Ignored (they inherit 'this' context)
|
|
* 5. Constructors: Direct 'this' usage allowed for property assignment
|
|
*
|
|
* PATTERNS:
|
|
* - jQuery: const $element = $(this) // Variable must start with $
|
|
* - Instance: const that = this // Standard instance aliasing
|
|
* - Static (exact): Use Class_Name // When you need exact class
|
|
* - Static (polymorphic): const CurrentClass = this // When inherited classes need different behavior
|
|
*
|
|
* This rule does NOT try to detect all jQuery callbacks - it offers the jQuery
|
|
* pattern as an option when 'this' violations are found in anonymous functions.
|
|
*/
|
|
class ThisUsage_CodeQualityRule extends CodeQualityRule_Abstract
|
|
{
|
|
public function get_id(): string
|
|
{
|
|
return 'JS-THIS-01';
|
|
}
|
|
|
|
public function get_name(): string
|
|
{
|
|
return "JavaScript 'this' Usage Check";
|
|
}
|
|
|
|
public function get_description(): string
|
|
{
|
|
return "Enforces clear 'this' patterns: jQuery callbacks use '\$element = \$(this)', instance methods use 'that = this'";
|
|
}
|
|
|
|
public function get_file_patterns(): array
|
|
{
|
|
return ['*.js'];
|
|
}
|
|
|
|
public function get_default_severity(): string
|
|
{
|
|
return 'high';
|
|
}
|
|
|
|
/**
|
|
* Check JavaScript file for improper 'this' usage
|
|
*/
|
|
public function check(string $file_path, string $contents, array $metadata = []): void
|
|
{
|
|
// Skip vendor and node_modules
|
|
if (str_contains($file_path, '/vendor/') || str_contains($file_path, '/node_modules/')) {
|
|
return;
|
|
}
|
|
|
|
// Skip CodeQuality directory
|
|
if (str_contains($file_path, '/CodeQuality/')) {
|
|
return;
|
|
}
|
|
|
|
// Only check JavaScript files that contain ES6 classes
|
|
if (!isset($metadata['class'])) {
|
|
return; // Not a class file
|
|
}
|
|
|
|
// Get violations from AST parser
|
|
$violations = $this->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;
|
|
}
|
|
} |