Fix bin/publish: copy docs.dist from project root

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>
This commit is contained in:
root
2025-10-21 02:08:33 +00:00
commit f6fac6c4bc
79758 changed files with 10547827 additions and 0 deletions

View File

@@ -0,0 +1,98 @@
<?php
namespace App\RSpade\CodeQuality\RuntimeChecks;
use RuntimeException;
use App\RSpade\CodeQuality\RuntimeChecks\YoureDoingItWrongException;
/**
* Developer-facing bundle validation errors
*
* These exceptions guide developers to fix bundle configuration issues
* during development. Each method builds a comprehensive error message
* explaining the problem and how to fix it.
*/
class BundleErrors
{
/**
* Bundle doesn't include the view/layout directory
*/
public static function view_not_covered(
string $view_path,
string $view_dir,
string $bundle_class,
array $include_paths
): void {
$bundle_short_name = basename(str_replace('\\', '/', $bundle_class));
$error_message = "RSX Bundle Path Coverage Error: Bundle doesn't scan the view/layout directory.\n\n";
$error_message .= "View/Layout file: {$view_path}\n";
$error_message .= "View directory: {$view_dir}/\n";
$error_message .= "Bundle: {$bundle_short_name}\n\n";
$error_message .= "The bundle's scan directories don't include the view's location.\n";
$error_message .= "Current scan directories:\n";
foreach ($include_paths as $path) {
$error_message .= " - {$path}\n";
}
$error_message .= "\n";
$error_message .= "To fix this, either:\n";
$error_message .= "1. Add '{$view_dir}' to the bundle's 'include' array\n";
$error_message .= "2. Use a different bundle that covers this module\n";
$error_message .= "3. Move the view to a directory covered by this bundle\n\n";
$error_message .= 'This ensures bundles are properly scoped to the modules they serve.';
throw new YoureDoingItWrongException($error_message);
}
/**
* Bundle doesn't include the current controller
*/
public static function controller_not_covered(
string $current_controller,
string $current_action,
string $controller_path,
string $controller_dir,
string $bundle_class,
array $include_paths
): void {
$bundle_short_name = basename(str_replace('\\', '/', $bundle_class));
$error_message = "RSX Bundle Controller Coverage Error: Bundle doesn't include the current controller.\n\n";
$error_message .= "Controller: {$current_controller}::{$current_action}\n";
$error_message .= "Controller file: {$controller_path}\n";
$error_message .= "Controller directory: {$controller_dir}/\n";
$error_message .= "Bundle: {$bundle_short_name}\n\n";
$error_message .= "The bundle's scan directories don't include the controller's location.\n";
$error_message .= "Current scan directories:\n";
foreach ($include_paths as $path) {
$error_message .= " - {$path}\n";
}
$error_message .= "\n";
$error_message .= "To fix this, either:\n";
$error_message .= "1. Add '{$controller_dir}' to the bundle's 'include' array\n";
$error_message .= "2. Move the controller to a directory covered by this bundle\n";
$error_message .= "3. Use a bundle that covers both the controller and view\n\n";
$error_message .= 'This ensures bundles include all code needed for the routes they serve.';
throw new YoureDoingItWrongException($error_message);
}
/**
* Cannot determine calling view path for bundle validation
*/
public static function cannot_determine_view_path(): void
{
throw new RuntimeException('Cannot determine calling view path. Ensure rsx_view() helper is used to render views.');
}
/**
* Bundle abstract class called directly
*/
public static function abstract_called_directly(): void
{
throw new RuntimeException(
'Rsx_Bundle_Abstract::render() cannot be called directly. ' .
'Call render() on a specific bundle class instead (e.g., Demo_Bundle::render()).'
);
}
}

View File

@@ -0,0 +1,142 @@
# RuntimeChecks - Convention Enforcement System
## CRITICAL DISTINCTION: Runtime Requirements vs Convention Enforcement
This directory contains **convention enforcement checks** that throw `YoureDoingItWrongException` when developers violate framework conventions. These are NOT runtime requirements.
### What BELONGS Here (Convention Enforcement)
Convention checks are "best practice" validations that:
- **Could technically work** if not enforced (the code would still execute)
- Ensure **consistency and uniformity** across the codebase
- Prevent **bad coding patterns** before they become technical debt
- Are primarily checked **in development mode only**
- Provide **verbose remediation guidance** explaining the right way
Examples:
- Bundle doesn't include the view/controller directory (organizational convention)
- Views having inline `<style>` or `<script>` tags (asset organization convention)
- Layouts missing `rsx_body_class()` or bundle includes (scoping convention)
- Using absolute paths instead of RSX IDs for blade includes
- Hardcoding URLs instead of using route helpers
### What does NOT Belong Here (Runtime Requirements)
Runtime sanity checks are **actual execution requirements** that:
- **Would break the code** if not enforced
- Are **algorithmic requirements** for the code to function
- Document **what the algorithm expects** to operate correctly
- Must be checked **in all environments** (dev and production)
- Usually have **brief error messages** (the fix is obvious or situation unexpected)
Examples:
- File doesn't exist that needs to be loaded
- Duplicate class names (breaks autoloading)
- Manifest not initialized when required
- Required configuration missing
- Invalid data types that would cause crashes
**These MUST stay inline with their related code** because:
1. They document the algorithm's requirements
2. A developer reading the code needs to understand these constraints
3. They're part of the logic flow, not optional conventions
## Decision Framework
When deciding whether to create a RuntimeCheck or keep inline:
### Create a RuntimeCheck if:
- ✅ The code would still technically execute without the check
- ✅ You're enforcing a "preferred way" when multiple ways exist
- ✅ The check is primarily for development mode
- ✅ The error message needs extensive explanation and remediation steps
- ✅ You're preventing future problems, not current execution failures
### Keep Inline if:
- ❌ The code would fail/crash without the check
- ❌ It's a fundamental requirement of the algorithm
- ❌ The check documents what the code expects to operate
- ❌ It must run in production too
- ❌ The error is unexpected with no clear remediation
**When in doubt, keep it inline.** It's better to have too much information about an algorithm's requirements visible than to hide critical constraints in a separate file.
## The Cardinal Sin
**NEVER remove critical algorithm information** by moving runtime requirements to this directory. If a check explains what an algorithm needs to function correctly, it MUST stay with that algorithm.
## Creating New RuntimeChecks
1. **Group by feature domain** (BundleErrors, ViewErrors, RouteErrors, etc.)
2. **Use static methods** that build and throw comprehensive error messages
3. **Always throw `YoureDoingItWrongException`** to make it clear this is a convention violation
4. **Include verbose remediation** with examples and exact steps to fix
5. **Name methods descriptively** (e.g., `view_has_inline_assets`, `bundle_missing_controller`)
### Example Structure
```php
<?php
namespace App\RSpade\CodeQuality\RuntimeChecks;
class BundleErrors
{
/**
* Bundle doesn't include the view directory (convention for organization)
*/
public static function view_not_covered(
string $view_path,
string $view_dir,
string $bundle_class,
array $include_paths
): void {
$error_message = "RSX Bundle Path Coverage Error: ...\n\n";
// Detailed explanation of the problem
$error_message .= "View file: {$view_path}\n";
$error_message .= "Bundle: {$bundle_class}\n\n";
// Clear remediation steps
$error_message .= "To fix this, either:\n";
$error_message .= "1. Add '{$view_dir}' to the bundle's 'include' array\n";
$error_message .= "2. Use a different bundle that covers this module\n\n";
// Why this convention exists
$error_message .= "This ensures bundles are properly scoped to the modules they serve.";
throw new YoureDoingItWrongException($error_message);
}
}
```
## Usage Pattern
In your code where you want to enforce a convention:
```php
// In development mode only
if (!app()->environment('production')) {
// Check convention (bundle should include view directory)
if (!$bundle_includes_view) {
// Verbose error explaining the convention
BundleErrors::view_not_covered($view_path, $view_dir, $bundle_class, $include_paths);
}
}
```
## File Organization
- `YoureDoingItWrongException.php` - The base exception class
- `BundleErrors.php` - Bundle-related convention violations
- `ViewErrors.php` - View/template convention violations
- `RouteErrors.php` - Routing convention violations
- etc. (grouped by feature domain)
## Remember
These checks are about **teaching developers the right way**, not preventing crashes. They're educational guardrails that can be removed without breaking functionality - but shouldn't be, because they prevent bad patterns from taking root.
The key test: **"Would the code still run if I commented out this check?"**
- Yes → RuntimeCheck (convention)
- No → Keep inline (requirement)

View File

@@ -0,0 +1,182 @@
<?php
namespace App\RSpade\CodeQuality\RuntimeChecks;
use App\RSpade\CodeQuality\RuntimeChecks\YoureDoingItWrongException;
/**
* Developer-facing manifest validation errors
*
* These exceptions guide developers to follow RSX naming conventions
* and code organization patterns during development.
*/
class ManifestErrors
{
/**
* File using invalid .old. naming pattern
*/
public static function old_file_pattern(string $file_path): void
{
$error_message = "==========================================\n";
$error_message .= "FATAL: Invalid file naming pattern detected\n";
$error_message .= "==========================================\n\n";
$error_message .= "File: {$file_path}\n\n";
$error_message .= "The .old.(extension) naming convention is NOT ALLOWED in RSpade.\n\n";
$error_message .= "WHY THIS IS A PROBLEM:\n";
$error_message .= "Files are scanned by their file extension. A file named 'something.old.php'\n";
$error_message .= "will still be treated as a .php file and included in bundles as live code.\n";
$error_message .= "This can cause duplicate class definitions, outdated code execution, and\n";
$error_message .= "other critical issues.\n\n";
$error_message .= "CORRECT ALTERNATIVES:\n";
$error_message .= "1. Rename to '.php.old' or '.js.old' (extension at the end)\n";
$error_message .= "2. Move to an archive directory outside scan paths\n";
$error_message .= "3. Delete the file if no longer needed\n\n";
$error_message .= "RECOMMENDED ACTION:\n";
$error_message .= "Move the file to /archived/ or delete it entirely.\n\n";
$error_message .= "==========================================";
throw new YoureDoingItWrongException($error_message);
}
/**
* JavaScript class using period in extends clause
*/
public static function js_extends_with_period(
string $file_path,
string $class_name,
string $extends
): void {
$error_message = "==========================================\n";
$error_message .= "FATAL: Invalid JavaScript class extends syntax\n";
$error_message .= "==========================================\n\n";
$error_message .= "File: {$file_path}\n";
$error_message .= "Class: {$class_name}\n";
$error_message .= "Extends: {$extends}\n\n";
$error_message .= "JavaScript classes in RSX cannot use periods in the extends clause.\n\n";
$error_message .= "PROBLEM:\n";
$error_message .= "The extends clause contains '{$extends}' which includes a period.\n";
$error_message .= "This breaks RSX class traversal and manifest processing.\n\n";
$error_message .= "WHY THIS MATTERS:\n";
$error_message .= "- RSX uses simple class names for inheritance tracking\n";
$error_message .= "- Period notation suggests property access rather than class extension\n";
$error_message .= "- The manifest system cannot properly track inheritance chains with dots\n";
$error_message .= "- This pattern is inconsistent with RSX naming conventions\n\n";
$error_message .= "SOLUTIONS:\n";
$error_message .= "1. If extending an NPM module class, import it properly:\n";
$error_message .= " - Configure the NPM import in your bundle's npm includes\n";
$error_message .= " - Use the simple imported name without window prefix\n\n";
$error_message .= "2. If extending a global class, ensure it's properly defined:\n";
$error_message .= " - Define the base class in a separate file\n";
$error_message .= " - Let RSX manifest make it globally available\n";
$error_message .= " - Use simple class name in extends\n\n";
$error_message .= "EXAMPLES:\n";
$error_message .= "❌ BAD: class MyComponent extends window.BaseComponent {}\n";
$error_message .= "✅ GOOD: class MyComponent extends BaseComponent {}\n\n";
$error_message .= "❌ BAD: class Widget extends jqhtml.Component {}\n";
$error_message .= "✅ GOOD: class Widget extends Jqhtml_Component {}\n\n";
$error_message .= "This restriction maintains consistency and enables proper class hierarchy tracking.\n";
$error_message .= "==========================================";
throw new YoureDoingItWrongException($error_message);
}
/**
* Route using invalid {param} syntax instead of :param
*/
public static function invalid_route_syntax(
string $file,
string $method_name,
string $pattern
): void {
$error_message = "Fatal: Invalid route syntax detected.\n";
$error_message .= "File: {$file}\n";
$error_message .= "Method: {$method_name}\n";
$error_message .= "Route: {$pattern}\n";
$error_message .= "The {param} syntax is not supported. Use :param syntax instead.\n";
$error_message .= "Example: Change '/users/{id}' to '/users/:id'";
throw new YoureDoingItWrongException($error_message);
}
/**
* Jqhtml component with render() method instead of template
*/
public static function jqhtml_render_method(
string $file,
int $line_num,
string $class_name
): void {
$error_message = "Fatal: Incorrect Jqhtml_Component implementation detected.\n\n";
$error_message .= "File: {$file}\n";
$error_message .= "Line {$line_num}: Found render() method\n\n";
$error_message .= "PROBLEM: The render() method should not exist in JavaScript component classes.\n\n";
$error_message .= "SOLUTION:\n";
$error_message .= "1. Remove the render() method from {$class_name}.js\n";
$error_message .= "2. Create a corresponding .jqhtml template file with the HTML\n\n";
$error_message .= "EXAMPLE STRUCTURE:\n";
$error_message .= "- Component class: /rsx/app/demo/components/User_Card.js (no render method)\n";
$error_message .= "- Template file: /rsx/app/demo/components/User_Card.jqhtml\n\n";
$error_message .= "TEMPLATE FORMAT (User_Card.jqhtml):\n";
$error_message .= "<Define:{$class_name}>\n";
$error_message .= " <div class=\"{$class_name}\">\n";
$error_message .= " <!-- Your HTML here -->\n";
$error_message .= " <h3 \$id=\"title\"><%= this.data.title %></h3>\n";
$error_message .= " <button \$onclick=\"handle_click\">Click Me</button>\n";
$error_message .= " </div>\n";
$error_message .= "</Define:{$class_name}>\n\n";
$error_message .= "See /rsx/app/demo/components/Counter_Widget.jqhtml for a complete example.";
throw new YoureDoingItWrongException($error_message);
}
/**
* Jqhtml component with incorrect lifecycle method naming
*/
public static function jqhtml_lifecycle_method(
string $file,
int $line_num,
string $method
): void {
$error_message = "Fatal: Incorrect Jqhtml_Component lifecycle method detected.\n\n";
$error_message .= "File: {$file}\n";
$error_message .= "Line {$line_num}: Found '{$method}()' method\n\n";
$error_message .= "PROBLEM: Jqhtml_Component lifecycle methods must be prefixed with 'on_'\n\n";
$error_message .= "SOLUTION: Rename the method:\n";
$error_message .= "- '{$method}()' should be 'on_{$method}()'\n\n";
$error_message .= "CORRECT LIFECYCLE METHODS:\n";
$error_message .= "- on_create() - Setup initial state and bind events\n";
$error_message .= "- on_load() - Fetch async data (no DOM manipulation)\n";
$error_message .= "- on_ready() - Final setup after component is loaded\n\n";
$error_message .= "All methods should be async if they need to wait for operations.\n\n";
$error_message .= "EXAMPLE (from User_Card.js):\n";
$error_message .= "async on_create() {\n";
$error_message .= " this.editing = false;\n";
$error_message .= "}\n\n";
$error_message .= "async on_load() {\n";
$error_message .= " // Fetch data here\n";
$error_message .= " return new Promise((resolve) => {\n";
$error_message .= " // Load data...\n";
$error_message .= " resolve();\n";
$error_message .= " });\n";
$error_message .= "}\n\n";
$error_message .= "async on_ready() {\n";
$error_message .= " this.\$.addClass('loaded');\n";
$error_message .= "}";
throw new YoureDoingItWrongException($error_message);
}
/**
* PHP file contains multiple class definitions
*/
public static function multiple_classes_in_file(
string $file_path,
array $class_names
): void {
$error_message = "Multiple classes detected in PHP file: {$file_path}\n";
$error_message .= "Classes found: " . implode(', ', $class_names) . "\n";
$error_message .= "PHP files must contain only one class per file.";
throw new YoureDoingItWrongException($error_message);
}
}

View File

@@ -0,0 +1,237 @@
<?php
namespace App\RSpade\CodeQuality\RuntimeChecks;
use App\RSpade\CodeQuality\RuntimeChecks\YoureDoingItWrongException;
/**
* Developer-facing view validation errors
*
* These exceptions guide developers to properly organize view assets
* and follow RSX conventions for blade templates.
*/
class ViewErrors
{
/**
* View contains inline styles or scripts
*/
public static function inline_assets_not_allowed(
string $file_path,
string $id,
bool $has_style,
bool $has_script
): void {
$dir_path = dirname($file_path);
$base_name = basename($file_path, '.blade.php');
$error_message = "RSX View Validation Error: Inline styles and scripts are not allowed in view files.\n\n";
$error_message .= "Violated in: {$file_path}\n";
$error_message .= "ID: {$id}\n\n";
$error_message .= "Inline <style> and <script> tags must be moved to separate files:\n\n";
if ($has_style) {
$scss_path = "{$dir_path}/{$base_name}.scss";
$error_message .= "For styles, create: {$scss_path}\n";
$error_message .= "----------------------------------------\n";
$error_message .= ".{$id} {\n";
$error_message .= " // Move your <style> content here\n";
$error_message .= " // All styles should be nested within this class\n";
$error_message .= "}\n";
$error_message .= "----------------------------------------\n\n";
}
if ($has_script) {
$js_path = "{$dir_path}/{$base_name}.js";
$error_message .= "For scripts, create: {$js_path}\n";
$error_message .= "----------------------------------------\n";
$error_message .= "class {$id} {\n";
$error_message .= " static init() {\n";
$error_message .= " if (!\$(\".{$id}\").length) return;\n";
$error_message .= " \n";
$error_message .= " // Move your <script> content here\n";
$error_message .= " }\n";
$error_message .= "}\n";
$error_message .= "----------------------------------------\n\n";
}
$error_message .= "This ensures proper scoping and organization of assets.";
throw new YoureDoingItWrongException($error_message);
}
/**
* Layout missing required body class function
*/
public static function layout_missing_body_class(string $layout_path): void
{
$error_message = "RSX Layout Validation Error: Layout is missing required body class function.\n\n";
$error_message .= "Layout file: {$layout_path}\n\n";
$error_message .= "The <body> tag must include the rsx_body_class() function:\n";
$error_message .= '<body class="{{ rsx_body_class() }}">' . "\n\n";
$error_message .= "This allows views to be properly styled with scoped CSS.";
throw new YoureDoingItWrongException($error_message);
}
/**
* Layout missing required bundle include
*/
public static function layout_missing_bundle(string $layout_path, string $module_name): void
{
$bundle_class = ucfirst($module_name) . '_Bundle';
$error_message = "RSX Layout Validation Error: Layout is missing required bundle include.\n\n";
$error_message .= "Layout file: {$layout_path}\n\n";
$error_message .= "All layout files must include a bundle. Add this to your <head> section:\n";
$error_message .= '{!! ' . $bundle_class . '::render() !!}' . "\n\n";
$error_message .= "Bundle files should be created at: ./rsx/app/{$module_name}/{$module_name}_bundle.php\n\n";
$error_message .= "NOTE: If this layout is for email notifications or print views, rename the file to include\n";
$error_message .= "'.mail.' or '.print.' in the filename to bypass this requirement.\n";
$error_message .= "Examples: layout.mail.blade.php or invoice.print.blade.php\n\n";
$error_message .= "Example bundle structure:\n";
$error_message .= "----------------------------------------\n";
$error_message .= "<?php\n";
$error_message .= "namespace Rsx\\App\\" . ucfirst($module_name) . ";\n\n";
$error_message .= "use App\\RSpade\\Core\\Bundle\\Rsx_Bundle_Abstract;\n\n";
$error_message .= "class {$bundle_class} extends Rsx_Bundle_Abstract\n";
$error_message .= "{\n";
$error_message .= " public static function define(): array\n";
$error_message .= " {\n";
$error_message .= " return [\n";
$error_message .= " 'modules' => ['bootstrap5'],\n";
$error_message .= " 'include' => ['rsx/app/{$module_name}/**'],\n";
$error_message .= " ];\n";
$error_message .= " }\n";
$error_message .= "}\n";
$error_message .= "----------------------------------------";
throw new YoureDoingItWrongException($error_message);
}
/**
* Top-most layout in chain is missing bundle include
*/
public static function topmost_layout_missing_bundle(string $layout_path, string $layout_id): void
{
// Get the module name from path for bundle suggestion
$path_parts = explode('/', $layout_path);
$module_name = '';
for ($i = count($path_parts) - 2; $i >= 0; $i--) {
if ($path_parts[$i] === 'app' && isset($path_parts[$i + 1])) {
$module_name = $path_parts[$i + 1];
break;
}
}
$bundle_class = ucfirst($module_name) . '_Bundle';
$error_message = "RSX Layout Chain Error: Top-most layout missing bundle include.\n\n";
$error_message .= "Layout: {$layout_id}\n";
$error_message .= "File: {$layout_path}\n\n";
$error_message .= "In embedded layout chains, the TOP-MOST layout MUST include a bundle.\n";
$error_message .= "Add this to the <head> section of this layout:\n\n";
$error_message .= '{!! ' . $bundle_class . '::render() !!}' . "\n\n";
$error_message .= "IMPORTANT: Only the top-most layout should have a bundle.\n";
$error_message .= "If this layout extends another layout, check that the parent\n";
$error_message .= "layout has the bundle render call instead.\n\n";
$error_message .= "Bundle hierarchy rule:\n";
$error_message .= "- The highest parent layout includes the bundle\n";
$error_message .= "- Child layouts extend parent layouts\n";
$error_message .= "- Views extend layouts\n";
$error_message .= "- Only ONE bundle per page is allowed";
throw new YoureDoingItWrongException($error_message);
}
/**
* Intermediate layout has bundle (only topmost should have it)
*/
public static function intermediate_layout_has_bundle(string $layout_path, string $layout_id): void
{
$error_message = "RSX Layout Chain Error: Intermediate layout has bundle render.\n\n";
$error_message .= "Layout: {$layout_id}\n";
$error_message .= "File: {$layout_path}\n\n";
$error_message .= "This layout extends another layout, so it should NOT render a bundle.\n";
$error_message .= "Only the TOP-MOST layout in the chain should include a bundle.\n\n";
$error_message .= "To fix this:\n";
$error_message .= "1. Remove the {!! *_Bundle::render() !!} call from this layout\n";
$error_message .= "2. Ensure the parent layout (that this extends) has the bundle render\n\n";
$error_message .= "Layout hierarchy example:\n";
$error_message .= "- app_layout.blade.php - HAS bundle render ✓\n";
$error_message .= " └─ section_layout.blade.php - NO bundle render ✓\n";
$error_message .= " └─ page.blade.php - NO bundle render ✓\n\n";
$error_message .= "Remember: Only ONE bundle per page is allowed.";
throw new YoureDoingItWrongException($error_message);
}
/**
* Layout is incomplete - has neither bundle nor @rsx_extends
*/
public static function layout_incomplete(string $layout_path): void
{
$error_message = "RSX Layout Error: Incomplete layout configuration.\n\n";
$error_message .= "File: {$layout_path}\n\n";
$error_message .= "This layout has neither a bundle render nor @rsx_extends directive.\n\n";
$error_message .= "A layout must either:\n";
$error_message .= "1. Be embedded in another layout using @rsx_extends('Parent_Layout'), OR\n";
$error_message .= "2. Be a complete HTML document with <html>, <head>, <body> tags and a bundle render\n\n";
$error_message .= "For embedded layouts (extends another layout):\n";
$error_message .= "Add at the top of your layout:\n";
$error_message .= "@rsx_extends('Parent_Layout_Name')\n\n";
$error_message .= "For top-level layouts (complete HTML document):\n";
$error_message .= "Include the full HTML structure with a bundle in <head>:\n";
$error_message .= "<!DOCTYPE html>\n";
$error_message .= "<html>\n";
$error_message .= "<head>\n";
$error_message .= " {!! Module_Bundle::render() !!}\n";
$error_message .= "</head>\n";
$error_message .= "<body class=\"{{ rsx_body_class() }}\">\n";
$error_message .= " @yield('content')\n";
$error_message .= "</body>\n";
$error_message .= "</html>";
throw new YoureDoingItWrongException($error_message);
}
/**
* Embedded layout trying to render bundle
*/
public static function embedded_layout_has_bundle(string $layout_path): void
{
$error_message = "RSX Layout Error: Embedded layout has bundle render.\n\n";
$error_message .= "File: {$layout_path}\n\n";
$error_message .= "This layout uses @rsx_extends, making it an embedded layout.\n";
$error_message .= "Embedded layouts should NOT render bundles.\n\n";
$error_message .= "Only the top-most layout in the chain should render a bundle.\n\n";
$error_message .= "To fix this:\n";
$error_message .= "1. Remove the {!! *_Bundle::render() !!} call from this layout\n";
$error_message .= "2. Ensure the parent layout (specified in @rsx_extends) has the bundle render\n\n";
$error_message .= "Layout hierarchy example:\n";
$error_message .= "- app_layout.blade.php - HAS bundle render ✓ (top-most)\n";
$error_message .= " └─ section_layout.blade.php - NO bundle, uses @rsx_extends ✓\n";
$error_message .= " └─ page.blade.php - NO bundle, uses @rsx_extends ✓";
throw new YoureDoingItWrongException($error_message);
}
/**
* Top-most layout incorrectly using @rsx_extends
*/
public static function topmost_layout_has_extends(string $layout_path): void
{
$error_message = "RSX Layout Error: Top-most layout uses @rsx_extends.\n\n";
$error_message .= "File: {$layout_path}\n\n";
$error_message .= "This layout contains a complete HTML document (has </html> tag) and renders a bundle,\n";
$error_message .= "making it a top-most layout. Top-most layouts should NOT use @rsx_extends.\n\n";
$error_message .= "@rsx_extends is only for embedded layouts that are part of another layout.\n\n";
$error_message .= "To fix this:\n";
$error_message .= "Remove the @rsx_extends directive from this layout.\n\n";
$error_message .= "Top-most layouts should:\n";
$error_message .= "- Include complete HTML structure (<html>, <head>, <body>)\n";
$error_message .= "- Render a bundle in the <head> section\n";
$error_message .= "- NOT use @rsx_extends\n";
$error_message .= "- Use @yield for content sections";
throw new YoureDoingItWrongException($error_message);
}
}

View File

@@ -0,0 +1,41 @@
<?php
namespace App\RSpade\CodeQuality\RuntimeChecks;
use RuntimeException;
/**
* Exception thrown when developers violate framework conventions
*
* This exception makes it immediately clear in stack traces that the error
* is due to incorrect usage of the framework, not a bug in the framework itself.
* The name is intentionally cheeky to be memorable and instructive.
*/
#[Instantiatable]
class YoureDoingItWrongException extends RuntimeException
{
/**
* Create a new YoureDoingItWrongException
*
* @param string $message The detailed error message explaining what's wrong and how to fix it
* @param int $code Optional error code (default: 0)
* @param \Throwable|null $previous Optional previous exception for chaining
* @param string|null $file Optional file path to show as the error source
* @param int|null $line Optional line number to show as the error source
*/
public function __construct(string $message = "", int $code = 0, ?\Throwable $previous = null, ?string $file = null, ?int $line = null)
{
parent::__construct($message, $code, $previous);
// Override the file and line if provided
if ($file !== null) {
$this->file = $file;
}
if ($line !== null) {
$this->line = $line;
}
// Mark manifest as bad to force rebuild on next attempt
\App\RSpade\Core\Manifest\Manifest::_set_manifest_is_bad();
}
}