Deduplicate CDN assets by URL in bundle compiler
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>
This commit is contained in:
@@ -132,19 +132,9 @@ class Prod_Build_Command extends Command
|
|||||||
if (!$this->option('skip-laravel-cache')) {
|
if (!$this->option('skip-laravel-cache')) {
|
||||||
$this->line('[3/3] Building Laravel caches...');
|
$this->line('[3/3] Building Laravel caches...');
|
||||||
|
|
||||||
passthru('php artisan config:cache 2>/dev/null', $exit_code);
|
passthru('php artisan optimize:cache 2>/dev/null', $exit_code);
|
||||||
if ($exit_code === 0) {
|
if ($exit_code === 0) {
|
||||||
$this->line(' Config cached');
|
$this->line(' Laravel caches built');
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
} else {
|
||||||
$this->line('[3/3] Skipping Laravel caches (--skip-laravel-cache)');
|
$this->line('[3/3] Skipping Laravel caches (--skip-laravel-cache)');
|
||||||
|
|||||||
@@ -463,9 +463,14 @@ class BundleCompiler
|
|||||||
'app_css_bundle_path' => !empty($app_css_files) ? basename($app_css_files[0]) : null,
|
'app_css_bundle_path' => !empty($app_css_files) ? basename($app_css_files[0]) : null,
|
||||||
];
|
];
|
||||||
|
|
||||||
// Also resolve CDN assets - they're served separately via /_vendor/ URLs
|
// Resolve CDN assets using the same code path as dev mode
|
||||||
// We need to resolve the bundle includes to get CDN asset definitions
|
// This ensures directory scanning, nested bundles, etc. all work the same
|
||||||
$this->_resolve_cdn_assets_only();
|
// File collection happens but we ignore it since we use cached bundles
|
||||||
|
$bundle_fqcn = $this->_get_bundle_fqcn($this->bundle_name);
|
||||||
|
$this->resolved_includes[$bundle_fqcn] = true;
|
||||||
|
$this->root_bundle_class = $bundle_fqcn;
|
||||||
|
$this->_process_required_bundles();
|
||||||
|
$this->_resolve_bundle($bundle_fqcn);
|
||||||
|
|
||||||
if (!empty($this->cdn_assets['js'])) {
|
if (!empty($this->cdn_assets['js'])) {
|
||||||
$result['cdn_js'] = $this->_prepare_cdn_assets($this->cdn_assets['js'], 'js');
|
$result['cdn_js'] = $this->_prepare_cdn_assets($this->cdn_assets['js'], 'js');
|
||||||
@@ -479,81 +484,6 @@ class BundleCompiler
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Resolve only CDN assets without full bundle compilation
|
|
||||||
*
|
|
||||||
* Used when serving from production cache to get CDN asset URLs
|
|
||||||
* without re-resolving and re-compiling all bundle files.
|
|
||||||
*/
|
|
||||||
protected function _resolve_cdn_assets_only(): void
|
|
||||||
{
|
|
||||||
// Process required bundles first (they may have CDN assets)
|
|
||||||
$required_bundles = config('rsx.required_bundles', []);
|
|
||||||
$bundle_aliases = config('rsx.bundle_aliases', []);
|
|
||||||
|
|
||||||
foreach ($required_bundles as $alias) {
|
|
||||||
if (isset($bundle_aliases[$alias])) {
|
|
||||||
$this->_collect_cdn_assets_from_include($bundle_aliases[$alias]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the bundle's own CDN assets
|
|
||||||
$fqcn = $this->_get_bundle_fqcn($this->bundle_name);
|
|
||||||
$definition = $fqcn::define();
|
|
||||||
|
|
||||||
// Add CDN assets from the bundle definition (same format as main resolution)
|
|
||||||
if (!empty($definition['cdn_assets'])) {
|
|
||||||
if (!empty($definition['cdn_assets']['js'])) {
|
|
||||||
$this->cdn_assets['js'] = array_merge($this->cdn_assets['js'], $definition['cdn_assets']['js']);
|
|
||||||
}
|
|
||||||
if (!empty($definition['cdn_assets']['css'])) {
|
|
||||||
$this->cdn_assets['css'] = array_merge($this->cdn_assets['css'], $definition['cdn_assets']['css']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Also check for Asset Bundle includes that may have CDN assets
|
|
||||||
foreach ($definition['include'] ?? [] as $include) {
|
|
||||||
$this->_collect_cdn_assets_from_include($include);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Collect CDN assets from a bundle include without full resolution
|
|
||||||
*/
|
|
||||||
protected function _collect_cdn_assets_from_include($include): void
|
|
||||||
{
|
|
||||||
// Handle config array format (from bundle aliases like jquery, lodash)
|
|
||||||
if (is_array($include) && isset($include['cdn'])) {
|
|
||||||
foreach ($include['cdn'] as $cdn_item) {
|
|
||||||
// Determine type from URL extension
|
|
||||||
$url = $cdn_item['url'] ?? '';
|
|
||||||
$type = str_ends_with($url, '.css') ? 'css' : 'js';
|
|
||||||
$this->cdn_assets[$type][] = $cdn_item;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle class name includes (could be Asset Bundles with CDN assets)
|
|
||||||
// Normalize FQCN to simple name for manifest lookup (manifest stores simple names)
|
|
||||||
if (is_string($include)) {
|
|
||||||
$simple_name = Manifest::_normalize_class_name($include);
|
|
||||||
if (isset(Manifest::$data['data']['php_classes'][$simple_name])) {
|
|
||||||
if (Manifest::php_is_subclass_of($simple_name, 'Rsx_Asset_Bundle_Abstract')) {
|
|
||||||
$asset_def = $include::define();
|
|
||||||
if (!empty($asset_def['cdn_assets'])) {
|
|
||||||
if (!empty($asset_def['cdn_assets']['js'])) {
|
|
||||||
$this->cdn_assets['js'] = array_merge($this->cdn_assets['js'], $asset_def['cdn_assets']['js']);
|
|
||||||
}
|
|
||||||
if (!empty($asset_def['cdn_assets']['css'])) {
|
|
||||||
$this->cdn_assets['css'] = array_merge($this->cdn_assets['css'], $asset_def['cdn_assets']['css']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process required bundles (jquery, lodash, jqhtml)
|
* Process required bundles (jquery, lodash, jqhtml)
|
||||||
*/
|
*/
|
||||||
@@ -2459,18 +2389,27 @@ implode("\n", array_map(fn ($f) => ' - ' . str_replace(base_path() . '/', '',
|
|||||||
*/
|
*/
|
||||||
protected function _prepare_cdn_assets(array $assets, string $type): array
|
protected function _prepare_cdn_assets(array $assets, string $type): array
|
||||||
{
|
{
|
||||||
// In development mode, return as-is (use CDN URLs directly)
|
// Deduplicate by URL (same asset may be included via multiple bundles)
|
||||||
|
$seen_urls = [];
|
||||||
|
$deduplicated = [];
|
||||||
|
foreach ($assets as $asset) {
|
||||||
|
$url = $asset['url'] ?? '';
|
||||||
|
if (empty($url) || isset($seen_urls[$url])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$seen_urls[$url] = true;
|
||||||
|
$deduplicated[] = $asset;
|
||||||
|
}
|
||||||
|
|
||||||
|
// In development mode, return deduplicated (use CDN URLs directly)
|
||||||
if (!$this->is_production) {
|
if (!$this->is_production) {
|
||||||
return $assets;
|
return $deduplicated;
|
||||||
}
|
}
|
||||||
|
|
||||||
// In production-like modes, ensure cached and add filename
|
// In production-like modes, ensure cached and add filename
|
||||||
$prepared = [];
|
$prepared = [];
|
||||||
foreach ($assets as $asset) {
|
foreach ($deduplicated as $asset) {
|
||||||
$url = $asset['url'] ?? '';
|
$url = $asset['url'];
|
||||||
if (empty($url)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure the asset is cached (downloads if not already)
|
// Ensure the asset is cached (downloads if not already)
|
||||||
Cdn_Cache::get($url, $type);
|
Cdn_Cache::get($url, $type);
|
||||||
|
|||||||
Reference in New Issue
Block a user