fix(boot): a stylesheet lost on the way is asked for again
A lost stylesheet left the page unstyled until a reload, and every e2e step that finds elements by testid still passed on it. Only axe noticed: the one-off target-size failure on eight scattered controls. Losing the main stylesheet on purpose gives those same eight.
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
// A stylesheet can be lost on the way (a poor line, a loaded machine), and a browser never asks again
|
||||
// for one by itself: the page then stays unstyled until a reload, every control a bare browser button.
|
||||
// A module script waits for the document's stylesheets (measured: a sheet held 3 s held the module
|
||||
// too), so by the time this runs each link has either loaded or failed, and no event is left to wait
|
||||
// for. A lost one is asked for again in its own place, so the cascade's order stays as it was built.
|
||||
//
|
||||
// All of these sheets are this site's own: reading the rules of a loaded one is allowed, so a throw
|
||||
// says it failed (measured: Chromium, WebKit). Three tries, then the page stays as it is. A reload here
|
||||
// could loop, and the unstyled page still works.
|
||||
import { log } from "./log";
|
||||
|
||||
const RETRY_AFTER_MS = [0, 1000, 3000];
|
||||
|
||||
function holdsRules(link: HTMLLinkElement): boolean {
|
||||
try {
|
||||
return (link.sheet?.cssRules.length ?? 0) > 0;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function askAgain(link: HTMLLinkElement, attempt: number): void {
|
||||
const wait = RETRY_AFTER_MS[attempt];
|
||||
if (wait === undefined) {
|
||||
log.error("app.stylesheet_gave_up", "a stylesheet could not be loaded", { href: link.href });
|
||||
return;
|
||||
}
|
||||
window.setTimeout(() => {
|
||||
const next = link.cloneNode(false) as HTMLLinkElement;
|
||||
next.addEventListener("load", () =>
|
||||
log.info("app.stylesheet_recovered", "a lost stylesheet loaded on a new request", {
|
||||
href: next.href,
|
||||
attempt: attempt + 1,
|
||||
}),
|
||||
);
|
||||
next.addEventListener("error", () => askAgain(next, attempt + 1));
|
||||
link.replaceWith(next);
|
||||
}, wait);
|
||||
}
|
||||
|
||||
/** Run once, from a page's entry module. */
|
||||
export function retryLostStylesheets(): void {
|
||||
for (const link of document.querySelectorAll<HTMLLinkElement>('link[rel="stylesheet"]')) {
|
||||
if (holdsRules(link)) continue;
|
||||
log.warn("app.stylesheet_lost", "a stylesheet was lost on the way; asking again", {
|
||||
href: link.href,
|
||||
});
|
||||
askAgain(link, 0);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user