vite-plugin-component-instrumentation
Version:
A professional Vite plugin that automatically adds identifying data attributes to React components for debugging, testing, and analytics
78 lines (77 loc) • 2.52 kB
JavaScript
// Dev-only minimal state event publisher using React DevTools hook
// Security: does not expose values; only emits component id/name placeholders
// Performance: batches per-commit, idle-callback throttled
const DEFAULT_CFG = {
throttleMs: 120,
maxItemsPerFlush: 200,
};
export function setupStateEvents() {
const cfg = DEFAULT_CFG;
const queue = [];
let scheduled = false;
function enqueue(item) {
queue.push(item);
if (!scheduled) {
scheduled = true;
const schedule = window.requestIdleCallback || ((cb) => setTimeout(cb, cfg.throttleMs));
schedule(flush);
}
}
function flush() {
scheduled = false;
if (!queue.length)
return;
const items = queue.splice(0, DEFAULT_CFG.maxItemsPerFlush);
window.dispatchEvent(new CustomEvent('ct:state', { detail: { ts: Date.now(), items } }));
}
const hook = window.__REACT_DEVTOOLS_GLOBAL_HOOK__;
if (!hook || !hook.onCommitFiberRoot)
return;
const original = hook.onCommitFiberRoot.bind(hook);
hook.onCommitFiberRoot = function patched(rendererID, root, ...rest) {
try {
const current = root?.current;
if (current)
walk(current, (fiber) => {
const node = findHostNode(fiber);
if (!node)
return;
const id = node.getAttribute?.('data-ct-id') || node.getAttribute?.('data-debug-id');
if (!id)
return;
const name = node.getAttribute?.('data-ct-name') || node.getAttribute?.('data-debug-name');
enqueue({ id, name: name || undefined });
});
}
catch { }
return original(rendererID, root, ...rest);
};
}
function walk(fiber, visit) {
try {
visit(fiber);
}
catch { }
let child = fiber.child;
while (child) {
walk(child, visit);
child = child.sibling;
}
}
function findHostNode(fiber) {
// Prefer closest host child
let f = fiber;
while (f && !isHost(f))
f = f.child || f.sibling;
if (isHost(f))
return f.stateNode;
// Fallback upward
f = fiber;
while (f && !isHost(f))
f = f.return;
return isHost(f) ? f.stateNode : null;
}
function isHost(f) {
// HostComponent = 5, HostText = 6
return f && (f.tag === 5 || f.tag === 6) && f.stateNode instanceof Element;
}