react-grep 0.1.2 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -41,6 +41,25 @@ destroy(); // stop and clean up
41
41
 
42
42
  The inspector starts automatically when the script loads.
43
43
 
44
+ ## Compatibility
45
+
46
+ react-grep works with any React app that uses `react-dom` in development mode. It reads React's internal fiber tree, so no framework-specific plugin is needed.
47
+
48
+ | Framework / Bundler | Status |
49
+ | --------------------------------- | ---------------------- |
50
+ | Vite + React | Tested |
51
+ | Next.js 15 (Turbopack) | Tested |
52
+ | Next.js (Webpack) | Untested — should work |
53
+ | Create React App | Untested — should work |
54
+ | Remix | Untested — should work |
55
+ | Gatsby | Untested — should work |
56
+ | Custom Webpack / Rollup / esbuild | Untested — should work |
57
+ | React Native | Not supported (no DOM) |
58
+
59
+ Next.js has dedicated support for server component names and Turbopack indexed source maps.
60
+
61
+ Source map resolution is automatic — if your dev server serves source maps (inline or external), react-grep will resolve bundled locations back to original files.
62
+
44
63
  ## How it works
45
64
 
46
65
  react-grep reads React's internal fiber tree to find component names and source locations (`_debugSource` / `_debugStack`). This data is only available in **development builds** of React — production builds strip it out.
@@ -1,3 +1,3 @@
1
- var ReactGrep=(function(exports){'use strict';var k=new Map,T="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",P=[];for(let e=0;e<T.length;e++)P[T.charCodeAt(e)]=e;var m=(e,t)=>{let n=0,i=0;for(;t.i<e.length;){let o=P[e.charCodeAt(t.i++)];if(i+=(o&31)<<n,!(o&32))return i&1?-(i>>1):i>>1;n+=5;}return 0},F=e=>{let t=[],n=0,i=0,o=0;for(let r of e.split(";")){let s=[],a=0;if(r){let l={i:0};for(;l.i<r.length;){if(r.charCodeAt(l.i)===44){l.i++;continue}a+=m(r,l),!(l.i>=r.length||r.charCodeAt(l.i)===44)&&(n+=m(r,l),i+=m(r,l),o+=m(r,l),l.i<r.length&&r.charCodeAt(l.i)!==44&&m(r,l),s.push([a,n,i,o]));}}t.push(s);}return t},U=/^data:application\/json[^,]*;base64,([A-Za-z0-9+/=]+)$/,j=(e,t)=>{try{return new URL(e).origin===new URL(t).origin}catch{return false}},I=async(e,t)=>{if(e.startsWith("data:")){let o=U.exec(e);return o?atob(o[1]):null}let n=new URL(e,t).href;if(!j(t,n))return null;let i=await fetch(n);return i.ok?i.text():null},K=e=>{let t=[],n=[];for(let i of e){let o=F(i.map.mappings),r=i.offset.line,s=i.offset.column,a=t.length;for(;n.length<r+o.length;)n.push([]);for(let l=0;l<o.length;l++){let d=n[r+l];for(let h of o[l])d.push([l===0?h[0]+s:h[0],h[1]+a,h[2],h[3]]);}t.push(...i.map.sources);}for(let i of n)i.length>1&&i.sort((o,r)=>o[0]-r[0]);return {sources:t,mappings:n}},g=e=>{try{let t=JSON.parse(e);return Array.isArray(t.sections)?K(t.sections):!t.sources||!t.mappings?null:{sources:t.sources,mappings:F(t.mappings)}}catch{return null}},W=async e=>{try{let t=await fetch(e),i=(await t.text()).match(/\/\/[#@]\s*sourceMappingURL=([^\s]+)$/m);if(i){let s=await I(i[1].trim(),e);if(s){let a=g(s);if(a)return a}}let o=t.headers.get("SourceMap")??t.headers.get("X-SourceMap");if(o){let s=await I(o.trim(),e);if(s){let a=g(s);if(a)return a}}let r=await fetch(`${e}.map`);if(r.ok){let s=await r.text();return g(s)}return null}catch{return null}},R=/^about:\/\/React\/Server\/file:\/\/\//,G=/[/\\](\.next[/\\].+?)(?:\?.*)?$/,X=async e=>{try{let t=decodeURIComponent(e.replace(R,"")),n=G.exec(t);if(!n)return null;let o=`${typeof location<"u"?location.origin:""}/__nextjs_source-map?filename=${encodeURIComponent(n[1])}`,r=await fetch(o);if(!r.ok)return null;let s=await r.text();return s?g(s):null}catch{return null}},z=e=>{let t=k.get(e);return t||(t=R.test(e)?X(e):W(e),k.set(e,t)),t},V=(e,t,n)=>{if(t<0||t>=e.mappings.length)return null;let i=e.mappings[t];if(!i.length)return null;let o=0,r=i.length-1;for(;o<r;){let s=o+r+1>>1;i[s][0]<=n?o=s:r=s-1;}return i[o]},D=async(e,t,n)=>{let i=await z(e);if(!i)return null;let o=V(i,t-1,n-1);if(!o)return null;let r=i.sources[o[1]];return r.startsWith("file:///")&&(r=decodeURIComponent(new URL(r).pathname)),{fileName:r,lineNumber:o[2]+1,columnNumber:o[3]+1}};var S=e=>"env"in e&&typeof e.name=="string",w=new Set([0,1,11,14,15]),Y=e=>{try{let t=Object.keys(e).find(n=>n.startsWith("__reactFiber$"));return t?e[t]:null}catch{return null}},B=e=>{let t=e;for(;t;){if(w.has(t.tag))return t;t=t.return;}return null},J=e=>{if(typeof e=="function")return e;if(e&&typeof e=="object"){if("render"in e&&typeof e.render=="function")return e.render;if("type"in e&&typeof e.type=="function")return e.type}return null},v=e=>{let{type:t}=e;if(typeof t=="function")return t.displayName||t.name||"Anonymous";if(t&&typeof t=="object"){if("displayName"in t&&t.displayName)return t.displayName;let n=J(t);if(n)return n.displayName||n.name||"Anonymous"}return "Anonymous"},Q=new Set(["jsxDEV","jsxs","jsx","react-stack-top-frame","react_stack_bottom_frame","fakeJSXCallSite"]),q=/at (?:(\S+) )?\(?(.+):(\d+):(\d+)\)?$/,C=e=>{let t=e._debugStack?.stack;if(!t)return null;for(let n of t.split(`
2
- `)){let i=q.exec(n.trim());if(!i)continue;let[,o,r,s,a]=i;if(!(o&&Q.has(o))&&!r.includes("/node_modules/"))return {url:r,line:Number(s),column:Number(a)}}return null},x=async e=>{let t=await D(e.url,e.line,e.column);if(t)return t;let n=e.url;try{let i=new URL(e.url);n=decodeURIComponent(i.pathname);let o=n.indexOf("?");o!==-1&&(n=n.substring(0,o));}catch{}return n=n.replace(/\.\.\//g,""),n.startsWith("/")&&(n=n.substring(1)),{fileName:n,lineNumber:e.line,columnNumber:e.column}},_=async e=>{if(e._debugSource)return e._debugSource;if(e._debugOwner?._debugSource)return e._debugOwner._debugSource;let t=C(e);if(t)return x(t);if(e._debugOwner){let n=C(e._debugOwner);if(n)return x(n)}return null},$=async e=>{if(e._debugSource)return e._debugSource;let t=C(e);return t?x(t):null},M=async e=>{let t=Y(e);if(!t)return null;let n=B(t);if(!n)return null;if(t.return!=null&&w.has(t.return.tag))return {kind:"component",name:v(n),elementTag:null,source:await _(n),callSite:null};let o=t._debugOwner,r=typeof t.type=="string"?t.type:null;return o&&!S(o)&&o===n?{kind:"element",name:v(o),elementTag:r,source:await $(t),callSite:await _(n)}:{kind:"children",name:o&&S(o)?o.name:v(o&&!S(o)&&w.has(o.tag)?o:n),elementTag:r,source:await $(t),callSite:null}};var Z=typeof navigator<"u"&&/Mac|iPhone|iPad/.test(navigator.platform),c={name:"#93c5fd",tag:"#a78bfa",path:"#71717a",pathActive:"#a1a1aa",pathDim:"#3f3f46",hint:"#52525b"},tt={position:"fixed",pointerEvents:"none",zIndex:"2147483646",backgroundColor:"rgba(66, 135, 245, 0.15)",border:"1.5px solid rgba(66, 135, 245, 0.6)",borderRadius:"3px",display:"none",transition:"top 60ms ease-out, left 60ms ease-out, width 60ms ease-out, height 60ms ease-out"},et={position:"fixed",pointerEvents:"none",zIndex:"2147483647",display:"none",fontFamily:"ui-monospace, SFMono-Regular, 'SF Mono', Menlo, monospace",fontSize:"12px",lineHeight:"1.4",color:"#e4e4e7",backgroundColor:"#18181b",border:"1px solid #3f3f46",borderRadius:"6px",padding:"4px 8px",whiteSpace:"nowrap",maxWidth:"500px",overflow:"hidden",textOverflow:"ellipsis",boxShadow:"0 4px 12px rgba(0, 0, 0, 0.4)"},N=(e,t)=>{Object.assign(e.style,t);},f=e=>{let t=e.split("/");return t.length<=2?e:`.../${t.slice(-2).join("/")}`},u=(e,t)=>{let n=document.createElement("span");return n.textContent=e,Object.assign(n.style,t),n},y=class{highlight=null;tooltip=null;init(){this.highlight||(this.highlight=document.createElement("div"),this.highlight.dataset.reactGrep="highlight",N(this.highlight,tt),document.body.appendChild(this.highlight),this.tooltip=document.createElement("div"),this.tooltip.dataset.reactGrep="tooltip",N(this.tooltip,et),document.body.appendChild(this.tooltip));}show(t,n,i="source"){if(!this.highlight||!this.tooltip)return;let o=t.getBoundingClientRect();this.highlight.style.top=`${o.top}px`,this.highlight.style.left=`${o.left}px`,this.highlight.style.width=`${o.width}px`,this.highlight.style.height=`${o.height}px`,this.highlight.style.display="block",this.tooltip.textContent="",this.tooltip.appendChild(u(n.name,{color:c.name,fontWeight:"600"})),n.elementTag!=null&&(this.tooltip.appendChild(u(" > ",{color:c.path})),this.tooltip.appendChild(u(n.elementTag,{color:c.tag,fontWeight:"600"})));let r=n.source?`${n.source.fileName}:${n.source.lineNumber}`:null,s=n.callSite?`${n.callSite.fileName}:${n.callSite.lineNumber}`:null;if(r&&s){let h=i==="source"?c.pathActive:c.pathDim,O=i==="callSite"?c.pathActive:c.pathDim,A=Z?"\u21E7":"Shift",L=f(r),H=f(s);this.tooltip.appendChild(u(` ${i==="callSite"?"(":""}${L}${i==="callSite"?")":""}`,{color:h})),this.tooltip.appendChild(u(` ${A} `,{color:c.hint})),this.tooltip.appendChild(u(`${i==="source"?"(":""}${H}${i==="source"?")":""}`,{color:O}));}else r&&this.tooltip.appendChild(u(` ${f(r)}`,{color:c.path}));let a=this.tooltip.getBoundingClientRect(),l=o.top-a.height-6,d=o.left;l<4&&(l=o.bottom+6),d+a.width>window.innerWidth-4&&(d=window.innerWidth-a.width-4),this.tooltip.style.top=`${l}px`,this.tooltip.style.left=`${Math.max(4,d)}px`,this.tooltip.style.display="block";}showCopied(t){this.tooltip&&(this.tooltip.textContent="",this.tooltip.appendChild(u("Copied!",{color:"#4ade80",fontWeight:"600"})),this.tooltip.appendChild(u(` ${f(t)}`,{color:"#a1a1aa"})),this.tooltip.style.display="block",setTimeout(()=>this.hide(),1500));}hide(){this.highlight&&(this.highlight.style.display="none"),this.tooltip&&(this.tooltip.style.display="none");}destroy(){this.highlight?.remove(),this.tooltip?.remove(),this.highlight=null,this.tooltip=null;}};var E=typeof navigator<"u"&&/Mac|iPhone|iPad/.test(navigator.platform),b=class{overlay=new y;moveGeneration=0;lastTarget=null;lastInfo=null;sourceToggled=false;shiftPressedClean=false;boundHandlers;constructor(){this.boundHandlers={mousemove:this.handleMouseMove.bind(this),click:this.handleClick.bind(this),keydown:this.handleKeyDown.bind(this),keyup:this.handleKeyUp.bind(this)};}start(){window.addEventListener("mousemove",this.boundHandlers.mousemove),window.addEventListener("click",this.boundHandlers.click,true),window.addEventListener("keydown",this.boundHandlers.keydown),window.addEventListener("keyup",this.boundHandlers.keyup);}stop(){window.removeEventListener("mousemove",this.boundHandlers.mousemove),window.removeEventListener("click",this.boundHandlers.click,true),window.removeEventListener("keydown",this.boundHandlers.keydown),window.removeEventListener("keyup",this.boundHandlers.keyup),this.overlay.destroy(),document.body.style.cursor="",this.lastTarget=null,this.lastInfo=null,this.sourceToggled=false,this.shiftPressedClean=false;}isModifierHeld(t){return E?t.metaKey:t.ctrlKey}async handleMouseMove(t){if(!this.isModifierHeld(t)){this.overlay.hide(),document.body.style.cursor="",this.lastTarget=null,this.lastInfo=null;return}this.overlay.init(),document.body.style.cursor="crosshair";let n=document.elementFromPoint(t.clientX,t.clientY);if(!n||n.closest("[data-react-grep]"))return;n!==this.lastTarget&&(this.sourceToggled=false);let i=++this.moveGeneration,o=await M(n);if(i===this.moveGeneration){if(!o){this.overlay.hide(),this.lastTarget=null,this.lastInfo=null;return}this.lastTarget=n,this.lastInfo=o,this.overlay.show(n,o,this.getActiveSource());}}async handleClick(t){if(!this.isModifierHeld(t)||!t.shiftKey)return;let n=document.elementFromPoint(t.clientX,t.clientY);if(!n||n.closest("[data-react-grep]"))return;t.preventDefault(),t.stopPropagation(),t.stopImmediatePropagation(),this.shiftPressedClean=false;let i=await M(n);if(!i)return;let o=this.getActiveCopySource(i);if(!o)return;let{fileName:r,lineNumber:s,columnNumber:a}=o,l=a!=null?`${r}:${s}:${a}`:`${r}:${s}`;await this.copyToClipboard(l),this.overlay.showCopied(l);}handleKeyDown(t){t.key==="Shift"&&this.isModifierHeld(t)&&(this.shiftPressedClean=true);}handleKeyUp(t){if(E&&t.key==="Meta"||!E&&t.key==="Control"){this.overlay.hide(),document.body.style.cursor="",this.lastTarget=null,this.lastInfo=null;return}t.key==="Shift"&&this.shiftPressedClean&&this.lastTarget&&this.lastInfo&&this.lastInfo.callSite&&(this.sourceToggled=!this.sourceToggled,this.overlay.show(this.lastTarget,this.lastInfo,this.getActiveSource())),this.shiftPressedClean=false;}getActiveSource(){return this.sourceToggled?"callSite":"source"}getActiveCopySource(t){return this.sourceToggled&&t.callSite?t.callSite:t.source}async copyToClipboard(t){try{await navigator.clipboard.writeText(t);}catch{}}};var p=null,nt=()=>{p||(p=new b,p.start());},ht=()=>{p&&(p.stop(),p=null);};if(typeof window<"u"){let e=()=>nt();document.readyState==="loading"?document.addEventListener("DOMContentLoaded",e):e();}
3
- exports.destroy=ht;exports.init=nt;return exports;})({});
1
+ var ReactGrep=(function(exports){'use strict';var d=typeof navigator<"u"&&/mac/i.test(navigator.userAgentData?.platform??navigator.userAgent);var g=new Map,M="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",R=[];for(let e=0;e<M.length;e++)R[M.charCodeAt(e)]=e;var f=(e,t)=>{let n=0,i=0;for(;t.i<e.length;){let o=R[e.charCodeAt(t.i++)];if(i+=(o&31)<<n,!(o&32))return i&1?-(i>>1):i>>1;n+=5;}return 0},D=e=>{let t=[],n=0,i=0,o=0;for(let r of e.split(";")){let s=[],a=0;if(r){let l={i:0};for(;l.i<r.length;){if(r.charCodeAt(l.i)===44){l.i++;continue}a+=f(r,l),!(l.i>=r.length||r.charCodeAt(l.i)===44)&&(n+=f(r,l),i+=f(r,l),o+=f(r,l),l.i<r.length&&r.charCodeAt(l.i)!==44&&f(r,l),s.push([a,n,i,o]));}}t.push(s);}return t},U=/^data:application\/json[^,]*;base64,([A-Za-z0-9+/=]+)$/,j=(e,t)=>{try{return new URL(e).origin===new URL(t).origin}catch{return false}},I=async(e,t)=>{if(e.startsWith("data:")){let o=U.exec(e);return o?atob(o[1]):null}let n=new URL(e,t).href;if(!j(t,n))return null;let i=await fetch(n);return i.ok?i.text():null},K=e=>{let t=[],n=[];for(let i of e){let o=D(i.map.mappings),r=i.offset.line,s=i.offset.column,a=t.length;for(;n.length<r+o.length;)n.push([]);for(let l=0;l<o.length;l++){let h=n[r+l];for(let p of o[l])h.push([l===0?p[0]+s:p[0],p[1]+a,p[2],p[3]]);}t.push(...i.map.sources);}for(let i of n)i.length>1&&i.sort((o,r)=>o[0]-r[0]);return {sources:t,mappings:n}},b=e=>{try{let t=JSON.parse(e);return Array.isArray(t.sections)?K(t.sections):!t.sources||!t.mappings?null:{sources:t.sources,mappings:D(t.mappings)}}catch{return null}},G=async e=>{try{let t=await fetch(e),i=(await t.text()).match(/\/\/[#@]\s*sourceMappingURL=([^\s]+)$/m);if(i){let s=await I(i[1].trim(),e);if(s){let a=b(s);if(a)return a}}let o=t.headers.get("SourceMap")??t.headers.get("X-SourceMap");if(o){let s=await I(o.trim(),e);if(s){let a=b(s);if(a)return a}}let r=await fetch(`${e}.map`);if(r.ok){let s=await r.text();return b(s)}return null}catch{return null}},A=/^about:\/\/React\/Server\/file:\/\/\//,W=/[/\\](\.next[/\\].+?)(?:\?.*)?$/,X=async e=>{try{let t=decodeURIComponent(e.replace(A,"")),n=W.exec(t);if(!n)return null;let o=`${typeof location<"u"?location.origin:""}/__nextjs_source-map?filename=${encodeURIComponent(n[1])}`,r=await fetch(o);if(!r.ok)return null;let s=await r.text();return s?b(s):null}catch{return null}},z=e=>{let t=g.get(e);if(!t){if(g.size>=100){let n=g.keys().next().value;g.delete(n);}t=A.test(e)?X(e):G(e),g.set(e,t);}return t},V=(e,t,n)=>{if(t<0||t>=e.mappings.length)return null;let i=e.mappings[t];if(!i.length)return null;let o=0,r=i.length-1;for(;o<r;){let s=o+r+1>>1;i[s][0]<=n?o=s:r=s-1;}return i[o]},F=async(e,t,n)=>{let i=await z(e);if(!i)return null;let o=V(i,t-1,n-1);if(!o)return null;let r=i.sources[o[1]];return r.startsWith("file:///")&&(r=decodeURIComponent(new URL(r).pathname)),{fileName:r,lineNumber:o[2]+1,columnNumber:o[3]+1}};var y=e=>"env"in e&&typeof e.name=="string",x=new Set([0,1,11,14,15]),Y=e=>{try{let t=Object.keys(e).find(n=>n.startsWith("__reactFiber$"));return t?e[t]:null}catch{return null}},B=e=>{let t=e;for(;t;){if(x.has(t.tag))return t;t=t.return;}return null},J=e=>{if(typeof e=="function")return e;if(e&&typeof e=="object"){if("render"in e&&typeof e.render=="function")return e.render;if("type"in e&&typeof e.type=="function")return e.type}return null},C=e=>{let{type:t}=e;if(typeof t=="function")return t.displayName||t.name||"Anonymous";if(t&&typeof t=="object"){if("displayName"in t&&t.displayName)return t.displayName;let n=J(t);if(n)return n.displayName||n.name||"Anonymous"}return "Anonymous"},Z=new Set(["jsxDEV","jsxs","jsx","react-stack-top-frame","react_stack_bottom_frame","fakeJSXCallSite"]),Q=/at (?:(\S+) )?\(?(.+):(\d+):(\d+)\)?$/,T=e=>{let t=e._debugStack?.stack;if(!t)return null;for(let n of t.split(`
2
+ `)){let i=Q.exec(n.trim());if(!i)continue;let[,o,r,s,a]=i;if(!(o&&Z.has(o))&&!r.includes("/node_modules/"))return {url:r,line:Number(s),column:Number(a)}}return null},k=async e=>{let t=await F(e.url,e.line,e.column);if(t)return t;let n=e.url;try{let i=new URL(e.url);n=decodeURIComponent(i.pathname);let o=n.indexOf("?");o!==-1&&(n=n.substring(0,o));}catch{}return n=n.replace(/\.\.\//g,""),n.startsWith("/")&&(n=n.substring(1)),{fileName:n,lineNumber:e.line,columnNumber:e.column}},P=async e=>{if(e._debugSource)return e._debugSource;let t=e._debugOwner;if(t&&!y(t)&&t._debugSource)return t._debugSource;let n=T(e);if(n)return k(n);if(t&&!y(t)){let i=T(t);if(i)return k(i)}return null},_=async e=>{if(e._debugSource)return e._debugSource;let t=T(e);return t?k(t):null},E=async e=>{let t=Y(e);if(!t)return null;let n=B(t);if(!n)return null;if(t.return!=null&&x.has(t.return.tag))return {kind:"component",name:C(n),elementTag:null,source:await P(n),callSite:null};let o=t._debugOwner,r=typeof t.type=="string"?t.type:null;return o&&!y(o)&&o===n?{kind:"element",name:C(o),elementTag:r,source:await _(t),callSite:await P(n)}:{kind:"children",name:o&&y(o)?o.name:C(o&&!y(o)&&x.has(o.tag)?o:n),elementTag:r,source:await _(t),callSite:null}};var c={name:"#93c5fd",tag:"#a78bfa",path:"#71717a",pathActive:"#a1a1aa",pathDim:"#3f3f46",hint:"#52525b"},q={position:"fixed",pointerEvents:"none",zIndex:"2147483646",backgroundColor:"rgba(66, 135, 245, 0.15)",border:"1.5px solid rgba(66, 135, 245, 0.6)",borderRadius:"3px",display:"none",transition:"top 60ms ease-out, left 60ms ease-out, width 60ms ease-out, height 60ms ease-out"},tt={position:"fixed",pointerEvents:"none",zIndex:"2147483647",display:"none",fontFamily:"ui-monospace, SFMono-Regular, 'SF Mono', Menlo, monospace",fontSize:"12px",lineHeight:"1.4",color:"#e4e4e7",backgroundColor:"#18181b",border:"1px solid #3f3f46",borderRadius:"6px",padding:"4px 8px",whiteSpace:"nowrap",maxWidth:"500px",overflow:"hidden",textOverflow:"ellipsis",boxShadow:"0 4px 12px rgba(0, 0, 0, 0.4)"},$=(e,t)=>{Object.assign(e.style,t);},v=e=>{let t=e.split("/");return t.length<=2?e:`.../${t.slice(-2).join("/")}`},u=(e,t)=>{let n=document.createElement("span");return n.textContent=e,Object.assign(n.style,t),n},S=class{highlight=null;tooltip=null;copiedTimer=null;init(){this.highlight||(this.highlight=document.createElement("div"),this.highlight.dataset.reactGrep="highlight",$(this.highlight,q),document.body.appendChild(this.highlight),this.tooltip=document.createElement("div"),this.tooltip.dataset.reactGrep="tooltip",$(this.tooltip,tt),document.body.appendChild(this.tooltip));}show(t,n,i="source"){if(!this.highlight||!this.tooltip)return;let o=t.getBoundingClientRect();this.highlight.style.top=`${o.top}px`,this.highlight.style.left=`${o.left}px`,this.highlight.style.width=`${o.width}px`,this.highlight.style.height=`${o.height}px`,this.highlight.style.display="block",this.tooltip.textContent="",this.tooltip.appendChild(u(n.name,{color:c.name,fontWeight:"600"})),n.elementTag!=null&&(this.tooltip.appendChild(u(" > ",{color:c.path})),this.tooltip.appendChild(u(n.elementTag,{color:c.tag,fontWeight:"600"})));let r=n.source?`${n.source.fileName}:${n.source.lineNumber}`:null,s=n.callSite?`${n.callSite.fileName}:${n.callSite.lineNumber}`:null;if(r&&s){let p=i==="source"?c.pathActive:c.pathDim,N=i==="callSite"?c.pathActive:c.pathDim,L=d?"\u21E7":"Shift",H=v(r),O=v(s);this.tooltip.appendChild(u(` ${i==="callSite"?"(":""}${H}${i==="callSite"?")":""}`,{color:p})),this.tooltip.appendChild(u(` ${L} `,{color:c.hint})),this.tooltip.appendChild(u(`${i==="source"?"(":""}${O}${i==="source"?")":""}`,{color:N}));}else r&&this.tooltip.appendChild(u(` ${v(r)}`,{color:c.path}));let a=this.tooltip.getBoundingClientRect(),l=o.top-a.height-6,h=o.left;l<4&&(l=o.bottom+6),h+a.width>window.innerWidth-4&&(h=window.innerWidth-a.width-4),this.tooltip.style.top=`${l}px`,this.tooltip.style.left=`${Math.max(4,h)}px`,this.tooltip.style.display="block";}showCopied(t){this.tooltip&&(this.tooltip.textContent="",this.tooltip.appendChild(u("Copied!",{color:"#4ade80",fontWeight:"600"})),this.tooltip.appendChild(u(` ${v(t)}`,{color:"#a1a1aa"})),this.tooltip.style.display="block",this.copiedTimer&&clearTimeout(this.copiedTimer),this.copiedTimer=setTimeout(()=>this.hide(),1500));}hide(){this.highlight&&(this.highlight.style.display="none"),this.tooltip&&(this.tooltip.style.display="none");}destroy(){this.copiedTimer&&(clearTimeout(this.copiedTimer),this.copiedTimer=null),this.highlight?.remove(),this.tooltip?.remove(),this.highlight=null,this.tooltip=null;}};var w=class{overlay=new S;moveGeneration=0;clickGeneration=0;lastTarget=null;lastInfo=null;sourceToggled=false;shiftPressedClean=false;savedCursor="";boundHandlers;constructor(){this.boundHandlers={mousemove:this.handleMouseMove.bind(this),click:this.handleClick.bind(this),keydown:this.handleKeyDown.bind(this),keyup:this.handleKeyUp.bind(this)};}start(){window.addEventListener("mousemove",this.boundHandlers.mousemove),window.addEventListener("click",this.boundHandlers.click,true),window.addEventListener("keydown",this.boundHandlers.keydown),window.addEventListener("keyup",this.boundHandlers.keyup);}stop(){window.removeEventListener("mousemove",this.boundHandlers.mousemove),window.removeEventListener("click",this.boundHandlers.click,true),window.removeEventListener("keydown",this.boundHandlers.keydown),window.removeEventListener("keyup",this.boundHandlers.keyup),this.overlay.destroy(),this.restoreCursor(),this.lastTarget=null,this.lastInfo=null,this.sourceToggled=false,this.shiftPressedClean=false;}isModifierHeld(t){return d?t.metaKey:t.ctrlKey}async handleMouseMove(t){if(!this.isModifierHeld(t)){this.overlay.hide(),this.restoreCursor(),this.lastTarget=null,this.lastInfo=null;return}this.overlay.init(),this.setCrosshairCursor();let n=document.elementFromPoint(t.clientX,t.clientY);if(!n||n.closest("[data-react-grep]"))return;n!==this.lastTarget&&(this.sourceToggled=false);let i=++this.moveGeneration,o=await E(n);if(i===this.moveGeneration){if(!o){this.overlay.hide(),this.lastTarget=null,this.lastInfo=null;return}this.lastTarget=n,this.lastInfo=o,this.overlay.show(n,o,this.getActiveSource());}}async handleClick(t){if(!this.isModifierHeld(t)||!t.shiftKey)return;let n=document.elementFromPoint(t.clientX,t.clientY);if(!n||n.closest("[data-react-grep]"))return;t.preventDefault(),t.stopPropagation(),t.stopImmediatePropagation(),this.shiftPressedClean=false;let i=++this.clickGeneration,o=await E(n);if(i!==this.clickGeneration||!o)return;let r=this.getActiveCopySource(o);if(!r)return;let{fileName:s,lineNumber:a,columnNumber:l}=r,h=l!=null?`${s}:${a}:${l}`:`${s}:${a}`;await this.copyToClipboard(h),this.overlay.showCopied(h);}handleKeyDown(t){t.key==="Shift"&&this.isModifierHeld(t)&&(this.shiftPressedClean=true);}handleKeyUp(t){if(d&&t.key==="Meta"||!d&&t.key==="Control"){this.overlay.hide(),this.restoreCursor(),this.lastTarget=null,this.lastInfo=null;return}t.key==="Shift"&&this.shiftPressedClean&&this.lastTarget&&this.lastInfo&&this.lastInfo.callSite&&(this.sourceToggled=!this.sourceToggled,this.overlay.show(this.lastTarget,this.lastInfo,this.getActiveSource())),this.shiftPressedClean=false;}getActiveSource(){return this.sourceToggled?"callSite":"source"}getActiveCopySource(t){return this.sourceToggled&&t.callSite?t.callSite:t.source}setCrosshairCursor(){document.body.style.cursor!=="crosshair"&&(this.savedCursor=document.body.style.cursor,document.body.style.cursor="crosshair");}restoreCursor(){document.body.style.cursor==="crosshair"&&(document.body.style.cursor=this.savedCursor);}async copyToClipboard(t){try{await navigator.clipboard.writeText(t);}catch{}}};var m=null,et=()=>{m||(m=new w,m.start());},dt=()=>{m&&(m.stop(),m=null);};if(typeof window<"u"){let e=()=>et();document.readyState==="loading"?document.addEventListener("DOMContentLoaded",e):e();}
3
+ exports.destroy=dt;exports.init=et;return exports;})({});
package/dist/index.js CHANGED
@@ -1,4 +1,10 @@
1
+ // src/env.ts
2
+ var isMac = typeof navigator !== "undefined" && /mac/i.test(
3
+ navigator.userAgentData?.platform ?? navigator.userAgent
4
+ );
5
+
1
6
  // src/source-map.ts
7
+ var MAX_CACHE_SIZE = 100;
2
8
  var cache = /* @__PURE__ */ new Map();
3
9
  var VLQ_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
4
10
  var charToInt = [];
@@ -144,6 +150,10 @@ var fetchAndParseServerFile = async (url) => {
144
150
  var getSourceMap = (url) => {
145
151
  let promise = cache.get(url);
146
152
  if (!promise) {
153
+ if (cache.size >= MAX_CACHE_SIZE) {
154
+ const oldest = cache.keys().next().value;
155
+ cache.delete(oldest);
156
+ }
147
157
  promise = ABOUT_SERVER_RE.test(url) ? fetchAndParseServerFile(url) : fetchAndParse(url);
148
158
  cache.set(url, promise);
149
159
  }
@@ -212,10 +222,8 @@ var getCompositeComponentFiber = (fiber) => {
212
222
  var getInnerFunction = (type) => {
213
223
  if (typeof type === "function") return type;
214
224
  if (type && typeof type === "object") {
215
- if ("render" in type && typeof type.render === "function")
216
- return type.render;
217
- if ("type" in type && typeof type.type === "function")
218
- return type.type;
225
+ if ("render" in type && typeof type.render === "function") return type.render;
226
+ if ("type" in type && typeof type.type === "function") return type.type;
219
227
  }
220
228
  return null;
221
229
  };
@@ -270,11 +278,12 @@ var resolveFrame = async (frame) => {
270
278
  };
271
279
  var getCompositeDebugSource = async (fiber) => {
272
280
  if (fiber._debugSource) return fiber._debugSource;
273
- if (fiber._debugOwner?._debugSource) return fiber._debugOwner._debugSource;
281
+ const owner = fiber._debugOwner;
282
+ if (owner && !isServerComponent(owner) && owner._debugSource) return owner._debugSource;
274
283
  const frame = parseFirstUserFrame(fiber);
275
284
  if (frame) return resolveFrame(frame);
276
- if (fiber._debugOwner) {
277
- const ownerFrame = parseFirstUserFrame(fiber._debugOwner);
285
+ if (owner && !isServerComponent(owner)) {
286
+ const ownerFrame = parseFirstUserFrame(owner);
278
287
  if (ownerFrame) return resolveFrame(ownerFrame);
279
288
  }
280
289
  return null;
@@ -324,7 +333,6 @@ var getComponentInfo = async (el) => {
324
333
  };
325
334
 
326
335
  // src/overlay.ts
327
- var isMac = typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform);
328
336
  var COLORS = {
329
337
  name: "#93c5fd",
330
338
  tag: "#a78bfa",
@@ -379,6 +387,7 @@ var createSpan = (text, styles) => {
379
387
  var OverlayManager = class {
380
388
  highlight = null;
381
389
  tooltip = null;
390
+ copiedTimer = null;
382
391
  init() {
383
392
  if (this.highlight) return;
384
393
  this.highlight = document.createElement("div");
@@ -449,13 +458,18 @@ var OverlayManager = class {
449
458
  this.tooltip.appendChild(createSpan("Copied!", { color: "#4ade80", fontWeight: "600" }));
450
459
  this.tooltip.appendChild(createSpan(` ${truncatePath(location2)}`, { color: "#a1a1aa" }));
451
460
  this.tooltip.style.display = "block";
452
- setTimeout(() => this.hide(), 1500);
461
+ if (this.copiedTimer) clearTimeout(this.copiedTimer);
462
+ this.copiedTimer = setTimeout(() => this.hide(), 1500);
453
463
  }
454
464
  hide() {
455
465
  if (this.highlight) this.highlight.style.display = "none";
456
466
  if (this.tooltip) this.tooltip.style.display = "none";
457
467
  }
458
468
  destroy() {
469
+ if (this.copiedTimer) {
470
+ clearTimeout(this.copiedTimer);
471
+ this.copiedTimer = null;
472
+ }
459
473
  this.highlight?.remove();
460
474
  this.tooltip?.remove();
461
475
  this.highlight = null;
@@ -464,14 +478,15 @@ var OverlayManager = class {
464
478
  };
465
479
 
466
480
  // src/inspector.ts
467
- var isMac2 = typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform);
468
481
  var Inspector = class {
469
482
  overlay = new OverlayManager();
470
483
  moveGeneration = 0;
484
+ clickGeneration = 0;
471
485
  lastTarget = null;
472
486
  lastInfo = null;
473
487
  sourceToggled = false;
474
488
  shiftPressedClean = false;
489
+ savedCursor = "";
475
490
  boundHandlers;
476
491
  constructor() {
477
492
  this.boundHandlers = {
@@ -493,25 +508,25 @@ var Inspector = class {
493
508
  window.removeEventListener("keydown", this.boundHandlers.keydown);
494
509
  window.removeEventListener("keyup", this.boundHandlers.keyup);
495
510
  this.overlay.destroy();
496
- document.body.style.cursor = "";
511
+ this.restoreCursor();
497
512
  this.lastTarget = null;
498
513
  this.lastInfo = null;
499
514
  this.sourceToggled = false;
500
515
  this.shiftPressedClean = false;
501
516
  }
502
517
  isModifierHeld(e) {
503
- return isMac2 ? e.metaKey : e.ctrlKey;
518
+ return isMac ? e.metaKey : e.ctrlKey;
504
519
  }
505
520
  async handleMouseMove(e) {
506
521
  if (!this.isModifierHeld(e)) {
507
522
  this.overlay.hide();
508
- document.body.style.cursor = "";
523
+ this.restoreCursor();
509
524
  this.lastTarget = null;
510
525
  this.lastInfo = null;
511
526
  return;
512
527
  }
513
528
  this.overlay.init();
514
- document.body.style.cursor = "crosshair";
529
+ this.setCrosshairCursor();
515
530
  const target = document.elementFromPoint(e.clientX, e.clientY);
516
531
  if (!target || target.closest("[data-react-grep]")) return;
517
532
  if (target !== this.lastTarget) {
@@ -538,8 +553,9 @@ var Inspector = class {
538
553
  e.stopPropagation();
539
554
  e.stopImmediatePropagation();
540
555
  this.shiftPressedClean = false;
556
+ const gen = ++this.clickGeneration;
541
557
  const info = await getComponentInfo(target);
542
- if (!info) return;
558
+ if (gen !== this.clickGeneration || !info) return;
543
559
  const source = this.getActiveCopySource(info);
544
560
  if (!source) return;
545
561
  const { fileName, lineNumber, columnNumber } = source;
@@ -553,9 +569,9 @@ var Inspector = class {
553
569
  }
554
570
  }
555
571
  handleKeyUp(e) {
556
- if (isMac2 && e.key === "Meta" || !isMac2 && e.key === "Control") {
572
+ if (isMac && e.key === "Meta" || !isMac && e.key === "Control") {
557
573
  this.overlay.hide();
558
- document.body.style.cursor = "";
574
+ this.restoreCursor();
559
575
  this.lastTarget = null;
560
576
  this.lastInfo = null;
561
577
  return;
@@ -572,6 +588,17 @@ var Inspector = class {
572
588
  getActiveCopySource(info) {
573
589
  return this.sourceToggled && info.callSite ? info.callSite : info.source;
574
590
  }
591
+ setCrosshairCursor() {
592
+ if (document.body.style.cursor !== "crosshair") {
593
+ this.savedCursor = document.body.style.cursor;
594
+ document.body.style.cursor = "crosshair";
595
+ }
596
+ }
597
+ restoreCursor() {
598
+ if (document.body.style.cursor === "crosshair") {
599
+ document.body.style.cursor = this.savedCursor;
600
+ }
601
+ }
575
602
  async copyToClipboard(text) {
576
603
  try {
577
604
  await navigator.clipboard.writeText(text);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-grep",
3
- "version": "0.1.2",
3
+ "version": "0.2.1",
4
4
  "description": "Hold CMD to see React component names + file:line overlaid on any element",
5
5
  "keywords": [
6
6
  "component",
@@ -30,21 +30,15 @@
30
30
  ],
31
31
  "type": "module",
32
32
  "sideEffects": true,
33
- "main": "dist/index.cjs",
34
33
  "module": "dist/index.js",
35
34
  "browser": "dist/index.global.js",
36
35
  "types": "dist/index.d.ts",
37
36
  "exports": {
38
37
  "./package.json": "./package.json",
39
38
  ".": {
40
- "import": {
41
- "types": "./dist/index.d.ts",
42
- "default": "./dist/index.js"
43
- },
44
- "require": {
45
- "types": "./dist/index.d.cts",
46
- "default": "./dist/index.cjs"
47
- }
39
+ "types": "./dist/index.d.ts",
40
+ "import": "./dist/index.js",
41
+ "default": "./dist/index.js"
48
42
  }
49
43
  },
50
44
  "publishConfig": {
package/dist/index.cjs DELETED
@@ -1,607 +0,0 @@
1
- 'use strict';
2
-
3
- // src/source-map.ts
4
- var cache = /* @__PURE__ */ new Map();
5
- var VLQ_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
6
- var charToInt = [];
7
- for (let i = 0; i < VLQ_CHARS.length; i++) charToInt[VLQ_CHARS.charCodeAt(i)] = i;
8
- var decodeVLQ = (str, pos) => {
9
- let shift = 0;
10
- let value = 0;
11
- while (pos.i < str.length) {
12
- const digit = charToInt[str.charCodeAt(pos.i++)];
13
- value += (digit & 31) << shift;
14
- if (!(digit & 32)) return value & 1 ? -(value >> 1) : value >> 1;
15
- shift += 5;
16
- }
17
- return 0;
18
- };
19
- var decodeMappings = (raw) => {
20
- const result = [];
21
- let srcIdx = 0;
22
- let origLine = 0;
23
- let origCol = 0;
24
- for (const line of raw.split(";")) {
25
- const segments = [];
26
- let genCol = 0;
27
- if (line) {
28
- const pos = { i: 0 };
29
- while (pos.i < line.length) {
30
- if (line.charCodeAt(pos.i) === 44) {
31
- pos.i++;
32
- continue;
33
- }
34
- genCol += decodeVLQ(line, pos);
35
- if (pos.i >= line.length || line.charCodeAt(pos.i) === 44) continue;
36
- srcIdx += decodeVLQ(line, pos);
37
- origLine += decodeVLQ(line, pos);
38
- origCol += decodeVLQ(line, pos);
39
- if (pos.i < line.length && line.charCodeAt(pos.i) !== 44) decodeVLQ(line, pos);
40
- segments.push([genCol, srcIdx, origLine, origCol]);
41
- }
42
- }
43
- result.push(segments);
44
- }
45
- return result;
46
- };
47
- var DATA_URI_RE = /^data:application\/json[^,]*;base64,([A-Za-z0-9+/=]+)$/;
48
- var isSameOrigin = (a, b) => {
49
- try {
50
- return new URL(a).origin === new URL(b).origin;
51
- } catch {
52
- return false;
53
- }
54
- };
55
- var fetchSourceMapJson = async (ref, baseUrl) => {
56
- if (ref.startsWith("data:")) {
57
- const dataMatch = DATA_URI_RE.exec(ref);
58
- return dataMatch ? atob(dataMatch[1]) : null;
59
- }
60
- const mapUrl = new URL(ref, baseUrl).href;
61
- if (!isSameOrigin(baseUrl, mapUrl)) return null;
62
- const mapRes = await fetch(mapUrl);
63
- if (!mapRes.ok) return null;
64
- return mapRes.text();
65
- };
66
- var flattenIndexedMap = (sections) => {
67
- const allSources = [];
68
- const allMappings = [];
69
- for (const section of sections) {
70
- const decoded = decodeMappings(section.map.mappings);
71
- const lineOff = section.offset.line;
72
- const colOff = section.offset.column;
73
- const srcOff = allSources.length;
74
- while (allMappings.length < lineOff + decoded.length) allMappings.push([]);
75
- for (let i = 0; i < decoded.length; i++) {
76
- const target = allMappings[lineOff + i];
77
- for (const seg of decoded[i]) {
78
- target.push([i === 0 ? seg[0] + colOff : seg[0], seg[1] + srcOff, seg[2], seg[3]]);
79
- }
80
- }
81
- allSources.push(...section.map.sources);
82
- }
83
- for (const line of allMappings) {
84
- if (line.length > 1) line.sort((a, b) => a[0] - b[0]);
85
- }
86
- return { sources: allSources, mappings: allMappings };
87
- };
88
- var parseSourceMap = (json) => {
89
- try {
90
- const raw = JSON.parse(json);
91
- if (Array.isArray(raw.sections)) return flattenIndexedMap(raw.sections);
92
- if (!raw.sources || !raw.mappings) return null;
93
- return { sources: raw.sources, mappings: decodeMappings(raw.mappings) };
94
- } catch {
95
- return null;
96
- }
97
- };
98
- var fetchAndParse = async (url) => {
99
- try {
100
- const res = await fetch(url);
101
- const text = await res.text();
102
- const match = text.match(/\/\/[#@]\s*sourceMappingURL=([^\s]+)$/m);
103
- if (match) {
104
- const json = await fetchSourceMapJson(match[1].trim(), url);
105
- if (json) {
106
- const map = parseSourceMap(json);
107
- if (map) return map;
108
- }
109
- }
110
- const headerRef = res.headers.get("SourceMap") ?? res.headers.get("X-SourceMap");
111
- if (headerRef) {
112
- const json = await fetchSourceMapJson(headerRef.trim(), url);
113
- if (json) {
114
- const map = parseSourceMap(json);
115
- if (map) return map;
116
- }
117
- }
118
- const conventionRes = await fetch(`${url}.map`);
119
- if (conventionRes.ok) {
120
- const json = await conventionRes.text();
121
- return parseSourceMap(json);
122
- }
123
- return null;
124
- } catch {
125
- return null;
126
- }
127
- };
128
- var ABOUT_SERVER_RE = /^about:\/\/React\/Server\/file:\/\/\//;
129
- var NEXT_DOTDIR_RE = /[/\\](\.next[/\\].+?)(?:\?.*)?$/;
130
- var fetchAndParseServerFile = async (url) => {
131
- try {
132
- const filePath = decodeURIComponent(url.replace(ABOUT_SERVER_RE, ""));
133
- const dotNextMatch = NEXT_DOTDIR_RE.exec(filePath);
134
- if (!dotNextMatch) return null;
135
- const origin = typeof location !== "undefined" ? location.origin : "";
136
- const mapUrl = `${origin}/__nextjs_source-map?filename=${encodeURIComponent(dotNextMatch[1])}`;
137
- const res = await fetch(mapUrl);
138
- if (!res.ok) return null;
139
- const json = await res.text();
140
- if (!json) return null;
141
- return parseSourceMap(json);
142
- } catch {
143
- return null;
144
- }
145
- };
146
- var getSourceMap = (url) => {
147
- let promise = cache.get(url);
148
- if (!promise) {
149
- promise = ABOUT_SERVER_RE.test(url) ? fetchAndParseServerFile(url) : fetchAndParse(url);
150
- cache.set(url, promise);
151
- }
152
- return promise;
153
- };
154
- var lookup = (map, genLine, genCol) => {
155
- if (genLine < 0 || genLine >= map.mappings.length) return null;
156
- const segments = map.mappings[genLine];
157
- if (!segments.length) return null;
158
- let lo = 0;
159
- let hi = segments.length - 1;
160
- while (lo < hi) {
161
- const mid = lo + hi + 1 >> 1;
162
- if (segments[mid][0] <= genCol) lo = mid;
163
- else hi = mid - 1;
164
- }
165
- return segments[lo];
166
- };
167
- var resolveOriginalPosition = async (url, line, column) => {
168
- const map = await getSourceMap(url);
169
- if (!map) return null;
170
- const seg = lookup(map, line - 1, column - 1);
171
- if (!seg) return null;
172
- let fileName = map.sources[seg[1]];
173
- if (fileName.startsWith("file:///")) {
174
- fileName = decodeURIComponent(new URL(fileName).pathname);
175
- }
176
- return {
177
- fileName,
178
- lineNumber: seg[2] + 1,
179
- columnNumber: seg[3] + 1
180
- };
181
- };
182
-
183
- // src/fiber.ts
184
- var isServerComponent = (owner) => "env" in owner && typeof owner.name === "string";
185
- var COMPOSITE_TAGS = /* @__PURE__ */ new Set([
186
- 0,
187
- // FunctionComponent
188
- 1,
189
- // ClassComponent
190
- 11,
191
- // ForwardRef
192
- 14,
193
- // MemoComponent
194
- 15
195
- // SimpleMemoComponent
196
- ]);
197
- var getFiberFromElement = (el) => {
198
- try {
199
- const key = Object.keys(el).find((k) => k.startsWith("__reactFiber$"));
200
- if (!key) return null;
201
- return el[key];
202
- } catch {
203
- return null;
204
- }
205
- };
206
- var getCompositeComponentFiber = (fiber) => {
207
- let current = fiber;
208
- while (current) {
209
- if (COMPOSITE_TAGS.has(current.tag)) return current;
210
- current = current.return;
211
- }
212
- return null;
213
- };
214
- var getInnerFunction = (type) => {
215
- if (typeof type === "function") return type;
216
- if (type && typeof type === "object") {
217
- if ("render" in type && typeof type.render === "function")
218
- return type.render;
219
- if ("type" in type && typeof type.type === "function")
220
- return type.type;
221
- }
222
- return null;
223
- };
224
- var getComponentName = (fiber) => {
225
- const { type } = fiber;
226
- if (typeof type === "function") {
227
- return type.displayName || type.name || "Anonymous";
228
- }
229
- if (type && typeof type === "object") {
230
- if ("displayName" in type && type.displayName) return type.displayName;
231
- const inner = getInnerFunction(type);
232
- if (inner) return inner.displayName || inner.name || "Anonymous";
233
- }
234
- return "Anonymous";
235
- };
236
- var SKIP_FRAMES = /* @__PURE__ */ new Set([
237
- "jsxDEV",
238
- "jsxs",
239
- "jsx",
240
- "react-stack-top-frame",
241
- "react_stack_bottom_frame",
242
- "fakeJSXCallSite"
243
- ]);
244
- var FRAME_RE = /at (?:(\S+) )?\(?(.+):(\d+):(\d+)\)?$/;
245
- var parseFirstUserFrame = (fiber) => {
246
- const stack = fiber._debugStack?.stack;
247
- if (!stack) return null;
248
- for (const line of stack.split("\n")) {
249
- const match = FRAME_RE.exec(line.trim());
250
- if (!match) continue;
251
- const [, fnName, url, lineStr, colStr] = match;
252
- if (fnName && SKIP_FRAMES.has(fnName)) continue;
253
- if (url.includes("/node_modules/")) continue;
254
- return { url, line: Number(lineStr), column: Number(colStr) };
255
- }
256
- return null;
257
- };
258
- var resolveFrame = async (frame) => {
259
- const resolved = await resolveOriginalPosition(frame.url, frame.line, frame.column);
260
- if (resolved) return resolved;
261
- let fileName = frame.url;
262
- try {
263
- const parsed = new URL(frame.url);
264
- fileName = decodeURIComponent(parsed.pathname);
265
- const qIdx = fileName.indexOf("?");
266
- if (qIdx !== -1) fileName = fileName.substring(0, qIdx);
267
- } catch {
268
- }
269
- fileName = fileName.replace(/\.\.\//g, "");
270
- if (fileName.startsWith("/")) fileName = fileName.substring(1);
271
- return { fileName, lineNumber: frame.line, columnNumber: frame.column };
272
- };
273
- var getCompositeDebugSource = async (fiber) => {
274
- if (fiber._debugSource) return fiber._debugSource;
275
- if (fiber._debugOwner?._debugSource) return fiber._debugOwner._debugSource;
276
- const frame = parseFirstUserFrame(fiber);
277
- if (frame) return resolveFrame(frame);
278
- if (fiber._debugOwner) {
279
- const ownerFrame = parseFirstUserFrame(fiber._debugOwner);
280
- if (ownerFrame) return resolveFrame(ownerFrame);
281
- }
282
- return null;
283
- };
284
- var getDomDebugSource = async (fiber) => {
285
- if (fiber._debugSource) return fiber._debugSource;
286
- const frame = parseFirstUserFrame(fiber);
287
- if (frame) return resolveFrame(frame);
288
- return null;
289
- };
290
- var getComponentInfo = async (el) => {
291
- const domFiber = getFiberFromElement(el);
292
- if (!domFiber) return null;
293
- const composite = getCompositeComponentFiber(domFiber);
294
- if (!composite) return null;
295
- const isComponentRoot = domFiber.return != null && COMPOSITE_TAGS.has(domFiber.return.tag);
296
- if (isComponentRoot) {
297
- return {
298
- kind: "component",
299
- name: getComponentName(composite),
300
- elementTag: null,
301
- source: await getCompositeDebugSource(composite),
302
- callSite: null
303
- };
304
- }
305
- const owner = domFiber._debugOwner;
306
- const elementTag = typeof domFiber.type === "string" ? domFiber.type : null;
307
- if (owner && !isServerComponent(owner) && owner === composite) {
308
- return {
309
- kind: "element",
310
- name: getComponentName(owner),
311
- elementTag,
312
- source: await getDomDebugSource(domFiber),
313
- callSite: await getCompositeDebugSource(composite)
314
- };
315
- }
316
- const name = owner && isServerComponent(owner) ? owner.name : getComponentName(
317
- owner && !isServerComponent(owner) && COMPOSITE_TAGS.has(owner.tag) ? owner : composite
318
- );
319
- return {
320
- kind: "children",
321
- name,
322
- elementTag,
323
- source: await getDomDebugSource(domFiber),
324
- callSite: null
325
- };
326
- };
327
-
328
- // src/overlay.ts
329
- var isMac = typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform);
330
- var COLORS = {
331
- name: "#93c5fd",
332
- tag: "#a78bfa",
333
- path: "#71717a",
334
- pathActive: "#a1a1aa",
335
- pathDim: "#3f3f46",
336
- hint: "#52525b"
337
- };
338
- var HIGHLIGHT_STYLES = {
339
- position: "fixed",
340
- pointerEvents: "none",
341
- zIndex: "2147483646",
342
- backgroundColor: "rgba(66, 135, 245, 0.15)",
343
- border: "1.5px solid rgba(66, 135, 245, 0.6)",
344
- borderRadius: "3px",
345
- display: "none",
346
- transition: "top 60ms ease-out, left 60ms ease-out, width 60ms ease-out, height 60ms ease-out"
347
- };
348
- var TOOLTIP_STYLES = {
349
- position: "fixed",
350
- pointerEvents: "none",
351
- zIndex: "2147483647",
352
- display: "none",
353
- fontFamily: "ui-monospace, SFMono-Regular, 'SF Mono', Menlo, monospace",
354
- fontSize: "12px",
355
- lineHeight: "1.4",
356
- color: "#e4e4e7",
357
- backgroundColor: "#18181b",
358
- border: "1px solid #3f3f46",
359
- borderRadius: "6px",
360
- padding: "4px 8px",
361
- whiteSpace: "nowrap",
362
- maxWidth: "500px",
363
- overflow: "hidden",
364
- textOverflow: "ellipsis",
365
- boxShadow: "0 4px 12px rgba(0, 0, 0, 0.4)"
366
- };
367
- var applyStyles = (el, styles) => {
368
- Object.assign(el.style, styles);
369
- };
370
- var truncatePath = (filePath) => {
371
- const parts = filePath.split("/");
372
- if (parts.length <= 2) return filePath;
373
- return `.../${parts.slice(-2).join("/")}`;
374
- };
375
- var createSpan = (text, styles) => {
376
- const span = document.createElement("span");
377
- span.textContent = text;
378
- Object.assign(span.style, styles);
379
- return span;
380
- };
381
- var OverlayManager = class {
382
- highlight = null;
383
- tooltip = null;
384
- init() {
385
- if (this.highlight) return;
386
- this.highlight = document.createElement("div");
387
- this.highlight.dataset.reactGrep = "highlight";
388
- applyStyles(this.highlight, HIGHLIGHT_STYLES);
389
- document.body.appendChild(this.highlight);
390
- this.tooltip = document.createElement("div");
391
- this.tooltip.dataset.reactGrep = "tooltip";
392
- applyStyles(this.tooltip, TOOLTIP_STYLES);
393
- document.body.appendChild(this.tooltip);
394
- }
395
- show(el, info, activeSource = "source") {
396
- if (!this.highlight || !this.tooltip) return;
397
- const rect = el.getBoundingClientRect();
398
- this.highlight.style.top = `${rect.top}px`;
399
- this.highlight.style.left = `${rect.left}px`;
400
- this.highlight.style.width = `${rect.width}px`;
401
- this.highlight.style.height = `${rect.height}px`;
402
- this.highlight.style.display = "block";
403
- this.tooltip.textContent = "";
404
- this.tooltip.appendChild(createSpan(info.name, { color: COLORS.name, fontWeight: "600" }));
405
- if (info.elementTag != null) {
406
- this.tooltip.appendChild(createSpan(" > ", { color: COLORS.path }));
407
- this.tooltip.appendChild(
408
- createSpan(info.elementTag, { color: COLORS.tag, fontWeight: "600" })
409
- );
410
- }
411
- const filePath = info.source ? `${info.source.fileName}:${info.source.lineNumber}` : null;
412
- const callSitePath = info.callSite ? `${info.callSite.fileName}:${info.callSite.lineNumber}` : null;
413
- if (filePath && callSitePath) {
414
- const sourceColor = activeSource === "source" ? COLORS.pathActive : COLORS.pathDim;
415
- const callSiteColor = activeSource === "callSite" ? COLORS.pathActive : COLORS.pathDim;
416
- const shiftHint = isMac ? "\u21E7" : "Shift";
417
- const sourceText = truncatePath(filePath);
418
- const callSiteText = truncatePath(callSitePath);
419
- this.tooltip.appendChild(
420
- createSpan(
421
- ` ${activeSource === "callSite" ? "(" : ""}${sourceText}${activeSource === "callSite" ? ")" : ""}`,
422
- { color: sourceColor }
423
- )
424
- );
425
- this.tooltip.appendChild(createSpan(` ${shiftHint} `, { color: COLORS.hint }));
426
- this.tooltip.appendChild(
427
- createSpan(
428
- `${activeSource === "source" ? "(" : ""}${callSiteText}${activeSource === "source" ? ")" : ""}`,
429
- { color: callSiteColor }
430
- )
431
- );
432
- } else if (filePath) {
433
- this.tooltip.appendChild(createSpan(` ${truncatePath(filePath)}`, { color: COLORS.path }));
434
- }
435
- const tooltipRect = this.tooltip.getBoundingClientRect();
436
- let top = rect.top - tooltipRect.height - 6;
437
- let left = rect.left;
438
- if (top < 4) {
439
- top = rect.bottom + 6;
440
- }
441
- if (left + tooltipRect.width > window.innerWidth - 4) {
442
- left = window.innerWidth - tooltipRect.width - 4;
443
- }
444
- this.tooltip.style.top = `${top}px`;
445
- this.tooltip.style.left = `${Math.max(4, left)}px`;
446
- this.tooltip.style.display = "block";
447
- }
448
- showCopied(location2) {
449
- if (!this.tooltip) return;
450
- this.tooltip.textContent = "";
451
- this.tooltip.appendChild(createSpan("Copied!", { color: "#4ade80", fontWeight: "600" }));
452
- this.tooltip.appendChild(createSpan(` ${truncatePath(location2)}`, { color: "#a1a1aa" }));
453
- this.tooltip.style.display = "block";
454
- setTimeout(() => this.hide(), 1500);
455
- }
456
- hide() {
457
- if (this.highlight) this.highlight.style.display = "none";
458
- if (this.tooltip) this.tooltip.style.display = "none";
459
- }
460
- destroy() {
461
- this.highlight?.remove();
462
- this.tooltip?.remove();
463
- this.highlight = null;
464
- this.tooltip = null;
465
- }
466
- };
467
-
468
- // src/inspector.ts
469
- var isMac2 = typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform);
470
- var Inspector = class {
471
- overlay = new OverlayManager();
472
- moveGeneration = 0;
473
- lastTarget = null;
474
- lastInfo = null;
475
- sourceToggled = false;
476
- shiftPressedClean = false;
477
- boundHandlers;
478
- constructor() {
479
- this.boundHandlers = {
480
- mousemove: this.handleMouseMove.bind(this),
481
- click: this.handleClick.bind(this),
482
- keydown: this.handleKeyDown.bind(this),
483
- keyup: this.handleKeyUp.bind(this)
484
- };
485
- }
486
- start() {
487
- window.addEventListener("mousemove", this.boundHandlers.mousemove);
488
- window.addEventListener("click", this.boundHandlers.click, true);
489
- window.addEventListener("keydown", this.boundHandlers.keydown);
490
- window.addEventListener("keyup", this.boundHandlers.keyup);
491
- }
492
- stop() {
493
- window.removeEventListener("mousemove", this.boundHandlers.mousemove);
494
- window.removeEventListener("click", this.boundHandlers.click, true);
495
- window.removeEventListener("keydown", this.boundHandlers.keydown);
496
- window.removeEventListener("keyup", this.boundHandlers.keyup);
497
- this.overlay.destroy();
498
- document.body.style.cursor = "";
499
- this.lastTarget = null;
500
- this.lastInfo = null;
501
- this.sourceToggled = false;
502
- this.shiftPressedClean = false;
503
- }
504
- isModifierHeld(e) {
505
- return isMac2 ? e.metaKey : e.ctrlKey;
506
- }
507
- async handleMouseMove(e) {
508
- if (!this.isModifierHeld(e)) {
509
- this.overlay.hide();
510
- document.body.style.cursor = "";
511
- this.lastTarget = null;
512
- this.lastInfo = null;
513
- return;
514
- }
515
- this.overlay.init();
516
- document.body.style.cursor = "crosshair";
517
- const target = document.elementFromPoint(e.clientX, e.clientY);
518
- if (!target || target.closest("[data-react-grep]")) return;
519
- if (target !== this.lastTarget) {
520
- this.sourceToggled = false;
521
- }
522
- const gen = ++this.moveGeneration;
523
- const info = await getComponentInfo(target);
524
- if (gen !== this.moveGeneration) return;
525
- if (!info) {
526
- this.overlay.hide();
527
- this.lastTarget = null;
528
- this.lastInfo = null;
529
- return;
530
- }
531
- this.lastTarget = target;
532
- this.lastInfo = info;
533
- this.overlay.show(target, info, this.getActiveSource());
534
- }
535
- async handleClick(e) {
536
- if (!this.isModifierHeld(e) || !e.shiftKey) return;
537
- const target = document.elementFromPoint(e.clientX, e.clientY);
538
- if (!target || target.closest("[data-react-grep]")) return;
539
- e.preventDefault();
540
- e.stopPropagation();
541
- e.stopImmediatePropagation();
542
- this.shiftPressedClean = false;
543
- const info = await getComponentInfo(target);
544
- if (!info) return;
545
- const source = this.getActiveCopySource(info);
546
- if (!source) return;
547
- const { fileName, lineNumber, columnNumber } = source;
548
- const location2 = columnNumber != null ? `${fileName}:${lineNumber}:${columnNumber}` : `${fileName}:${lineNumber}`;
549
- await this.copyToClipboard(location2);
550
- this.overlay.showCopied(location2);
551
- }
552
- handleKeyDown(e) {
553
- if (e.key === "Shift" && this.isModifierHeld(e)) {
554
- this.shiftPressedClean = true;
555
- }
556
- }
557
- handleKeyUp(e) {
558
- if (isMac2 && e.key === "Meta" || !isMac2 && e.key === "Control") {
559
- this.overlay.hide();
560
- document.body.style.cursor = "";
561
- this.lastTarget = null;
562
- this.lastInfo = null;
563
- return;
564
- }
565
- if (e.key === "Shift" && this.shiftPressedClean && this.lastTarget && this.lastInfo && this.lastInfo.callSite) {
566
- this.sourceToggled = !this.sourceToggled;
567
- this.overlay.show(this.lastTarget, this.lastInfo, this.getActiveSource());
568
- }
569
- this.shiftPressedClean = false;
570
- }
571
- getActiveSource() {
572
- return this.sourceToggled ? "callSite" : "source";
573
- }
574
- getActiveCopySource(info) {
575
- return this.sourceToggled && info.callSite ? info.callSite : info.source;
576
- }
577
- async copyToClipboard(text) {
578
- try {
579
- await navigator.clipboard.writeText(text);
580
- } catch {
581
- }
582
- }
583
- };
584
-
585
- // src/index.ts
586
- var inspector = null;
587
- var init = () => {
588
- if (inspector) return;
589
- inspector = new Inspector();
590
- inspector.start();
591
- };
592
- var destroy = () => {
593
- if (!inspector) return;
594
- inspector.stop();
595
- inspector = null;
596
- };
597
- if (typeof window !== "undefined") {
598
- const bootstrap = () => init();
599
- if (document.readyState === "loading") {
600
- document.addEventListener("DOMContentLoaded", bootstrap);
601
- } else {
602
- bootstrap();
603
- }
604
- }
605
-
606
- exports.destroy = destroy;
607
- exports.init = init;
package/dist/index.d.cts DELETED
@@ -1,4 +0,0 @@
1
- declare const init: () => void;
2
- declare const destroy: () => void;
3
-
4
- export { destroy, init };