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>
181 lines
6.3 KiB
PHP
Executable File
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';
|
|
}
|
|
}
|