@buoy-gg/highlight-updates 7.0.35 → 7.0.38

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.
Files changed (107) hide show
  1. package/README.md +28 -6
  2. package/lib/commonjs/highlight-updates/HighlightUpdatesOverlay.js +1 -281
  3. package/lib/commonjs/highlight-updates/components/HighlightFilterView.js +1 -1474
  4. package/lib/commonjs/highlight-updates/components/HighlightUpdatesModal.js +1 -641
  5. package/lib/commonjs/highlight-updates/components/IdentifierBadge.js +1 -273
  6. package/lib/commonjs/highlight-updates/components/IsolatedRenderList.js +1 -188
  7. package/lib/commonjs/highlight-updates/components/ModalHeaderContent.js +1 -293
  8. package/lib/commonjs/highlight-updates/components/RenderCauseBadge.js +1 -508
  9. package/lib/commonjs/highlight-updates/components/RenderDetailView.js +1 -846
  10. package/lib/commonjs/highlight-updates/components/RenderHistoryViewer.js +1 -915
  11. package/lib/commonjs/highlight-updates/components/RenderListItem.js +1 -235
  12. package/lib/commonjs/highlight-updates/components/RendersCopySettingsView.js +1 -466
  13. package/lib/commonjs/highlight-updates/components/StatsDisplay.js +1 -73
  14. package/lib/commonjs/highlight-updates/types/copySettings.js +1 -107
  15. package/lib/commonjs/highlight-updates/utils/CommitProfiler.js +1 -446
  16. package/lib/commonjs/highlight-updates/utils/HighlightUpdatesController.js +1 -1920
  17. package/lib/commonjs/highlight-updates/utils/PerformanceLogger.js +1 -359
  18. package/lib/commonjs/highlight-updates/utils/ProfilerInterceptor.js +1 -371
  19. package/lib/commonjs/highlight-updates/utils/RenderCauseDetector.js +1 -1840
  20. package/lib/commonjs/highlight-updates/utils/RenderTracker.js +1 -952
  21. package/lib/commonjs/highlight-updates/utils/ViewTypeMapper.js +1 -264
  22. package/lib/commonjs/highlight-updates/utils/copySettingsStorage.js +1 -32
  23. package/lib/commonjs/highlight-updates/utils/devtoolsComponentNames.js +1 -188
  24. package/lib/commonjs/highlight-updates/utils/namedComposite.js +1 -41
  25. package/lib/commonjs/highlight-updates/utils/ownStateChanged.js +1 -103
  26. package/lib/commonjs/highlight-updates/utils/rawEventEmitter.js +1 -50
  27. package/lib/commonjs/highlight-updates/utils/renderExportFormatter.js +1 -58
  28. package/lib/commonjs/highlight-updates/utils/rendersExportFormatter.js +1 -485
  29. package/lib/commonjs/highlight-updates/utils/screenElements.js +1 -1046
  30. package/lib/commonjs/highlight-updates/utils/tapMatch.js +1 -58
  31. package/lib/commonjs/highlight-updates/utils/testIDs.js +1 -68
  32. package/lib/commonjs/highlight-updates/utils/touchCapture.js +1 -277
  33. package/lib/commonjs/index.js +1 -429
  34. package/lib/commonjs/preset.js +1 -278
  35. package/lib/commonjs/sync/highlightUpdatesSyncAdapter.js +1 -235
  36. package/lib/module/highlight-updates/HighlightUpdatesOverlay.js +1 -274
  37. package/lib/module/highlight-updates/components/HighlightFilterView.js +1 -1468
  38. package/lib/module/highlight-updates/components/HighlightUpdatesModal.js +1 -634
  39. package/lib/module/highlight-updates/components/IdentifierBadge.js +1 -265
  40. package/lib/module/highlight-updates/components/IsolatedRenderList.js +1 -183
  41. package/lib/module/highlight-updates/components/ModalHeaderContent.js +1 -288
  42. package/lib/module/highlight-updates/components/RenderCauseBadge.js +1 -499
  43. package/lib/module/highlight-updates/components/RenderDetailView.js +1 -840
  44. package/lib/module/highlight-updates/components/RenderHistoryViewer.js +1 -908
  45. package/lib/module/highlight-updates/components/RenderListItem.js +1 -230
  46. package/lib/module/highlight-updates/components/RendersCopySettingsView.js +1 -462
  47. package/lib/module/highlight-updates/components/StatsDisplay.js +1 -70
  48. package/lib/module/highlight-updates/types/copySettings.js +1 -102
  49. package/lib/module/highlight-updates/utils/CommitProfiler.js +1 -442
  50. package/lib/module/highlight-updates/utils/HighlightUpdatesController.js +1 -1916
  51. package/lib/module/highlight-updates/utils/PerformanceLogger.js +1 -353
  52. package/lib/module/highlight-updates/utils/ProfilerInterceptor.js +1 -358
  53. package/lib/module/highlight-updates/utils/RenderCauseDetector.js +1 -1830
  54. package/lib/module/highlight-updates/utils/RenderTracker.js +1 -949
  55. package/lib/module/highlight-updates/utils/ViewTypeMapper.js +1 -255
  56. package/lib/module/highlight-updates/utils/copySettingsStorage.js +1 -26
  57. package/lib/module/highlight-updates/utils/devtoolsComponentNames.js +1 -178
  58. package/lib/module/highlight-updates/utils/namedComposite.js +1 -36
  59. package/lib/module/highlight-updates/utils/ownStateChanged.js +1 -98
  60. package/lib/module/highlight-updates/utils/rawEventEmitter.js +1 -46
  61. package/lib/module/highlight-updates/utils/renderExportFormatter.js +1 -54
  62. package/lib/module/highlight-updates/utils/rendersExportFormatter.js +1 -478
  63. package/lib/module/highlight-updates/utils/screenElements.js +1 -1040
  64. package/lib/module/highlight-updates/utils/tapMatch.js +1 -52
  65. package/lib/module/highlight-updates/utils/testIDs.js +1 -60
  66. package/lib/module/highlight-updates/utils/touchCapture.js +1 -270
  67. package/lib/module/index.js +1 -130
  68. package/lib/module/preset.js +1 -272
  69. package/lib/module/sync/highlightUpdatesSyncAdapter.js +1 -230
  70. package/lib/typescript/highlight-updates/utils/screenElements.d.ts +52 -1
  71. package/lib/typescript/highlight-updates/utils/tapMatch.d.ts +11 -0
  72. package/lib/typescript/sync/highlightUpdatesSyncAdapter.d.ts +1 -1
  73. package/package.json +8 -8
  74. package/lib/typescript/highlight-updates/HighlightUpdatesOverlay.d.ts.map +0 -1
  75. package/lib/typescript/highlight-updates/components/HighlightFilterView.d.ts.map +0 -1
  76. package/lib/typescript/highlight-updates/components/HighlightUpdatesModal.d.ts.map +0 -1
  77. package/lib/typescript/highlight-updates/components/IdentifierBadge.d.ts.map +0 -1
  78. package/lib/typescript/highlight-updates/components/IsolatedRenderList.d.ts.map +0 -1
  79. package/lib/typescript/highlight-updates/components/ModalHeaderContent.d.ts.map +0 -1
  80. package/lib/typescript/highlight-updates/components/RenderCauseBadge.d.ts.map +0 -1
  81. package/lib/typescript/highlight-updates/components/RenderDetailView.d.ts.map +0 -1
  82. package/lib/typescript/highlight-updates/components/RenderHistoryViewer.d.ts.map +0 -1
  83. package/lib/typescript/highlight-updates/components/RenderListItem.d.ts.map +0 -1
  84. package/lib/typescript/highlight-updates/components/RendersCopySettingsView.d.ts.map +0 -1
  85. package/lib/typescript/highlight-updates/components/StatsDisplay.d.ts.map +0 -1
  86. package/lib/typescript/highlight-updates/types/copySettings.d.ts.map +0 -1
  87. package/lib/typescript/highlight-updates/utils/CommitProfiler.d.ts.map +0 -1
  88. package/lib/typescript/highlight-updates/utils/HighlightUpdatesController.d.ts.map +0 -1
  89. package/lib/typescript/highlight-updates/utils/PerformanceLogger.d.ts.map +0 -1
  90. package/lib/typescript/highlight-updates/utils/ProfilerInterceptor.d.ts.map +0 -1
  91. package/lib/typescript/highlight-updates/utils/RenderCauseDetector.d.ts.map +0 -1
  92. package/lib/typescript/highlight-updates/utils/RenderTracker.d.ts.map +0 -1
  93. package/lib/typescript/highlight-updates/utils/ViewTypeMapper.d.ts.map +0 -1
  94. package/lib/typescript/highlight-updates/utils/copySettingsStorage.d.ts.map +0 -1
  95. package/lib/typescript/highlight-updates/utils/devtoolsComponentNames.d.ts.map +0 -1
  96. package/lib/typescript/highlight-updates/utils/namedComposite.d.ts.map +0 -1
  97. package/lib/typescript/highlight-updates/utils/ownStateChanged.d.ts.map +0 -1
  98. package/lib/typescript/highlight-updates/utils/rawEventEmitter.d.ts.map +0 -1
  99. package/lib/typescript/highlight-updates/utils/renderExportFormatter.d.ts.map +0 -1
  100. package/lib/typescript/highlight-updates/utils/rendersExportFormatter.d.ts.map +0 -1
  101. package/lib/typescript/highlight-updates/utils/screenElements.d.ts.map +0 -1
  102. package/lib/typescript/highlight-updates/utils/tapMatch.d.ts.map +0 -1
  103. package/lib/typescript/highlight-updates/utils/testIDs.d.ts.map +0 -1
  104. package/lib/typescript/highlight-updates/utils/touchCapture.d.ts.map +0 -1
  105. package/lib/typescript/index.d.ts.map +0 -1
  106. package/lib/typescript/preset.d.ts.map +0 -1
  107. package/lib/typescript/sync/highlightUpdatesSyncAdapter.d.ts.map +0 -1
@@ -1,485 +1 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.estimateExportSize = estimateExportSize;
7
- exports.generateExport = generateExport;
8
- exports.generateSingleComponentExport = generateSingleComponentExport;
9
- exports.getExportSummary = getExportSummary;
10
- /**
11
- * Renders Export Formatter
12
- *
13
- * Pipeline: TrackedRender[] -> filter -> group -> aggregate -> sort -> format.
14
- * Goal: compact, debug-focused output. Reports facts, not opinions.
15
- */
16
-
17
- // =============================================================================
18
- // Internal types
19
- // =============================================================================
20
-
21
- // =============================================================================
22
- // Pipeline
23
- // =============================================================================
24
-
25
- function buildReport(renders, settings) {
26
- let working = renders.filter(r => r.renderCount >= settings.minRenders);
27
- if (settings.filterCauses.length > 0) {
28
- const allowed = new Set(settings.filterCauses);
29
- working = working.filter(r => allowed.has(r.lastRenderCause?.type ?? "unknown"));
30
- }
31
-
32
- // Window — computed from the original render list, not after topN
33
- const allTimes = renders.flatMap(r => [r.firstRenderTime, r.lastRenderTime]).filter(t => Number.isFinite(t));
34
- const windowMs = allTimes.length > 0 ? Math.max(...allTimes) - Math.min(...allTimes) : 0;
35
- const windowSec = Math.max(0.001, windowMs / 1000);
36
- const groups = settings.groupByName ? groupByComponentName(working) : working.map(r => [r]);
37
- const processed = groups.map(group => processGroup(group, settings, windowSec));
38
- sortRows(processed, settings.sortBy);
39
- const truncated = settings.topN === -1 ? 0 : Math.max(0, processed.length - settings.topN);
40
- const rows = settings.topN === -1 ? processed : processed.slice(0, settings.topN);
41
- const totalRenders = renders.reduce((sum, r) => sum + r.renderCount, 0);
42
- const causeBreakdown = aggregateCauseBreakdown(renders);
43
-
44
- // Data-gap detection from the source renders (not after filtering)
45
- let rendersWithoutCause = 0;
46
- let historyPresent = false;
47
- for (const r of renders) {
48
- if (!r.lastRenderCause) rendersWithoutCause += r.renderCount;
49
- if (r.renderHistory && r.renderHistory.length > 0) historyPresent = true;
50
- }
51
- return {
52
- totalUniqueComponents: working.length,
53
- totalRenders,
54
- windowMs,
55
- causeBreakdown,
56
- rows,
57
- truncated,
58
- dataGaps: {
59
- historyOff: !historyPresent,
60
- // We can't directly observe whether the capture settings are on,
61
- // but we can detect their effects: capturedProps/capturedState in events.
62
- propsCaptureOff: !renders.some(r => r.renderHistory?.some(e => e.capturedProps !== undefined)),
63
- stateCaptureOff: !renders.some(r => r.renderHistory?.some(e => e.capturedState !== undefined)),
64
- rendersWithoutCause
65
- }
66
- };
67
- }
68
- function groupByComponentName(renders) {
69
- const map = new Map();
70
- for (const r of renders) {
71
- const key = r.componentName || r.displayName || r.viewType;
72
- const existing = map.get(key);
73
- if (existing) {
74
- existing.push(r);
75
- } else {
76
- map.set(key, [r]);
77
- }
78
- }
79
- return Array.from(map.values());
80
- }
81
- function processGroup(group, settings, windowSec) {
82
- const first = group[0];
83
- const name = first.componentName || first.displayName || first.viewType;
84
- let renders = 0;
85
- const causeMix = {};
86
- const hookCounter = new Map();
87
- const changedKeyCounter = new Map();
88
- const parentNameCounter = new Map();
89
- let unknownPropsCount = 0;
90
- let parentWithOwnChange = 0;
91
- let parentNoChange = 0;
92
- for (const r of group) {
93
- renders += r.renderCount;
94
- const cause = r.lastRenderCause;
95
- if (!cause) continue;
96
- const causeType = cause.type;
97
- causeMix[causeType] = (causeMix[causeType] ?? 0) + r.renderCount;
98
-
99
- // Track parent-cascade subtypes
100
- const hadHookChange = (cause.hookChanges?.length ?? 0) > 0;
101
- const hadKeyChange = (cause.changedKeys?.length ?? 0) > 0;
102
- if (causeType === "parent") {
103
- const ownChange = cause.componentCause === "state" || cause.componentCause === "props" || hadHookChange || hadKeyChange;
104
- if (ownChange) parentWithOwnChange += r.renderCount;else parentNoChange += r.renderCount;
105
- }
106
-
107
- // Track props with no key info (cause detector saw a change but couldn't identify which)
108
- if (causeType === "props" && !hadKeyChange) {
109
- unknownPropsCount += r.renderCount;
110
- }
111
-
112
- // Parent name observation
113
- if (cause.parentComponentName) {
114
- parentNameCounter.set(cause.parentComponentName, (parentNameCounter.get(cause.parentComponentName) ?? 0) + 1);
115
- }
116
-
117
- // Aggregate hook causes
118
- if (settings.aggregateCauses && cause.hookChanges) {
119
- for (const hc of cause.hookChanges) {
120
- const key = `${hc.type}[${hc.index}]`;
121
- const existing = hookCounter.get(key);
122
- if (existing) {
123
- existing.count += 1;
124
- } else {
125
- hookCounter.set(key, {
126
- count: 1,
127
- sample: formatHookSample(hc)
128
- });
129
- }
130
- }
131
- }
132
-
133
- // Aggregate changed keys (skip ref-only marks)
134
- if (cause.changedKeys) {
135
- for (const key of cause.changedKeys) {
136
- if (key.includes("(ref only)")) continue;
137
- changedKeyCounter.set(key, (changedKeyCounter.get(key) ?? 0) + 1);
138
- }
139
- }
140
- }
141
- const hookCauses = Array.from(hookCounter.entries()).sort((a, b) => b[1].count - a[1].count).slice(0, 3).map(([hook, info]) => ({
142
- hook,
143
- count: info.count,
144
- sample: info.sample
145
- }));
146
- const changedKeys = Array.from(changedKeyCounter.entries()).sort((a, b) => b[1] - a[1]).slice(0, 5).map(([k]) => k);
147
- let dominantCause = "unknown";
148
- let max = -1;
149
- for (const [cause, count] of Object.entries(causeMix)) {
150
- if ((count ?? 0) > max) {
151
- max = count ?? 0;
152
- dominantCause = cause;
153
- }
154
- }
155
-
156
- // Pick the most-observed parent name
157
- let parentName;
158
- let parentMax = 0;
159
- for (const [n, c] of parentNameCounter) {
160
- if (c > parentMax) {
161
- parentMax = c;
162
- parentName = n;
163
- }
164
- }
165
- const instanceCounts = group.map(r => r.renderCount).sort((a, b) => b - a);
166
- const rate = renders / windowSec;
167
- return {
168
- name,
169
- viewType: first.viewType,
170
- instances: group.length,
171
- renders,
172
- causeMix,
173
- dominantCause,
174
- hookCauses,
175
- changedKeys,
176
- unknownPropsCount,
177
- parentWithOwnChange,
178
- parentNoChange,
179
- parentName,
180
- instanceCounts,
181
- rate,
182
- source: group
183
- };
184
- }
185
- function aggregateCauseBreakdown(renders) {
186
- const out = {};
187
- for (const r of renders) {
188
- const t = r.lastRenderCause?.type ?? "unknown";
189
- out[t] = (out[t] ?? 0) + r.renderCount;
190
- }
191
- return out;
192
- }
193
- function sortRows(rows, sortBy) {
194
- switch (sortBy) {
195
- case "renderCount":
196
- rows.sort((a, b) => b.renders - a.renders);
197
- break;
198
- case "rate":
199
- rows.sort((a, b) => b.rate - a.rate);
200
- break;
201
- case "name":
202
- rows.sort((a, b) => a.name.localeCompare(b.name));
203
- break;
204
- }
205
- }
206
-
207
- // =============================================================================
208
- // Helpers
209
- // =============================================================================
210
-
211
- const VALUE_MAX_LEN = 32;
212
- function truncateValue(v) {
213
- if (v === null) return "null";
214
- if (v === undefined) return "undefined";
215
- if (typeof v === "function") return "[Fn]";
216
- if (typeof v === "object") {
217
- if (Array.isArray(v)) return `[Array(${v.length})]`;
218
- return "[Obj]";
219
- }
220
- const s = String(v);
221
- return s.length > VALUE_MAX_LEN ? s.slice(0, VALUE_MAX_LEN - 1) + "…" : s;
222
- }
223
- function formatHookSample(hc) {
224
- if (hc.previousValue === undefined && hc.currentValue === undefined) {
225
- return hc.description;
226
- }
227
- return `${truncateValue(hc.previousValue)} → ${truncateValue(hc.currentValue)}`;
228
- }
229
- function formatWindow(ms) {
230
- if (ms < 1000) return `${ms}ms`;
231
- return `${(ms / 1000).toFixed(1)}s`;
232
- }
233
- function formatRate(rate) {
234
- if (rate >= 100) return `${rate.toFixed(0)}/s`;
235
- if (rate >= 10) return `${rate.toFixed(1)}/s`;
236
- return `${rate.toFixed(2)}/s`;
237
- }
238
- function causeBreakdownLine(breakdown) {
239
- const entries = Object.entries(breakdown).sort((a, b) => (b[1] ?? 0) - (a[1] ?? 0));
240
- if (entries.length === 0) return "(no causes recorded)";
241
- return entries.map(([k, v]) => `${k} ${v}`).join(" · ");
242
- }
243
- function describeCause(row) {
244
- const parts = [];
245
-
246
- // Per-row cause mix when at least 2 causes contribute (≥10% each)
247
- const mixEntries = Object.entries(row.causeMix).filter(([, v]) => (v ?? 0) > 0).sort((a, b) => (b[1] ?? 0) - (a[1] ?? 0));
248
- const showMix = mixEntries.length >= 2 && (mixEntries[1]?.[1] ?? 0) >= row.renders * 0.1;
249
- if (row.dominantCause === "hooks" && row.hookCauses.length > 0) {
250
- const top = row.hookCauses[0];
251
- if (top.sample) {
252
- parts.push(`hooks ×${top.count} ${top.hook}: ${top.sample}`);
253
- } else {
254
- parts.push(`hooks ×${top.count} ${top.hook}`);
255
- }
256
- } else if (row.dominantCause === "props") {
257
- if (row.changedKeys.length > 0) {
258
- const keys = row.changedKeys.slice(0, 3).join(", ");
259
- parts.push(`props (${keys})`);
260
- } else {
261
- parts.push(`props (keys not captured)`);
262
- }
263
- if (row.unknownPropsCount > 0 && row.changedKeys.length > 0) {
264
- parts.push(`[${row.unknownPropsCount} unidentified]`);
265
- }
266
- } else if (row.dominantCause === "parent") {
267
- let label = "parent cascade";
268
- if (row.parentNoChange > 0 && row.parentWithOwnChange === 0) {
269
- label = "parent cascade, no own change";
270
- }
271
- if (row.parentName) label += ` ← ${row.parentName}`;
272
- parts.push(label);
273
- } else if (row.dominantCause === "mount") {
274
- parts.push("mount");
275
- } else if (row.dominantCause === "state") {
276
- parts.push("state");
277
- } else if (row.dominantCause === "context") {
278
- parts.push("context");
279
- } else {
280
- parts.push("unknown");
281
- }
282
- if (showMix) {
283
- const mixStr = mixEntries.map(([k, v]) => `${k}:${v}`).join(" + ");
284
- parts.push(`(${mixStr})`);
285
- }
286
- return parts.join(" ");
287
- }
288
- function describeInstances(row) {
289
- if (row.instances <= 1) return row.name;
290
- const counts = row.instanceCounts;
291
- const max = counts[0] ?? 0;
292
- const min = counts[counts.length - 1] ?? 0;
293
- const evenly = max - min <= 1;
294
- if (evenly) {
295
- return `${row.name} ×${row.instances} (${max} each)`;
296
- }
297
- // Uneven — show top instance vs others
298
- const others = counts.slice(1);
299
- const othersSum = others.reduce((s, n) => s + n, 0);
300
- const othersAvg = others.length > 0 ? othersSum / others.length : 0;
301
- return `${row.name} ×${row.instances} (top ${max}, others ~${Math.round(othersAvg)})`;
302
- }
303
-
304
- // =============================================================================
305
- // Format generators
306
- // =============================================================================
307
-
308
- const LLM_PREAMBLE = "React Native render report — review the data and identify why components are re-rendering.";
309
- function generateSummary(report, _settings) {
310
- const lines = [];
311
- lines.push(LLM_PREAMBLE);
312
- lines.push("");
313
- lines.push(`Window: ${formatWindow(report.windowMs)} · ${report.totalUniqueComponents} components · ${report.totalRenders} renders`);
314
- lines.push(`Cause mix: ${causeBreakdownLine(report.causeBreakdown)}`);
315
- lines.push("");
316
- lines.push("Top offenders (count · rate · component · cause):");
317
- lines.push("");
318
- const nameStrings = report.rows.map(describeInstances);
319
- const nameWidth = Math.min(36, Math.max(8, ...nameStrings.map(n => n.length)));
320
- const countWidth = Math.max(3, ...report.rows.map(r => `×${r.renders}`.length));
321
- const rateWidth = Math.max(5, ...report.rows.map(r => formatRate(r.rate).length));
322
- for (let i = 0; i < report.rows.length; i++) {
323
- const row = report.rows[i];
324
- const name = (nameStrings[i] ?? row.name).padEnd(nameWidth);
325
- const count = `×${row.renders}`.padStart(countWidth);
326
- const rate = formatRate(row.rate).padStart(rateWidth);
327
- const reason = describeCause(row);
328
- lines.push(` ${count} ${rate} ${name} ${reason}`);
329
- }
330
- if (report.truncated > 0) {
331
- lines.push("");
332
- lines.push(`... ${report.truncated} more (set topN=-1 to expand)`);
333
- }
334
-
335
- // Data-availability footer
336
- const footer = buildDataFooter(report);
337
- if (footer.length > 0) {
338
- lines.push("");
339
- lines.push("Data availability:");
340
- for (const line of footer) lines.push(` · ${line}`);
341
- }
342
- return lines.join("\n");
343
- }
344
- function buildDataFooter(report) {
345
- const out = [];
346
- if (report.dataGaps.rendersWithoutCause > 0) {
347
- out.push(`${report.dataGaps.rendersWithoutCause} renders had no cause info (enable trackRenderCauses for more detail)`);
348
- }
349
- if (report.dataGaps.propsCaptureOff) {
350
- out.push("prop snapshots not captured (enable capturePropsOnRender to see prop diffs for 'props (keys not captured)' rows)");
351
- }
352
- if (report.dataGaps.stateCaptureOff) {
353
- out.push("state snapshots not captured (enable captureStateOnRender)");
354
- }
355
- if (report.dataGaps.historyOff) {
356
- out.push("render history off (enable enableRenderHistory for per-render timeline)");
357
- }
358
- return out;
359
- }
360
- function generateMarkdownTable(report, _settings) {
361
- const lines = [];
362
- lines.push(`# React Render Report`);
363
- lines.push("");
364
- lines.push(`**Window:** ${formatWindow(report.windowMs)} · **Components:** ${report.totalUniqueComponents} · **Renders:** ${report.totalRenders}`);
365
- lines.push(`**Cause mix:** ${causeBreakdownLine(report.causeBreakdown)}`);
366
- lines.push("");
367
- lines.push("| Component | Renders | Rate | Cause |");
368
- lines.push("|---|---:|---:|---|");
369
- for (const row of report.rows) {
370
- const name = describeInstances(row);
371
- const reason = describeCause(row).replace(/\|/g, "\\|");
372
- lines.push(`| ${name} | ${row.renders} | ${formatRate(row.rate)} | ${reason} |`);
373
- }
374
- if (report.truncated > 0) {
375
- lines.push("");
376
- lines.push(`_... ${report.truncated} more rows_`);
377
- }
378
- const footer = buildDataFooter(report);
379
- if (footer.length > 0) {
380
- lines.push("");
381
- lines.push("**Data availability:**");
382
- for (const line of footer) lines.push(`- ${line}`);
383
- }
384
- return lines.join("\n");
385
- }
386
- function generateJson(_renders, report, settings) {
387
- const exportData = {
388
- exportedAt: new Date().toISOString(),
389
- summary: {
390
- windowMs: report.windowMs,
391
- totalComponents: report.totalUniqueComponents,
392
- totalRenders: report.totalRenders,
393
- causeBreakdown: report.causeBreakdown,
394
- truncated: report.truncated,
395
- dataGaps: report.dataGaps
396
- },
397
- components: report.rows.map(row => ({
398
- name: row.name,
399
- viewType: row.viewType,
400
- instances: row.instances,
401
- instanceCounts: row.instances > 1 ? row.instanceCounts : undefined,
402
- renders: row.renders,
403
- ratePerSec: Number(row.rate.toFixed(3)),
404
- dominantCause: row.dominantCause,
405
- causeMix: row.causeMix,
406
- hookCauses: row.hookCauses,
407
- changedKeys: row.changedKeys,
408
- unknownPropsCount: row.unknownPropsCount || undefined,
409
- parentName: row.parentName,
410
- parentWithOwnChange: row.parentWithOwnChange || undefined,
411
- parentNoChange: row.parentNoChange || undefined,
412
- ...(settings.includeHistory ? {
413
- history: extractHistory(row.source)
414
- } : {})
415
- }))
416
- };
417
- return settings.includeHistory ? JSON.stringify(exportData, null, 2) : JSON.stringify(exportData);
418
- }
419
- function extractHistory(source) {
420
- return source.map(r => ({
421
- name: r.componentName || r.displayName,
422
- nativeTag: r.nativeTag,
423
- renderCount: r.renderCount,
424
- history: r.renderHistory?.map(e => ({
425
- n: e.renderNumber,
426
- t: e.timestamp,
427
- cause: {
428
- type: e.cause.type,
429
- componentCause: e.cause.componentCause,
430
- changedKeys: e.cause.changedKeys,
431
- parentComponentName: e.cause.parentComponentName,
432
- hookChanges: e.cause.hookChanges?.map(h => ({
433
- type: h.type,
434
- index: h.index,
435
- desc: h.description
436
- }))
437
- }
438
- }))
439
- }));
440
- }
441
-
442
- // =============================================================================
443
- // Public API
444
- // =============================================================================
445
-
446
- function generateExport(renders, settings) {
447
- const report = buildReport(renders, settings);
448
- switch (settings.format) {
449
- case "summary":
450
- return generateSummary(report, settings);
451
- case "markdown-table":
452
- return generateMarkdownTable(report, settings);
453
- case "json":
454
- return generateJson(renders, report, settings);
455
- default:
456
- return generateSummary(report, settings);
457
- }
458
- }
459
- function estimateExportSize(renders, settings) {
460
- return generateExport(renders, settings).length;
461
- }
462
- function getExportSummary(renders) {
463
- return {
464
- totalComponents: renders.length,
465
- totalRenders: renders.reduce((sum, r) => sum + r.renderCount, 0)
466
- };
467
- }
468
-
469
- /**
470
- * Export a single component's detail (used by RenderDetailView copy button).
471
- * Always uses the summary format since detail copies are inherently small.
472
- */
473
- function generateSingleComponentExport(render) {
474
- const settings = {
475
- format: "summary",
476
- topN: -1,
477
- minRenders: 1,
478
- filterCauses: [],
479
- groupByName: false,
480
- aggregateCauses: true,
481
- sortBy: "renderCount",
482
- includeHistory: false
483
- };
484
- return generateExport([render], settings);
485
- }
1
+ "use strict";function buildReport(e,n){let t=e.filter(e=>e.renderCount>=n.minRenders);if(n.filterCauses.length>0){const e=new Set(n.filterCauses);t=t.filter(n=>e.has(n.lastRenderCause?.type??"unknown"))}const o=e.flatMap(e=>[e.firstRenderTime,e.lastRenderTime]).filter(e=>Number.isFinite(e)),r=o.length>0?Math.max(...o)-Math.min(...o):0,a=Math.max(.001,r/1e3),s=(n.groupByName?groupByComponentName(t):t.map(e=>[e])).map(e=>processGroup(e,n,a));sortRows(s,n.sortBy);const u=-1===n.topN?0:Math.max(0,s.length-n.topN),p=-1===n.topN?s:s.slice(0,n.topN),i=e.reduce((e,n)=>e+n.renderCount,0),c=aggregateCauseBreakdown(e);let d=0,m=!1;for(const n of e)n.lastRenderCause||(d+=n.renderCount),n.renderHistory&&n.renderHistory.length>0&&(m=!0);return{totalUniqueComponents:t.length,totalRenders:i,windowMs:r,causeBreakdown:c,rows:p,truncated:u,dataGaps:{historyOff:!m,propsCaptureOff:!e.some(e=>e.renderHistory?.some(e=>void 0!==e.capturedProps)),stateCaptureOff:!e.some(e=>e.renderHistory?.some(e=>void 0!==e.capturedState)),rendersWithoutCause:d}}}function groupByComponentName(e){const n=new Map;for(const t of e){const e=t.componentName||t.displayName||t.viewType,o=n.get(e);o?o.push(t):n.set(e,[t])}return Array.from(n.values())}function processGroup(e,n,t){const o=e[0],r=o.componentName||o.displayName||o.viewType;let a=0;const s={},u=new Map,p=new Map,i=new Map;let c=0,d=0,m=0;for(const t of e){a+=t.renderCount;const e=t.lastRenderCause;if(!e)continue;const o=e.type;s[o]=(s[o]??0)+t.renderCount;const r=(e.hookChanges?.length??0)>0,h=(e.changedKeys?.length??0)>0;if("parent"===o&&("state"===e.componentCause||"props"===e.componentCause||r||h?d+=t.renderCount:m+=t.renderCount),"props"!==o||h||(c+=t.renderCount),e.parentComponentName&&i.set(e.parentComponentName,(i.get(e.parentComponentName)??0)+1),n.aggregateCauses&&e.hookChanges)for(const n of e.hookChanges){const e=`${n.type}[${n.index}]`,t=u.get(e);t?t.count+=1:u.set(e,{count:1,sample:formatHookSample(n)})}if(e.changedKeys)for(const n of e.changedKeys)n.includes("(ref only)")||p.set(n,(p.get(n)??0)+1)}const h=Array.from(u.entries()).sort((e,n)=>n[1].count-e[1].count).slice(0,3).map(([e,n])=>({hook:e,count:n.count,sample:n.sample})),l=Array.from(p.entries()).sort((e,n)=>n[1]-e[1]).slice(0,5).map(([e])=>e);let f,g="unknown",C=-1;for(const[e,n]of Object.entries(s))(n??0)>C&&(C=n??0,g=e);let y=0;for(const[e,n]of i)n>y&&(y=n,f=e);const w=e.map(e=>e.renderCount).sort((e,n)=>n-e),$=a/t;return{name:r,viewType:o.viewType,instances:e.length,renders:a,causeMix:s,dominantCause:g,hookCauses:h,changedKeys:l,unknownPropsCount:c,parentWithOwnChange:d,parentNoChange:m,parentName:f,instanceCounts:w,rate:$,source:e}}function aggregateCauseBreakdown(e){const n={};for(const t of e){const e=t.lastRenderCause?.type??"unknown";n[e]=(n[e]??0)+t.renderCount}return n}function sortRows(e,n){switch(n){case"renderCount":e.sort((e,n)=>n.renders-e.renders);break;case"rate":e.sort((e,n)=>n.rate-e.rate);break;case"name":e.sort((e,n)=>e.name.localeCompare(n.name))}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.estimateExportSize=estimateExportSize,exports.generateExport=generateExport,exports.generateSingleComponentExport=generateSingleComponentExport,exports.getExportSummary=getExportSummary;const VALUE_MAX_LEN=32;function truncateValue(e){if(null===e)return"null";if(void 0===e)return"undefined";if("function"==typeof e)return"[Fn]";if("object"==typeof e)return Array.isArray(e)?`[Array(${e.length})]`:"[Obj]";const n=String(e);return n.length>32?n.slice(0,31)+"…":n}function formatHookSample(e){return void 0===e.previousValue&&void 0===e.currentValue?e.description:`${truncateValue(e.previousValue)} → ${truncateValue(e.currentValue)}`}function formatWindow(e){return e<1e3?`${e}ms`:`${(e/1e3).toFixed(1)}s`}function formatRate(e){return e>=100?`${e.toFixed(0)}/s`:e>=10?`${e.toFixed(1)}/s`:`${e.toFixed(2)}/s`}function causeBreakdownLine(e){const n=Object.entries(e).sort((e,n)=>(n[1]??0)-(e[1]??0));return 0===n.length?"(no causes recorded)":n.map(([e,n])=>`${e} ${n}`).join(" · ")}function describeCause(e){const n=[],t=Object.entries(e.causeMix).filter(([,e])=>(e??0)>0).sort((e,n)=>(n[1]??0)-(e[1]??0)),o=t.length>=2&&(t[1]?.[1]??0)>=.1*e.renders;if("hooks"===e.dominantCause&&e.hookCauses.length>0){const t=e.hookCauses[0];t.sample?n.push(`hooks ×${t.count} ${t.hook}: ${t.sample}`):n.push(`hooks ×${t.count} ${t.hook}`)}else if("props"===e.dominantCause){if(e.changedKeys.length>0){const t=e.changedKeys.slice(0,3).join(", ");n.push(`props (${t})`)}else n.push("props (keys not captured)");e.unknownPropsCount>0&&e.changedKeys.length>0&&n.push(`[${e.unknownPropsCount} unidentified]`)}else if("parent"===e.dominantCause){let t="parent cascade";e.parentNoChange>0&&0===e.parentWithOwnChange&&(t="parent cascade, no own change"),e.parentName&&(t+=` ← ${e.parentName}`),n.push(t)}else"mount"===e.dominantCause?n.push("mount"):"state"===e.dominantCause?n.push("state"):"context"===e.dominantCause?n.push("context"):n.push("unknown");if(o){const e=t.map(([e,n])=>`${e}:${n}`).join(" + ");n.push(`(${e})`)}return n.join(" ")}function describeInstances(e){if(e.instances<=1)return e.name;const n=e.instanceCounts,t=n[0]??0;if(t-(n[n.length-1]??0)<=1)return`${e.name} ×${e.instances} (${t} each)`;const o=n.slice(1),r=o.reduce((e,n)=>e+n,0),a=o.length>0?r/o.length:0;return`${e.name} ×${e.instances} (top ${t}, others ~${Math.round(a)})`}const LLM_PREAMBLE="React Native render report — review the data and identify why components are re-rendering.";function generateSummary(e,n){const t=[];t.push(LLM_PREAMBLE),t.push(""),t.push(`Window: ${formatWindow(e.windowMs)} · ${e.totalUniqueComponents} components · ${e.totalRenders} renders`),t.push(`Cause mix: ${causeBreakdownLine(e.causeBreakdown)}`),t.push(""),t.push("Top offenders (count · rate · component · cause):"),t.push("");const o=e.rows.map(describeInstances),r=Math.min(36,Math.max(8,...o.map(e=>e.length))),a=Math.max(3,...e.rows.map(e=>`×${e.renders}`.length)),s=Math.max(5,...e.rows.map(e=>formatRate(e.rate).length));for(let n=0;n<e.rows.length;n++){const u=e.rows[n],p=(o[n]??u.name).padEnd(r),i=`×${u.renders}`.padStart(a),c=formatRate(u.rate).padStart(s),d=describeCause(u);t.push(` ${i} ${c} ${p} ${d}`)}e.truncated>0&&(t.push(""),t.push(`... ${e.truncated} more (set topN=-1 to expand)`));const u=buildDataFooter(e);if(u.length>0){t.push(""),t.push("Data availability:");for(const e of u)t.push(` · ${e}`)}return t.join("\n")}function buildDataFooter(e){const n=[];return e.dataGaps.rendersWithoutCause>0&&n.push(`${e.dataGaps.rendersWithoutCause} renders had no cause info (enable trackRenderCauses for more detail)`),e.dataGaps.propsCaptureOff&&n.push("prop snapshots not captured (enable capturePropsOnRender to see prop diffs for 'props (keys not captured)' rows)"),e.dataGaps.stateCaptureOff&&n.push("state snapshots not captured (enable captureStateOnRender)"),e.dataGaps.historyOff&&n.push("render history off (enable enableRenderHistory for per-render timeline)"),n}function generateMarkdownTable(e,n){const t=[];t.push("# React Render Report"),t.push(""),t.push(`**Window:** ${formatWindow(e.windowMs)} · **Components:** ${e.totalUniqueComponents} · **Renders:** ${e.totalRenders}`),t.push(`**Cause mix:** ${causeBreakdownLine(e.causeBreakdown)}`),t.push(""),t.push("| Component | Renders | Rate | Cause |"),t.push("|---|---:|---:|---|");for(const n of e.rows){const e=describeInstances(n),o=describeCause(n).replace(/\|/g,"\\|");t.push(`| ${e} | ${n.renders} | ${formatRate(n.rate)} | ${o} |`)}e.truncated>0&&(t.push(""),t.push(`_... ${e.truncated} more rows_`));const o=buildDataFooter(e);if(o.length>0){t.push(""),t.push("**Data availability:**");for(const e of o)t.push(`- ${e}`)}return t.join("\n")}function generateJson(e,n,t){const o={exportedAt:(new Date).toISOString(),summary:{windowMs:n.windowMs,totalComponents:n.totalUniqueComponents,totalRenders:n.totalRenders,causeBreakdown:n.causeBreakdown,truncated:n.truncated,dataGaps:n.dataGaps},components:n.rows.map(e=>({name:e.name,viewType:e.viewType,instances:e.instances,instanceCounts:e.instances>1?e.instanceCounts:void 0,renders:e.renders,ratePerSec:Number(e.rate.toFixed(3)),dominantCause:e.dominantCause,causeMix:e.causeMix,hookCauses:e.hookCauses,changedKeys:e.changedKeys,unknownPropsCount:e.unknownPropsCount||void 0,parentName:e.parentName,parentWithOwnChange:e.parentWithOwnChange||void 0,parentNoChange:e.parentNoChange||void 0,...t.includeHistory?{history:extractHistory(e.source)}:{}}))};return t.includeHistory?JSON.stringify(o,null,2):JSON.stringify(o)}function extractHistory(e){return e.map(e=>({name:e.componentName||e.displayName,nativeTag:e.nativeTag,renderCount:e.renderCount,history:e.renderHistory?.map(e=>({n:e.renderNumber,t:e.timestamp,cause:{type:e.cause.type,componentCause:e.cause.componentCause,changedKeys:e.cause.changedKeys,parentComponentName:e.cause.parentComponentName,hookChanges:e.cause.hookChanges?.map(e=>({type:e.type,index:e.index,desc:e.description}))}}))}))}function generateExport(e,n){const t=buildReport(e,n);switch(n.format){case"summary":default:return generateSummary(t,n);case"markdown-table":return generateMarkdownTable(t,n);case"json":return generateJson(e,t,n)}}function estimateExportSize(e,n){return generateExport(e,n).length}function getExportSummary(e){return{totalComponents:e.length,totalRenders:e.reduce((e,n)=>e+n.renderCount,0)}}function generateSingleComponentExport(e){return generateExport([e],{format:"summary",topN:-1,minRenders:1,filterCauses:[],groupByName:!1,aggregateCauses:!0,sortBy:"renderCount",includeHistory:!1})}