@react-native/debugger-frontend
Version:
Debugger frontend for React Native based on Chrome DevTools
2 lines (1 loc) • 7.7 kB
JavaScript
import*as t from"../../core/common/common.js";import*as e from"../../core/host/host.js";import*as i from"../../core/i18n/i18n.js";import*as n from"../../core/platform/platform.js";import*as a from"../../core/sdk/sdk.js";import*as s from"../emulation/emulation.js";import*as o from"./web-vitals-injected/spec/spec.js";const r={lcpEmulationWarning:"Simulating a new device after the page loads can affect LCP. Reload the page after simulating a new device for accurate LCP data.",lcpVisibilityWarning:"LCP value may be inflated because the page started loading in the background."},l=i.i18n.registerUIStrings("models/live-metrics/LiveMetrics.ts",r),c=i.i18n.getLocalizedString.bind(void 0,l),d="DevTools Performance Metrics";let u;class h{static#t;static async get(){if(!this.#t){const t=new URL("./web-vitals-injected/web-vitals-injected.generated.js",import.meta.url),e=await fetch(t);this.#t=await e.text()}return this.#t}}class m extends t.ObjectWrapper.ObjectWrapper{#e=!1;#i;#n;#a;#s;#o;#r;#l=new Map;#c=new Map;#d=[];#u;#h=new t.Mutex.Mutex;#m=s.DeviceModeModel.DeviceModeModel.tryInstance();constructor(){super(),a.TargetManager.TargetManager.instance().observeTargets(this)}static instance(t={forceNew:!1}){const{forceNew:e}=t;return u&&!e||(u=new m),u}get lcpValue(){return this.#s}get clsValue(){return this.#o}get inpValue(){return this.#r}get interactions(){return this.#l}get layoutShifts(){return this.#d}async logInteractionScripts(t){if(!this.#i)return!1;const e=this.#a;if(!e)return!1;const i=[];for(const e of t.longAnimationFrameTimings)for(const n of e.scripts){const e=n.startTime+n.duration;if(e<t.startTime)continue;const a=Math.round(e-Math.max(t.startTime,n.startTime));i.push({"Blocking duration":a,"Invoker type":n.invokerType||null,Invoker:n.invoker||null,Function:n.sourceFunctionName||null,Source:n.sourceURL||null,"Char position":n.sourceCharPosition||null})}try{const n=o.LOAF_LIMIT*o.SCRIPTS_PER_LOAF_LIMIT,a=i.length===n?` (limited to ${n})`:"",s=t.longAnimationFrameTimings.length===o.LOAF_LIMIT?` (limited to last ${o.LOAF_LIMIT})`:"";await this.#i.runtimeAgent().invoke_evaluate({expression:`\n console.group('[DevTools] Long animation frames for ${t.duration}ms ${t.interactionType} interaction');\n console.log('Scripts${a}:');\n console.table(${JSON.stringify(i)});\n console.log('Intersecting long animation frame events${s}:', ${JSON.stringify(t.longAnimationFrameTimings)});\n console.groupEnd();\n `,contextId:e})}catch{return!1}return!0}#p(){this.#u=Date.now()}async#g(e,i){if(!this.#i)return null;const n=this.#i.model(a.RuntimeModel.RuntimeModel);if(!n)return null;const s=this.#i.model(a.DOMModel.DOMModel);if(!s)return null;const{result:o}=await this.#i.runtimeAgent().invoke_evaluate({expression:`window.getNodeForIndex(${e})`,contextId:i});if(!o)return null;let r;try{r=n.createRemoteObject(o);const e=await s.pushObjectAsNodeToFrontend(r);if(!e)return null;return{node:e,link:await t.Linkifier.Linkifier.linkify(e)}}catch{return null}finally{r?.release()}}#f(){this.dispatchEventToListeners("status",{lcp:this.#s,cls:this.#o,inp:this.#r,interactions:this.#l,layoutShifts:this.#d})}setStatusForTesting(t){this.#s=t.lcp,this.#o=t.cls,this.#r=t.inp,this.#l=t.interactions,this.#d=t.layoutShifts,this.#f()}async#v(e){const i=e.data,n=[this.#s?.nodeRef,...this.#l.values().map((t=>t.nodeRef)),...this.#d.flatMap((t=>t.affectedNodeRefs))].filter((t=>!!t)),a=new Set(n.map((t=>t.node.backendNodeId()))),s=await i.pushNodesByBackendIdsToFrontend(a);if(!s)return;const o=n.map((async e=>{const i=s.get(e.node.backendNodeId());i&&(e.node=i,e.link=await t.Linkifier.Linkifier.linkify(i))}));await Promise.all(o),this.#f()}async#I(t,e){switch(t.name){case"LCP":{const i=[],n={value:t.value,phases:t.phases,warnings:i};if(void 0!==t.nodeIndex){const i=await this.#g(t.nodeIndex,e);i&&(n.nodeRef=i)}this.#u&&Date.now()-this.#u<500&&i.push(c(r.lcpEmulationWarning)),t.startedHidden&&i.push(c(r.lcpVisibilityWarning)),this.#s=n;break}case"CLS":{const e={value:t.value,clusterShiftIds:t.clusterShiftIds};this.#o=e;break}case"INP":{const e={value:t.value,phases:t.phases,interactionId:`interaction-${t.entryGroupId}-${t.startTime}`};this.#r=e;break}case"InteractionEntry":{const i=n.MapUtilities.getWithDefault(this.#c,t.entryGroupId,(()=>[]));let a=i.find((e=>Math.abs(e.nextPaintTime-t.nextPaintTime)<8));if(a||(a={interactionId:`interaction-${t.entryGroupId}-${t.startTime}`,interactionType:t.interactionType,duration:t.duration,eventNames:[],phases:t.phases,startTime:t.startTime,nextPaintTime:t.nextPaintTime,longAnimationFrameTimings:t.longAnimationFrameEntries},i.push(a),this.#l.set(a.interactionId,a)),a.eventNames.includes(t.eventName)||a.eventNames.push(t.eventName),void 0!==t.nodeIndex){const i=await this.#g(t.nodeIndex,e);i&&(a.nodeRef=i)}break}case"LayoutShift":{const i=t.affectedNodeIndices.map((t=>this.#g(t,e))),n=(await Promise.all(i)).filter((t=>!!t)),a={score:t.score,uniqueLayoutShiftId:t.uniqueLayoutShiftId,affectedNodeRefs:n};this.#d.push(a);break}case"reset":this.#s=void 0,this.#o=void 0,this.#r=void 0,this.#l.clear(),this.#d=[]}this.#f()}async#M(t){if(!this.#i)return null;const e=this.#i.model(a.RuntimeModel.RuntimeModel);if(!e)return null;const i=e.executionContext(t);if(!i)return null;const n=i.frameId;if(!n)return null;const s=a.FrameManager.FrameManager.instance();return await s.getOrWaitForFrame(n)}async#y(t){const{data:e}=t;e.name===o.EVENT_BINDING_NAME&&await this.#h.run((async()=>{const t=JSON.parse(e.payload);if(this.#a!==e.executionContextId){if("reset"!==t.name)return;const i=await this.#M(e.executionContextId);if(!i?.isPrimaryFrame())return;this.#a=e.executionContextId}await this.#I(t,e.executionContextId)}))}async#T(){const t=this.#i;if(!t)return;const e=t.model(a.RuntimeModel.RuntimeModel);if(!e)return;const i=e.executionContexts().filter((t=>t.name===d&&!t.isDefault)).map((e=>t.runtimeAgent().invoke_evaluate({expression:`window?.${o.INTERNAL_KILL_SWITCH}?.()`,contextId:e.id})));await Promise.all(i)}clearInteractions(){this.#l.clear(),this.#f()}clearLayoutShifts(){this.#d=[],this.#f()}async targetAdded(t){t===a.TargetManager.TargetManager.instance().primaryPageTarget()&&(this.#i=t,await this.enable())}async targetRemoved(t){if(t!==this.#i)return;await this.disable(),this.#i=void 0;const e=a.TargetManager.TargetManager.instance().primaryPageTarget();e&&(this.#i=e,await this.enable())}async enable(){if(e.InspectorFrontendHost.isUnderTest())return;if(!this.#i||this.#e)return;if(this.#i.type()!==a.Target.Type.FRAME)return;const t=this.#i.model(a.DOMModel.DOMModel);if(!t)return;t.addEventListener(a.DOMModel.Events.DocumentUpdated,this.#v,this);const i=this.#i.model(a.RuntimeModel.RuntimeModel);if(!i)return;i.addEventListener(a.RuntimeModel.Events.BindingCalled,this.#y,this),await i.addBinding({name:o.EVENT_BINDING_NAME,executionContextName:d}),await this.#T();const n=await h.get(),{identifier:s}=await this.#i.pageAgent().invoke_addScriptToEvaluateOnNewDocument({source:n,worldName:d,runImmediately:!0});this.#n=s,this.#m?.addEventListener("Updated",this.#p,this),this.#e=!0}async disable(){if(!this.#i||!this.#e)return;await this.#T();const t=this.#i.model(a.RuntimeModel.RuntimeModel);t&&(await t.removeBinding({name:o.EVENT_BINDING_NAME}),t.removeEventListener(a.RuntimeModel.Events.BindingCalled,this.#y,this));const e=this.#i.model(a.DOMModel.DOMModel);e&&e.removeEventListener(a.DOMModel.Events.DocumentUpdated,this.#v,this),this.#n&&await this.#i.pageAgent().invoke_removeScriptToEvaluateOnNewDocument({identifier:this.#n}),this.#n=void 0,this.#m?.removeEventListener("Updated",this.#p,this),this.#e=!1}}export{m as LiveMetrics};