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>
242 lines
7.5 KiB
PHP
Executable File
242 lines
7.5 KiB
PHP
Executable File
<?php
|
|
|
|
namespace App\RSpade\Commands\Rsx;
|
|
|
|
use Illuminate\Console\Command;
|
|
use App\RSpade\Core\Mode\Rsx_Mode;
|
|
|
|
/**
|
|
* Set the application mode (development, debug, or production)
|
|
*
|
|
* Changes the RSX_MODE in .env, clears caches, and rebuilds as appropriate.
|
|
*/
|
|
class Mode_Set_Command extends Command
|
|
{
|
|
protected $signature = 'rsx:mode:set
|
|
{mode : Mode to set (dev|development|debug|prod|production)}';
|
|
|
|
protected $description = 'Set the application mode (development, debug, or production)';
|
|
|
|
public function handle(): int
|
|
{
|
|
$mode = $this->argument('mode');
|
|
|
|
// Normalize mode aliases
|
|
$normalized = match (strtolower($mode)) {
|
|
'dev', 'development' => Rsx_Mode::DEVELOPMENT,
|
|
'debug' => Rsx_Mode::DEBUG,
|
|
'prod', 'production' => Rsx_Mode::PRODUCTION,
|
|
default => null,
|
|
};
|
|
|
|
if ($normalized === null) {
|
|
$this->error("Invalid mode: {$mode}");
|
|
$this->line('');
|
|
$this->line('Valid modes:');
|
|
$this->line(' dev, development - Auto-rebuild, full debugging');
|
|
$this->line(' debug - Production optimizations with sourcemaps');
|
|
$this->line(' prod, production - Full optimization');
|
|
|
|
return 1;
|
|
}
|
|
|
|
// Check current mode (for display purposes only - always rebuild)
|
|
$current_mode = $this->_get_current_env_mode();
|
|
if ($current_mode === $normalized) {
|
|
$this->info("Rebuilding {$normalized} mode...");
|
|
} else {
|
|
$this->info("Switching to {$normalized} mode...");
|
|
}
|
|
$this->newLine();
|
|
|
|
// Step 1: Update .env file
|
|
$this->line(' [1/3] Updating .env...');
|
|
$this->_update_env_mode($normalized);
|
|
|
|
// Clear the cached mode so subsequent calls see the new value
|
|
Rsx_Mode::clear_cache();
|
|
|
|
// Step 2: Clear all caches
|
|
$this->line(' [2/3] Clearing caches...');
|
|
|
|
// Clear Laravel caches by deleting files directly (avoids triggering Manifest::init)
|
|
$this->_clear_laravel_caches();
|
|
|
|
// Clear RSX caches directly (avoids triggering Manifest::init via artisan boot)
|
|
$this->_clear_rsx_caches();
|
|
|
|
// Step 3: Build appropriate assets
|
|
$this->line(' [3/3] Building assets...');
|
|
|
|
if ($normalized === Rsx_Mode::DEVELOPMENT) {
|
|
// In development, pre-warm the bundle cache
|
|
// Explicitly pass RSX_MODE to ensure subprocess uses correct mode
|
|
passthru("RSX_MODE={$normalized} php artisan rsx:bundle:compile", $exit_code);
|
|
if ($exit_code !== 0) {
|
|
$this->warn('Bundle compilation had warnings, but continuing...');
|
|
}
|
|
} else {
|
|
// In debug/production, run the production build
|
|
// RSX_FORCE_BUILD allows manifest rebuild even in production mode
|
|
// Explicitly pass RSX_MODE to ensure subprocess uses correct mode
|
|
passthru("RSX_MODE={$normalized} RSX_FORCE_BUILD=1 php artisan rsx:prod:build", $exit_code);
|
|
if ($exit_code !== 0) {
|
|
$this->error('Production build failed');
|
|
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
$this->newLine();
|
|
$this->info("[OK] Switched to {$normalized} mode");
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Get current RSX_MODE from .env file (not from env() which may be cached)
|
|
*/
|
|
private function _get_current_env_mode(): string
|
|
{
|
|
$env_path = base_path('.env');
|
|
if (!file_exists($env_path)) {
|
|
return Rsx_Mode::DEVELOPMENT;
|
|
}
|
|
|
|
$contents = file_get_contents($env_path);
|
|
if (preg_match('/^RSX_MODE=(.*)$/m', $contents, $matches)) {
|
|
return trim($matches[1]);
|
|
}
|
|
|
|
return Rsx_Mode::DEVELOPMENT;
|
|
}
|
|
|
|
/**
|
|
* Update RSX_MODE in .env file
|
|
*/
|
|
private function _update_env_mode(string $mode): void
|
|
{
|
|
$env_path = base_path('.env');
|
|
|
|
if (!file_exists($env_path)) {
|
|
// Create .env with just RSX_MODE
|
|
file_put_contents($env_path, "RSX_MODE={$mode}\n");
|
|
|
|
return;
|
|
}
|
|
|
|
$contents = file_get_contents($env_path);
|
|
|
|
if (preg_match('/^RSX_MODE=.*$/m', $contents)) {
|
|
// Replace existing RSX_MODE
|
|
$contents = preg_replace('/^RSX_MODE=.*$/m', "RSX_MODE={$mode}", $contents);
|
|
} else {
|
|
// Add RSX_MODE after APP_DEBUG or APP_URL if they exist, otherwise at end
|
|
if (preg_match('/^APP_DEBUG=.*$/m', $contents)) {
|
|
$contents = preg_replace(
|
|
'/^(APP_DEBUG=.*)$/m',
|
|
"$1\nRSX_MODE={$mode}",
|
|
$contents,
|
|
1
|
|
);
|
|
} elseif (preg_match('/^APP_URL=.*$/m', $contents)) {
|
|
$contents = preg_replace(
|
|
'/^(APP_URL=.*)$/m',
|
|
"$1\nRSX_MODE={$mode}",
|
|
$contents,
|
|
1
|
|
);
|
|
} else {
|
|
// Append to end
|
|
$contents = rtrim($contents) . "\nRSX_MODE={$mode}\n";
|
|
}
|
|
}
|
|
|
|
file_put_contents($env_path, $contents);
|
|
}
|
|
|
|
/**
|
|
* Clear Laravel caches by deleting files directly
|
|
* (Avoids running artisan commands which trigger Manifest::init)
|
|
*/
|
|
private function _clear_laravel_caches(): void
|
|
{
|
|
$bootstrap_cache = base_path('bootstrap/cache');
|
|
|
|
// Config cache
|
|
$config_cache = "{$bootstrap_cache}/config.php";
|
|
if (file_exists($config_cache)) {
|
|
unlink($config_cache);
|
|
}
|
|
|
|
// Route cache
|
|
$route_cache = "{$bootstrap_cache}/routes-v7.php";
|
|
if (file_exists($route_cache)) {
|
|
unlink($route_cache);
|
|
}
|
|
|
|
// Services cache
|
|
$services_cache = "{$bootstrap_cache}/services.php";
|
|
if (file_exists($services_cache)) {
|
|
unlink($services_cache);
|
|
}
|
|
|
|
// Packages cache
|
|
$packages_cache = "{$bootstrap_cache}/packages.php";
|
|
if (file_exists($packages_cache)) {
|
|
unlink($packages_cache);
|
|
}
|
|
|
|
// Compiled views
|
|
$views_path = storage_path('framework/views');
|
|
if (is_dir($views_path)) {
|
|
$files = glob("{$views_path}/*.php");
|
|
foreach ($files as $file) {
|
|
unlink($file);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Clear RSX caches by deleting directories directly
|
|
* (Avoids running artisan commands which trigger Manifest::init)
|
|
*/
|
|
private function _clear_rsx_caches(): void
|
|
{
|
|
// Clear rsx-build directory
|
|
$build_path = storage_path('rsx-build');
|
|
if (is_dir($build_path)) {
|
|
$this->_clear_directory_contents($build_path);
|
|
}
|
|
|
|
// Clear rsx-tmp directory
|
|
$tmp_path = storage_path('rsx-tmp');
|
|
if (is_dir($tmp_path)) {
|
|
$this->_clear_directory_contents($tmp_path);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Recursively clear directory contents
|
|
*/
|
|
private function _clear_directory_contents(string $path): void
|
|
{
|
|
if (!is_dir($path)) {
|
|
return;
|
|
}
|
|
|
|
$files = new \RecursiveIteratorIterator(
|
|
new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS),
|
|
\RecursiveIteratorIterator::CHILD_FIRST
|
|
);
|
|
|
|
foreach ($files as $file) {
|
|
if ($file->isDir()) {
|
|
rmdir($file->getRealPath());
|
|
} else {
|
|
unlink($file->getRealPath());
|
|
}
|
|
}
|
|
}
|
|
}
|