Files
rspade_system/app/RSpade/Commands/Rsx/Prod_Build_Command.php
root d523f0f600 Fix code quality violations and exclude Manifest from checks
Document application modes (development/debug/production)
Add global file drop handler, order column normalization, SPA hash fix
Serve CDN assets via /_vendor/ URLs instead of merging into bundles
Add production minification with license preservation
Improve JSON formatting for debugging and production optimization
Add CDN asset caching with CSS URL inlining for production builds
Add three-mode system (development, debug, production)
Update Manifest CLAUDE.md to reflect helper class architecture
Refactor Manifest.php into helper classes for better organization
Pre-manifest-refactor checkpoint: Add app_mode documentation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-14 10:38:22 +00:00

181 lines
6.3 KiB
PHP
Executable File

<?php
namespace App\RSpade\Commands\Rsx;
use App\RSpade\Core\Bundle\BundleCompiler;
use App\RSpade\Core\Bundle\Minifier;
use App\RSpade\Core\Manifest\Manifest;
use App\RSpade\Core\Mode\Rsx_Mode;
use Exception;
use Illuminate\Console\Command;
/**
* Build all production assets
*
* Rebuilds manifest and compiles all bundles for production/debug mode.
* Must be run before the application can serve pages in production-like modes.
*/
class Prod_Build_Command extends Command
{
protected $signature = 'rsx:prod:build
{--force : Force rebuild even if assets exist}
{--skip-laravel-cache : Skip Laravel config/route/view caching}';
protected $description = 'Build all production assets (manifest, bundles, caches)';
public function handle(): int
{
$mode = Rsx_Mode::get();
if ($mode === Rsx_Mode::DEVELOPMENT) {
$this->warn('Warning: Building production assets in development mode.');
$this->line('Assets will be built but will not be used until you switch modes:');
$this->line(' php artisan rsx:mode:set debug');
$this->line(' php artisan rsx:mode:set production');
$this->newLine();
}
$this->info('Building production assets...');
$this->newLine();
$start_time = microtime(true);
// Step 1: Rebuild manifest
$this->line('[1/3] Building manifest...');
try {
// Enable force build mode to allow rebuilding in production-like modes
Manifest::$_force_build = true;
// Force a fresh manifest rebuild by clearing and re-initializing
Manifest::clear();
Manifest::init();
$this->line(' Manifest built successfully');
} catch (Exception $e) {
$this->error(' Failed to build manifest: ' . $e->getMessage());
return 1;
} finally {
Manifest::$_force_build = false;
}
// Step 2: Compile all bundles
$this->line('[2/3] Compiling bundles...');
// Force restart minify server to pick up any code changes
if (Rsx_Mode::is_production()) {
Minifier::force_restart();
}
$manifest_data = Manifest::get_all();
$bundle_classes = [];
foreach ($manifest_data as $file_info) {
$class_name = $file_info['class'] ?? null;
if ($class_name && Manifest::php_is_subclass_of($class_name, 'Rsx_Module_Bundle_Abstract')) {
$fqcn = $file_info['fqcn'] ?? $class_name;
$bundle_classes[$fqcn] = $class_name;
}
}
if (empty($bundle_classes)) {
$this->warn(' No bundles found in manifest');
} else {
// Ensure storage directory exists
$bundle_dir = storage_path('rsx-build/bundles');
if (!is_dir($bundle_dir)) {
mkdir($bundle_dir, 0755, true);
}
$compiled_count = 0;
$failed_bundles = [];
foreach ($bundle_classes as $fqcn => $class_name) {
$this->line(" Compiling: {$class_name}");
try {
$compiler = new BundleCompiler();
$compiled = $compiler->compile($fqcn, ['force_build' => true]);
// Get output file info (always vendor/app split)
$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(' JS: ' . $this->_format_size($js_size) . ', CSS: ' . $this->_format_size($css_size));
$compiled_count++;
} catch (Exception $e) {
$this->error(" Failed: {$e->getMessage()}");
$failed_bundles[$class_name] = $e->getMessage();
}
}
if (!empty($failed_bundles)) {
$this->error(" {$compiled_count} compiled, " . count($failed_bundles) . ' failed');
return 1;
}
$this->line(" {$compiled_count} bundles compiled");
}
// Step 3: Laravel caches
if (!$this->option('skip-laravel-cache')) {
$this->line('[3/3] Building Laravel caches...');
passthru('php artisan config:cache 2>/dev/null', $exit_code);
if ($exit_code === 0) {
$this->line(' Config cached');
}
passthru('php artisan route:cache 2>/dev/null', $exit_code);
if ($exit_code === 0) {
$this->line(' Routes cached');
}
passthru('php artisan view:cache 2>/dev/null', $exit_code);
if ($exit_code === 0) {
$this->line(' Views cached');
}
} else {
$this->line('[3/3] Skipping Laravel caches (--skip-laravel-cache)');
}
$elapsed = round(microtime(true) - $start_time, 2);
$this->newLine();
$this->info("[OK] Production build complete ({$elapsed}s)");
if ($mode === Rsx_Mode::DEVELOPMENT) {
$this->newLine();
$this->line('To use these assets, switch to production mode:');
$this->line(' php artisan rsx:mode:set production');
}
return 0;
}
private 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';
}
}