> */ public function themes(): array { return $this->loader->all()->map(function (Theme $theme): array { return [ 'key' => $theme->key(), 'name' => $theme->name(), 'parent' => $theme->parent(), 'path' => $theme->path(), 'views' => count($this->viewFiles($theme)), 'entry_view' => $theme->entryView(), ]; })->values()->all(); } /** @return array> */ public function duplicateViews(): array { $ownership = []; foreach ($this->loader->all() as $theme) { foreach ($this->viewFiles($theme) as $relative => $absolute) { $ownership[$relative][] = [ 'theme' => $theme->key(), 'path' => $absolute, ]; } } return array_filter($ownership, fn (array $owners): bool => count($owners) > 1); } /** @return array */ public function issues(): array { $issues = []; $themes = $this->loader->all(); foreach ($themes as $theme) { $parent = $theme->parent(); if ($parent !== null && ! $themes->has($parent)) { $issues[] = [ 'severity' => 'error', 'theme' => $theme->key(), 'message' => "Parent theme [{$parent}] is not installed.", ]; } if ($this->hasInheritanceCycle($theme, $themes->all())) { $issues[] = [ 'severity' => 'error', 'theme' => $theme->key(), 'message' => 'Theme inheritance contains a cycle.', ]; } $entry = $theme->path().'/views/'.str_replace('.', '/', $theme->entryView()).'.blade.php'; if (! $this->files->exists($entry) && $parent === null) { $issues[] = [ 'severity' => 'warning', 'theme' => $theme->key(), 'message' => "Entry view [{$theme->entryView()}] does not exist in this theme.", ]; } } foreach ($this->duplicateViews() as $view => $owners) { $issues[] = [ 'severity' => 'error', 'theme' => implode(', ', array_column($owners, 'theme')), 'message' => "View [{$view}] has multiple owners.", ]; } $default = (string) config('stylist.default_theme'); if ($default !== '' && ! $themes->has($default)) { $issues[] = [ 'severity' => 'warning', 'theme' => $default, 'message' => 'Configured default theme is not installed.', ]; } return $issues; } /** @return array */ private function viewFiles(Theme $theme): array { $root = $theme->path().'/views'; if (! $this->files->isDirectory($root)) { return []; } $views = []; foreach ($this->files->allFiles($root) as $file) { if (! str_ends_with($file->getFilename(), '.blade.php')) { continue; } $absolute = str_replace('\\', '/', $file->getPathname()); $relative = ltrim(str_replace(str_replace('\\', '/', $root), '', $absolute), '/'); $views[$relative] = $absolute; } ksort($views); return $views; } /** @param array $themes */ private function hasInheritanceCycle(Theme $theme, array $themes): bool { $seen = []; $key = $theme->key(); while (isset($themes[$key])) { if (isset($seen[$key])) { return true; } $seen[$key] = true; $key = $themes[$key]->parent() ?? ''; if ($key === '') { return false; } } return false; } }