diff --git a/config/livewire-material.php b/config/livewire-material.php index 2b258fbe..25f52200 100644 --- a/config/livewire-material.php +++ b/config/livewire-material.php @@ -192,16 +192,13 @@ return [ | | Every component in every variant, rendered in the application's own | scheme. Off unless the application runs locally. 'vite' names the - | application's own entry points: its JavaScript (Livewire, Alpine) and, - | for an application that still builds one of its own, a CSS entry. The - | showcase page draws its CSS only from its own bundle - | (ShowcaseAssetController serves Stylesheets::bundle() of all.css, - | showcase.css and the scheme) — nothing on it needs the application's - | build, Tailwind included — so the showcase's layout keeps only the - | entries here that are not a stylesheet before passing the rest to - | @vite(), and links its own bundle after them. The error pages have no - | bundle of their own to fall back on, so ErrorPage::assets() still - | passes this whole list, CSS included, to @vite(). + | application's own entry points: its JavaScript (Livewire, Alpine) and + | the CSS entry that imports this package's stylesheets. The showcase's + | pages pass @vite() only the entries that are not a stylesheet: their + | CSS is a bundle of their own (all.css, showcase.css and the scheme, + | served by ShowcaseAssetController), so they need nothing from the + | application's build. The error pages have no such bundle, so + | ErrorPage::assets() passes this whole list, CSS included, to @vite(). | */ diff --git a/resources/views/showcase/layout.blade.php b/resources/views/showcase/layout.blade.php index d81c2da2..bb951fbe 100644 --- a/resources/views/showcase/layout.blade.php +++ b/resources/views/showcase/layout.blade.php @@ -1,5 +1,6 @@ {{-- The showcase's frame, and the package's own scaffold at work: the rail groups every section, - collapses and expands from `lg`, and opens as a modal from the app bar's menu button on a phone. + collapses and expands from `expanded` (840px), and opens as a modal from the app bar's menu + button on a phone. Pages move with wire:navigate, so the rail keeps its place and the theme stays. --}} @php @@ -38,16 +39,14 @@ - {{-- The application's own entries, JavaScript only (Livewire, Alpine): no showcase view - needs anything Tailwind builds any more, and linking its CSS here would open the - document's first @layer statement (Workbench package.css's header explains the trick) - ahead of the showcase's own bundle below, re-anchoring `material` before Tailwind's - own layers instead of after. config('livewire-material.showcase.vite')'s own comment - says why ErrorPage::assets() still passes the whole array. --}} - @vite(array_filter(config('livewire-material.showcase.vite'), fn (string $entry): bool => ! str_ends_with($entry, '.css'))) + {{-- The application's own entries without its stylesheets (ShowcaseAssetController::scripts() + says why): the CSS of every showcase page is the bundle linked below, and nothing else. --}} + @if (($scripts = \NoNameWeb\LivewireMaterial\Http\Controllers\ShowcaseAssetController::scripts()) !== []) + @vite($scripts) + @endif @livewireStyles - {{-- The showcase's own chrome: does not depend on the build above. --}} + {{-- all.css, showcase.css and the application's scheme, without the application's build. --}} diff --git a/resources/views/showcase/shell.blade.php b/resources/views/showcase/shell.blade.php index 4c98682b..e53f1b2c 100644 --- a/resources/views/showcase/shell.blade.php +++ b/resources/views/showcase/shell.blade.php @@ -35,14 +35,14 @@ - {{-- Kept only for the Livewire/Alpine runtime here — this page has no Tailwind class of its - own. The next batches of step 38 leave it to the sections that still need it. It opens - the document's first @layer statement (Workbench package.css's header explains the - trick), which the link below must follow, not precede — see layout.blade.php's comment. --}} - @vite(config('livewire-material.showcase.vite')) + {{-- As layout.blade.php: the application's entries without its stylesheets, then the + showcase's own bundle as the page's only CSS. --}} + @if (($scripts = \NoNameWeb\LivewireMaterial\Http\Controllers\ShowcaseAssetController::scripts()) !== []) + @vite($scripts) + @endif @livewireStyles - {{-- The showcase's own chrome: does not depend on the build above. --}} + {{-- all.css, showcase.css and the application's scheme, without the application's build. --}} diff --git a/src/Http/Controllers/ShowcaseAssetController.php b/src/Http/Controllers/ShowcaseAssetController.php index 8dedf0c1..c9fd7559 100644 --- a/src/Http/Controllers/ShowcaseAssetController.php +++ b/src/Http/Controllers/ShowcaseAssetController.php @@ -104,6 +104,25 @@ class ShowcaseAssetController return route('livewire-material.stylesheet', ['hash' => self::hash(self::build())]); } + /** + * The application's Vite entries (`livewire-material.showcase.vite`) the showcase's own pages + * pass to `@vite()`: every one but a stylesheet — a path Laravel's Vite itself treats as CSS — + * since those pages draw their CSS from `url()`'s bundle alone. An application's CSS entry + * would load Tailwind for nothing, or the package's stylesheets a second time. Empty when the + * list names only stylesheets, and then the page skips `@vite()` altogether (it would read a + * build manifest for nothing). A JavaScript entry that imports a stylesheet itself still + * brings it: Vite links a chunk's own CSS. + * + * @return list + */ + public static function scripts(): array + { + return array_values(array_filter( + (array) config('livewire-material.showcase.vite', []), + fn (mixed $entry): bool => is_string($entry) && preg_match('/\.(css|less|sass|scss|styl|stylus|pcss|postcss)(\?[^\.]*)?$/', $entry) !== 1, + )); + } + protected static function build(): string { $files = [self::path('all.css'), self::path('showcase.css')]; diff --git a/tests/Feature/ShowcaseAssetsTest.php b/tests/Feature/ShowcaseAssetsTest.php index 1c1b4f6c..ec4dae63 100644 --- a/tests/Feature/ShowcaseAssetsTest.php +++ b/tests/Feature/ShowcaseAssetsTest.php @@ -1,5 +1,6 @@ refreshApplication(); } }); + +it('passes Vite the application entries but its stylesheets, on every kind of showcase page', function () { + $hot = sys_get_temp_dir().'/livewire-material-showcase-hot-'.Str::random(8); + file_put_contents($hot, 'http://vite.test'); + Vite::useHotFile($hot); + + config(['livewire-material.showcase.vite' => ['resources/css/app.css', 'resources/sass/extra.scss', 'resources/js/app.js']]); + + try { + expect(ShowcaseAssetController::scripts())->toBe(['resources/js/app.js']); + + foreach (['/material', '/material/progress', '/material/layout/feed', '/material/shell'] as $page) { + $this->get($page) + ->assertOk() + ->assertSee('http://vite.test/resources/js/app.js', false) + ->assertDontSee('http://vite.test/resources/css/app.css', false) + ->assertDontSee('http://vite.test/resources/sass/extra.scss', false) + ->assertSee(ShowcaseAssetController::url(), false); + } + + // A list of stylesheets alone leaves @vite() out, rather than reading a manifest for nothing. + config(['livewire-material.showcase.vite' => ['resources/css/app.css']]); + + expect(ShowcaseAssetController::scripts())->toBe([]); + + $this->get('/material/progress')->assertOk()->assertDontSee('http://vite.test', false); + } finally { + unlink($hot); + } +});