react-render-detective 0.5.0 → 0.6.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/CHANGELOG.md +55 -0
- package/README.md +2 -2
- package/dist/babel.cjs +2 -2
- package/dist/babel.d.cts +11 -0
- package/dist/babel.d.ts +11 -0
- package/dist/babel.js +1 -1
- package/dist/{chunk-DI5STJM4.js → chunk-3TZNM7JO.js} +66 -3
- package/dist/chunk-3TZNM7JO.js.map +1 -0
- package/dist/{chunk-HOTY7J3X.js → chunk-D7M4UC3Y.js} +26 -2
- package/dist/chunk-D7M4UC3Y.js.map +1 -0
- package/dist/{chunk-PHYYA67T.cjs → chunk-EXDRRUUQ.cjs} +69 -2
- package/dist/chunk-EXDRRUUQ.cjs.map +1 -0
- package/dist/{chunk-OSPTERGK.cjs → chunk-LCGXVRJR.cjs} +66 -3
- package/dist/chunk-LCGXVRJR.cjs.map +1 -0
- package/dist/{chunk-MCIQMYNR.js → chunk-QVHAC7AD.js} +69 -2
- package/dist/chunk-QVHAC7AD.js.map +1 -0
- package/dist/{chunk-JD4IJ4MN.cjs → chunk-RGT44QQI.cjs} +26 -2
- package/dist/chunk-RGT44QQI.cjs.map +1 -0
- package/dist/core.cjs +18 -18
- package/dist/core.d.cts +13 -3
- package/dist/core.d.ts +13 -3
- package/dist/core.js +1 -1
- package/dist/index.cjs +72 -274
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +35 -115
- package/dist/index.d.ts +35 -115
- package/dist/index.js +41 -237
- package/dist/index.js.map +1 -1
- package/dist/interactions.cjs +252 -0
- package/dist/interactions.cjs.map +1 -0
- package/dist/interactions.d.cts +110 -0
- package/dist/interactions.d.ts +110 -0
- package/dist/interactions.js +241 -0
- package/dist/interactions.js.map +1 -0
- package/dist/overlay.cjs +5 -5
- package/dist/overlay.js +2 -2
- package/dist/testing.d.cts +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/{types-gl13xwmN.d.cts → types-CRET8EhB.d.cts} +20 -2
- package/dist/{types-gl13xwmN.d.ts → types-CRET8EhB.d.ts} +20 -2
- package/dist/vite.cjs +2 -2
- package/dist/vite.js +1 -1
- package/package.json +6 -1
- package/dist/chunk-DI5STJM4.js.map +0 -1
- package/dist/chunk-HOTY7J3X.js.map +0 -1
- package/dist/chunk-JD4IJ4MN.cjs.map +0 -1
- package/dist/chunk-MCIQMYNR.js.map +0 -1
- package/dist/chunk-OSPTERGK.cjs.map +0 -1
- package/dist/chunk-PHYYA67T.cjs.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
var chunkOSPTERGK_cjs = require('./chunk-OSPTERGK.cjs');
|
|
3
|
+
var chunkRGT44QQI_cjs = require('./chunk-RGT44QQI.cjs');
|
|
5
4
|
var chunkH5RG2EPP_cjs = require('./chunk-H5RG2EPP.cjs');
|
|
5
|
+
var chunkLCGXVRJR_cjs = require('./chunk-LCGXVRJR.cjs');
|
|
6
6
|
var React = require('react');
|
|
7
7
|
var jsxRuntime = require('react/jsx-runtime');
|
|
8
8
|
|
|
@@ -189,7 +189,7 @@ function printVerbose(event) {
|
|
|
189
189
|
if (event.unchangedProps.length > 0) console.log(`Props same: ${event.unchangedProps.join(", ")}`);
|
|
190
190
|
if (event.trackedState.length > 0) {
|
|
191
191
|
console.log(
|
|
192
|
-
`State: ${event.trackedState.map((s) => `${s.name}: ${
|
|
192
|
+
`State: ${event.trackedState.map((s) => `${s.name}: ${chunkLCGXVRJR_cjs.formatInspected(s.previous)} \u2192 ${chunkLCGXVRJR_cjs.formatInspected(s.current)}`).join(", ")}`
|
|
193
193
|
);
|
|
194
194
|
}
|
|
195
195
|
if (event.contextChanges.length > 0) {
|
|
@@ -206,11 +206,11 @@ function printVerbose(event) {
|
|
|
206
206
|
}
|
|
207
207
|
function propLine(c) {
|
|
208
208
|
const head = `${c.key} (${c.valueType}) \u2014 ${describeKind(c)}`;
|
|
209
|
-
if (c.kind === "added") return `${head}: ${
|
|
210
|
-
if (c.kind === "removed") return `${head}: was ${
|
|
209
|
+
if (c.kind === "added") return `${head}: ${chunkLCGXVRJR_cjs.formatInspected(c.current)}`;
|
|
210
|
+
if (c.kind === "removed") return `${head}: was ${chunkLCGXVRJR_cjs.formatInspected(c.previous)}`;
|
|
211
211
|
return `${head}
|
|
212
|
-
previous: ${
|
|
213
|
-
current: ${
|
|
212
|
+
previous: ${chunkLCGXVRJR_cjs.formatInspected(c.previous)}
|
|
213
|
+
current: ${chunkLCGXVRJR_cjs.formatInspected(c.current)}`;
|
|
214
214
|
}
|
|
215
215
|
function describeKind(c) {
|
|
216
216
|
switch (c.kind) {
|
|
@@ -237,6 +237,8 @@ function label(event) {
|
|
|
237
237
|
return "context update";
|
|
238
238
|
case "state":
|
|
239
239
|
return "state changed";
|
|
240
|
+
case "store":
|
|
241
|
+
return "store update";
|
|
240
242
|
case "state-or-external":
|
|
241
243
|
return "state or external store";
|
|
242
244
|
case "unknown":
|
|
@@ -244,194 +246,6 @@ function label(event) {
|
|
|
244
246
|
}
|
|
245
247
|
}
|
|
246
248
|
|
|
247
|
-
// src/core/interactions.ts
|
|
248
|
-
var COMMIT_SLACK_MS = 100;
|
|
249
|
-
var FALLBACK_CLOSE_MS = 50;
|
|
250
|
-
var InteractionTracker = class {
|
|
251
|
-
constructor(capacity = 50) {
|
|
252
|
-
this.capacity = capacity;
|
|
253
|
-
this.records = [];
|
|
254
|
-
this.nextId = 0;
|
|
255
|
-
}
|
|
256
|
-
/** Returns false when the browser cannot report event timing. */
|
|
257
|
-
start() {
|
|
258
|
-
if (this.observer) return true;
|
|
259
|
-
const PO = globalThis.PerformanceObserver;
|
|
260
|
-
const supported = PO?.supportedEntryTypes?.includes("event");
|
|
261
|
-
if (!PO || !supported) return false;
|
|
262
|
-
try {
|
|
263
|
-
const observer = new PO((list) => {
|
|
264
|
-
for (const entry of list.getEntries()) {
|
|
265
|
-
const timing = entry;
|
|
266
|
-
this.record({
|
|
267
|
-
name: timing.name,
|
|
268
|
-
startTime: timing.startTime,
|
|
269
|
-
duration: timing.duration,
|
|
270
|
-
target: describeTarget(timing.target)
|
|
271
|
-
});
|
|
272
|
-
}
|
|
273
|
-
});
|
|
274
|
-
observer.observe({ type: "event", buffered: true, durationThreshold: 16 });
|
|
275
|
-
this.observer = observer;
|
|
276
|
-
return true;
|
|
277
|
-
} catch {
|
|
278
|
-
return false;
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
stop() {
|
|
282
|
-
this.observer?.disconnect();
|
|
283
|
-
this.observer = void 0;
|
|
284
|
-
}
|
|
285
|
-
/** Is the automatic path available in this browser? */
|
|
286
|
-
get automatic() {
|
|
287
|
-
return this.observer !== void 0;
|
|
288
|
-
}
|
|
289
|
-
/**
|
|
290
|
-
* Time an interaction by hand.
|
|
291
|
-
*
|
|
292
|
-
* The automatic path depends on the Event Timing API, which Safari only
|
|
293
|
-
* gained in 16.4 and which does not fire for synthetic input at all — so
|
|
294
|
-
* anything driven by a test harness records nothing. This measures a specific
|
|
295
|
-
* action instead, up to the paint that follows it, and needs no browser
|
|
296
|
-
* support beyond `performance.now`.
|
|
297
|
-
*/
|
|
298
|
-
measure(label2, action) {
|
|
299
|
-
const startTime = now();
|
|
300
|
-
let handlerMs = 0;
|
|
301
|
-
let finished = false;
|
|
302
|
-
const finish = () => {
|
|
303
|
-
if (finished) return;
|
|
304
|
-
finished = true;
|
|
305
|
-
this.record({ name: label2, startTime, duration: now() - startTime, handlerMs });
|
|
306
|
-
};
|
|
307
|
-
let result;
|
|
308
|
-
try {
|
|
309
|
-
result = action();
|
|
310
|
-
handlerMs = now() - startTime;
|
|
311
|
-
} catch (error) {
|
|
312
|
-
handlerMs = now() - startTime;
|
|
313
|
-
finish();
|
|
314
|
-
throw error;
|
|
315
|
-
}
|
|
316
|
-
const raf = globalThis.requestAnimationFrame;
|
|
317
|
-
if (raf) raf(() => raf(finish));
|
|
318
|
-
setTimeout(finish, FALLBACK_CLOSE_MS);
|
|
319
|
-
return result;
|
|
320
|
-
}
|
|
321
|
-
/** Exposed for tests and for `measure`. */
|
|
322
|
-
record(timing) {
|
|
323
|
-
const record = {
|
|
324
|
-
id: `interaction_${++this.nextId}`,
|
|
325
|
-
type: timing.name,
|
|
326
|
-
target: timing.target,
|
|
327
|
-
startTime: timing.startTime,
|
|
328
|
-
durationMs: timing.duration,
|
|
329
|
-
handlerMs: timing.handlerMs,
|
|
330
|
-
renders: [],
|
|
331
|
-
renderTimeMs: 0,
|
|
332
|
-
avoidableRenderTimeMs: 0
|
|
333
|
-
};
|
|
334
|
-
this.records.push(record);
|
|
335
|
-
if (this.records.length > this.capacity) this.records.shift();
|
|
336
|
-
return record;
|
|
337
|
-
}
|
|
338
|
-
clear() {
|
|
339
|
-
this.records.length = 0;
|
|
340
|
-
}
|
|
341
|
-
/**
|
|
342
|
-
* Joins render events to interactions by commit time. A render belongs to an
|
|
343
|
-
* interaction when it committed between the event starting and shortly after
|
|
344
|
-
* it finished — React commits just after the event handler returns.
|
|
345
|
-
*/
|
|
346
|
-
attribute(events) {
|
|
347
|
-
for (const record of this.records) {
|
|
348
|
-
const from = record.startTime;
|
|
349
|
-
const to = record.startTime + record.durationMs + COMMIT_SLACK_MS;
|
|
350
|
-
record.renders = events.filter((e) => e.timings.commitTime >= from && e.timings.commitTime <= to);
|
|
351
|
-
record.renderTimeMs = record.renders.reduce((a, e) => a + e.timings.selfDuration, 0);
|
|
352
|
-
record.avoidableRenderTimeMs = record.renders.filter((e) => e.diagnosis.potentiallyAvoidable).reduce((a, e) => a + e.timings.selfDuration, 0);
|
|
353
|
-
}
|
|
354
|
-
return [...this.records].sort((a, b) => b.durationMs - a.durationMs);
|
|
355
|
-
}
|
|
356
|
-
};
|
|
357
|
-
function summarise(record) {
|
|
358
|
-
const byComponent = /* @__PURE__ */ new Map();
|
|
359
|
-
for (const event of record.renders) {
|
|
360
|
-
const entry = byComponent.get(event.component.name) ?? {
|
|
361
|
-
renders: 0,
|
|
362
|
-
totalMs: 0,
|
|
363
|
-
source: event.component.source,
|
|
364
|
-
cause: event.diagnosis.reason
|
|
365
|
-
};
|
|
366
|
-
entry.renders++;
|
|
367
|
-
entry.totalMs += event.timings.selfDuration;
|
|
368
|
-
byComponent.set(event.component.name, entry);
|
|
369
|
-
}
|
|
370
|
-
const contributors = [...byComponent.entries()].map(([component, v]) => ({ component, source: v.source, renders: v.renders, totalMs: v.totalMs, cause: v.cause })).sort((a, b) => b.totalMs - a.totalMs);
|
|
371
|
-
const top = contributors[0];
|
|
372
|
-
const accounted = (record.handlerMs ?? record.durationMs) + record.renderTimeMs;
|
|
373
|
-
const idleWindow = record.handlerMs !== void 0 && record.durationMs > accounted * 3 && record.durationMs - accounted > 100;
|
|
374
|
-
const effectiveMs = idleWindow ? accounted : record.durationMs;
|
|
375
|
-
const share = effectiveMs > 0 ? record.renderTimeMs / effectiveMs : 0;
|
|
376
|
-
let headline;
|
|
377
|
-
let nextStep;
|
|
378
|
-
let confidence = "medium";
|
|
379
|
-
if (idleWindow) {
|
|
380
|
-
headline = `${record.type}: ${fmt(record.handlerMs ?? 0)} in the handler and ${fmt(record.renderTimeMs)} rendering. The measured window was ${fmt(record.durationMs)}, but most of that was the page waiting for a frame \u2014 ignore it.`;
|
|
381
|
-
nextStep = record.renderTimeMs > (record.handlerMs ?? 0) ? `Rendering dominates the real work${top ? `; start with ${top.component}` : ""}.` : "The handler itself costs more than rendering. Profile the handler, not React.";
|
|
382
|
-
return { interaction: record, contributors, headline, nextStep, confidence: "medium" };
|
|
383
|
-
}
|
|
384
|
-
if (record.renders.length === 0) {
|
|
385
|
-
headline = `${record.type} took ${fmt(record.durationMs)}, and no instrumented component rendered inside it.`;
|
|
386
|
-
nextStep = "The cost is somewhere other than React rendering \u2014 an event handler, a layout, or an uninstrumented component. Instrument more of the tree to narrow it down.";
|
|
387
|
-
confidence = "low";
|
|
388
|
-
} else if (share >= 0.4 && record.avoidableRenderTimeMs > 0) {
|
|
389
|
-
headline = `${record.type} took ${fmt(record.durationMs)}; ${fmt(record.renderTimeMs)} of it was rendering, and ${fmt(record.avoidableRenderTimeMs)} of that had no input change to explain it.`;
|
|
390
|
-
nextStep = top ? `Start with ${top.component}${top.source ? ` (${top.source})` : ""} \u2014 ${fmt(top.totalMs)} across ${top.renders} render${top.renders === 1 ? "" : "s"}.` : "Look at the top contributor below.";
|
|
391
|
-
confidence = "high";
|
|
392
|
-
} else if (share >= 0.4) {
|
|
393
|
-
headline = `${record.type} took ${fmt(record.durationMs)}; ${fmt(record.renderTimeMs)} of it was rendering, all of it explained by real input changes.`;
|
|
394
|
-
nextStep = "This is genuine work. Make the renders cheaper rather than fewer \u2014 or do less of it per interaction.";
|
|
395
|
-
confidence = "high";
|
|
396
|
-
} else {
|
|
397
|
-
headline = `${record.type} took ${fmt(record.durationMs)}, but only ${fmt(record.renderTimeMs)} was React rendering.`;
|
|
398
|
-
nextStep = "Most of the cost is outside rendering \u2014 event handlers, layout or paint. A browser profile will show more than this tool can.";
|
|
399
|
-
confidence = "medium";
|
|
400
|
-
}
|
|
401
|
-
return { interaction: record, contributors, headline, nextStep, confidence };
|
|
402
|
-
}
|
|
403
|
-
function formatInteraction(summary) {
|
|
404
|
-
const { interaction: i } = summary;
|
|
405
|
-
const lines = [
|
|
406
|
-
`${i.type}${i.target ? ` on ${i.target}` : ""} ${fmt(i.durationMs)}`,
|
|
407
|
-
"",
|
|
408
|
-
summary.headline
|
|
409
|
-
];
|
|
410
|
-
if (summary.contributors.length > 0) {
|
|
411
|
-
lines.push("", "Rendering inside this interaction");
|
|
412
|
-
for (const c of summary.contributors.slice(0, 8)) {
|
|
413
|
-
lines.push(
|
|
414
|
-
` ${c.component.padEnd(22)} ${String(c.renders).padStart(4)} render(s) ${fmt(c.totalMs).padStart(8)} ${c.cause}`
|
|
415
|
-
);
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
lines.push("", `Next step`, ` ${summary.nextStep}`, "", `Confidence: ${summary.confidence}`);
|
|
419
|
-
return lines.join("\n");
|
|
420
|
-
}
|
|
421
|
-
function describeTarget(target) {
|
|
422
|
-
if (!target || typeof target !== "object") return void 0;
|
|
423
|
-
const el = target;
|
|
424
|
-
if (!el.tagName) return void 0;
|
|
425
|
-
const tag = el.tagName.toLowerCase();
|
|
426
|
-
if (el.id) return `${tag}#${el.id}`;
|
|
427
|
-
const className = typeof el.className === "string" ? el.className.trim().split(/\s+/)[0] : void 0;
|
|
428
|
-
if (className) return `${tag}.${className}`;
|
|
429
|
-
const text = el.textContent?.trim().slice(0, 20);
|
|
430
|
-
return text ? `${tag} "${text}"` : tag;
|
|
431
|
-
}
|
|
432
|
-
var fmt = (ms) => `${ms.toFixed(1)}ms`;
|
|
433
|
-
var now = () => typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
|
|
434
|
-
|
|
435
249
|
// src/core/opportunities.ts
|
|
436
250
|
var DEFAULT_MIN_SAVING_MS = 1;
|
|
437
251
|
function rankOpportunities({ events, lifecycles, minSavingMs = DEFAULT_MIN_SAVING_MS }) {
|
|
@@ -439,7 +253,7 @@ function rankOpportunities({ events, lifecycles, minSavingMs = DEFAULT_MIN_SAVIN
|
|
|
439
253
|
const out = [];
|
|
440
254
|
for (const name of names) {
|
|
441
255
|
const lifecycle = lifecycles.get(name);
|
|
442
|
-
const explanation =
|
|
256
|
+
const explanation = chunkRGT44QQI_cjs.explainEvents(name, events, lifecycle);
|
|
443
257
|
if (!explanation) continue;
|
|
444
258
|
const mounts = events.filter((e) => e.component.name === name && e.phase === "mount");
|
|
445
259
|
const averageMountCost = mounts.length ? mounts.reduce((a, e) => a + e.timings.selfDuration, 0) / mounts.length : 0;
|
|
@@ -485,7 +299,7 @@ function formatOpportunities(opportunities) {
|
|
|
485
299
|
var AncestryContext = React.createContext(void 0);
|
|
486
300
|
AncestryContext.displayName = "RenderDetectiveAncestry";
|
|
487
301
|
function useInstrumentedNode(name, props, source) {
|
|
488
|
-
const detective =
|
|
302
|
+
const detective = chunkLCGXVRJR_cjs.getDetective();
|
|
489
303
|
const parent = React.useContext(AncestryContext);
|
|
490
304
|
const [node] = React.useState(
|
|
491
305
|
() => detective.enabled ? detective.createNode(name, parent, source) : void 0
|
|
@@ -535,7 +349,7 @@ var IS_WRAPPER = /* @__PURE__ */ Symbol.for("react-render-detective.wrapper");
|
|
|
535
349
|
function withRenderDetective(Component, options = {}) {
|
|
536
350
|
if (Component[IS_WRAPPER]) return Component;
|
|
537
351
|
const name = options.name ?? componentName(Component);
|
|
538
|
-
|
|
352
|
+
chunkLCGXVRJR_cjs.getDetective().noteDefinition(name, options.source, options.declaredInRender === true);
|
|
539
353
|
function RenderDetected(props) {
|
|
540
354
|
const { node, onRender } = useInstrumentedNode(name, props, options.source);
|
|
541
355
|
return renderInstrumented(node, onRender, /* @__PURE__ */ jsxRuntime.jsx(Component, { ...props }));
|
|
@@ -550,8 +364,40 @@ function RenderDetective({ name, children }) {
|
|
|
550
364
|
const { node, onRender } = useInstrumentedNode(name, { children });
|
|
551
365
|
return renderInstrumented(node, onRender, children);
|
|
552
366
|
}
|
|
367
|
+
var useOwnerId = typeof React__namespace.useId === "function" ? React__namespace.useId : () => "";
|
|
368
|
+
function trackSelector(value, options = {}) {
|
|
369
|
+
const detective = chunkLCGXVRJR_cjs.getDetective();
|
|
370
|
+
const node = React.useContext(AncestryContext);
|
|
371
|
+
const ownerId = useOwnerId();
|
|
372
|
+
const previous = React.useRef(void 0);
|
|
373
|
+
if (!detective.enabled) return value;
|
|
374
|
+
if (node && node.stateOwner === void 0) node.stateOwner = ownerId;
|
|
375
|
+
const owns = !node || node.stateOwner === ownerId;
|
|
376
|
+
const prior = previous.current;
|
|
377
|
+
previous.current = { value };
|
|
378
|
+
if (!prior || !node || !owns) return value;
|
|
379
|
+
if (Object.is(prior.value, value)) return value;
|
|
380
|
+
try {
|
|
381
|
+
const equal = chunkLCGXVRJR_cjs.shallowEqual(prior.value, value, detective.config);
|
|
382
|
+
detective.recordSelectorChange(node, {
|
|
383
|
+
name: options.name ?? options.source ?? "selector",
|
|
384
|
+
source: options.source,
|
|
385
|
+
referenceOnly: equal === true,
|
|
386
|
+
previous: chunkLCGXVRJR_cjs.inspect(prior.value, detective.config.inspection),
|
|
387
|
+
current: chunkLCGXVRJR_cjs.inspect(value, detective.config.inspection)
|
|
388
|
+
});
|
|
389
|
+
} catch {
|
|
390
|
+
}
|
|
391
|
+
return value;
|
|
392
|
+
}
|
|
393
|
+
function createTrackedSelectorHook(hook, options = {}) {
|
|
394
|
+
return function useTrackedSelector(...args) {
|
|
395
|
+
const value = hook(...args);
|
|
396
|
+
return trackSelector(value, options);
|
|
397
|
+
};
|
|
398
|
+
}
|
|
553
399
|
function useRenderDiagnostics(name, props) {
|
|
554
|
-
const detective =
|
|
400
|
+
const detective = chunkLCGXVRJR_cjs.getDetective();
|
|
555
401
|
const parent = React.useContext(AncestryContext);
|
|
556
402
|
const [node] = React.useState(() => {
|
|
557
403
|
if (!detective.enabled) return void 0;
|
|
@@ -584,11 +430,11 @@ function useRenderDiagnostics(name, props) {
|
|
|
584
430
|
}
|
|
585
431
|
return last.current;
|
|
586
432
|
}
|
|
587
|
-
var
|
|
433
|
+
var useOwnerId2 = typeof React__namespace.useId === "function" ? React__namespace.useId : () => "";
|
|
588
434
|
function useTrackedState(name, initial) {
|
|
589
|
-
const detective =
|
|
435
|
+
const detective = chunkLCGXVRJR_cjs.getDetective();
|
|
590
436
|
const node = React.useContext(AncestryContext);
|
|
591
|
-
const ownerId =
|
|
437
|
+
const ownerId = useOwnerId2();
|
|
592
438
|
const [state, setState] = React.useState(initial);
|
|
593
439
|
const previous = React.useRef(state);
|
|
594
440
|
if (node && node.stateOwner === void 0) node.stateOwner = ownerId;
|
|
@@ -596,8 +442,8 @@ function useTrackedState(name, initial) {
|
|
|
596
442
|
if (node && owns && !Object.is(previous.current, state)) {
|
|
597
443
|
detective.recordStateChange(node, {
|
|
598
444
|
name,
|
|
599
|
-
previous:
|
|
600
|
-
current:
|
|
445
|
+
previous: chunkLCGXVRJR_cjs.inspect(previous.current, detective.config.inspection),
|
|
446
|
+
current: chunkLCGXVRJR_cjs.inspect(state, detective.config.inspection)
|
|
601
447
|
});
|
|
602
448
|
previous.current = state;
|
|
603
449
|
} else if (!owns) {
|
|
@@ -606,7 +452,7 @@ function useTrackedState(name, initial) {
|
|
|
606
452
|
return [state, setState];
|
|
607
453
|
}
|
|
608
454
|
function useTrackedEffect(name, effect, deps) {
|
|
609
|
-
const detective =
|
|
455
|
+
const detective = chunkLCGXVRJR_cjs.getDetective();
|
|
610
456
|
const previous = React.useRef(void 0);
|
|
611
457
|
const changed = React.useRef([]);
|
|
612
458
|
if (detective.enabled) {
|
|
@@ -622,7 +468,7 @@ function useTrackedEffect(name, effect, deps) {
|
|
|
622
468
|
}, deps);
|
|
623
469
|
}
|
|
624
470
|
function useTrackedContextValue(contextName, value) {
|
|
625
|
-
const detective =
|
|
471
|
+
const detective = chunkLCGXVRJR_cjs.getDetective();
|
|
626
472
|
const previous = React.useRef(void 0);
|
|
627
473
|
const first = React.useRef(true);
|
|
628
474
|
if (detective.enabled) {
|
|
@@ -630,8 +476,8 @@ function useTrackedContextValue(contextName, value) {
|
|
|
630
476
|
first.current = false;
|
|
631
477
|
} else if (!Object.is(previous.current, value)) {
|
|
632
478
|
const prev = previous.current;
|
|
633
|
-
const equal =
|
|
634
|
-
const changedKeys = isRecord(prev) && isRecord(value) ?
|
|
479
|
+
const equal = chunkLCGXVRJR_cjs.shallowEqual(prev, value, detective.config);
|
|
480
|
+
const changedKeys = isRecord(prev) && isRecord(value) ? chunkLCGXVRJR_cjs.diffProps(prev, value, detective.config).changed.map((c) => c.key) : [];
|
|
635
481
|
detective.recordContextChange({
|
|
636
482
|
contextName,
|
|
637
483
|
changedKeys,
|
|
@@ -649,21 +495,12 @@ function isRecord(v) {
|
|
|
649
495
|
|
|
650
496
|
// src/index.ts
|
|
651
497
|
var REPORTER = /* @__PURE__ */ Symbol.for("react-render-detective.reporter");
|
|
652
|
-
var INTERACTIONS = /* @__PURE__ */ Symbol.for("react-render-detective.interactions");
|
|
653
|
-
function tracker() {
|
|
654
|
-
const g = globalThis;
|
|
655
|
-
if (!g[INTERACTIONS]) g[INTERACTIONS] = new InteractionTracker();
|
|
656
|
-
return g[INTERACTIONS];
|
|
657
|
-
}
|
|
658
498
|
function init(options = {}) {
|
|
659
|
-
const detective =
|
|
499
|
+
const detective = chunkLCGXVRJR_cjs.getDetective();
|
|
660
500
|
detective.init(options);
|
|
661
501
|
const g = globalThis;
|
|
662
502
|
g[REPORTER]?.();
|
|
663
503
|
g[REPORTER] = void 0;
|
|
664
|
-
if (detective.enabled) {
|
|
665
|
-
tracker().start();
|
|
666
|
-
}
|
|
667
504
|
if (detective.enabled && detective.config.mode !== "silent") {
|
|
668
505
|
g[REPORTER] = attachConsoleReporter(detective);
|
|
669
506
|
} else if (!detective.enabled) {
|
|
@@ -673,77 +510,49 @@ function init(options = {}) {
|
|
|
673
510
|
}
|
|
674
511
|
}
|
|
675
512
|
function configure(options) {
|
|
676
|
-
|
|
513
|
+
chunkLCGXVRJR_cjs.getDetective().configure(options);
|
|
677
514
|
}
|
|
678
515
|
function getConfig() {
|
|
679
|
-
return
|
|
516
|
+
return chunkLCGXVRJR_cjs.getDetective().config;
|
|
680
517
|
}
|
|
681
518
|
function isEnabled() {
|
|
682
|
-
return
|
|
519
|
+
return chunkLCGXVRJR_cjs.getDetective().enabled;
|
|
683
520
|
}
|
|
684
521
|
function getEvents() {
|
|
685
|
-
return
|
|
522
|
+
return chunkLCGXVRJR_cjs.getDetective().getEvents();
|
|
686
523
|
}
|
|
687
524
|
function getStats() {
|
|
688
|
-
return
|
|
525
|
+
return chunkLCGXVRJR_cjs.getDetective().getStats();
|
|
689
526
|
}
|
|
690
527
|
function getComponentStats(name) {
|
|
691
|
-
return
|
|
528
|
+
return chunkLCGXVRJR_cjs.getDetective().getComponentStats(name);
|
|
692
529
|
}
|
|
693
530
|
function subscribe(listener) {
|
|
694
|
-
return
|
|
531
|
+
return chunkLCGXVRJR_cjs.getDetective().subscribe(listener);
|
|
695
532
|
}
|
|
696
533
|
function clear() {
|
|
697
|
-
|
|
698
|
-
tracker().clear();
|
|
534
|
+
chunkLCGXVRJR_cjs.getDetective().clear();
|
|
699
535
|
}
|
|
700
536
|
function reset() {
|
|
701
537
|
const g = globalThis;
|
|
702
538
|
g[REPORTER]?.();
|
|
703
539
|
g[REPORTER] = void 0;
|
|
704
|
-
|
|
705
|
-
g[INTERACTIONS] = void 0;
|
|
706
|
-
chunkOSPTERGK_cjs.getDetective().reset();
|
|
540
|
+
chunkLCGXVRJR_cjs.getDetective().reset();
|
|
707
541
|
}
|
|
708
542
|
function explain(componentName2) {
|
|
709
543
|
const explanation = explainStructured(componentName2);
|
|
710
|
-
return explanation ?
|
|
544
|
+
return explanation ? chunkRGT44QQI_cjs.formatExplanation(explanation) : void 0;
|
|
711
545
|
}
|
|
712
546
|
function explainStructured(componentName2) {
|
|
713
|
-
return
|
|
547
|
+
return chunkRGT44QQI_cjs.explainEvents(componentName2, getEvents(), chunkLCGXVRJR_cjs.getDetective().lifecycleOf(componentName2));
|
|
714
548
|
}
|
|
715
549
|
function getRenderProfile(scenario) {
|
|
716
550
|
const remounts = {};
|
|
717
|
-
for (const stats of
|
|
551
|
+
for (const stats of chunkLCGXVRJR_cjs.getDetective().getComponentStats()) remounts[stats.name] = stats.remountCount;
|
|
718
552
|
return chunkH5RG2EPP_cjs.profileFromEvents(scenario, getEvents(), remounts);
|
|
719
553
|
}
|
|
720
|
-
function getInteractions() {
|
|
721
|
-
return tracker().attribute(getEvents());
|
|
722
|
-
}
|
|
723
|
-
function explainInteractionStructured(id) {
|
|
724
|
-
const records = getInteractions();
|
|
725
|
-
const record = id ? records.find((r) => r.id === id) : records[0];
|
|
726
|
-
return record ? summarise(record) : void 0;
|
|
727
|
-
}
|
|
728
|
-
function explainInteraction(id) {
|
|
729
|
-
const summary = explainInteractionStructured(id);
|
|
730
|
-
return summary ? formatInteraction(summary) : void 0;
|
|
731
|
-
}
|
|
732
|
-
function printInteractions(limit = 5) {
|
|
733
|
-
const records = getInteractions().slice(0, limit);
|
|
734
|
-
if (records.length === 0) {
|
|
735
|
-
console.log(
|
|
736
|
-
tracker().automatic ? "No interactions recorded yet. Event timing is working \u2014 nothing has taken longer than 16ms.\nSynthetic clicks from a test harness never produce these entries; use measureInteraction() there." : "This browser does not report event timing (Safari before 16.4, jsdom).\nUse measureInteraction(label, fn) to time interactions by hand."
|
|
737
|
-
);
|
|
738
|
-
return;
|
|
739
|
-
}
|
|
740
|
-
console.log(records.map((r) => formatInteraction(summarise(r))).join("\n\n"));
|
|
741
|
-
}
|
|
742
|
-
function measureInteraction(label2, action) {
|
|
743
|
-
return tracker().measure(label2, action);
|
|
744
|
-
}
|
|
745
554
|
function getOpportunities(limit = 10) {
|
|
746
|
-
const detective =
|
|
555
|
+
const detective = chunkLCGXVRJR_cjs.getDetective();
|
|
747
556
|
const lifecycles = /* @__PURE__ */ new Map();
|
|
748
557
|
for (const stats of detective.getComponentStats()) {
|
|
749
558
|
lifecycles.set(stats.name, { remounts: stats.remountCount });
|
|
@@ -800,51 +609,40 @@ var ReactRenderDetective = {
|
|
|
800
609
|
explainStructured,
|
|
801
610
|
getOpportunities,
|
|
802
611
|
printOpportunities,
|
|
803
|
-
getInteractions,
|
|
804
|
-
explainInteraction,
|
|
805
|
-
explainInteractionStructured,
|
|
806
|
-
printInteractions,
|
|
807
|
-
measureInteraction,
|
|
808
612
|
getRenderProfile,
|
|
809
613
|
printStats
|
|
810
614
|
};
|
|
811
615
|
|
|
812
616
|
Object.defineProperty(exports, "explainEvents", {
|
|
813
617
|
enumerable: true,
|
|
814
|
-
get: function () { return
|
|
618
|
+
get: function () { return chunkRGT44QQI_cjs.explainEvents; }
|
|
815
619
|
});
|
|
816
620
|
Object.defineProperty(exports, "formatExplanation", {
|
|
817
621
|
enumerable: true,
|
|
818
|
-
get: function () { return
|
|
622
|
+
get: function () { return chunkRGT44QQI_cjs.formatExplanation; }
|
|
819
623
|
});
|
|
820
|
-
exports.InteractionTracker = InteractionTracker;
|
|
821
624
|
exports.ReactRenderDetective = ReactRenderDetective;
|
|
822
625
|
exports.RenderDetective = RenderDetective;
|
|
823
626
|
exports.clear = clear;
|
|
824
627
|
exports.configure = configure;
|
|
628
|
+
exports.createTrackedSelectorHook = createTrackedSelectorHook;
|
|
825
629
|
exports.explain = explain;
|
|
826
|
-
exports.explainInteraction = explainInteraction;
|
|
827
|
-
exports.explainInteractionStructured = explainInteractionStructured;
|
|
828
630
|
exports.explainStructured = explainStructured;
|
|
829
|
-
exports.formatInteraction = formatInteraction;
|
|
830
631
|
exports.formatOpportunities = formatOpportunities;
|
|
831
632
|
exports.getComponentStats = getComponentStats;
|
|
832
633
|
exports.getConfig = getConfig;
|
|
833
634
|
exports.getEvents = getEvents;
|
|
834
|
-
exports.getInteractions = getInteractions;
|
|
835
635
|
exports.getOpportunities = getOpportunities;
|
|
836
636
|
exports.getRenderProfile = getRenderProfile;
|
|
837
637
|
exports.getStats = getStats;
|
|
838
638
|
exports.init = init;
|
|
839
639
|
exports.isEnabled = isEnabled;
|
|
840
|
-
exports.measureInteraction = measureInteraction;
|
|
841
|
-
exports.printInteractions = printInteractions;
|
|
842
640
|
exports.printOpportunities = printOpportunities;
|
|
843
641
|
exports.printStats = printStats;
|
|
844
642
|
exports.rankOpportunities = rankOpportunities;
|
|
845
643
|
exports.reset = reset;
|
|
846
644
|
exports.subscribe = subscribe;
|
|
847
|
-
exports.
|
|
645
|
+
exports.trackSelector = trackSelector;
|
|
848
646
|
exports.useRenderDiagnostics = useRenderDiagnostics;
|
|
849
647
|
exports.useTrackedContextValue = useTrackedContextValue;
|
|
850
648
|
exports.useTrackedEffect = useTrackedEffect;
|