$line) { $line_number = $line_num + 1; // Skip comments using sanitized version if (isset($sanitized_lines[$line_num])) { $sanitized_trimmed = trim($sanitized_lines[$line_num]); if (empty($sanitized_trimmed)) { continue; // Skip empty/comment lines } } // Check for window.onload patterns if (preg_match('/\bwindow\s*\.\s*onload\s*=/', $line)) { $violations[] = [ 'type' => 'window_onload', 'line' => $line_number, 'code' => trim($line) ]; break; // Only need first violation } // Check for various jQuery ready patterns and DOMContentLoaded // Patterns: $().ready, $(document).ready, $("document").ready, $('document').ready, $(function(), DOMContentLoaded $jquery_ready_patterns = [ '/\$\s*\(\s*\)\s*\.\s*ready\s*\(/', // $().ready( '/\$\s*\(\s*document\s*\)\s*\.\s*ready\s*\(/', // $(document).ready( with spaces '/\$\s*\(\s*["\']document["\']\s*\)\s*\.\s*ready\s*\(/', // $("document").ready( or $('document').ready( '/\$\s*\(\s*function\s*\(/', // $(function() - shorthand for $(document).ready '/jQuery\s*\(\s*document\s*\)\s*\.\s*ready\s*\(/', // jQuery(document).ready( '/jQuery\s*\(\s*["\']document["\']\s*\)\s*\.\s*ready\s*\(/', // jQuery("document").ready( or jQuery('document').ready( '/jQuery\s*\(\s*function\s*\(/', // jQuery(function() - shorthand '/document\s*\.\s*addEventListener\s*\(\s*["\']DOMContentLoaded[\"\']/', // document.addEventListener("DOMContentLoaded" or 'DOMContentLoaded' ]; foreach ($jquery_ready_patterns as $pattern) { if (preg_match($pattern, $line)) { $violations[] = [ 'type' => 'jquery_ready', 'line' => $line_number, 'code' => trim($line) ]; break; // Only report once per line } } // Stop after first violation if (!empty($violations)) { break; } } if (!empty($violations)) { return ['document_ready_violations' => $violations]; } return null; } /** * Check JavaScript file for document ready violations stored in metadata */ public function check(string $file_path, string $contents, array $metadata = []): void { // Skip vendor and node_modules directories if (str_contains($file_path, '/vendor/') || str_contains($file_path, '/node_modules/')) { return; } // Skip CodeQuality directory if (str_contains($file_path, '/CodeQuality/')) { return; } // Check for violations in code quality metadata if (isset($metadata['code_quality_metadata']['JS-READY-01']['document_ready_violations'])) { $violations = $metadata['code_quality_metadata']['JS-READY-01']['document_ready_violations']; // Get appropriate suggestion based on code location $suggestion = InitializationSuggestions::get_suggestion($file_path); // Throw on first violation foreach ($violations as $violation) { $type = $violation['type']; $line = $violation['line']; $code = $violation['code']; if ($type === 'window_onload') { $error_message = "Code Quality Violation (JS-READY-01) - Prohibited Window Onload Pattern\n\n"; $error_message .= "window.onload is not allowed. Use ES6 class with lifecycle methods instead.\n\n"; } else { $error_message = "Code Quality Violation (JS-READY-01) - Prohibited jQuery Ready Pattern\n\n"; $error_message .= "jQuery ready/DOMContentLoaded patterns are not allowed. Use ES6 class with lifecycle methods instead.\n\n"; } $error_message .= "File: {$file_path}\n"; $error_message .= "Line: {$line}\n"; $error_message .= "Code: {$code}\n\n"; $error_message .= $suggestion; throw new \App\RSpade\CodeQuality\RuntimeChecks\YoureDoingItWrongException( $error_message, 0, null, base_path($file_path), $line ); } } } }