react-grep 0.1.1 → 0.2.0

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 x=new Map,k="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",E=[];for(let e=0;e<k.length;e++)E[k.charCodeAt(e)]=e;var d=(e,t)=>{let n=0,i=0;for(;t.i<e.length;){let o=E[e.charCodeAt(t.i++)];if(i+=(o&31)<<n,!(o&32))return i&1?-(i>>1):i>>1;n+=5;}return 0},L=e=>{let t=[],n=0,i=0,o=0;for(let s of e.split(";")){let l=[],a=0;if(s){let r={i:0};for(;r.i<s.length;){if(s.charCodeAt(r.i)===44){r.i++;continue}a+=d(s,r),!(r.i>=s.length||s.charCodeAt(r.i)===44)&&(n+=d(s,r),i+=d(s,r),o+=d(s,r),r.i<s.length&&s.charCodeAt(r.i)!==44&&d(s,r),l.push([a,n,i,o]));}}t.push(l);}return t},_=/^data:application\/json[^,]*;base64,([A-Za-z0-9+/=]+)$/,A=(e,t)=>{try{return new URL(e).origin===new URL(t).origin}catch{return false}},H=async e=>{try{let i=(await(await fetch(e)).text()).match(/\/\/[#@]\s*sourceMappingURL=([^\s]+)$/m);if(!i)return null;let o=i[1].trim(),s;if(o.startsWith("data:")){let a=_.exec(o);if(!a)return null;s=atob(a[1]);}else {let a=new URL(o,e).href;if(!A(e,a))return null;s=await(await fetch(a)).text();}let l=JSON.parse(s);return {sources:l.sources,mappings:L(l.mappings)}}catch{return null}},O=e=>{let t=x.get(e);return t||(t=H(e),x.set(e,t)),t},K=(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,s=i.length-1;for(;o<s;){let l=o+s+1>>1;i[l][0]<=n?o=l:s=l-1;}return i[o]},M=async(e,t,n)=>{let i=await O(e);if(!i)return null;let o=K(i,t-1,n-1);return o?{fileName:i.sources[o[1]],lineNumber:o[2]+1,columnNumber:o[3]+1}:null};var b=new Set([0,1,11,14,15]),U=e=>{try{let t=Object.keys(e).find(n=>n.startsWith("__reactFiber$"));return t?e[t]:null}catch{return null}},j=e=>{let t=e;for(;t;){if(b.has(t.tag))return t;t=t.return;}return null},W=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},y=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=W(t);if(n)return n.displayName||n.name||"Anonymous"}return "Anonymous"},G=new Set(["jsxDEV","jsxs","jsx","react-stack-top-frame","react_stack_bottom_frame"]),z=/at (?:(\S+) )?\(?(.+):(\d+):(\d+)\)?$/,v=e=>{let t=e._debugStack?.stack;if(!t)return null;for(let n of t.split(`
2
- `)){let i=z.exec(n.trim());if(!i)continue;let[,o,s,l,a]=i;if(!(o&&G.has(o))&&!s.includes("/node_modules/"))return {url:s,line:Number(l),column:Number(a)}}return null},S=async e=>{let t=await M(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}},T=async e=>{if(e._debugSource)return e._debugSource;if(e._debugOwner?._debugSource)return e._debugOwner._debugSource;let t=v(e);if(t)return S(t);if(e._debugOwner){let n=v(e._debugOwner);if(n)return S(n)}return null},F=async e=>{if(e._debugSource)return e._debugSource;let t=v(e);return t?S(t):null},w=async e=>{let t=U(e);if(!t)return null;let n=j(t);if(!n)return null;if(t.return!=null&&b.has(t.return.tag))return {kind:"component",name:y(n),elementTag:null,source:await T(n),callSite:null};let o=t._debugOwner,s=typeof t.type=="string"?t.type:null;if(o&&o===n)return {kind:"element",name:y(o),elementTag:s,source:await F(t),callSite:await T(n)};let l=o&&b.has(o.tag)?o:n;return {kind:"children",name:y(l),elementTag:s,source:await F(t),callSite:null}};var Y=typeof navigator<"u"&&/Mac|iPhone|iPad/.test(navigator.platform),c={name:"#93c5fd",tag:"#a78bfa",path:"#71717a",pathActive:"#a1a1aa",pathDim:"#3f3f46",hint:"#52525b"},V={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"},B={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)"},P=(e,t)=>{Object.assign(e.style,t);},p=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},m=class{highlight=null;tooltip=null;init(){this.highlight||(this.highlight=document.createElement("div"),this.highlight.dataset.reactGrep="highlight",P(this.highlight,V),document.body.appendChild(this.highlight),this.tooltip=document.createElement("div"),this.tooltip.dataset.reactGrep="tooltip",P(this.tooltip,B),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 s=n.source?`${n.source.fileName}:${n.source.lineNumber}`:null,l=n.callSite?`${n.callSite.fileName}:${n.callSite.lineNumber}`:null;if(s&&l){let I=i==="source"?c.pathActive:c.pathDim,D=i==="callSite"?c.pathActive:c.pathDim,N=Y?"\u21E7":"Shift",$=p(s),R=p(l);this.tooltip.appendChild(u(` ${i==="callSite"?"(":""}${$}${i==="callSite"?")":""}`,{color:I})),this.tooltip.appendChild(u(` ${N} `,{color:c.hint})),this.tooltip.appendChild(u(`${i==="source"?"(":""}${R}${i==="source"?")":""}`,{color:D}));}else s&&this.tooltip.appendChild(u(` ${p(s)}`,{color:c.path}));let a=this.tooltip.getBoundingClientRect(),r=o.top-a.height-6,f=o.left;r<4&&(r=o.bottom+6),f+a.width>window.innerWidth-4&&(f=window.innerWidth-a.width-4),this.tooltip.style.top=`${r}px`,this.tooltip.style.left=`${Math.max(4,f)}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(` ${p(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 C=typeof navigator<"u"&&/Mac|iPhone|iPad/.test(navigator.platform),g=class{overlay=new m;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 C?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 w(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 w(n);if(!i)return;let o=this.getActiveCopySource(i);if(!o)return;let{fileName:s,lineNumber:l,columnNumber:a}=o,r=a!=null?`${s}:${l}:${a}`:`${s}:${l}`;await this.copyToClipboard(r),this.overlay.showCopied(r);}handleKeyDown(t){t.key==="Shift"&&this.isModifierHeld(t)&&(this.shiftPressedClean=true);}handleKeyUp(t){if(C&&t.key==="Meta"||!C&&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 h=null,J=()=>{h||(h=new g,h.start());},it=()=>{h&&(h.stop(),h=null);};if(typeof window<"u"){let e=()=>J();document.readyState==="loading"?document.addEventListener("DOMContentLoaded",e):e();}
3
- exports.destroy=it;exports.init=J;return exports;})({});
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}},f=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=f(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=f(s);if(a)return a}}let r=await fetch(`${e}.map`);if(r.ok){let s=await r.text();return f(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?f(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 g=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;let t=e._debugOwner;if(t&&!g(t)&&t._debugSource)return t._debugSource;let n=C(e);if(n)return x(n);if(t&&!g(t)){let i=C(t);if(i)return x(i)}return null},N=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&&!g(o)&&o===n?{kind:"element",name:v(o),elementTag:r,source:await N(t),callSite:await $(n)}:{kind:"children",name:o&&g(o)?o.name:v(o&&!g(o)&&w.has(o.tag)?o:n),elementTag:r,source:await N(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)"},_=(e,t)=>{Object.assign(e.style,t);},y=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},b=class{highlight=null;tooltip=null;init(){this.highlight||(this.highlight=document.createElement("div"),this.highlight.dataset.reactGrep="highlight",_(this.highlight,tt),document.body.appendChild(this.highlight),this.tooltip=document.createElement("div"),this.tooltip.dataset.reactGrep="tooltip",_(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,A=i==="callSite"?c.pathActive:c.pathDim,L=Z?"\u21E7":"Shift",O=y(r),H=y(s);this.tooltip.appendChild(u(` ${i==="callSite"?"(":""}${O}${i==="callSite"?")":""}`,{color:h})),this.tooltip.appendChild(u(` ${L} `,{color:c.hint})),this.tooltip.appendChild(u(`${i==="source"?"(":""}${H}${i==="source"?")":""}`,{color:A}));}else r&&this.tooltip.appendChild(u(` ${y(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(` ${y(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),S=class{overlay=new b;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 S,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;})({});
package/dist/index.js CHANGED
@@ -50,26 +50,93 @@ var isSameOrigin = (a, b) => {
50
50
  return false;
51
51
  }
52
52
  };
53
+ var fetchSourceMapJson = async (ref, baseUrl) => {
54
+ if (ref.startsWith("data:")) {
55
+ const dataMatch = DATA_URI_RE.exec(ref);
56
+ return dataMatch ? atob(dataMatch[1]) : null;
57
+ }
58
+ const mapUrl = new URL(ref, baseUrl).href;
59
+ if (!isSameOrigin(baseUrl, mapUrl)) return null;
60
+ const mapRes = await fetch(mapUrl);
61
+ if (!mapRes.ok) return null;
62
+ return mapRes.text();
63
+ };
64
+ var flattenIndexedMap = (sections) => {
65
+ const allSources = [];
66
+ const allMappings = [];
67
+ for (const section of sections) {
68
+ const decoded = decodeMappings(section.map.mappings);
69
+ const lineOff = section.offset.line;
70
+ const colOff = section.offset.column;
71
+ const srcOff = allSources.length;
72
+ while (allMappings.length < lineOff + decoded.length) allMappings.push([]);
73
+ for (let i = 0; i < decoded.length; i++) {
74
+ const target = allMappings[lineOff + i];
75
+ for (const seg of decoded[i]) {
76
+ target.push([i === 0 ? seg[0] + colOff : seg[0], seg[1] + srcOff, seg[2], seg[3]]);
77
+ }
78
+ }
79
+ allSources.push(...section.map.sources);
80
+ }
81
+ for (const line of allMappings) {
82
+ if (line.length > 1) line.sort((a, b) => a[0] - b[0]);
83
+ }
84
+ return { sources: allSources, mappings: allMappings };
85
+ };
86
+ var parseSourceMap = (json) => {
87
+ try {
88
+ const raw = JSON.parse(json);
89
+ if (Array.isArray(raw.sections)) return flattenIndexedMap(raw.sections);
90
+ if (!raw.sources || !raw.mappings) return null;
91
+ return { sources: raw.sources, mappings: decodeMappings(raw.mappings) };
92
+ } catch {
93
+ return null;
94
+ }
95
+ };
53
96
  var fetchAndParse = async (url) => {
54
97
  try {
55
98
  const res = await fetch(url);
56
99
  const text = await res.text();
57
100
  const match = text.match(/\/\/[#@]\s*sourceMappingURL=([^\s]+)$/m);
58
- if (!match) return null;
59
- const ref = match[1].trim();
60
- let mapJson;
61
- if (ref.startsWith("data:")) {
62
- const dataMatch = DATA_URI_RE.exec(ref);
63
- if (!dataMatch) return null;
64
- mapJson = atob(dataMatch[1]);
65
- } else {
66
- const mapUrl = new URL(ref, url).href;
67
- if (!isSameOrigin(url, mapUrl)) return null;
68
- const mapRes = await fetch(mapUrl);
69
- mapJson = await mapRes.text();
101
+ if (match) {
102
+ const json = await fetchSourceMapJson(match[1].trim(), url);
103
+ if (json) {
104
+ const map = parseSourceMap(json);
105
+ if (map) return map;
106
+ }
70
107
  }
71
- const raw = JSON.parse(mapJson);
72
- return { sources: raw.sources, mappings: decodeMappings(raw.mappings) };
108
+ const headerRef = res.headers.get("SourceMap") ?? res.headers.get("X-SourceMap");
109
+ if (headerRef) {
110
+ const json = await fetchSourceMapJson(headerRef.trim(), url);
111
+ if (json) {
112
+ const map = parseSourceMap(json);
113
+ if (map) return map;
114
+ }
115
+ }
116
+ const conventionRes = await fetch(`${url}.map`);
117
+ if (conventionRes.ok) {
118
+ const json = await conventionRes.text();
119
+ return parseSourceMap(json);
120
+ }
121
+ return null;
122
+ } catch {
123
+ return null;
124
+ }
125
+ };
126
+ var ABOUT_SERVER_RE = /^about:\/\/React\/Server\/file:\/\/\//;
127
+ var NEXT_DOTDIR_RE = /[/\\](\.next[/\\].+?)(?:\?.*)?$/;
128
+ var fetchAndParseServerFile = async (url) => {
129
+ try {
130
+ const filePath = decodeURIComponent(url.replace(ABOUT_SERVER_RE, ""));
131
+ const dotNextMatch = NEXT_DOTDIR_RE.exec(filePath);
132
+ if (!dotNextMatch) return null;
133
+ const origin = typeof location !== "undefined" ? location.origin : "";
134
+ const mapUrl = `${origin}/__nextjs_source-map?filename=${encodeURIComponent(dotNextMatch[1])}`;
135
+ const res = await fetch(mapUrl);
136
+ if (!res.ok) return null;
137
+ const json = await res.text();
138
+ if (!json) return null;
139
+ return parseSourceMap(json);
73
140
  } catch {
74
141
  return null;
75
142
  }
@@ -77,7 +144,7 @@ var fetchAndParse = async (url) => {
77
144
  var getSourceMap = (url) => {
78
145
  let promise = cache.get(url);
79
146
  if (!promise) {
80
- promise = fetchAndParse(url);
147
+ promise = ABOUT_SERVER_RE.test(url) ? fetchAndParseServerFile(url) : fetchAndParse(url);
81
148
  cache.set(url, promise);
82
149
  }
83
150
  return promise;
@@ -100,7 +167,10 @@ var resolveOriginalPosition = async (url, line, column) => {
100
167
  if (!map) return null;
101
168
  const seg = lookup(map, line - 1, column - 1);
102
169
  if (!seg) return null;
103
- const fileName = map.sources[seg[1]];
170
+ let fileName = map.sources[seg[1]];
171
+ if (fileName.startsWith("file:///")) {
172
+ fileName = decodeURIComponent(new URL(fileName).pathname);
173
+ }
104
174
  return {
105
175
  fileName,
106
176
  lineNumber: seg[2] + 1,
@@ -109,6 +179,7 @@ var resolveOriginalPosition = async (url, line, column) => {
109
179
  };
110
180
 
111
181
  // src/fiber.ts
182
+ var isServerComponent = (owner) => "env" in owner && typeof owner.name === "string";
112
183
  var COMPOSITE_TAGS = /* @__PURE__ */ new Set([
113
184
  0,
114
185
  // FunctionComponent
@@ -165,7 +236,8 @@ var SKIP_FRAMES = /* @__PURE__ */ new Set([
165
236
  "jsxs",
166
237
  "jsx",
167
238
  "react-stack-top-frame",
168
- "react_stack_bottom_frame"
239
+ "react_stack_bottom_frame",
240
+ "fakeJSXCallSite"
169
241
  ]);
170
242
  var FRAME_RE = /at (?:(\S+) )?\(?(.+):(\d+):(\d+)\)?$/;
171
243
  var parseFirstUserFrame = (fiber) => {
@@ -198,11 +270,12 @@ var resolveFrame = async (frame) => {
198
270
  };
199
271
  var getCompositeDebugSource = async (fiber) => {
200
272
  if (fiber._debugSource) return fiber._debugSource;
201
- if (fiber._debugOwner?._debugSource) return fiber._debugOwner._debugSource;
273
+ const owner = fiber._debugOwner;
274
+ if (owner && !isServerComponent(owner) && owner._debugSource) return owner._debugSource;
202
275
  const frame = parseFirstUserFrame(fiber);
203
276
  if (frame) return resolveFrame(frame);
204
- if (fiber._debugOwner) {
205
- const ownerFrame = parseFirstUserFrame(fiber._debugOwner);
277
+ if (owner && !isServerComponent(owner)) {
278
+ const ownerFrame = parseFirstUserFrame(owner);
206
279
  if (ownerFrame) return resolveFrame(ownerFrame);
207
280
  }
208
281
  return null;
@@ -230,7 +303,7 @@ var getComponentInfo = async (el) => {
230
303
  }
231
304
  const owner = domFiber._debugOwner;
232
305
  const elementTag = typeof domFiber.type === "string" ? domFiber.type : null;
233
- if (owner && owner === composite) {
306
+ if (owner && !isServerComponent(owner) && owner === composite) {
234
307
  return {
235
308
  kind: "element",
236
309
  name: getComponentName(owner),
@@ -239,10 +312,12 @@ var getComponentInfo = async (el) => {
239
312
  callSite: await getCompositeDebugSource(composite)
240
313
  };
241
314
  }
242
- const nameSource = owner && COMPOSITE_TAGS.has(owner.tag) ? owner : composite;
315
+ const name = owner && isServerComponent(owner) ? owner.name : getComponentName(
316
+ owner && !isServerComponent(owner) && COMPOSITE_TAGS.has(owner.tag) ? owner : composite
317
+ );
243
318
  return {
244
319
  kind: "children",
245
- name: getComponentName(nameSource),
320
+ name,
246
321
  elementTag,
247
322
  source: await getDomDebugSource(domFiber),
248
323
  callSite: null
@@ -369,11 +444,11 @@ var OverlayManager = class {
369
444
  this.tooltip.style.left = `${Math.max(4, left)}px`;
370
445
  this.tooltip.style.display = "block";
371
446
  }
372
- showCopied(location) {
447
+ showCopied(location2) {
373
448
  if (!this.tooltip) return;
374
449
  this.tooltip.textContent = "";
375
450
  this.tooltip.appendChild(createSpan("Copied!", { color: "#4ade80", fontWeight: "600" }));
376
- this.tooltip.appendChild(createSpan(` ${truncatePath(location)}`, { color: "#a1a1aa" }));
451
+ this.tooltip.appendChild(createSpan(` ${truncatePath(location2)}`, { color: "#a1a1aa" }));
377
452
  this.tooltip.style.display = "block";
378
453
  setTimeout(() => this.hide(), 1500);
379
454
  }
@@ -469,9 +544,9 @@ var Inspector = class {
469
544
  const source = this.getActiveCopySource(info);
470
545
  if (!source) return;
471
546
  const { fileName, lineNumber, columnNumber } = source;
472
- const location = columnNumber != null ? `${fileName}:${lineNumber}:${columnNumber}` : `${fileName}:${lineNumber}`;
473
- await this.copyToClipboard(location);
474
- this.overlay.showCopied(location);
547
+ const location2 = columnNumber != null ? `${fileName}:${lineNumber}:${columnNumber}` : `${fileName}:${lineNumber}`;
548
+ await this.copyToClipboard(location2);
549
+ this.overlay.showCopied(location2);
475
550
  }
476
551
  handleKeyDown(e) {
477
552
  if (e.key === "Shift" && this.isModifierHeld(e)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-grep",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
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,14 @@
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
+ "default": "./dist/index.js"
48
41
  }
49
42
  },
50
43
  "publishConfig": {
package/dist/index.cjs DELETED
@@ -1,533 +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 fetchAndParse = async (url) => {
56
- try {
57
- const res = await fetch(url);
58
- const text = await res.text();
59
- const match = text.match(/\/\/[#@]\s*sourceMappingURL=([^\s]+)$/m);
60
- if (!match) return null;
61
- const ref = match[1].trim();
62
- let mapJson;
63
- if (ref.startsWith("data:")) {
64
- const dataMatch = DATA_URI_RE.exec(ref);
65
- if (!dataMatch) return null;
66
- mapJson = atob(dataMatch[1]);
67
- } else {
68
- const mapUrl = new URL(ref, url).href;
69
- if (!isSameOrigin(url, mapUrl)) return null;
70
- const mapRes = await fetch(mapUrl);
71
- mapJson = await mapRes.text();
72
- }
73
- const raw = JSON.parse(mapJson);
74
- return { sources: raw.sources, mappings: decodeMappings(raw.mappings) };
75
- } catch {
76
- return null;
77
- }
78
- };
79
- var getSourceMap = (url) => {
80
- let promise = cache.get(url);
81
- if (!promise) {
82
- promise = fetchAndParse(url);
83
- cache.set(url, promise);
84
- }
85
- return promise;
86
- };
87
- var lookup = (map, genLine, genCol) => {
88
- if (genLine < 0 || genLine >= map.mappings.length) return null;
89
- const segments = map.mappings[genLine];
90
- if (!segments.length) return null;
91
- let lo = 0;
92
- let hi = segments.length - 1;
93
- while (lo < hi) {
94
- const mid = lo + hi + 1 >> 1;
95
- if (segments[mid][0] <= genCol) lo = mid;
96
- else hi = mid - 1;
97
- }
98
- return segments[lo];
99
- };
100
- var resolveOriginalPosition = async (url, line, column) => {
101
- const map = await getSourceMap(url);
102
- if (!map) return null;
103
- const seg = lookup(map, line - 1, column - 1);
104
- if (!seg) return null;
105
- const fileName = map.sources[seg[1]];
106
- return {
107
- fileName,
108
- lineNumber: seg[2] + 1,
109
- columnNumber: seg[3] + 1
110
- };
111
- };
112
-
113
- // src/fiber.ts
114
- var COMPOSITE_TAGS = /* @__PURE__ */ new Set([
115
- 0,
116
- // FunctionComponent
117
- 1,
118
- // ClassComponent
119
- 11,
120
- // ForwardRef
121
- 14,
122
- // MemoComponent
123
- 15
124
- // SimpleMemoComponent
125
- ]);
126
- var getFiberFromElement = (el) => {
127
- try {
128
- const key = Object.keys(el).find((k) => k.startsWith("__reactFiber$"));
129
- if (!key) return null;
130
- return el[key];
131
- } catch {
132
- return null;
133
- }
134
- };
135
- var getCompositeComponentFiber = (fiber) => {
136
- let current = fiber;
137
- while (current) {
138
- if (COMPOSITE_TAGS.has(current.tag)) return current;
139
- current = current.return;
140
- }
141
- return null;
142
- };
143
- var getInnerFunction = (type) => {
144
- if (typeof type === "function") return type;
145
- if (type && typeof type === "object") {
146
- if ("render" in type && typeof type.render === "function")
147
- return type.render;
148
- if ("type" in type && typeof type.type === "function")
149
- return type.type;
150
- }
151
- return null;
152
- };
153
- var getComponentName = (fiber) => {
154
- const { type } = fiber;
155
- if (typeof type === "function") {
156
- return type.displayName || type.name || "Anonymous";
157
- }
158
- if (type && typeof type === "object") {
159
- if ("displayName" in type && type.displayName) return type.displayName;
160
- const inner = getInnerFunction(type);
161
- if (inner) return inner.displayName || inner.name || "Anonymous";
162
- }
163
- return "Anonymous";
164
- };
165
- var SKIP_FRAMES = /* @__PURE__ */ new Set([
166
- "jsxDEV",
167
- "jsxs",
168
- "jsx",
169
- "react-stack-top-frame",
170
- "react_stack_bottom_frame"
171
- ]);
172
- var FRAME_RE = /at (?:(\S+) )?\(?(.+):(\d+):(\d+)\)?$/;
173
- var parseFirstUserFrame = (fiber) => {
174
- const stack = fiber._debugStack?.stack;
175
- if (!stack) return null;
176
- for (const line of stack.split("\n")) {
177
- const match = FRAME_RE.exec(line.trim());
178
- if (!match) continue;
179
- const [, fnName, url, lineStr, colStr] = match;
180
- if (fnName && SKIP_FRAMES.has(fnName)) continue;
181
- if (url.includes("/node_modules/")) continue;
182
- return { url, line: Number(lineStr), column: Number(colStr) };
183
- }
184
- return null;
185
- };
186
- var resolveFrame = async (frame) => {
187
- const resolved = await resolveOriginalPosition(frame.url, frame.line, frame.column);
188
- if (resolved) return resolved;
189
- let fileName = frame.url;
190
- try {
191
- const parsed = new URL(frame.url);
192
- fileName = decodeURIComponent(parsed.pathname);
193
- const qIdx = fileName.indexOf("?");
194
- if (qIdx !== -1) fileName = fileName.substring(0, qIdx);
195
- } catch {
196
- }
197
- fileName = fileName.replace(/\.\.\//g, "");
198
- if (fileName.startsWith("/")) fileName = fileName.substring(1);
199
- return { fileName, lineNumber: frame.line, columnNumber: frame.column };
200
- };
201
- var getCompositeDebugSource = async (fiber) => {
202
- if (fiber._debugSource) return fiber._debugSource;
203
- if (fiber._debugOwner?._debugSource) return fiber._debugOwner._debugSource;
204
- const frame = parseFirstUserFrame(fiber);
205
- if (frame) return resolveFrame(frame);
206
- if (fiber._debugOwner) {
207
- const ownerFrame = parseFirstUserFrame(fiber._debugOwner);
208
- if (ownerFrame) return resolveFrame(ownerFrame);
209
- }
210
- return null;
211
- };
212
- var getDomDebugSource = async (fiber) => {
213
- if (fiber._debugSource) return fiber._debugSource;
214
- const frame = parseFirstUserFrame(fiber);
215
- if (frame) return resolveFrame(frame);
216
- return null;
217
- };
218
- var getComponentInfo = async (el) => {
219
- const domFiber = getFiberFromElement(el);
220
- if (!domFiber) return null;
221
- const composite = getCompositeComponentFiber(domFiber);
222
- if (!composite) return null;
223
- const isComponentRoot = domFiber.return != null && COMPOSITE_TAGS.has(domFiber.return.tag);
224
- if (isComponentRoot) {
225
- return {
226
- kind: "component",
227
- name: getComponentName(composite),
228
- elementTag: null,
229
- source: await getCompositeDebugSource(composite),
230
- callSite: null
231
- };
232
- }
233
- const owner = domFiber._debugOwner;
234
- const elementTag = typeof domFiber.type === "string" ? domFiber.type : null;
235
- if (owner && owner === composite) {
236
- return {
237
- kind: "element",
238
- name: getComponentName(owner),
239
- elementTag,
240
- source: await getDomDebugSource(domFiber),
241
- callSite: await getCompositeDebugSource(composite)
242
- };
243
- }
244
- const nameSource = owner && COMPOSITE_TAGS.has(owner.tag) ? owner : composite;
245
- return {
246
- kind: "children",
247
- name: getComponentName(nameSource),
248
- elementTag,
249
- source: await getDomDebugSource(domFiber),
250
- callSite: null
251
- };
252
- };
253
-
254
- // src/overlay.ts
255
- var isMac = typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform);
256
- var COLORS = {
257
- name: "#93c5fd",
258
- tag: "#a78bfa",
259
- path: "#71717a",
260
- pathActive: "#a1a1aa",
261
- pathDim: "#3f3f46",
262
- hint: "#52525b"
263
- };
264
- var HIGHLIGHT_STYLES = {
265
- position: "fixed",
266
- pointerEvents: "none",
267
- zIndex: "2147483646",
268
- backgroundColor: "rgba(66, 135, 245, 0.15)",
269
- border: "1.5px solid rgba(66, 135, 245, 0.6)",
270
- borderRadius: "3px",
271
- display: "none",
272
- transition: "top 60ms ease-out, left 60ms ease-out, width 60ms ease-out, height 60ms ease-out"
273
- };
274
- var TOOLTIP_STYLES = {
275
- position: "fixed",
276
- pointerEvents: "none",
277
- zIndex: "2147483647",
278
- display: "none",
279
- fontFamily: "ui-monospace, SFMono-Regular, 'SF Mono', Menlo, monospace",
280
- fontSize: "12px",
281
- lineHeight: "1.4",
282
- color: "#e4e4e7",
283
- backgroundColor: "#18181b",
284
- border: "1px solid #3f3f46",
285
- borderRadius: "6px",
286
- padding: "4px 8px",
287
- whiteSpace: "nowrap",
288
- maxWidth: "500px",
289
- overflow: "hidden",
290
- textOverflow: "ellipsis",
291
- boxShadow: "0 4px 12px rgba(0, 0, 0, 0.4)"
292
- };
293
- var applyStyles = (el, styles) => {
294
- Object.assign(el.style, styles);
295
- };
296
- var truncatePath = (filePath) => {
297
- const parts = filePath.split("/");
298
- if (parts.length <= 2) return filePath;
299
- return `.../${parts.slice(-2).join("/")}`;
300
- };
301
- var createSpan = (text, styles) => {
302
- const span = document.createElement("span");
303
- span.textContent = text;
304
- Object.assign(span.style, styles);
305
- return span;
306
- };
307
- var OverlayManager = class {
308
- highlight = null;
309
- tooltip = null;
310
- init() {
311
- if (this.highlight) return;
312
- this.highlight = document.createElement("div");
313
- this.highlight.dataset.reactGrep = "highlight";
314
- applyStyles(this.highlight, HIGHLIGHT_STYLES);
315
- document.body.appendChild(this.highlight);
316
- this.tooltip = document.createElement("div");
317
- this.tooltip.dataset.reactGrep = "tooltip";
318
- applyStyles(this.tooltip, TOOLTIP_STYLES);
319
- document.body.appendChild(this.tooltip);
320
- }
321
- show(el, info, activeSource = "source") {
322
- if (!this.highlight || !this.tooltip) return;
323
- const rect = el.getBoundingClientRect();
324
- this.highlight.style.top = `${rect.top}px`;
325
- this.highlight.style.left = `${rect.left}px`;
326
- this.highlight.style.width = `${rect.width}px`;
327
- this.highlight.style.height = `${rect.height}px`;
328
- this.highlight.style.display = "block";
329
- this.tooltip.textContent = "";
330
- this.tooltip.appendChild(createSpan(info.name, { color: COLORS.name, fontWeight: "600" }));
331
- if (info.elementTag != null) {
332
- this.tooltip.appendChild(createSpan(" > ", { color: COLORS.path }));
333
- this.tooltip.appendChild(
334
- createSpan(info.elementTag, { color: COLORS.tag, fontWeight: "600" })
335
- );
336
- }
337
- const filePath = info.source ? `${info.source.fileName}:${info.source.lineNumber}` : null;
338
- const callSitePath = info.callSite ? `${info.callSite.fileName}:${info.callSite.lineNumber}` : null;
339
- if (filePath && callSitePath) {
340
- const sourceColor = activeSource === "source" ? COLORS.pathActive : COLORS.pathDim;
341
- const callSiteColor = activeSource === "callSite" ? COLORS.pathActive : COLORS.pathDim;
342
- const shiftHint = isMac ? "\u21E7" : "Shift";
343
- const sourceText = truncatePath(filePath);
344
- const callSiteText = truncatePath(callSitePath);
345
- this.tooltip.appendChild(
346
- createSpan(
347
- ` ${activeSource === "callSite" ? "(" : ""}${sourceText}${activeSource === "callSite" ? ")" : ""}`,
348
- { color: sourceColor }
349
- )
350
- );
351
- this.tooltip.appendChild(createSpan(` ${shiftHint} `, { color: COLORS.hint }));
352
- this.tooltip.appendChild(
353
- createSpan(
354
- `${activeSource === "source" ? "(" : ""}${callSiteText}${activeSource === "source" ? ")" : ""}`,
355
- { color: callSiteColor }
356
- )
357
- );
358
- } else if (filePath) {
359
- this.tooltip.appendChild(createSpan(` ${truncatePath(filePath)}`, { color: COLORS.path }));
360
- }
361
- const tooltipRect = this.tooltip.getBoundingClientRect();
362
- let top = rect.top - tooltipRect.height - 6;
363
- let left = rect.left;
364
- if (top < 4) {
365
- top = rect.bottom + 6;
366
- }
367
- if (left + tooltipRect.width > window.innerWidth - 4) {
368
- left = window.innerWidth - tooltipRect.width - 4;
369
- }
370
- this.tooltip.style.top = `${top}px`;
371
- this.tooltip.style.left = `${Math.max(4, left)}px`;
372
- this.tooltip.style.display = "block";
373
- }
374
- showCopied(location) {
375
- if (!this.tooltip) return;
376
- this.tooltip.textContent = "";
377
- this.tooltip.appendChild(createSpan("Copied!", { color: "#4ade80", fontWeight: "600" }));
378
- this.tooltip.appendChild(createSpan(` ${truncatePath(location)}`, { color: "#a1a1aa" }));
379
- this.tooltip.style.display = "block";
380
- setTimeout(() => this.hide(), 1500);
381
- }
382
- hide() {
383
- if (this.highlight) this.highlight.style.display = "none";
384
- if (this.tooltip) this.tooltip.style.display = "none";
385
- }
386
- destroy() {
387
- this.highlight?.remove();
388
- this.tooltip?.remove();
389
- this.highlight = null;
390
- this.tooltip = null;
391
- }
392
- };
393
-
394
- // src/inspector.ts
395
- var isMac2 = typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform);
396
- var Inspector = class {
397
- overlay = new OverlayManager();
398
- moveGeneration = 0;
399
- lastTarget = null;
400
- lastInfo = null;
401
- sourceToggled = false;
402
- shiftPressedClean = false;
403
- boundHandlers;
404
- constructor() {
405
- this.boundHandlers = {
406
- mousemove: this.handleMouseMove.bind(this),
407
- click: this.handleClick.bind(this),
408
- keydown: this.handleKeyDown.bind(this),
409
- keyup: this.handleKeyUp.bind(this)
410
- };
411
- }
412
- start() {
413
- window.addEventListener("mousemove", this.boundHandlers.mousemove);
414
- window.addEventListener("click", this.boundHandlers.click, true);
415
- window.addEventListener("keydown", this.boundHandlers.keydown);
416
- window.addEventListener("keyup", this.boundHandlers.keyup);
417
- }
418
- stop() {
419
- window.removeEventListener("mousemove", this.boundHandlers.mousemove);
420
- window.removeEventListener("click", this.boundHandlers.click, true);
421
- window.removeEventListener("keydown", this.boundHandlers.keydown);
422
- window.removeEventListener("keyup", this.boundHandlers.keyup);
423
- this.overlay.destroy();
424
- document.body.style.cursor = "";
425
- this.lastTarget = null;
426
- this.lastInfo = null;
427
- this.sourceToggled = false;
428
- this.shiftPressedClean = false;
429
- }
430
- isModifierHeld(e) {
431
- return isMac2 ? e.metaKey : e.ctrlKey;
432
- }
433
- async handleMouseMove(e) {
434
- if (!this.isModifierHeld(e)) {
435
- this.overlay.hide();
436
- document.body.style.cursor = "";
437
- this.lastTarget = null;
438
- this.lastInfo = null;
439
- return;
440
- }
441
- this.overlay.init();
442
- document.body.style.cursor = "crosshair";
443
- const target = document.elementFromPoint(e.clientX, e.clientY);
444
- if (!target || target.closest("[data-react-grep]")) return;
445
- if (target !== this.lastTarget) {
446
- this.sourceToggled = false;
447
- }
448
- const gen = ++this.moveGeneration;
449
- const info = await getComponentInfo(target);
450
- if (gen !== this.moveGeneration) return;
451
- if (!info) {
452
- this.overlay.hide();
453
- this.lastTarget = null;
454
- this.lastInfo = null;
455
- return;
456
- }
457
- this.lastTarget = target;
458
- this.lastInfo = info;
459
- this.overlay.show(target, info, this.getActiveSource());
460
- }
461
- async handleClick(e) {
462
- if (!this.isModifierHeld(e) || !e.shiftKey) return;
463
- const target = document.elementFromPoint(e.clientX, e.clientY);
464
- if (!target || target.closest("[data-react-grep]")) return;
465
- e.preventDefault();
466
- e.stopPropagation();
467
- e.stopImmediatePropagation();
468
- this.shiftPressedClean = false;
469
- const info = await getComponentInfo(target);
470
- if (!info) return;
471
- const source = this.getActiveCopySource(info);
472
- if (!source) return;
473
- const { fileName, lineNumber, columnNumber } = source;
474
- const location = columnNumber != null ? `${fileName}:${lineNumber}:${columnNumber}` : `${fileName}:${lineNumber}`;
475
- await this.copyToClipboard(location);
476
- this.overlay.showCopied(location);
477
- }
478
- handleKeyDown(e) {
479
- if (e.key === "Shift" && this.isModifierHeld(e)) {
480
- this.shiftPressedClean = true;
481
- }
482
- }
483
- handleKeyUp(e) {
484
- if (isMac2 && e.key === "Meta" || !isMac2 && e.key === "Control") {
485
- this.overlay.hide();
486
- document.body.style.cursor = "";
487
- this.lastTarget = null;
488
- this.lastInfo = null;
489
- return;
490
- }
491
- if (e.key === "Shift" && this.shiftPressedClean && this.lastTarget && this.lastInfo && this.lastInfo.callSite) {
492
- this.sourceToggled = !this.sourceToggled;
493
- this.overlay.show(this.lastTarget, this.lastInfo, this.getActiveSource());
494
- }
495
- this.shiftPressedClean = false;
496
- }
497
- getActiveSource() {
498
- return this.sourceToggled ? "callSite" : "source";
499
- }
500
- getActiveCopySource(info) {
501
- return this.sourceToggled && info.callSite ? info.callSite : info.source;
502
- }
503
- async copyToClipboard(text) {
504
- try {
505
- await navigator.clipboard.writeText(text);
506
- } catch {
507
- }
508
- }
509
- };
510
-
511
- // src/index.ts
512
- var inspector = null;
513
- var init = () => {
514
- if (inspector) return;
515
- inspector = new Inspector();
516
- inspector.start();
517
- };
518
- var destroy = () => {
519
- if (!inspector) return;
520
- inspector.stop();
521
- inspector = null;
522
- };
523
- if (typeof window !== "undefined") {
524
- const bootstrap = () => init();
525
- if (document.readyState === "loading") {
526
- document.addEventListener("DOMContentLoaded", bootstrap);
527
- } else {
528
- bootstrap();
529
- }
530
- }
531
-
532
- exports.destroy = destroy;
533
- 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 };