Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 16 additions & 16 deletions config/modulite.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@
'auto_register' => env('MODULITE_AUTO_REGISTER_PANELS', true),
'sort_by' => 'priority', // 'priority', 'name', 'none'
'respect_environment' => true,
'validate_before_register' => app()->hasDebugModeEnabled(),
'validate_before_register' => env('MODULITE_VALIDATE_BEFORE_REGISTER', env('APP_DEBUG', false)),
],

/*
Expand Down Expand Up @@ -189,7 +189,7 @@
'registration' => [
'auto_register' => env('MODULITE_AUTO_REGISTER_COMPONENTS', true),
'sort_by' => env('MODULITE_SORT_COMPONENTS', 'none'), // 'name', 'priority', 'none'
'validate_before_register' => env('MODULITE_VALIDATE_COMPONENTS', app()->hasDebugModeEnabled()),
'validate_before_register' => env('MODULITE_VALIDATE_COMPONENTS', env('APP_DEBUG', false)),
'group_by_module' => true,
],

Expand Down Expand Up @@ -251,7 +251,7 @@
| request. Recommended: true for production, false for development.
|
*/
'enabled' => env('MODULITE_CACHE_ENABLED', !app()->hasDebugModeEnabled()),
'enabled' => env('MODULITE_CACHE_ENABLED', !env('APP_DEBUG', false)),

/*
|--------------------------------------------------------------------------
Expand All @@ -275,7 +275,7 @@
| Set to 0 in production for maximum performance (never expires).
|
*/
'ttl' => env('MODULITE_CACHE_TTL', app()->hasDebugModeEnabled() ? 300 : 0),
'ttl' => env('MODULITE_CACHE_TTL', env('APP_DEBUG', false) ? 300 : 0),

/*
|--------------------------------------------------------------------------
Expand All @@ -286,7 +286,7 @@
| Only works in development mode for performance reasons.
|
*/
'auto_invalidate' => app()->hasDebugModeEnabled(),
'auto_invalidate' => env('MODULITE_AUTO_INVALIDATE', env('APP_DEBUG', false)),

/*
|--------------------------------------------------------------------------
Expand All @@ -312,9 +312,9 @@
*/
'optimizations' => [
'static_caching' => env('MODULITE_STATIC_CACHING', true),
'defer_validation' => env('MODULITE_DEFER_VALIDATION', !app()->hasDebugModeEnabled()),
'defer_validation' => env('MODULITE_DEFER_VALIDATION', !env('APP_DEBUG', false)),
'skip_duplicate_panels' => env('MODULITE_SKIP_DUPLICATE_PANELS', true),
'disable_reflection' => env('MODULITE_DISABLE_REFLECTION', app()->isProduction()),
'disable_reflection' => env('MODULITE_DISABLE_REFLECTION', 'production' === env('APP_ENV')),
],
],

Expand Down Expand Up @@ -436,7 +436,7 @@
| Enable/disable logging and configure log channels.
|
*/
'enabled' => env('MODULITE_LOGGING_ENABLED', app()->hasDebugModeEnabled()),
'enabled' => env('MODULITE_LOGGING_ENABLED', env('APP_DEBUG', false)),
'channel' => env('MODULITE_LOG_CHANNEL', 'stack'),
'level' => env('MODULITE_LOG_LEVEL', 'info'),

Expand All @@ -448,9 +448,9 @@
| Log performance metrics for optimization and monitoring.
|
*/
'log_discovery_time' => app()->hasDebugModeEnabled(),
'log_cache_hits' => app()->hasDebugModeEnabled(),
'log_scan_stats' => app()->hasDebugModeEnabled(),
'log_discovery_time' => env('MODULITE_LOG_DISCOVERY_TIME', env('APP_DEBUG', false)),
'log_cache_hits' => env('MODULITE_LOG_CACHE_HITS', env('APP_DEBUG', false)),
'log_scan_stats' => env('MODULITE_LOG_SCAN_STATS', env('APP_DEBUG', false)),
],

/*
Expand All @@ -470,9 +470,9 @@
| Control whether errors are thrown or handled silently.
|
*/
'fail_silently' => !app()->hasDebugModeEnabled(),
'log_errors' => true,
'max_errors_per_scan' => 10,
'fail_silently' => env('MODULITE_FAIL_SILENTLY', !env('APP_DEBUG', false)),
'log_errors' => env('MODULITE_LOG_ERRORS', true),
'max_errors_per_scan' => env('MODULITE_MAX_ERRORS_PER_SCAN', 10),

/*
|--------------------------------------------------------------------------
Expand All @@ -482,7 +482,7 @@
| Configure handling of validation errors during discovery.
|
*/
'throw_on_invalid_class' => app()->hasDebugModeEnabled(),
'throw_on_missing_requirements' => app()->hasDebugModeEnabled(),
'throw_on_invalid_class' => env('MODULITE_THROW_ON_INVALID_CLASS', env('APP_DEBUG', false)),
'throw_on_missing_requirements' => env('MODULITE_THROW_ON_MISSING_REQUIREMENTS', env('APP_DEBUG', false)),
],
];
7 changes: 1 addition & 6 deletions src/Console/Commands/ModuliteClearCacheCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

/**
* Command to clear all Modulite caches.
* This command is automatically called by Laravel's `optimize:clear` command.
*/
class ModuliteClearCacheCommand extends Command
{
Expand All @@ -20,12 +21,6 @@ class ModuliteClearCacheCommand extends Command

public function handle(CacheManagerInterface $cacheManager): int
{
if (!$this->option('force') && !$this->confirm('Are you sure you want to clear all Modulite caches?'))
{
$this->info('Cache clear cancelled.');
return self::SUCCESS;
}

$this->info('Clearing Modulite caches...');

try
Expand Down
80 changes: 75 additions & 5 deletions src/Console/Commands/ModuliteOptimizeCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use PanicDevs\Modulite\Contracts\CacheManagerInterface;
use PanicDevs\Modulite\Contracts\PanelScannerInterface;
use PanicDevs\Modulite\Contracts\ComponentScannerInterface;
use PanicDevs\Modulite\Contracts\ModuleResolverInterface;
use Throwable;

/**
Expand All @@ -28,7 +29,8 @@ class ModuliteOptimizeCommand extends Command
* @var string
*/
protected $signature = 'modulite:cache
{--force : Force cache regeneration even if cache exists}';
{--force : Force cache regeneration even if cache exists}
{--enable-cache : Temporarily enable caching even in debug mode}';

/**
* The console command description.
Expand All @@ -43,12 +45,21 @@ class ModuliteOptimizeCommand extends Command
public function handle(
CacheManagerInterface $cacheManager,
PanelScannerInterface $panelScanner,
ComponentScannerInterface $componentScanner
ComponentScannerInterface $componentScanner,
ModuleResolverInterface $moduleResolver
): int {
$this->info('Optimizing Modulite caches...');

try
{
// Temporarily enable cache if requested
if ($this->option('enable-cache'))
{
$this->info('Temporarily enabling cache for this operation...');
// Enable cache in the manager for this command only
$this->enableCacheForCommand($cacheManager);
}

// Clear existing caches if force flag is used
if ($this->option('force'))
{
Expand All @@ -60,6 +71,11 @@ public function handle(
$this->line('• Warming panel discovery cache...');
$panels = $panelScanner->discoverPanels();
$panelCount = count($panels);

// Store panels in cache using the same key as the service provider
$panelCacheKey = $this->generatePanelCacheKey($moduleResolver);
$cacheManager->put($panelCacheKey, $panels);

$this->line(" <fg=green>✓</> Found {$panelCount} panel providers");

// Warm component discovery cache for all discovered panels
Expand All @@ -69,9 +85,14 @@ public function handle(
foreach ($panels as $panelClass)
{
// Extract panel ID from class name (e.g., UserPanelProvider -> user)
$panelId = $this->extractPanelIdFromClass($panelClass);
$components = $componentScanner->discoverComponents($panelId);
$totalComponents += array_sum(array_map('count', $components));
$panelId = $this->extractPanelIdFromClass($panelClass);

// Force cache warming by calling discoverComponents which stores in cache
$components = $componentScanner->discoverComponents($panelId);
$componentCount = array_sum(array_map('count', $components));
$totalComponents += $componentCount;

$this->line(" - {$panelId}: {$componentCount} components");
}

$this->line(" <fg=green>✓</> Found {$totalComponents} components");
Expand Down Expand Up @@ -119,6 +140,55 @@ protected function extractPanelIdFromClass(string $className): string
return mb_strtolower($panelId);
}

/**
* Temporarily enable cache for this command operation.
*/
protected function enableCacheForCommand(CacheManagerInterface $cacheManager): void
{
// Check if cache manager supports runtime enable (UnifiedCacheManager does)
if (method_exists($cacheManager, 'enableTemporarily'))
{
$cacheManager->enableTemporarily();
} else
{
$this->warn('Cache manager does not support temporary enabling. Use MODULITE_CACHE_ENABLED=true instead.');
}
}

/**
* Generate cache key for panels (same logic as ModuliteServiceProvider).
*/
protected function generatePanelCacheKey(ModuleResolverInterface $moduleResolver): string
{
// Use the module resolver to get enabled modules
$enabledModules = $moduleResolver->getEnabledModules();

// Include module names - use simple array for consistent hashing
$moduleData = $enabledModules->sort()->values()->toArray();

// Include the module resolver approach in cache key
$approach = config('modulite.modules.approach', 'nwidart');

// Include configuration in cache key
$configHash = md5(serialize([
'panels' => config('modulite.panels', []),
'components' => config('modulite.components', []),
'modules' => config('modulite.modules', [])
]));

// Include environment
$environment = config('app.env', 'production');

$keyData = [
'modules' => $moduleData,
'approach' => $approach,
'config' => $configHash,
'environment' => $environment,
];

return 'panels:'.md5(serialize($keyData));
}

/**
* Display cache file information.
*/
Expand Down
56 changes: 27 additions & 29 deletions src/Providers/ModuliteServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,13 @@ public function register(): void
{
$this->registerConfiguration();
$this->registerCoreServices();
$this->app->beforeResolving(self::FILAMENT_NAMESPACE, fn() => $this->registerPanelDiscovery());
if ('nwidart' === config('modulite.modules.approach'))
{
$this->app->beforeResolving('filament', fn() => $this->registerPanelDiscovery());
} else
{
$this->registerPanelDiscovery();
}
$this->registerCacheInvalidationListeners();
}

Expand All @@ -96,6 +102,7 @@ public function boot(): void
$this->publishConfiguration();
$this->validateConfiguration();
$this->setupDevelopmentHelpers();
$this->registerCommands();
}

/**
Expand All @@ -119,7 +126,7 @@ protected function registerCoreServices(): void
});

// Register ModuleResolver based on configuration
$this->app->singleton(ModuleResolverInterface::class, fn (Application $app) => $this->createModuleResolver($app));
$this->app->singleton(ModuleResolverInterface::class, fn(Application $app) => $this->createModuleResolver($app));

// Register PanelScannerService with dependencies
$this->app->singleton(PanelScannerInterface::class, function (Application $app)
Expand Down Expand Up @@ -356,7 +363,7 @@ protected function publishConfiguration(): void
*/
protected function validateConfiguration(): void
{
if (!$this->app->hasDebugModeEnabled())
if (!config('app.debug', false))
{
return;
}
Expand Down Expand Up @@ -393,43 +400,34 @@ protected function validateConfiguration(): void
}
}

// Validate cache configuration
if ($config['cache']['enabled'] ?? false)
{
$driver = $config['cache']['driver'] ?? 'file';
if (!in_array($driver, ['file', 'redis', 'memcached', 'array', 'database'], true))
{
Log::channel(config('modulite.logging.channel', 'default'))
->warning("Modulite cache driver '{$driver}' may not be supported");
}
}
// Cache configuration validation removed as we use file-based cache only
}

/**
* Setup development mode helpers.
*/
protected function setupDevelopmentHelpers(): void
{
// Register artisan commands if available
if ($this->app->runningInConsole())
{
$this->registerConsoleCommands();
}
// Development helpers are now registered in registerCommands()
}

/**
* Register console commands for development.
* Register console commands.
*/
protected function registerConsoleCommands(): void
protected function registerCommands(): void
{
$this->commands([
ModuliteClearCacheCommand::class,
ModuliteStatusCommand::class,
ModuliteOptimizeCommand::class,
]);

// Note: Laravel optimize integration removed to prevent circular dependencies
// Users should manually run: php artisan modulite:cache
// Only register commands in console environment
if ($this->app->runningInConsole())
{
$this->commands([
ModuliteClearCacheCommand::class,
ModuliteStatusCommand::class,
ModuliteOptimizeCommand::class,
]);

// Register optimization commands with Laravel's optimize system
$this->optimizes('modulite:cache', clear: 'modulite:clear');
}
}

/**
Expand All @@ -444,7 +442,7 @@ protected function shouldPerformDiscovery(): bool
}

// In production, always perform discovery to ensure routes are cached properly
if ($this->app->isProduction())
if ('production' === config('app.env'))
{
return true;
}
Expand Down
4 changes: 2 additions & 2 deletions src/Services/ComponentDiscoveryService.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,13 @@ public function discoverComponents(string $panelName): array
}

// Only measure time in development
$startTime = app()->hasDebugModeEnabled() ? microtime(true) : 0;
$startTime = config('app.debug', false) ? microtime(true) : 0;

// Discover components efficiently
$components = $this->performOptimizedDiscovery($panelName);

// Update stats only in development
if (app()->hasDebugModeEnabled())
if (config('app.debug', false))
{
$this->stats['scan_time'] = microtime(true) - $startTime;
}
Expand Down
Loading