An over-engineering audit of the whole tree, applied in five reviewed batches. Behaviour stays the same except where UPGRADE.md says otherwise. PHP: the showcase and error-page stylesheets are prebuilt into resources/dist by bin/stylesheets.mjs, through Vite's own postcss-import (first occurrence kept, the order an application's build gives), instead of Stylesheets::bundle() inlining imports on every request; only the import walk DesignGuard needs stays. SchemeStylesheet::withProfiles() replaces three copies of the scheme-plus-profiles loop, material:scheme leaves spec and contrast checks to the node script that already made them, and the error page's scheme cache, the hashed view namespace, the translations path with no lang/ folder and DesignGuard's 1.x-name hints are gone. JS: the androidx shape port progress.js and both bin scripts each carried lives once in resources/js/shapes.js (the generated SVGs are unchanged); util.js holds ringIndex(), ms(), reopenGuard() and remember(), which were written out several times; listeners are released through AbortController; tooltip.js's hoverPopover() serves the rich tooltip too. CSS: every rule for an element inside the navigation rail queries `--md-navigation-rail-value` instead of repeating the seven collapsed conditions under five media branches; badge, alert, progress, slider and button read one non-inheriting colour-role table (components/color.css); the dialog chrome, the submenu's popover chrome, the chip's state layer and touch target, and the visually-hidden inputs use the shared rules they copied; foundation/tokens.css is folded into foundation.css. Views: Support\Field and Support\Link replace the error-key, bound-value and link-attribute blocks copied into the fields and link components; the timepicker period group, the menu filter and the showcase head are partials; the datepicker's steppers and entry fields are loops; component docblocks no longer restate SKILL.md. Tests and tooling: one dataset-driven ComponentStylesheetsTest replaces four per-group files, DesignGuardTest and the layout-component tests use datasets, browser tests share one ready() helper, CSS parsing lives in ComponentStylesheet alone. docs/audits and the finding IDs citing it are removed, as are pestphp/pest-plugin-laravel, the unused composer scripts and check:font; the lint job runs in the feature job, which now installs node packages so the prebuilt-stylesheet staleness test runs in CI. Feature suite 1177 passed, Chrome browser suite 299 passed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
111 lines
5.1 KiB
PHP
111 lines
5.1 KiB
PHP
<?php
|
|
|
|
namespace NoNameWeb\LivewireMaterial\Support;
|
|
|
|
use Illuminate\Contracts\Support\Htmlable;
|
|
use Illuminate\Foundation\Vite;
|
|
use Illuminate\Support\HtmlString;
|
|
use Throwable;
|
|
|
|
/**
|
|
* What the error layout needs from PHP, written so that nothing in it can fail the page: an
|
|
* error page is shown precisely when something else already went wrong.
|
|
*
|
|
* The page carries its own component rules whatever the application imports. An application's
|
|
* CSS entry imports the stylesheets of the components its own views render, and no view of its
|
|
* renders this layout — the framework does, from the package's error-view root — so nothing ever
|
|
* told an application to import `components/error-page.css`: `DesignGuard::missingStylesheets()`
|
|
* reads the application's views, not the package's, and `unusedStylesheets()` would call such an
|
|
* import unused. So an application imports nothing for its error pages: with a build the layout
|
|
* takes the foundation, tokens, scheme and font from the application's Vite tags (`assets()`) and
|
|
* inlines its own rules beside them (`layoutStyles()`); without one it inlines everything
|
|
* (`fallbackStyles()`).
|
|
*/
|
|
class ErrorPage
|
|
{
|
|
/**
|
|
* The application's Vite tags, or null when they cannot be made. They bring the foundation,
|
|
* the tokens, the scheme and the font; the layout's own rules come from `layoutStyles()`
|
|
* beside them, since the application's entry need not import them.
|
|
*
|
|
* A 500 during a deploy is the moment the manifest is missing, half-written or names files
|
|
* that are not there yet — a missing manifest throws a ViteException, a truncated one a
|
|
* PHP warning turned exception — so every failure means "no build", never a second error.
|
|
*/
|
|
public static function assets(): ?Htmlable
|
|
{
|
|
try {
|
|
$tags = app(Vite::class)(config('livewire-material.showcase.vite', []));
|
|
} catch (Throwable) {
|
|
return null;
|
|
}
|
|
|
|
return $tags instanceof Htmlable ? $tags : new HtmlString((string) $tags);
|
|
}
|
|
|
|
/**
|
|
* Where the Back button goes: the page the visitor came from, when there was one other than
|
|
* this page and the home page Home already offers. Null when the session behind it cannot
|
|
* be read.
|
|
*/
|
|
public static function backUrl(): ?string
|
|
{
|
|
try {
|
|
$previous = url()->previous();
|
|
$current = url()->current();
|
|
} catch (Throwable) {
|
|
return null;
|
|
}
|
|
|
|
$isHome = rtrim($previous, '/') === rtrim(url('/'), '/');
|
|
|
|
return $previous === $current || $isHome ? null : $previous;
|
|
}
|
|
|
|
/**
|
|
* The error layout's own rules for a page with its build: `components/error-page.css` and the
|
|
* stylesheets it imports, prebuilt into resources/dist/error-page.css (`npm run
|
|
* build:stylesheets`) and inlined beside the application's Vite tags, which bring everything
|
|
* else.
|
|
*
|
|
* Nothing is dropped from it, because nothing in it needs a build: the package's only
|
|
* `@font-face` is `tokens/font.css`'s, which only the foundation reaches and the application's
|
|
* build serves, and no stylesheet the layout imports writes a `url()` a request would have to
|
|
* resolve (tests/Feature/ErrorPagesTest.php checks both). An application whose entry also
|
|
* imports this layout (through `all.css`, say) gets the same rules twice, in the same layers and
|
|
* with the same declarations; the second copy decides nothing the first had not, so that is
|
|
* harmless.
|
|
*/
|
|
public static function layoutStyles(): HtmlString
|
|
{
|
|
return new HtmlString(self::dist('error-page.css'));
|
|
}
|
|
|
|
/**
|
|
* A stylesheet for a page without its build: the foundation and the error layout's own rules,
|
|
* prebuilt into resources/dist/error-page-fallback.css — the same inlining a Vite build does,
|
|
* so the fallback can never drift from `resources/css/components/error-page.css` the way a
|
|
* hand-written copy did, and without the `@font-face` (there is no build here to serve the font
|
|
* file; the brand typeface's stack in tokens/type.css falls back to the system fonts) — with
|
|
* the application's colours appended in `material-scheme.css`'s own shape
|
|
* (`SchemeStylesheet::withProfiles()`): the standard level, the medium and high contrast levels
|
|
* under `[data-contrast]`, and a block per colour profile under `[data-scheme]`, built from
|
|
* `Scheme::forStylesheet()` on every call. `<x-theme-script>` has already written
|
|
* `data-theme`, `data-contrast` and `data-scheme` onto `<html>` by the time this tag is parsed
|
|
* (it renders first), so the browser resolves the right block on its own — nothing here decides
|
|
* an active profile in PHP.
|
|
*/
|
|
public static function fallbackStyles(): HtmlString
|
|
{
|
|
return new HtmlString(self::dist('error-page-fallback.css').SchemeStylesheet::withProfiles(...Scheme::forStylesheet()));
|
|
}
|
|
|
|
/**
|
|
* A prebuilt stylesheet under resources/dist/.
|
|
*/
|
|
protected static function dist(string $file): string
|
|
{
|
|
return (string) file_get_contents(dirname(__DIR__, 2)."/resources/dist/{$file}");
|
|
}
|
|
}
|