Unify CDN asset collection between dev and prod modes 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
169 lines
5.9 KiB
PHP
Executable File
169 lines
5.9 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\Rsx;
|
|
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
|
|
{
|
|
if (Rsx::is_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::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 optimize:cache 2>/dev/null', $exit_code);
|
|
if ($exit_code === 0) {
|
|
$this->line(' Laravel caches built');
|
|
}
|
|
} 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 (Rsx::is_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';
|
|
}
|
|
}
|