fix(dev-toolbar): report development-appropriate budgets

Excludes the toolbar's own bundle from the JavaScript budget, raises the
development thresholds, and skips WRNexus UI and theme stylesheets when
measuring CSS coverage, so unminified development modules and framework
styles stop reading as application problems.

Pre-existing working-tree change, committed as-is rather than authored
here.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-18 19:17:01 +05:30
co-authored by Claude Opus 5
parent 5e114d867f
commit 5d7bd81601
2 changed files with 8 additions and 2 deletions
+2 -2
View File
@@ -43,8 +43,8 @@ export const DEV_TOOLBAR_RUNTIME = String.raw`(() => {
try{for(let i=0;i<localStorage.length;i++){const key=localStorage.key(i)||"";if(/(?:token|secret|password|session|credential|authorization)/i.test(key))found.push(issue("security/sensitive-local-storage","security","error","Sensitive value may be stored in localStorage","Storage key “"+key+"” looks authentication- or secret-related.",null,"Keep sessions and credentials in Secure, HttpOnly cookies."));}}catch{}
if(document.documentElement.scrollWidth>innerWidth+2)found.push(issue("responsive/document-overflow","responsive","error","Page has horizontal overflow","Document width exceeds the viewport.",null,"Inspect fixed widths, long text and overflowing media."));
q("body *").filter(visible).slice(0,2500).forEach(el=>{const r=el.getBoundingClientRect();if((r.right>innerWidth+8||r.left<-8)&&found.filter(x=>x.ruleId==="responsive/element-overflow").length<20)found.push(issue("responsive/element-overflow","responsive","warning","Element extends outside the viewport","Element bounds exceed the current viewport.",el,"Use fluid sizing, wrapping, max-width or an intentional scroll container."));});
const resources=performance.getEntriesByType("resource");const total=resources.reduce((s,e)=>s+(e.transferSize||0),0);const jsBytes=resources.filter(e=>/(?:\.m?js)(?:\?|$)/i.test(e.name)).reduce((sum,e)=>sum+(e.transferSize||0),0);if(resources.length>150)found.push(issue("performance/resource-count","performance","warning","Page loads many resources","Found "+resources.length+" resource requests.",null,"Remove duplicates and defer non-critical resources."));if(total>5000000)found.push(issue("performance/transfer-size","performance",total>10000000?"error":"warning","Page transfer size is large","Observed transfer size is about "+(total/1000000).toFixed(2)+" MB.",null,"Compress and optimize page resources."));if(jsBytes>150000)found.push(issue("performance/javascript-budget","javascript",jsBytes>300000?"error":"warning","JavaScript budget exceeded","JavaScript transfer is about "+(jsBytes/1000).toFixed(1)+" KB.",null,"Split routes and defer optional hydration."));const hydrationRoots=q("[data-wrn-client-module],[data-wrn-hydrate]");if(hydrationRoots.length>50)found.push(issue("performance/hydration-count","runtime","warning","Many components hydrate",hydrationRoots.length+" hydration boundaries were found.",null,"Use visible, idle or interaction hydration."));if(state.runtimeMetrics.longTasks.length)found.push(issue("performance/long-tasks","javascript","warning","Long main-thread tasks detected",state.runtimeMetrics.longTasks.length+" task(s) exceeded 50 ms.",null,"Split expensive work and reduce hydration.","high",{longestMs:Math.max(...state.runtimeMetrics.longTasks)}));
let checkedSelectors=0,unusedSelectors=0;for(const sheet of [...document.styleSheets]){let rules;try{rules=[...(sheet.cssRules||[])]}catch{continue}for(const rule of rules){if(checkedSelectors>=2000)break;const selector=rule.selectorText;if(!selector||selector.includes(":"))continue;checkedSelectors++;try{if(!document.querySelector(selector))unusedSelectors++}catch{}}}if(unusedSelectors)found.push(issue("css/unused-selectors","css","suggestion","Potentially unused CSS",unusedSelectors+" of "+checkedSelectors+" inspected selectors do not match this page.",null,"Review across routes before removing selectors.","medium",{checkedSelectors,unusedSelectors}));const memory=performance.memory;if(memory&&memory.jsHeapSizeLimit&&memory.usedJSHeapSize/memory.jsHeapSizeLimit>.8)found.push(issue("performance/memory-pressure","performance","warning","High JavaScript heap usage",Math.round(memory.usedJSHeapSize/1048576)+" MiB of "+Math.round(memory.jsHeapSizeLimit/1048576)+" MiB is in use.",null,"Inspect retained objects and repeated hydration."));
const resources=performance.getEntriesByType("resource");const total=resources.reduce((s,e)=>s+(e.transferSize||0),0);const jsResources=resources.filter(e=>/(?:\.m?js)(?:\?|$)/i.test(e.name)&&!/\/__wrnexus\/dev-toolbar\.js(?:\?|$)/.test(e.name));const jsBytes=jsResources.reduce((sum,e)=>sum+(e.transferSize||0),0);if(resources.length>150)found.push(issue("performance/resource-count","performance","warning","Page loads many resources","Found "+resources.length+" resource requests.",null,"Remove duplicates and defer non-critical resources."));if(total>5000000)found.push(issue("performance/transfer-size","performance",total>10000000?"error":"warning","Page transfer size is large","Observed transfer size is about "+(total/1000000).toFixed(2)+" MB.",null,"Compress and optimize page resources."));if(jsBytes>300000)found.push(issue("performance/javascript-budget","javascript",jsBytes>600000?"error":"warning","Development JavaScript is large","Application JavaScript transfer is about "+(jsBytes/1000).toFixed(1)+" KB in development.",null,"Check the production build report before splitting routes; development modules are unminified.","medium",{javascriptBytes:jsBytes,mode:"development"}));const hydrationRoots=q("[data-wrn-client-module],[data-wrn-hydrate]");if(hydrationRoots.length>50)found.push(issue("performance/hydration-count","runtime","warning","Many components hydrate",hydrationRoots.length+" hydration boundaries were found.",null,"Use visible, idle or interaction hydration."));if(state.runtimeMetrics.longTasks.length)found.push(issue("performance/long-tasks","javascript","warning","Long main-thread tasks detected",state.runtimeMetrics.longTasks.length+" task(s) exceeded 50 ms.",null,"Split expensive work and reduce hydration.","high",{longestMs:Math.max(...state.runtimeMetrics.longTasks)}));
let checkedSelectors=0,unusedSelectors=0;for(const sheet of [...document.styleSheets]){const href=sheet.href||"";if(/\/__wrnexus\/(?:ui|framework)\.css(?:\?|$)|\/__wrnexus\/theme\/[^/?]+\.css(?:\?|$)/.test(href))continue;let rules;try{rules=[...(sheet.cssRules||[])]}catch{continue}for(const rule of rules){if(checkedSelectors>=2000)break;const selector=rule.selectorText;if(!selector||selector.includes(":"))continue;checkedSelectors++;try{if(!document.querySelector(selector))unusedSelectors++}catch{}}}const unusedRatio=checkedSelectors?unusedSelectors/checkedSelectors:0;if(checkedSelectors>=20&&unusedRatio>=.8)found.push(issue("css/unused-selectors","css","suggestion","Low current-page CSS coverage",unusedSelectors+" of "+checkedSelectors+" inspected application selectors do not match this page.",null,"Review across routes before removing selectors. WRNexus UI and theme styles are excluded.","medium",{checkedSelectors,unusedSelectors,unusedRatio}));const memory=performance.memory;if(memory&&memory.jsHeapSizeLimit&&memory.usedJSHeapSize/memory.jsHeapSizeLimit>.8)found.push(issue("performance/memory-pressure","performance","warning","High JavaScript heap usage",Math.round(memory.usedJSHeapSize/1048576)+" MiB of "+Math.round(memory.jsHeapSizeLimit/1048576)+" MiB is in use.",null,"Inspect retained objects and repeated hydration."));
q("[data-wrn-client-module]").forEach(el=>found.push(issue("runtime/client-module","runtime","info","Client function module",el.getAttribute("data-wrn-client-module")||"Unknown module",el,"Loaded according to the component hydration strategy.","high",{hydration:el.getAttribute("data-wrn-hydrate"),runtime:el.getAttribute("data-wrn-runtime")})));
const storeContainer=window.__wrnexusStoreContainer;
if(storeContainer&&typeof storeContainer.inspect==="function"){
@@ -9,6 +9,12 @@ test("exports usable development assets", () => {
expect(DEV_TOOLBAR_RUNTIME).toContain('data-category="accessibility"');
expect(DEV_TOOLBAR_RUNTIME).toContain('issue("plugin/"+app.id');
expect(DEV_TOOLBAR_RUNTIME).toContain("apps.appendChild(button)");
expect(DEV_TOOLBAR_RUNTIME).toContain("Low current-page CSS coverage");
expect(DEV_TOOLBAR_RUNTIME).toContain("WRNexus UI and theme styles are excluded");
expect(DEV_TOOLBAR_RUNTIME).toContain("checkedSelectors>=20&&unusedRatio>=.8");
expect(DEV_TOOLBAR_RUNTIME).toContain("Development JavaScript is large");
expect(DEV_TOOLBAR_RUNTIME).toContain("dev-toolbar\\.js");
expect(DEV_TOOLBAR_RUNTIME).toContain("jsBytes>300000");
expect(DEV_TOOLBAR_CSS).toContain(".wrn-panel");
expect(DEV_TOOLBAR_CSS).toContain(".wrn-plugin-panel");
});