feat(csr): diagnose missing rendered theme tokens
This commit is contained in:
@@ -221,6 +221,36 @@ export const REACTIVE_RUNTIME = String.raw`
|
||||
{ binding: statement, functionName: match[1] },
|
||||
);
|
||||
}
|
||||
|
||||
function warnMissingThemeTokens() {
|
||||
var referenced = Object.create(null);
|
||||
function collect(rules) {
|
||||
Array.prototype.forEach.call(rules || [], function (rule) {
|
||||
var cssText = rule.cssText || "";
|
||||
var match;
|
||||
var pattern = /var\(\s*(--wire-[A-Za-z0-9_-]+)/g;
|
||||
while ((match = pattern.exec(cssText))) referenced[match[1]] = true;
|
||||
try {
|
||||
if (rule.cssRules) collect(rule.cssRules);
|
||||
} catch (_) {
|
||||
// Cross-origin and disabled stylesheets can deny CSSOM access.
|
||||
}
|
||||
});
|
||||
}
|
||||
Array.prototype.forEach.call(document.styleSheets || [], function (sheet) {
|
||||
try { collect(sheet.cssRules); } catch (_) {}
|
||||
});
|
||||
var rendered = window.getComputedStyle(document.documentElement);
|
||||
Object.keys(referenced).forEach(function (token) {
|
||||
if (rendered.getPropertyValue(token).trim()) return;
|
||||
warnOnce(
|
||||
"WRN-DEV-THEME-TOKEN-MISSING",
|
||||
"Theme token '" + token + "' is referenced by rendered CSS but is not defined.",
|
||||
document.documentElement,
|
||||
{ token: token },
|
||||
);
|
||||
});
|
||||
}
|
||||
/*__WRNEXUS_DEV_END__*/
|
||||
|
||||
function scheduleUpdateHook(element, callback) {
|
||||
@@ -3438,6 +3468,9 @@ export const REACTIVE_RUNTIME = String.raw`
|
||||
hydratePreferenceControllers(host);
|
||||
hydrateSelectControllers(host);
|
||||
hydratePinInputControllers(host);
|
||||
/*__WRNEXUS_DEV_START__*/
|
||||
warnMissingThemeTokens();
|
||||
/*__WRNEXUS_DEV_END__*/
|
||||
}
|
||||
|
||||
var navbarOutsideClickBound = false;
|
||||
|
||||
@@ -724,6 +724,32 @@ test("development runtime warns when a component binding names a missing functio
|
||||
expect(String(warnings[0]?.[0])).toContain("missingSave");
|
||||
});
|
||||
|
||||
test("development runtime warns for referenced theme tokens absent from rendered CSS", () => {
|
||||
const win = new Window() as unknown as Window & Record<string, unknown>;
|
||||
win.document.head.innerHTML =
|
||||
`<style>:root { --wire-present-test: red; } .probe { ` +
|
||||
`color: var(--wire-present-test); background: var(--wire-missing-test); }</style>`;
|
||||
win.document.body.innerHTML = `<div class="probe" data-scope=""></div>`;
|
||||
(globalThis as Record<string, unknown>).window = win;
|
||||
(globalThis as Record<string, unknown>).document = win.document;
|
||||
(globalThis as Record<string, unknown>).location = win.location;
|
||||
(globalThis as Record<string, unknown>).NodeFilter = (win as unknown as { NodeFilter: unknown }).NodeFilter;
|
||||
(globalThis as Record<string, unknown>).MutationObserver = (win as unknown as { MutationObserver: unknown }).MutationObserver;
|
||||
(globalThis as Record<string, unknown>).CustomEvent = (win as unknown as { CustomEvent: unknown }).CustomEvent;
|
||||
const warnings: unknown[][] = [];
|
||||
const originalWarn = console.warn;
|
||||
console.warn = (...args: unknown[]) => warnings.push(args);
|
||||
try {
|
||||
(0, eval)(getReactiveRuntime(true));
|
||||
(win as unknown as { __wrnexusHydrateScopes?: (root: unknown) => void }).__wrnexusHydrateScopes?.(win.document);
|
||||
} finally {
|
||||
console.warn = originalWarn;
|
||||
}
|
||||
expect(warnings).toHaveLength(1);
|
||||
expect(String(warnings[0]?.[0])).toContain("WRN-DEV-THEME-TOKEN-MISSING");
|
||||
expect(String(warnings[0]?.[0])).toContain("--wire-missing-test");
|
||||
});
|
||||
|
||||
test("production runtime strips development diagnostics", () => {
|
||||
const production = getReactiveRuntime();
|
||||
expect(production).not.toContain("WRN-DEV-");
|
||||
|
||||
Reference in New Issue
Block a user