Files
rspade_system/app/RSpade/Commands/Rsx/Bundle_Compile_Command.php
root f6fac6c4bc 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>
2025-10-21 02:08:33 +00:00

233 lines
8.7 KiB
PHP
Executable File

<?php
namespace App\RSpade\Commands\Rsx;
use App\RSpade\Core\Bundle\BundleCompiler;
use App\RSpade\Core\Manifest\Manifest;
use Exception;
use Illuminate\Console\Command;
class Bundle_Compile_Command extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'rsx:bundle:compile {bundle? : Bundle class name to compile (optional, compiles all if not specified)}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Compile RSX bundles into JavaScript and CSS files';
/**
* Execute the console command.
*/
public function handle()
{
$bundle_arg = $this->argument('bundle');
// Get all bundle classes from manifest
$manifest_data = Manifest::get_all();
$bundle_classes = [];
foreach ($manifest_data as $file_info) {
if (isset($file_info['extends']) && $file_info['extends'] === 'Rsx_Bundle_Abstract') {
$fqcn = $file_info['fqcn'] ?? $file_info['class'] ?? null;
if ($fqcn) {
$bundle_classes[$fqcn] = $file_info['class'] ?? $fqcn;
}
}
}
if (empty($bundle_classes)) {
$this->error('No bundle classes found in manifest');
return 1;
}
// If specific bundle requested, filter to just that one
if ($bundle_arg) {
$found = false;
foreach ($bundle_classes as $fqcn => $class_name) {
if ($class_name === $bundle_arg || $fqcn === $bundle_arg) {
$bundle_classes = [$fqcn => $class_name];
$found = true;
break;
}
}
if (!$found) {
$this->error("Bundle class not found: {$bundle_arg}");
$this->info('Available bundles:');
foreach ($bundle_classes as $fqcn => $class_name) {
$this->info(" - {$class_name}");
}
return 1;
}
}
// Ensure storage directory exists
$bundle_dir = storage_path('rsx-build/bundles');
if (!is_dir($bundle_dir)) {
mkdir($bundle_dir, 0755, true);
}
$this->info('Compiling bundles...');
$compiled_count = 0;
$skipped_count = 0;
$failed_bundles = [];
$is_single_bundle = count($bundle_classes) === 1;
foreach ($bundle_classes as $fqcn => $class_name) {
$this->info("Processing: {$class_name}");
// Generate hash for this bundle
if (env('ENHANCED_DEBUG', false)) {
// In debug mode, use only class name for predictable URLs
$bundle_hash = substr(hash('sha256', $fqcn), 0, 32);
} else {
// In production, include manifest hash for cache-busting
$manifest_hash = Manifest::get_build_key();
$bundle_hash = substr(hash('sha256', $manifest_hash . '|' . $fqcn), 0, 32);
}
$js_path = "{$bundle_dir}/app.{$bundle_hash}.js";
$css_path = "{$bundle_dir}/app.{$bundle_hash}.css";
// Check if files already exist
if (file_exists($js_path) && file_exists($css_path)) {
$this->line(" ✓ Already compiled (hash: {$bundle_hash})");
$skipped_count++;
continue;
}
try {
// Compile the bundle
$compiler = new BundleCompiler();
console_debug('BUNDLE', "Calling compile for {$fqcn}");
$compiled = $compiler->compile($fqcn);
console_debug('BUNDLE', 'Compile returned: ' . json_encode(array_keys($compiled)));
// Read compiled content from bundle files
$bundle_dir = storage_path('rsx-build/bundles');
$js_content = '';
$css_content = '';
// In production mode, we have single combined files
if (app()->environment('production')) {
if (isset($compiled['js_bundle_path'])) {
$js_content = file_get_contents("{$bundle_dir}/{$compiled['js_bundle_path']}");
}
if (isset($compiled['css_bundle_path'])) {
$css_content = file_get_contents("{$bundle_dir}/{$compiled['css_bundle_path']}");
}
// Write combined files for production
file_put_contents($js_path, $js_content);
file_put_contents($css_path, $css_content);
$js_size = strlen($js_content);
$css_size = strlen($css_content);
} else {
// In development mode, don't create combined files
// Just report on the individual vendor and app bundles
$js_size = 0;
$css_size = 0;
if (isset($compiled['vendor_js_bundle_path'])) {
$js_size += filesize("{$bundle_dir}/{$compiled['vendor_js_bundle_path']}");
}
if (isset($compiled['app_js_bundle_path'])) {
$js_size += filesize("{$bundle_dir}/{$compiled['app_js_bundle_path']}");
}
if (isset($compiled['vendor_css_bundle_path'])) {
$css_size += filesize("{$bundle_dir}/{$compiled['vendor_css_bundle_path']}");
}
if (isset($compiled['app_css_bundle_path'])) {
$css_size += filesize("{$bundle_dir}/{$compiled['app_css_bundle_path']}");
}
}
$this->line(' ✓ Compiled successfully');
if (app()->environment('production')) {
$this->line(' JS: ' . $this->format_size($js_size) . " → app.{$bundle_hash}.js");
$this->line(' CSS: ' . $this->format_size($css_size) . " → app.{$bundle_hash}.css");
} else {
// In dev mode, show the actual split files
if (isset($compiled['app_js_bundle_path'])) {
$app_js_size = filesize("{$bundle_dir}/{$compiled['app_js_bundle_path']}");
$this->line(' JS: ' . $this->format_size($app_js_size) . ' → ' . basename($compiled['app_js_bundle_path']));
}
if (isset($compiled['app_css_bundle_path'])) {
$app_css_size = filesize("{$bundle_dir}/{$compiled['app_css_bundle_path']}");
$this->line(' CSS: ' . $this->format_size($app_css_size) . ' → ' . basename($compiled['app_css_bundle_path']));
}
}
$compiled_count++;
} catch (Exception $e) {
$this->error(' ✗ Failed to compile: ' . $e->getMessage());
$failed_bundles[$class_name] = $e->getMessage();
// If compiling a single bundle, fail immediately
if ($is_single_bundle) {
return 1;
}
// For multiple bundles, continue to next bundle
continue;
}
}
$this->newLine();
// Report results
if (empty($failed_bundles)) {
// Success - all bundles compiled
$this->info("✅ Compilation complete: {$compiled_count} compiled, {$skipped_count} skipped");
if ($compiled_count > 0) {
$this->newLine();
$this->info("Tip: Use 'php artisan rsx:bundle:show <bundle>' to inspect bundle contents");
}
return 0;
} else {
// Failures occurred
$total_bundles = $compiled_count + $skipped_count + count($failed_bundles);
$this->error("❌ Bundle compilation failed");
$this->newLine();
$this->line("Results: {$compiled_count} compiled, {$skipped_count} skipped, " . count($failed_bundles) . " failed");
$this->newLine();
$this->error("Failed bundles:");
foreach ($failed_bundles as $bundle_name => $error_message) {
$this->line("{$bundle_name}");
$this->line(" {$error_message}");
}
return 1;
}
}
/**
* Format file size for display
*/
protected function format_size(int $bytes): string
{
if ($bytes < 1024) {
return "{$bytes} B";
}
if ($bytes < 1048576) {
return round($bytes / 1024, 1) . ' KB';
}
return round($bytes / 1048576, 2) . ' MB';
}
}