react-beetle-inspector
Version:
Debug re-renders, API calls and performance issues in React apps with an overlay.
226 lines (225 loc) • 8.01 kB
JavaScript
import r, { useRef as h, useEffect as x, useState as y, useLayoutEffect as O } from "react";
function W(n, e) {
return JSON.stringify(n) === JSON.stringify(e);
}
const D = /* @__PURE__ */ new Map(), X = () => [...D.entries()].sort((n, e) => e[1] - n[1]).slice(0, 5), F = (n, e = {}) => {
const o = h(e), a = h(1);
x(() => {
if (process.env.NODE_ENV !== "development") return;
const l = Object.entries(e).reduce((m, [d, f]) => (W(f, o.current[d]) || (m[d] = {
from: o.current[d],
to: f
}), m), {}), u = n || "Unknown";
D.set(u, (D.get(u) || 0) + 1), console.groupCollapsed(
`%c[${u}] Render #${a.current}`,
"color: dodgerblue;"
), Object.keys(l).length === 0 ? console.log("No prop changes.") : console.log("Changed Props:", l), console.trace(`[${u}] render trace`), console.groupEnd(), o.current = e, a.current += 1;
});
}, E = [], Y = 100;
let R = [];
const j = () => {
R.forEach((n) => n([...E]));
}, M = (n) => (R.push(n), n([...E]), () => {
R = R.filter((e) => e !== n);
}), C = ({ url: n, method: e, duration: o, status: a, stack: l }) => {
E.length > Y && E.shift(), E.push({ url: n, method: e, duration: o, status: a, time: /* @__PURE__ */ new Date(), stack: l }), j();
}, U = () => {
if (typeof window > "u") return;
const n = window.fetch;
window.fetch = async (...e) => {
var m;
const o = e[0], a = (((m = e[1]) == null ? void 0 : m.method) || "GET").toUpperCase(), l = performance.now(), u = new Error().stack.split(`
`).slice(2, 6).join(`
`);
try {
const d = await n(...e), f = (performance.now() - l).toFixed(2);
return C({
url: o,
method: a,
duration: f,
status: d.status,
stack: u
}), d;
} catch (d) {
const f = (performance.now() - l).toFixed(2);
throw C({
url: o,
method: a,
duration: f,
status: "ERROR",
stack: u
}), d;
}
};
};
function A(n) {
n.interceptors.request.use((e) => (e.metadata = { startTime: /* @__PURE__ */ new Date() }, e)), n.interceptors.response.use(
(e) => {
const o = /* @__PURE__ */ new Date() - e.config.metadata.startTime;
return C({
method: e.config.method.toUpperCase(),
url: e.config.url,
duration: o,
status: e.status
}), e;
},
(e) => {
const o = e.config || {}, a = o.metadata ? /* @__PURE__ */ new Date() - o.metadata.startTime : 0;
return C({
method: o.method ? o.method.toUpperCase() : "UNKNOWN",
url: o.url || "unknown",
duration: a,
status: e.response ? e.response.status : "ERR"
}), Promise.reject(e);
}
);
}
const $ = () => {
const [n, e] = y([]), [o, a] = y([]), [l, u] = y(!1), [m, d] = y(!1), [f, P] = y({ x: 0, y: 0 }), b = h(null), S = h(null), w = h({
x: window.innerWidth - 80,
y: window.innerHeight - 80
}), g = h({ isDragging: !1, offsetX: 0, offsetY: 0 }), v = h(!1), T = (t, i) => {
const s = b.current;
if (!s) return;
const p = Math.max(0, Math.min(t, window.innerWidth - s.offsetWidth)), c = Math.max(0, Math.min(i, window.innerHeight - s.offsetHeight));
s.style.left = `${p}px`, s.style.top = `${c}px`, s.style.right = "auto", s.style.bottom = "auto", w.current = { x: p, y: c };
}, H = () => {
const t = S.current;
if (!t) return;
const { offsetWidth: i, offsetHeight: s } = t, { x: p, y: c } = w.current, L = p < window.innerWidth / 2, k = c < window.innerHeight / 2, I = L ? p + 10 : p - i - 10, N = k ? c + 10 : c - s - 10;
P({ x: I, y: N });
};
return O(() => {
l && H();
}, [l]), x(() => {
const t = b.current;
if (!t) return;
const i = (c) => {
g.current.isDragging = !0, v.current = !1, g.current.offsetX = c.clientX - t.offsetLeft, g.current.offsetY = c.clientY - t.offsetTop;
}, s = (c) => {
if (!g.current.isDragging) return;
v.current = !0;
const L = c.clientX - g.current.offsetX, k = c.clientY - g.current.offsetY;
T(L, k);
}, p = () => {
g.current.isDragging = !1;
};
return t.addEventListener("pointerdown", i), window.addEventListener("pointermove", s), window.addEventListener("pointerup", p), () => {
t.removeEventListener("pointerdown", i), window.removeEventListener("pointermove", s), window.removeEventListener("pointerup", p);
};
}, []), x(() => {
const t = (i) => {
i.ctrlKey && i.key === "`" && d((s) => !s);
};
return window.addEventListener("keydown", t), () => window.removeEventListener("keydown", t);
}, []), x(() => {
const t = M(e), i = setInterval(() => {
a(X());
}, 2e3);
return () => {
t(), clearInterval(i);
};
}, []), m ? /* @__PURE__ */ r.createElement(
"div",
{
ref: b,
onClick: () => {
v.current || u((t) => !t);
},
style: {
position: "fixed",
zIndex: 1e4,
left: w.current.x,
top: w.current.y,
width: 50,
height: 50,
borderRadius: "50%",
background: "#000",
color: "#fff",
display: "none",
// hide visually
alignItems: "center",
justifyContent: "center",
fontSize: 24,
cursor: "grab",
userSelect: "none"
},
title: "Toggle Debug Panel"
},
"🐞"
) : /* @__PURE__ */ r.createElement(r.Fragment, null, /* @__PURE__ */ r.createElement(
"div",
{
ref: b,
onClick: () => {
v.current || u((t) => !t);
},
style: {
position: "fixed",
zIndex: 1e4,
left: w.current.x,
top: w.current.y,
width: 50,
height: 50,
borderRadius: "50%",
background: "#000",
color: "#fff",
display: "flex",
alignItems: "center",
justifyContent: "center",
fontSize: 24,
cursor: "grab",
userSelect: "none"
},
title: "Toggle Debug Panel"
},
"🐞"
), l && /* @__PURE__ */ r.createElement(
"div",
{
ref: S,
className: "debug-panel",
style: {
position: "fixed",
top: f.y,
left: f.x,
zIndex: 10001,
width: 320,
maxHeight: 400,
overflowY: "auto",
backgroundColor: "#111",
color: "#fff",
fontFamily: "monospace",
borderRadius: 12,
padding: 16,
boxShadow: "0 8px 16px rgba(0,0,0,0.3)",
transition: "opacity 0.3s ease"
}
},
/* @__PURE__ */ r.createElement("div", { style: { display: "flex", justifyContent: "space-between" } }, /* @__PURE__ */ r.createElement("strong", null, "Debug Info"), /* @__PURE__ */ r.createElement(
"button",
{
onClick: () => u(!1),
style: {
background: "transparent",
border: "none",
color: "#fff",
fontSize: "18px",
cursor: "pointer"
}
},
"×"
)),
/* @__PURE__ */ r.createElement("div", { style: { marginTop: 10 } }, /* @__PURE__ */ r.createElement("strong", null, "Top Rendered Components"), /* @__PURE__ */ r.createElement("ul", { style: { paddingLeft: 15 } }, o.map(([t, i], s) => /* @__PURE__ */ r.createElement("li", { key: s }, t, ": ", i)))),
/* @__PURE__ */ r.createElement("div", { style: { marginTop: 10 } }, /* @__PURE__ */ r.createElement("strong", null, "API Calls (", n.length, ")"), /* @__PURE__ */ r.createElement("ul", { style: { paddingLeft: 15 } }, n.slice(-5).reverse().map((t, i) => /* @__PURE__ */ r.createElement("li", { key: i }, /* @__PURE__ */ r.createElement("div", { style: { wordBreak: "break-word" } }, t.method, " ", t.url), /* @__PURE__ */ r.createElement("small", null, t.duration, "ms | ", t.status, " |", " ", t.time.toLocaleTimeString())))))
));
};
export {
$ as DebugOverlay,
X as getRenderStats,
A as patchAxiosInstance,
U as patchGlobalFetch,
M as subscribeToApiLogs,
F as useWhyRender
};
//# sourceMappingURL=index.es.js.map