milestone 23: restore the focus ring on select options, check layer order, close preflight gaps
This commit is contained in:
@@ -4,6 +4,16 @@
|
||||
// selector says, so a single unlayered rule silently beats the StyleX atomic
|
||||
// rules it was written to sit under. That failure renders wrong and passes
|
||||
// every other gate: no test asserts computed style, and the bundler is happy.
|
||||
// It also checks the layer ORDER, which is the invariant that actually matters:
|
||||
// a later layer beats an earlier one, so `reset` has to be declared first.
|
||||
//
|
||||
// What it does not catch, so nobody reads more into a pass than is there: an
|
||||
// unlayered rule that sets only custom properties is allowed, because StyleX
|
||||
// emits its token `:root` block exactly that way and this cannot tell that
|
||||
// block from an override of it; a declaration value containing `@layer` or a
|
||||
// brace inside a string blinds the stripper; and with several stylesheets it
|
||||
// judges each alone, not their load order in the document.
|
||||
//
|
||||
// This check runs from web/ as part of `npm run build`.
|
||||
|
||||
import { readdirSync, readFileSync } from "node:fs";
|
||||
@@ -58,9 +68,36 @@ function stylesSomething(body) {
|
||||
.some((declaration) => declaration.length > 0 && !declaration.startsWith("--"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Layer names in the order their position is fixed, which is where each name is
|
||||
* first mentioned — a later block under an already-named layer does not move it.
|
||||
*/
|
||||
function layerOrder(css) {
|
||||
const seen = [];
|
||||
for (const [, names] of css.replace(/\/\*[\s\S]*?\*\//g, "").matchAll(/@layer\s+([^{;]+)[{;]/g)) {
|
||||
for (const name of names.split(",")) {
|
||||
const trimmed = name.trim();
|
||||
if (trimmed.length > 0 && !seen.includes(trimmed)) seen.push(trimmed);
|
||||
}
|
||||
}
|
||||
return seen;
|
||||
}
|
||||
|
||||
let failed = false;
|
||||
for (const sheet of sheets) {
|
||||
const leftover = outsideLayers(readFileSync(join(distDir, sheet), "utf8"));
|
||||
const css = readFileSync(join(distDir, sheet), "utf8");
|
||||
|
||||
// Order is the whole point: a later layer wins, so the reset has to be first.
|
||||
const order = layerOrder(css);
|
||||
if (order.length > 0 && order[0] !== "reset") {
|
||||
console.error(
|
||||
`assert-css-layers: ${sheet} declares layers in the order ${order.join(", ")} — ` +
|
||||
`'reset' must come first or it outranks the StyleX rules written against it.`,
|
||||
);
|
||||
failed = true;
|
||||
}
|
||||
|
||||
const leftover = outsideLayers(css);
|
||||
for (const [, selector, body] of leftover.matchAll(/([^{}]+)\{([^{}]*)\}/g)) {
|
||||
if (!stylesSomething(body)) continue;
|
||||
console.error(
|
||||
|
||||
Reference in New Issue
Block a user