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>
This commit is contained in:
root
2026-01-14 10:38:22 +00:00
parent bb9046af1b
commit d523f0f600
2355 changed files with 231384 additions and 32223 deletions

View File

@@ -0,0 +1,231 @@
<?php
namespace App\RSpade\Commands\Rsx;
use Illuminate\Console\Command;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
/**
* Export the application for deployment
*
* Runs rsx:prod:build then copies all necessary files to an export directory.
* The export can be deployed to production servers.
*/
class Prod_Export_Command extends Command
{
protected $signature = 'rsx:prod:export
{--path=./rsx-export : Export destination directory}
{--skip-build : Skip running rsx:prod:build (use existing assets)}';
protected $description = 'Export application for deployment';
public function handle(): int
{
$export_path = $this->option('path');
// Resolve relative path from base_path parent (since we're in /system)
if (str_starts_with($export_path, './')) {
$export_path = dirname(base_path()) . '/' . substr($export_path, 2);
} elseif (!str_starts_with($export_path, '/')) {
$export_path = dirname(base_path()) . '/' . $export_path;
}
$this->info('Exporting application for deployment...');
$this->line(" Destination: {$export_path}");
$this->newLine();
// Step 1: Run production build (unless --skip-build)
if (!$this->option('skip-build')) {
$this->line('[1/3] Running production build...');
passthru('php artisan rsx:prod:build', $exit_code);
if ($exit_code !== 0) {
$this->error('Production build failed');
return 1;
}
$this->newLine();
} else {
$this->line('[1/3] Skipping production build (--skip-build)');
}
// Step 2: Prepare export directory
$this->line('[2/3] Preparing export directory...');
// Clear existing export if it exists
if (is_dir($export_path)) {
$this->line(' Clearing existing export...');
$this->_clear_directory($export_path);
}
// Create export directory
if (!mkdir($export_path, 0755, true) && !is_dir($export_path)) {
$this->error("Failed to create export directory: {$export_path}");
return 1;
}
// Step 3: Copy files
$this->line('[3/3] Copying files...');
$base = dirname(base_path()); // Parent of /system
$copied_files = 0;
$copied_dirs = 0;
// Directories to copy
$dirs_to_copy = [
'system' => 'system',
'rsx' => 'rsx',
'node_modules' => 'node_modules',
'vendor' => 'vendor',
];
// Also include rsx-build from storage
$storage_rsx_build = base_path('storage/rsx-build');
if (is_dir($storage_rsx_build)) {
$dirs_to_copy['system/storage/rsx-build'] = 'system/storage/rsx-build';
}
foreach ($dirs_to_copy as $src_rel => $dest_rel) {
$src = "{$base}/{$src_rel}";
$dest = "{$export_path}/{$dest_rel}";
if (!is_dir($src)) {
$this->warn(" Skipping {$src_rel} (not found)");
continue;
}
$this->line(" Copying {$src_rel}/...");
$count = $this->_copy_directory($src, $dest, $src_rel);
$copied_files += $count;
$copied_dirs++;
}
// Copy *.json files from root
$json_files = glob("{$base}/*.json");
foreach ($json_files as $json_file) {
$filename = basename($json_file);
copy($json_file, "{$export_path}/{$filename}");
$copied_files++;
}
if (!empty($json_files)) {
$this->line(' Copying *.json files...');
}
// Create .gitignore for export directory
file_put_contents(
"{$export_path}/.gitignore",
"# This export should not be committed to version control\n*\n"
);
$this->newLine();
$this->info("[OK] Export complete");
$this->line(" {$copied_dirs} directories, {$copied_files} files");
$this->line(" Location: {$export_path}");
$this->newLine();
$this->line('Next steps:');
$this->line(' 1. Copy the export directory to your production server');
$this->line(' 2. Configure .env on the production server');
$this->line(' 3. Point your web server to the deployment');
return 0;
}
/**
* Copy a directory recursively, excluding certain paths
*/
private function _copy_directory(string $src, string $dest, string $rel_path): int
{
// Paths to exclude (relative to source)
$exclude_patterns = [
'.env',
'.env.example',
'storage/app',
'storage/logs',
'storage/framework/cache',
'storage/framework/sessions',
'storage/framework/views',
'storage/rsx-tmp',
'rsx-export',
'.git',
'.idea',
'.vscode',
'tests',
];
// For system directory, exclude additional paths
if ($rel_path === 'system') {
$exclude_patterns[] = 'storage/rsx-tmp';
}
if (!is_dir($dest)) {
mkdir($dest, 0755, true);
}
$copied = 0;
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($src, RecursiveDirectoryIterator::SKIP_DOTS),
RecursiveIteratorIterator::SELF_FIRST
);
foreach ($iterator as $item) {
$sub_path = $iterator->getSubPathname();
// Check exclusions
$skip = false;
foreach ($exclude_patterns as $pattern) {
if (str_starts_with($sub_path, $pattern) || str_contains($sub_path, "/{$pattern}")) {
$skip = true;
break;
}
}
if ($skip) {
continue;
}
$dest_path = "{$dest}/{$sub_path}";
if ($item->isDir()) {
if (!is_dir($dest_path)) {
mkdir($dest_path, 0755, true);
}
} else {
// Ensure parent directory exists
$parent = dirname($dest_path);
if (!is_dir($parent)) {
mkdir($parent, 0755, true);
}
copy($item->getPathname(), $dest_path);
$copied++;
}
}
return $copied;
}
/**
* Clear a directory recursively
*/
private function _clear_directory(string $path): void
{
if (!is_dir($path)) {
return;
}
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS),
RecursiveIteratorIterator::CHILD_FIRST
);
foreach ($iterator as $item) {
if ($item->isDir()) {
rmdir($item->getPathname());
} else {
unlink($item->getPathname());
}
}
rmdir($path);
}
}