partition(fn (string $span): bool => preg_match('/^\s*\.[\w-]+[^{]*\{[^}]*\}\s*$/', $span) === 1); $cssLine = $rules->implode(' '); $viewLine = $spans ->map(fn (string $span): ?string => match (true) { str_contains($span, '<') => $span, boostSpanIsClassList($span) => '
', default => null, }) ->filter() ->implode(' '); } $view[] = $viewLine; $css[] = $cssLine; } return ['view' => implode("\n", $view), 'css' => implode("\n", $css)]; } /** * Whether an inline span reads as a class list: every token in it, and there has to be at least * one, has to be an `md-*` class — the only vocabulary this check holds prose to. A single bare * word (`outline`, `link`, `hidden`), a property or prop name (`border-color`, * `placeholder-value`), a colour role (`outline-variant`), a CSS function (`var(…)`) and a shell * command (`php artisan …`) all fail that test on their own, with no list of names to keep here. */ function boostSpanIsClassList(string $span): bool { $tokens = preg_split('/\s+/', trim($span)); foreach ($tokens as $token) { // Every class the package declares is `md-*`, so that prefix is what tells a class list // from the prop values, slot names and config keys the prose lists the same way // (`top-start`, `rail-header`, `livewire-material.profiles`). A class of an application's // own that an example names is nobody's to declare here. if (preg_match('/^(?:[a-z][\w-]*:)*md-[a-z\d-]+$/', $token) !== 1) { return false; } } return true; } it('teaches only classes and variants the stylesheets define', function () { $directory = sys_get_temp_dir().'/livewire-material-boost-'.bin2hex(random_bytes(4)); $files = []; foreach (BOOST_TEXTS as $index => $path) { $code = boostTextAsCode(File::get(__DIR__.'/../../'.$path)); File::ensureDirectoryExists($directory); File::put($files["{$directory}/text-{$index}.blade.php"] = "{$directory}/text-{$index}.blade.php", $code['view']); File::put($files["{$directory}/text-{$index}.css"] = "{$directory}/text-{$index}.css", $code['css']); } try { $violations = DesignGuard::scan(realpath($directory))->violations(); } finally { File::deleteDirectory($directory); } $named = array_map(function (string $violation) use ($directory): string { return (string) preg_replace_callback( '#^'.preg_quote(realpath(dirname($directory)) ?: dirname($directory), '#').'/[^/]+/text-(\d+)\.(?:blade\.php|css)#', fn (array $match): string => BOOST_TEXTS[(int) $match[1]], $violation, ); }, $violations); expect($named)->toBe([]); });