@buoy-gg/highlight-updates 2.1.12 → 2.1.13
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/lib/commonjs/highlight-updates/HighlightUpdatesOverlay.js +1 -285
- package/lib/commonjs/highlight-updates/components/HighlightFilterView.js +1 -1371
- package/lib/commonjs/highlight-updates/components/HighlightUpdatesModal.js +1 -591
- package/lib/commonjs/highlight-updates/components/IdentifierBadge.js +1 -267
- package/lib/commonjs/highlight-updates/components/IsolatedRenderList.js +1 -178
- package/lib/commonjs/highlight-updates/components/ModalHeaderContent.js +1 -303
- package/lib/commonjs/highlight-updates/components/RenderCauseBadge.js +1 -500
- package/lib/commonjs/highlight-updates/components/RenderDetailView.js +1 -830
- package/lib/commonjs/highlight-updates/components/RenderHistoryViewer.js +1 -894
- package/lib/commonjs/highlight-updates/components/RenderListItem.js +1 -220
- package/lib/commonjs/highlight-updates/components/StatsDisplay.js +1 -70
- package/lib/commonjs/highlight-updates/components/index.js +1 -97
- package/lib/commonjs/highlight-updates/utils/HighlightUpdatesController.js +1 -1435
- package/lib/commonjs/highlight-updates/utils/PerformanceLogger.js +1 -359
- package/lib/commonjs/highlight-updates/utils/ProfilerInterceptor.js +1 -371
- package/lib/commonjs/highlight-updates/utils/RenderCauseDetector.js +1 -1828
- package/lib/commonjs/highlight-updates/utils/RenderTracker.js +1 -903
- package/lib/commonjs/highlight-updates/utils/ViewTypeMapper.js +1 -264
- package/lib/commonjs/highlight-updates/utils/renderExportFormatter.js +1 -58
- package/lib/commonjs/index.js +1 -311
- package/lib/commonjs/preset.js +1 -278
- package/lib/module/highlight-updates/HighlightUpdatesOverlay.js +1 -278
- package/lib/module/highlight-updates/components/HighlightFilterView.js +1 -1365
- package/lib/module/highlight-updates/components/HighlightUpdatesModal.js +1 -585
- package/lib/module/highlight-updates/components/IdentifierBadge.js +1 -259
- package/lib/module/highlight-updates/components/IsolatedRenderList.js +1 -174
- package/lib/module/highlight-updates/components/ModalHeaderContent.js +1 -298
- package/lib/module/highlight-updates/components/RenderCauseBadge.js +1 -491
- package/lib/module/highlight-updates/components/RenderDetailView.js +1 -826
- package/lib/module/highlight-updates/components/RenderHistoryViewer.js +1 -888
- package/lib/module/highlight-updates/components/RenderListItem.js +1 -215
- package/lib/module/highlight-updates/components/StatsDisplay.js +1 -67
- package/lib/module/highlight-updates/components/index.js +1 -16
- package/lib/module/highlight-updates/utils/HighlightUpdatesController.js +1 -1431
- package/lib/module/highlight-updates/utils/PerformanceLogger.js +1 -353
- package/lib/module/highlight-updates/utils/ProfilerInterceptor.js +1 -358
- package/lib/module/highlight-updates/utils/RenderCauseDetector.js +1 -1818
- package/lib/module/highlight-updates/utils/RenderTracker.js +1 -900
- package/lib/module/highlight-updates/utils/ViewTypeMapper.js +1 -255
- package/lib/module/highlight-updates/utils/renderExportFormatter.js +1 -54
- package/lib/module/index.js +1 -71
- package/lib/module/preset.js +1 -272
- package/package.json +7 -7
- package/lib/typescript/highlight-updates/HighlightUpdatesOverlay.d.ts.map +0 -1
- package/lib/typescript/highlight-updates/components/HighlightFilterView.d.ts.map +0 -1
- package/lib/typescript/highlight-updates/components/HighlightUpdatesModal.d.ts.map +0 -1
- package/lib/typescript/highlight-updates/components/IdentifierBadge.d.ts.map +0 -1
- package/lib/typescript/highlight-updates/components/IsolatedRenderList.d.ts.map +0 -1
- package/lib/typescript/highlight-updates/components/ModalHeaderContent.d.ts.map +0 -1
- package/lib/typescript/highlight-updates/components/RenderCauseBadge.d.ts.map +0 -1
- package/lib/typescript/highlight-updates/components/RenderDetailView.d.ts.map +0 -1
- package/lib/typescript/highlight-updates/components/RenderHistoryViewer.d.ts.map +0 -1
- package/lib/typescript/highlight-updates/components/RenderListItem.d.ts.map +0 -1
- package/lib/typescript/highlight-updates/components/StatsDisplay.d.ts.map +0 -1
- package/lib/typescript/highlight-updates/components/index.d.ts.map +0 -1
- package/lib/typescript/highlight-updates/utils/HighlightUpdatesController.d.ts.map +0 -1
- package/lib/typescript/highlight-updates/utils/PerformanceLogger.d.ts.map +0 -1
- package/lib/typescript/highlight-updates/utils/ProfilerInterceptor.d.ts.map +0 -1
- package/lib/typescript/highlight-updates/utils/RenderCauseDetector.d.ts.map +0 -1
- package/lib/typescript/highlight-updates/utils/RenderTracker.d.ts.map +0 -1
- package/lib/typescript/highlight-updates/utils/ViewTypeMapper.d.ts.map +0 -1
- package/lib/typescript/highlight-updates/utils/renderExportFormatter.d.ts.map +0 -1
- package/lib/typescript/index.d.ts.map +0 -1
- package/lib/typescript/preset.d.ts.map +0 -1
|
@@ -1,900 +1 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* RenderTracker
|
|
3
|
-
*
|
|
4
|
-
* Singleton that tracks component render history for the Highlight Updates modal.
|
|
5
|
-
* Stores information about each tracked component including render counts,
|
|
6
|
-
* timestamps, and identifying props (testID, nativeID, etc.)
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
"use strict";
|
|
10
|
-
|
|
11
|
-
import { getComponentDisplayName } from "./ViewTypeMapper";
|
|
12
|
-
import { PerformanceLogger } from "./PerformanceLogger";
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Debug logging levels for render cause detection.
|
|
16
|
-
* Controls verbosity of console output for debugging "Why Did You Render" feature.
|
|
17
|
-
*
|
|
18
|
-
* - "off": No debug logging (default, best for production)
|
|
19
|
-
* - "minimal": Only log state/hook value changes (e.g., "useState: 3334 → 3335")
|
|
20
|
-
* - "verbose": Log component info + cause + value changes
|
|
21
|
-
* - "all": Full fiber dump with everything (native fiber, component fiber, hooks, batch context)
|
|
22
|
-
*/
|
|
23
|
-
|
|
24
|
-
// Render cause types - why did a component render?
|
|
25
|
-
|
|
26
|
-
// Could not determine
|
|
27
|
-
|
|
28
|
-
// Component-level cause - why did the React component re-render?
|
|
29
|
-
|
|
30
|
-
// Could not determine
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Represents a change in a single hook's state
|
|
34
|
-
* Used to show meaningful before/after values for debugging
|
|
35
|
-
*/
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* A single render event in the history
|
|
39
|
-
* Captures everything about one render occurrence
|
|
40
|
-
*/
|
|
41
|
-
|
|
42
|
-
/** Callback for individual render events (for unified events integration) */
|
|
43
|
-
|
|
44
|
-
// Maximum number of tracked components to prevent memory issues
|
|
45
|
-
const MAX_TRACKED_COMPONENTS = 200;
|
|
46
|
-
|
|
47
|
-
// Default batch size for highlight rendering
|
|
48
|
-
const DEFAULT_BATCH_SIZE = 150;
|
|
49
|
-
class RenderTrackerSingleton {
|
|
50
|
-
renders = new Map();
|
|
51
|
-
listeners = new Set();
|
|
52
|
-
stateListeners = new Set();
|
|
53
|
-
settingsListeners = new Set();
|
|
54
|
-
filterListeners = new Set();
|
|
55
|
-
/** Callbacks for individual render events (unified events integration) */
|
|
56
|
-
renderEventCallbacks = new Set();
|
|
57
|
-
isTracking = false;
|
|
58
|
-
isPaused = false;
|
|
59
|
-
settings = {
|
|
60
|
-
batchSize: DEFAULT_BATCH_SIZE,
|
|
61
|
-
showRenderCount: true,
|
|
62
|
-
performanceLogging: false,
|
|
63
|
-
trackRenderCauses: false,
|
|
64
|
-
// History settings (always enabled)
|
|
65
|
-
enableRenderHistory: true,
|
|
66
|
-
maxRenderHistoryPerComponent: 20,
|
|
67
|
-
capturePropsOnRender: false,
|
|
68
|
-
captureStateOnRender: false,
|
|
69
|
-
// Debug settings
|
|
70
|
-
debugLogLevel: "off",
|
|
71
|
-
// Dev tools visibility - exclude by default
|
|
72
|
-
excludeDevTools: true
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
// Batch mode: defer notifyListeners until endBatch is called
|
|
76
|
-
isBatchMode = false;
|
|
77
|
-
batchDirty = false;
|
|
78
|
-
filters = {
|
|
79
|
-
includeTestID: new Set(),
|
|
80
|
-
includeNativeID: new Set(),
|
|
81
|
-
includeViewType: new Set(),
|
|
82
|
-
includeComponent: new Set(),
|
|
83
|
-
excludeTestID: new Set(),
|
|
84
|
-
excludeNativeID: new Set(),
|
|
85
|
-
excludeViewType: new Set(),
|
|
86
|
-
excludeComponent: new Set(),
|
|
87
|
-
includePatterns: [],
|
|
88
|
-
excludePatterns: []
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Track a component render
|
|
93
|
-
*/
|
|
94
|
-
trackRender(data) {
|
|
95
|
-
if (this.isPaused) return;
|
|
96
|
-
const id = String(data.nativeTag);
|
|
97
|
-
const now = Date.now();
|
|
98
|
-
const existing = this.renders.get(id);
|
|
99
|
-
if (existing) {
|
|
100
|
-
// Mutate in place to keep Map entry correct
|
|
101
|
-
existing.renderCount = data.count;
|
|
102
|
-
existing.lastRenderTime = now;
|
|
103
|
-
existing.color = data.color;
|
|
104
|
-
if (data.measurements) {
|
|
105
|
-
existing.measurements = data.measurements;
|
|
106
|
-
}
|
|
107
|
-
// Update props if they weren't set before
|
|
108
|
-
if (data.testID && !existing.testID) existing.testID = data.testID;
|
|
109
|
-
if (data.nativeID && !existing.nativeID) existing.nativeID = data.nativeID;
|
|
110
|
-
if (data.accessibilityLabel && !existing.accessibilityLabel) {
|
|
111
|
-
existing.accessibilityLabel = data.accessibilityLabel;
|
|
112
|
-
}
|
|
113
|
-
if (data.componentName && !existing.componentName) {
|
|
114
|
-
existing.componentName = data.componentName;
|
|
115
|
-
}
|
|
116
|
-
// Update render cause if provided
|
|
117
|
-
if (data.renderCause) {
|
|
118
|
-
existing.lastRenderCause = data.renderCause;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// Add to render history if enabled (create minimal cause if not provided)
|
|
122
|
-
if (this.settings.enableRenderHistory) {
|
|
123
|
-
const cause = data.renderCause || {
|
|
124
|
-
type: "unknown",
|
|
125
|
-
timestamp: now
|
|
126
|
-
};
|
|
127
|
-
this.addRenderEvent(existing, cause, data.capturedProps, data.capturedState);
|
|
128
|
-
}
|
|
129
|
-
} else {
|
|
130
|
-
// Add new render
|
|
131
|
-
const newRender = {
|
|
132
|
-
id,
|
|
133
|
-
nativeTag: data.nativeTag,
|
|
134
|
-
viewType: data.viewType,
|
|
135
|
-
displayName: getComponentDisplayName(data.viewType),
|
|
136
|
-
testID: data.testID,
|
|
137
|
-
nativeID: data.nativeID,
|
|
138
|
-
accessibilityLabel: data.accessibilityLabel,
|
|
139
|
-
componentName: data.componentName,
|
|
140
|
-
renderCount: data.count,
|
|
141
|
-
firstRenderTime: now,
|
|
142
|
-
lastRenderTime: now,
|
|
143
|
-
measurements: data.measurements,
|
|
144
|
-
color: data.color,
|
|
145
|
-
lastRenderCause: data.renderCause
|
|
146
|
-
};
|
|
147
|
-
|
|
148
|
-
// Initialize render history if enabled (create minimal cause if not provided)
|
|
149
|
-
if (this.settings.enableRenderHistory) {
|
|
150
|
-
newRender.renderHistory = [];
|
|
151
|
-
const cause = data.renderCause || {
|
|
152
|
-
type: data.count === 1 ? "mount" : "unknown",
|
|
153
|
-
timestamp: now
|
|
154
|
-
};
|
|
155
|
-
this.addRenderEvent(newRender, cause, data.capturedProps, data.capturedState);
|
|
156
|
-
}
|
|
157
|
-
this.renders.set(id, newRender);
|
|
158
|
-
|
|
159
|
-
// Enforce max limit - remove oldest renders
|
|
160
|
-
if (this.renders.size > MAX_TRACKED_COMPONENTS) {
|
|
161
|
-
const sorted = Array.from(this.renders.values()).sort((a, b) => a.lastRenderTime - b.lastRenderTime);
|
|
162
|
-
const toRemove = sorted.slice(0, this.renders.size - MAX_TRACKED_COMPONENTS);
|
|
163
|
-
for (const render of toRemove) {
|
|
164
|
-
this.renders.delete(render.id);
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
// In batch mode, defer notification until endBatch()
|
|
170
|
-
if (this.isBatchMode) {
|
|
171
|
-
this.batchDirty = true;
|
|
172
|
-
} else {
|
|
173
|
-
this.notifyListeners();
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* Add a render event to a component's history (circular buffer)
|
|
179
|
-
*/
|
|
180
|
-
addRenderEvent(render, cause, capturedProps, capturedState) {
|
|
181
|
-
// Initialize history array if needed
|
|
182
|
-
if (!render.renderHistory) {
|
|
183
|
-
render.renderHistory = [];
|
|
184
|
-
}
|
|
185
|
-
const event = {
|
|
186
|
-
id: `${render.nativeTag}-${cause.timestamp}`,
|
|
187
|
-
timestamp: cause.timestamp,
|
|
188
|
-
cause,
|
|
189
|
-
renderNumber: render.renderCount,
|
|
190
|
-
// Only include captured data if settings allow and data is provided
|
|
191
|
-
capturedProps: this.settings.capturePropsOnRender ? capturedProps : undefined,
|
|
192
|
-
capturedState: this.settings.captureStateOnRender ? capturedState : undefined
|
|
193
|
-
};
|
|
194
|
-
render.renderHistory.push(event);
|
|
195
|
-
|
|
196
|
-
// Enforce max history size (circular buffer behavior)
|
|
197
|
-
const maxHistory = Math.max(5, Math.min(50, this.settings.maxRenderHistoryPerComponent));
|
|
198
|
-
if (render.renderHistory.length > maxHistory) {
|
|
199
|
-
// Remove oldest events
|
|
200
|
-
render.renderHistory = render.renderHistory.slice(-maxHistory);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// Notify render event callbacks (for unified events integration)
|
|
204
|
-
this.notifyRenderEventCallbacks(event, render);
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
/**
|
|
208
|
-
* Notify all render event callbacks
|
|
209
|
-
*/
|
|
210
|
-
notifyRenderEventCallbacks(event, render) {
|
|
211
|
-
this.renderEventCallbacks.forEach(callback => {
|
|
212
|
-
try {
|
|
213
|
-
callback(event, render);
|
|
214
|
-
} catch {
|
|
215
|
-
// Silently ignore callback errors
|
|
216
|
-
}
|
|
217
|
-
});
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
* Emit a render event to callbacks only (without storing in the render list).
|
|
222
|
-
* Used by Events tool when visual highlights are off - we want events but not list updates.
|
|
223
|
-
*/
|
|
224
|
-
emitRenderEvent(data) {
|
|
225
|
-
if (this.renderEventCallbacks.size === 0) return;
|
|
226
|
-
const now = Date.now();
|
|
227
|
-
const cause = data.renderCause || {
|
|
228
|
-
type: "unknown",
|
|
229
|
-
timestamp: now
|
|
230
|
-
};
|
|
231
|
-
|
|
232
|
-
// Create a minimal TrackedRender for the event (not stored)
|
|
233
|
-
const render = {
|
|
234
|
-
id: String(data.nativeTag),
|
|
235
|
-
nativeTag: data.nativeTag,
|
|
236
|
-
viewType: data.viewType,
|
|
237
|
-
displayName: getComponentDisplayName(data.viewType),
|
|
238
|
-
testID: data.testID,
|
|
239
|
-
nativeID: data.nativeID,
|
|
240
|
-
accessibilityLabel: data.accessibilityLabel,
|
|
241
|
-
componentName: data.componentName,
|
|
242
|
-
renderCount: data.count,
|
|
243
|
-
firstRenderTime: now,
|
|
244
|
-
lastRenderTime: now,
|
|
245
|
-
measurements: data.measurements,
|
|
246
|
-
color: data.color,
|
|
247
|
-
lastRenderCause: cause,
|
|
248
|
-
renderHistory: []
|
|
249
|
-
};
|
|
250
|
-
|
|
251
|
-
// Create the event
|
|
252
|
-
const event = {
|
|
253
|
-
id: `${data.nativeTag}-${now}-${data.count}`,
|
|
254
|
-
timestamp: now,
|
|
255
|
-
renderNumber: data.count,
|
|
256
|
-
cause,
|
|
257
|
-
capturedProps: undefined,
|
|
258
|
-
capturedState: undefined
|
|
259
|
-
};
|
|
260
|
-
|
|
261
|
-
// Notify callbacks only (no storage, no list listeners)
|
|
262
|
-
this.notifyRenderEventCallbacks(event, render);
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
/**
|
|
266
|
-
* Start batch mode - defers listener notifications until endBatch() is called.
|
|
267
|
-
* Use this when tracking multiple renders in a loop to avoid O(n²) notifications.
|
|
268
|
-
*/
|
|
269
|
-
startBatch() {
|
|
270
|
-
this.isBatchMode = true;
|
|
271
|
-
this.batchDirty = false;
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
/**
|
|
275
|
-
* End batch mode and notify listeners if any renders were tracked.
|
|
276
|
-
*/
|
|
277
|
-
endBatch() {
|
|
278
|
-
this.isBatchMode = false;
|
|
279
|
-
if (this.batchDirty) {
|
|
280
|
-
this.batchDirty = false;
|
|
281
|
-
this.notifyListeners();
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
/**
|
|
286
|
-
* Get all tracked renders
|
|
287
|
-
* Creates new object copies to trigger React.memo re-renders
|
|
288
|
-
*/
|
|
289
|
-
getRenders() {
|
|
290
|
-
return Array.from(this.renders.values()).map(r => ({
|
|
291
|
-
...r
|
|
292
|
-
}));
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
/**
|
|
296
|
-
* Check if a component passes all active filters.
|
|
297
|
-
* Used by both the modal list and the overlay to filter components.
|
|
298
|
-
*
|
|
299
|
-
* @param info Component info to check (can be TrackedRender or extracted component info)
|
|
300
|
-
* @returns true if the component passes all filters, false if it should be hidden
|
|
301
|
-
*/
|
|
302
|
-
passesFilters(info) {
|
|
303
|
-
// Check new unified include patterns (if any are set, must match at least one)
|
|
304
|
-
if (this.filters.includePatterns.length > 0) {
|
|
305
|
-
const matchesInclude = this.matchesAnyPatternForInfo(info, this.filters.includePatterns);
|
|
306
|
-
if (!matchesInclude) return false;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
// Check new unified exclude patterns
|
|
310
|
-
if (this.filters.excludePatterns.length > 0) {
|
|
311
|
-
const matchesExclude = this.matchesAnyPatternForInfo(info, this.filters.excludePatterns);
|
|
312
|
-
if (matchesExclude) return false;
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
// Legacy filter support (for backwards compatibility)
|
|
316
|
-
// Include filters: if any are set, component must match at least one
|
|
317
|
-
if (this.filters.includeViewType.size > 0) {
|
|
318
|
-
const viewType = info.viewType || '';
|
|
319
|
-
const displayName = info.displayName || '';
|
|
320
|
-
if (!this.matchesPattern(viewType, this.filters.includeViewType) && !this.matchesPattern(displayName, this.filters.includeViewType)) {
|
|
321
|
-
return false;
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
if (this.filters.includeTestID.size > 0) {
|
|
325
|
-
if (!info.testID || !this.matchesPattern(info.testID, this.filters.includeTestID)) {
|
|
326
|
-
return false;
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
if (this.filters.includeNativeID.size > 0) {
|
|
330
|
-
if (!info.nativeID || !this.matchesPattern(info.nativeID, this.filters.includeNativeID)) {
|
|
331
|
-
return false;
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
if (this.filters.includeComponent.size > 0) {
|
|
335
|
-
if (!info.componentName || !this.matchesPattern(info.componentName, this.filters.includeComponent)) {
|
|
336
|
-
return false;
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
// Exclude filters: if component matches any, it should be hidden
|
|
341
|
-
if (this.filters.excludeViewType.size > 0) {
|
|
342
|
-
const viewType = info.viewType || '';
|
|
343
|
-
const displayName = info.displayName || '';
|
|
344
|
-
if (this.matchesPattern(viewType, this.filters.excludeViewType) || this.matchesPattern(displayName, this.filters.excludeViewType)) {
|
|
345
|
-
return false;
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
if (this.filters.excludeTestID.size > 0) {
|
|
349
|
-
if (info.testID && this.matchesPattern(info.testID, this.filters.excludeTestID)) {
|
|
350
|
-
return false;
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
if (this.filters.excludeNativeID.size > 0) {
|
|
354
|
-
if (info.nativeID && this.matchesPattern(info.nativeID, this.filters.excludeNativeID)) {
|
|
355
|
-
return false;
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
if (this.filters.excludeComponent.size > 0) {
|
|
359
|
-
if (info.componentName && this.matchesPattern(info.componentName, this.filters.excludeComponent)) {
|
|
360
|
-
return false;
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
return true;
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
/**
|
|
367
|
-
* Check if component info matches any of the given patterns
|
|
368
|
-
*/
|
|
369
|
-
matchesAnyPatternForInfo(info, patterns) {
|
|
370
|
-
for (const pattern of patterns) {
|
|
371
|
-
const lowerValue = pattern.value.toLowerCase();
|
|
372
|
-
switch (pattern.type) {
|
|
373
|
-
case "any":
|
|
374
|
-
// Match against all fields
|
|
375
|
-
if (info.viewType.toLowerCase().includes(lowerValue) || info.displayName?.toLowerCase().includes(lowerValue) || info.testID?.toLowerCase().includes(lowerValue) || info.nativeID?.toLowerCase().includes(lowerValue) || info.componentName?.toLowerCase().includes(lowerValue) || info.accessibilityLabel?.toLowerCase().includes(lowerValue)) {
|
|
376
|
-
return true;
|
|
377
|
-
}
|
|
378
|
-
break;
|
|
379
|
-
case "viewType":
|
|
380
|
-
if (info.viewType.toLowerCase().includes(lowerValue) || info.displayName?.toLowerCase().includes(lowerValue)) {
|
|
381
|
-
return true;
|
|
382
|
-
}
|
|
383
|
-
break;
|
|
384
|
-
case "testID":
|
|
385
|
-
if (info.testID?.toLowerCase().includes(lowerValue)) {
|
|
386
|
-
return true;
|
|
387
|
-
}
|
|
388
|
-
break;
|
|
389
|
-
case "nativeID":
|
|
390
|
-
if (info.nativeID?.toLowerCase().includes(lowerValue)) {
|
|
391
|
-
return true;
|
|
392
|
-
}
|
|
393
|
-
break;
|
|
394
|
-
case "component":
|
|
395
|
-
if (info.componentName?.toLowerCase().includes(lowerValue)) {
|
|
396
|
-
return true;
|
|
397
|
-
}
|
|
398
|
-
break;
|
|
399
|
-
case "accessibilityLabel":
|
|
400
|
-
if (info.accessibilityLabel?.toLowerCase().includes(lowerValue)) {
|
|
401
|
-
return true;
|
|
402
|
-
}
|
|
403
|
-
break;
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
return false;
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
/**
|
|
410
|
-
* Check if any filters are currently active
|
|
411
|
-
*/
|
|
412
|
-
hasActiveFilters() {
|
|
413
|
-
return this.filters.includePatterns.length > 0 || this.filters.excludePatterns.length > 0 || this.filters.includeViewType.size > 0 || this.filters.includeTestID.size > 0 || this.filters.includeNativeID.size > 0 || this.filters.includeComponent.size > 0 || this.filters.excludeViewType.size > 0 || this.filters.excludeTestID.size > 0 || this.filters.excludeNativeID.size > 0 || this.filters.excludeComponent.size > 0 || this.filters.minRenderCount !== undefined || this.filters.maxRenderCount !== undefined;
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
/**
|
|
417
|
-
* Get filtered renders based on current filter config
|
|
418
|
-
*/
|
|
419
|
-
getFilteredRenders(searchText = "") {
|
|
420
|
-
let renders = this.getRenders();
|
|
421
|
-
|
|
422
|
-
// Apply search filter
|
|
423
|
-
if (searchText.trim()) {
|
|
424
|
-
const search = searchText.toLowerCase();
|
|
425
|
-
renders = renders.filter(r => {
|
|
426
|
-
return r.viewType.toLowerCase().includes(search) || r.displayName.toLowerCase().includes(search) || r.testID?.toLowerCase().includes(search) || r.nativeID?.toLowerCase().includes(search) || r.accessibilityLabel?.toLowerCase().includes(search) || r.componentName?.toLowerCase().includes(search) || String(r.nativeTag).includes(search);
|
|
427
|
-
});
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
// Apply filters using the shared passesFilters method
|
|
431
|
-
renders = renders.filter(r => this.passesFilters(r));
|
|
432
|
-
|
|
433
|
-
// Apply render count range filter
|
|
434
|
-
if (this.filters.minRenderCount !== undefined) {
|
|
435
|
-
renders = renders.filter(r => r.renderCount >= this.filters.minRenderCount);
|
|
436
|
-
}
|
|
437
|
-
if (this.filters.maxRenderCount !== undefined) {
|
|
438
|
-
renders = renders.filter(r => r.renderCount <= this.filters.maxRenderCount);
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
// Sort by last render time (most recent first)
|
|
442
|
-
return renders.sort((a, b) => b.lastRenderTime - a.lastRenderTime);
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
/**
|
|
446
|
-
* Check if a value matches any pattern in the set
|
|
447
|
-
*/
|
|
448
|
-
matchesPattern(value, patterns) {
|
|
449
|
-
const lowerValue = value.toLowerCase();
|
|
450
|
-
for (const pattern of patterns) {
|
|
451
|
-
const lowerPattern = pattern.toLowerCase();
|
|
452
|
-
if (lowerValue.includes(lowerPattern)) {
|
|
453
|
-
return true;
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
return false;
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
/**
|
|
460
|
-
* Get a single render by id
|
|
461
|
-
*/
|
|
462
|
-
getRender(id) {
|
|
463
|
-
return this.renders.get(id);
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
/**
|
|
467
|
-
* Clear all tracked renders
|
|
468
|
-
*/
|
|
469
|
-
clear() {
|
|
470
|
-
this.renders.clear();
|
|
471
|
-
this.notifyListeners();
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
/**
|
|
475
|
-
* Reset render count for a specific component
|
|
476
|
-
*/
|
|
477
|
-
resetRenderCount(id) {
|
|
478
|
-
const render = this.renders.get(id);
|
|
479
|
-
if (render) {
|
|
480
|
-
render.renderCount = 0;
|
|
481
|
-
this.notifyListeners();
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
/**
|
|
486
|
-
* Start tracking
|
|
487
|
-
*/
|
|
488
|
-
start() {
|
|
489
|
-
this.isTracking = true;
|
|
490
|
-
this.isPaused = false;
|
|
491
|
-
this.notifyStateListeners();
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
/**
|
|
495
|
-
* Stop tracking
|
|
496
|
-
*/
|
|
497
|
-
stop() {
|
|
498
|
-
this.isTracking = false;
|
|
499
|
-
this.isPaused = false;
|
|
500
|
-
this.notifyStateListeners();
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
/**
|
|
504
|
-
* Pause tracking (keeps state but stops adding new renders)
|
|
505
|
-
*/
|
|
506
|
-
pause() {
|
|
507
|
-
this.isPaused = true;
|
|
508
|
-
this.notifyStateListeners();
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
/**
|
|
512
|
-
* Resume tracking
|
|
513
|
-
*/
|
|
514
|
-
resume() {
|
|
515
|
-
this.isPaused = false;
|
|
516
|
-
this.notifyStateListeners();
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
/**
|
|
520
|
-
* Toggle pause state
|
|
521
|
-
*/
|
|
522
|
-
togglePause() {
|
|
523
|
-
this.isPaused = !this.isPaused;
|
|
524
|
-
this.notifyStateListeners();
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
/**
|
|
528
|
-
* Get current tracking state
|
|
529
|
-
*/
|
|
530
|
-
getState() {
|
|
531
|
-
return {
|
|
532
|
-
isTracking: this.isTracking,
|
|
533
|
-
isPaused: this.isPaused
|
|
534
|
-
};
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
/**
|
|
538
|
-
* Get filter config
|
|
539
|
-
*/
|
|
540
|
-
getFilters() {
|
|
541
|
-
return this.filters;
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
/**
|
|
545
|
-
* Update filter config
|
|
546
|
-
*/
|
|
547
|
-
setFilters(filters) {
|
|
548
|
-
this.filters = {
|
|
549
|
-
...this.filters,
|
|
550
|
-
...filters
|
|
551
|
-
};
|
|
552
|
-
this.notifyListeners();
|
|
553
|
-
this.notifyFilterListeners();
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
/**
|
|
557
|
-
* Add an include pattern
|
|
558
|
-
*/
|
|
559
|
-
addIncludePattern(type, pattern) {
|
|
560
|
-
const key = `include${type.charAt(0).toUpperCase() + type.slice(1)}`;
|
|
561
|
-
this.filters[key].add(pattern);
|
|
562
|
-
this.notifyListeners();
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
/**
|
|
566
|
-
* Remove an include pattern
|
|
567
|
-
*/
|
|
568
|
-
removeIncludePattern(type, pattern) {
|
|
569
|
-
const key = `include${type.charAt(0).toUpperCase() + type.slice(1)}`;
|
|
570
|
-
this.filters[key].delete(pattern);
|
|
571
|
-
this.notifyListeners();
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
/**
|
|
575
|
-
* Add an exclude pattern
|
|
576
|
-
*/
|
|
577
|
-
addExcludePattern(type, pattern) {
|
|
578
|
-
const key = `exclude${type.charAt(0).toUpperCase() + type.slice(1)}`;
|
|
579
|
-
this.filters[key].add(pattern);
|
|
580
|
-
this.notifyListeners();
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
/**
|
|
584
|
-
* Remove an exclude pattern
|
|
585
|
-
*/
|
|
586
|
-
removeExcludePattern(type, pattern) {
|
|
587
|
-
const key = `exclude${type.charAt(0).toUpperCase() + type.slice(1)}`;
|
|
588
|
-
this.filters[key].delete(pattern);
|
|
589
|
-
this.notifyListeners();
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
/**
|
|
593
|
-
* Clear all filters
|
|
594
|
-
*/
|
|
595
|
-
clearFilters() {
|
|
596
|
-
this.filters = {
|
|
597
|
-
includeTestID: new Set(),
|
|
598
|
-
includeNativeID: new Set(),
|
|
599
|
-
includeViewType: new Set(),
|
|
600
|
-
includeComponent: new Set(),
|
|
601
|
-
excludeTestID: new Set(),
|
|
602
|
-
excludeNativeID: new Set(),
|
|
603
|
-
excludeViewType: new Set(),
|
|
604
|
-
excludeComponent: new Set(),
|
|
605
|
-
includePatterns: [],
|
|
606
|
-
excludePatterns: [],
|
|
607
|
-
minRenderCount: undefined,
|
|
608
|
-
maxRenderCount: undefined
|
|
609
|
-
};
|
|
610
|
-
this.notifyListeners();
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
/**
|
|
614
|
-
* Get available prop values from tracked renders
|
|
615
|
-
*/
|
|
616
|
-
getAvailableProps() {
|
|
617
|
-
const viewTypes = new Set();
|
|
618
|
-
const testIDs = new Set();
|
|
619
|
-
const nativeIDs = new Set();
|
|
620
|
-
const componentNames = new Set();
|
|
621
|
-
const accessibilityLabels = new Set();
|
|
622
|
-
for (const render of this.renders.values()) {
|
|
623
|
-
if (render.viewType) viewTypes.add(render.viewType);
|
|
624
|
-
if (render.testID) testIDs.add(render.testID);
|
|
625
|
-
if (render.nativeID) nativeIDs.add(render.nativeID);
|
|
626
|
-
if (render.componentName) componentNames.add(render.componentName);
|
|
627
|
-
if (render.accessibilityLabel) accessibilityLabels.add(render.accessibilityLabel);
|
|
628
|
-
}
|
|
629
|
-
return {
|
|
630
|
-
viewTypes: Array.from(viewTypes).sort(),
|
|
631
|
-
testIDs: Array.from(testIDs).sort(),
|
|
632
|
-
nativeIDs: Array.from(nativeIDs).sort(),
|
|
633
|
-
componentNames: Array.from(componentNames).sort(),
|
|
634
|
-
accessibilityLabels: Array.from(accessibilityLabels).sort()
|
|
635
|
-
};
|
|
636
|
-
}
|
|
637
|
-
|
|
638
|
-
/**
|
|
639
|
-
* Get summary stats
|
|
640
|
-
*/
|
|
641
|
-
getStats() {
|
|
642
|
-
let totalRenders = 0;
|
|
643
|
-
for (const render of this.renders.values()) {
|
|
644
|
-
totalRenders += render.renderCount;
|
|
645
|
-
}
|
|
646
|
-
return {
|
|
647
|
-
totalComponents: this.renders.size,
|
|
648
|
-
totalRenders
|
|
649
|
-
};
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
/**
|
|
653
|
-
* Subscribe to render updates
|
|
654
|
-
*/
|
|
655
|
-
subscribe(listener) {
|
|
656
|
-
this.listeners.add(listener);
|
|
657
|
-
// Immediately notify with current state
|
|
658
|
-
listener(this.getRenders());
|
|
659
|
-
return () => {
|
|
660
|
-
this.listeners.delete(listener);
|
|
661
|
-
};
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
/**
|
|
665
|
-
* Subscribe to state changes (tracking/paused)
|
|
666
|
-
*/
|
|
667
|
-
subscribeToState(listener) {
|
|
668
|
-
this.stateListeners.add(listener);
|
|
669
|
-
// Immediately notify with current state
|
|
670
|
-
listener(this.getState());
|
|
671
|
-
return () => {
|
|
672
|
-
this.stateListeners.delete(listener);
|
|
673
|
-
};
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
/**
|
|
677
|
-
* Subscribe to settings changes
|
|
678
|
-
*/
|
|
679
|
-
subscribeToSettings(listener) {
|
|
680
|
-
this.settingsListeners.add(listener);
|
|
681
|
-
// Immediately notify with current settings
|
|
682
|
-
listener(this.settings);
|
|
683
|
-
return () => {
|
|
684
|
-
this.settingsListeners.delete(listener);
|
|
685
|
-
};
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
/**
|
|
689
|
-
* Subscribe to filter changes
|
|
690
|
-
*/
|
|
691
|
-
subscribeToFilters(listener) {
|
|
692
|
-
this.filterListeners.add(listener);
|
|
693
|
-
// Immediately notify with current filters
|
|
694
|
-
listener(this.filters);
|
|
695
|
-
return () => {
|
|
696
|
-
this.filterListeners.delete(listener);
|
|
697
|
-
};
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
/**
|
|
701
|
-
* Subscribe to individual render events as they occur.
|
|
702
|
-
* Used by unified events system to integrate render events into the timeline.
|
|
703
|
-
*
|
|
704
|
-
* @param callback - Called for each render event with the event and component info
|
|
705
|
-
* @returns Unsubscribe function
|
|
706
|
-
*/
|
|
707
|
-
onRenderEvent(callback) {
|
|
708
|
-
this.renderEventCallbacks.add(callback);
|
|
709
|
-
return () => {
|
|
710
|
-
this.renderEventCallbacks.delete(callback);
|
|
711
|
-
};
|
|
712
|
-
}
|
|
713
|
-
|
|
714
|
-
/**
|
|
715
|
-
* Get the number of render event callbacks (for debugging)
|
|
716
|
-
*/
|
|
717
|
-
getRenderEventCallbackCount() {
|
|
718
|
-
return this.renderEventCallbacks.size;
|
|
719
|
-
}
|
|
720
|
-
|
|
721
|
-
/**
|
|
722
|
-
* Check if a render should be visible based on current filters
|
|
723
|
-
* Used by the overlay to filter frozen highlights
|
|
724
|
-
*/
|
|
725
|
-
shouldShowRender(render) {
|
|
726
|
-
const filters = this.filters;
|
|
727
|
-
|
|
728
|
-
// Check new pattern-based filters first
|
|
729
|
-
if (filters.includePatterns.length > 0) {
|
|
730
|
-
if (!this.matchesAnyPatternForInfo(render, filters.includePatterns)) {
|
|
731
|
-
return false;
|
|
732
|
-
}
|
|
733
|
-
}
|
|
734
|
-
if (filters.excludePatterns.length > 0) {
|
|
735
|
-
if (this.matchesAnyPatternForInfo(render, filters.excludePatterns)) {
|
|
736
|
-
return false;
|
|
737
|
-
}
|
|
738
|
-
}
|
|
739
|
-
|
|
740
|
-
// Legacy set-based filters
|
|
741
|
-
const hasLegacyIncludeFilters = filters.includeViewType.size > 0 || filters.includeTestID.size > 0 || filters.includeNativeID.size > 0 || filters.includeComponent.size > 0;
|
|
742
|
-
if (hasLegacyIncludeFilters) {
|
|
743
|
-
const matchesInclude = filters.includeViewType.size > 0 && filters.includeViewType.has(render.viewType) || filters.includeTestID.size > 0 && render.testID && filters.includeTestID.has(render.testID) || filters.includeNativeID.size > 0 && render.nativeID && filters.includeNativeID.has(render.nativeID) || filters.includeComponent.size > 0 && render.componentName && filters.includeComponent.has(render.componentName);
|
|
744
|
-
if (!matchesInclude) {
|
|
745
|
-
return false;
|
|
746
|
-
}
|
|
747
|
-
}
|
|
748
|
-
|
|
749
|
-
// Check exclude filters
|
|
750
|
-
if (filters.excludeViewType.has(render.viewType)) return false;
|
|
751
|
-
if (render.testID && filters.excludeTestID.has(render.testID)) return false;
|
|
752
|
-
if (render.nativeID && filters.excludeNativeID.has(render.nativeID)) return false;
|
|
753
|
-
if (render.componentName && filters.excludeComponent.has(render.componentName)) return false;
|
|
754
|
-
return true;
|
|
755
|
-
}
|
|
756
|
-
|
|
757
|
-
/**
|
|
758
|
-
* Get current settings
|
|
759
|
-
*/
|
|
760
|
-
getSettings() {
|
|
761
|
-
return {
|
|
762
|
-
...this.settings
|
|
763
|
-
};
|
|
764
|
-
}
|
|
765
|
-
|
|
766
|
-
/**
|
|
767
|
-
* Update settings
|
|
768
|
-
*/
|
|
769
|
-
setSettings(newSettings) {
|
|
770
|
-
// Validate batchSize
|
|
771
|
-
if (newSettings.batchSize !== undefined) {
|
|
772
|
-
newSettings.batchSize = Math.max(10, Math.min(500, newSettings.batchSize));
|
|
773
|
-
}
|
|
774
|
-
|
|
775
|
-
// Validate maxRenderHistoryPerComponent
|
|
776
|
-
if (newSettings.maxRenderHistoryPerComponent !== undefined) {
|
|
777
|
-
newSettings.maxRenderHistoryPerComponent = Math.max(5, Math.min(50, newSettings.maxRenderHistoryPerComponent));
|
|
778
|
-
}
|
|
779
|
-
|
|
780
|
-
// If enabling render history, also enable trackRenderCauses
|
|
781
|
-
if (newSettings.enableRenderHistory && !this.settings.trackRenderCauses) {
|
|
782
|
-
newSettings.trackRenderCauses = true;
|
|
783
|
-
}
|
|
784
|
-
|
|
785
|
-
// If disabling trackRenderCauses, also disable render history
|
|
786
|
-
if (newSettings.trackRenderCauses === false && this.settings.enableRenderHistory) {
|
|
787
|
-
newSettings.enableRenderHistory = false;
|
|
788
|
-
}
|
|
789
|
-
this.settings = {
|
|
790
|
-
...this.settings,
|
|
791
|
-
...newSettings
|
|
792
|
-
};
|
|
793
|
-
|
|
794
|
-
// Sync performance logging with PerformanceLogger
|
|
795
|
-
if (newSettings.performanceLogging !== undefined) {
|
|
796
|
-
PerformanceLogger.setEnabled(newSettings.performanceLogging);
|
|
797
|
-
}
|
|
798
|
-
this.notifySettingsListeners();
|
|
799
|
-
}
|
|
800
|
-
|
|
801
|
-
/**
|
|
802
|
-
* Clear render history for all components
|
|
803
|
-
*/
|
|
804
|
-
clearAllRenderHistory() {
|
|
805
|
-
for (const render of this.renders.values()) {
|
|
806
|
-
render.renderHistory = [];
|
|
807
|
-
}
|
|
808
|
-
this.notifyListeners();
|
|
809
|
-
}
|
|
810
|
-
|
|
811
|
-
/**
|
|
812
|
-
* Clear render history for a specific component
|
|
813
|
-
*/
|
|
814
|
-
clearRenderHistory(id) {
|
|
815
|
-
const render = this.renders.get(id);
|
|
816
|
-
if (render) {
|
|
817
|
-
render.renderHistory = [];
|
|
818
|
-
this.notifyListeners();
|
|
819
|
-
}
|
|
820
|
-
}
|
|
821
|
-
|
|
822
|
-
/**
|
|
823
|
-
* Get render history stats for debugging
|
|
824
|
-
*/
|
|
825
|
-
getRenderHistoryStats() {
|
|
826
|
-
let totalEvents = 0;
|
|
827
|
-
let componentsWithHistory = 0;
|
|
828
|
-
for (const render of this.renders.values()) {
|
|
829
|
-
if (render.renderHistory && render.renderHistory.length > 0) {
|
|
830
|
-
totalEvents += render.renderHistory.length;
|
|
831
|
-
componentsWithHistory++;
|
|
832
|
-
}
|
|
833
|
-
}
|
|
834
|
-
return {
|
|
835
|
-
totalEvents,
|
|
836
|
-
componentsWithHistory,
|
|
837
|
-
averageEventsPerComponent: componentsWithHistory > 0 ? totalEvents / componentsWithHistory : 0
|
|
838
|
-
};
|
|
839
|
-
}
|
|
840
|
-
|
|
841
|
-
/**
|
|
842
|
-
* Get batch size (convenience method)
|
|
843
|
-
*/
|
|
844
|
-
getBatchSize() {
|
|
845
|
-
return this.settings.batchSize;
|
|
846
|
-
}
|
|
847
|
-
|
|
848
|
-
/**
|
|
849
|
-
* Set batch size (convenience method)
|
|
850
|
-
*/
|
|
851
|
-
setBatchSize(size) {
|
|
852
|
-
this.setSettings({
|
|
853
|
-
batchSize: size
|
|
854
|
-
});
|
|
855
|
-
}
|
|
856
|
-
notifyListeners() {
|
|
857
|
-
const renders = this.getRenders();
|
|
858
|
-
for (const listener of this.listeners) {
|
|
859
|
-
try {
|
|
860
|
-
listener(renders);
|
|
861
|
-
} catch (error) {
|
|
862
|
-
console.error("[RenderTracker] Error in listener:", error);
|
|
863
|
-
}
|
|
864
|
-
}
|
|
865
|
-
}
|
|
866
|
-
notifyStateListeners() {
|
|
867
|
-
const state = this.getState();
|
|
868
|
-
for (const listener of this.stateListeners) {
|
|
869
|
-
try {
|
|
870
|
-
listener(state);
|
|
871
|
-
} catch (error) {
|
|
872
|
-
console.error("[RenderTracker] Error in state listener:", error);
|
|
873
|
-
}
|
|
874
|
-
}
|
|
875
|
-
}
|
|
876
|
-
notifySettingsListeners() {
|
|
877
|
-
const settings = this.getSettings();
|
|
878
|
-
for (const listener of this.settingsListeners) {
|
|
879
|
-
try {
|
|
880
|
-
listener(settings);
|
|
881
|
-
} catch (error) {
|
|
882
|
-
console.error("[RenderTracker] Error in settings listener:", error);
|
|
883
|
-
}
|
|
884
|
-
}
|
|
885
|
-
}
|
|
886
|
-
notifyFilterListeners() {
|
|
887
|
-
const filters = this.getFilters();
|
|
888
|
-
for (const listener of this.filterListeners) {
|
|
889
|
-
try {
|
|
890
|
-
listener(filters);
|
|
891
|
-
} catch (error) {
|
|
892
|
-
console.error("[RenderTracker] Error in filter listener:", error);
|
|
893
|
-
}
|
|
894
|
-
}
|
|
895
|
-
}
|
|
896
|
-
}
|
|
897
|
-
|
|
898
|
-
// Export singleton instance
|
|
899
|
-
export const RenderTracker = new RenderTrackerSingleton();
|
|
900
|
-
export default RenderTracker;
|
|
1
|
+
"use strict";import{getComponentDisplayName}from"./ViewTypeMapper";import{PerformanceLogger}from"./PerformanceLogger";const MAX_TRACKED_COMPONENTS=200,DEFAULT_BATCH_SIZE=150;class RenderTrackerSingleton{renders=new Map;listeners=new Set;stateListeners=new Set;settingsListeners=new Set;filterListeners=new Set;renderEventCallbacks=new Set;isTracking=!1;isPaused=!1;settings={batchSize:150,showRenderCount:!0,performanceLogging:!1,trackRenderCauses:!1,enableRenderHistory:!0,maxRenderHistoryPerComponent:20,capturePropsOnRender:!1,captureStateOnRender:!1,debugLogLevel:"off",excludeDevTools:!0};isBatchMode=!1;batchDirty=!1;filters={includeTestID:new Set,includeNativeID:new Set,includeViewType:new Set,includeComponent:new Set,excludeTestID:new Set,excludeNativeID:new Set,excludeViewType:new Set,excludeComponent:new Set,includePatterns:[],excludePatterns:[]};trackRender(e){if(this.isPaused)return;const t=String(e.nativeTag),s=Date.now(),i=this.renders.get(t);if(i){if(i.renderCount=e.count,i.lastRenderTime=s,i.color=e.color,e.measurements&&(i.measurements=e.measurements),e.testID&&!i.testID&&(i.testID=e.testID),e.nativeID&&!i.nativeID&&(i.nativeID=e.nativeID),e.accessibilityLabel&&!i.accessibilityLabel&&(i.accessibilityLabel=e.accessibilityLabel),e.componentName&&!i.componentName&&(i.componentName=e.componentName),e.renderCause&&(i.lastRenderCause=e.renderCause),this.settings.enableRenderHistory){const t=e.renderCause||{type:"unknown",timestamp:s};this.addRenderEvent(i,t,e.capturedProps,e.capturedState)}}else{const i={id:t,nativeTag:e.nativeTag,viewType:e.viewType,displayName:getComponentDisplayName(e.viewType),testID:e.testID,nativeID:e.nativeID,accessibilityLabel:e.accessibilityLabel,componentName:e.componentName,renderCount:e.count,firstRenderTime:s,lastRenderTime:s,measurements:e.measurements,color:e.color,lastRenderCause:e.renderCause};if(this.settings.enableRenderHistory){i.renderHistory=[];const t=e.renderCause||{type:1===e.count?"mount":"unknown",timestamp:s};this.addRenderEvent(i,t,e.capturedProps,e.capturedState)}if(this.renders.set(t,i),this.renders.size>200){const e=Array.from(this.renders.values()).sort((e,t)=>e.lastRenderTime-t.lastRenderTime).slice(0,this.renders.size-200);for(const t of e)this.renders.delete(t.id)}}this.isBatchMode?this.batchDirty=!0:this.notifyListeners()}addRenderEvent(e,t,s,i){e.renderHistory||(e.renderHistory=[]);const n={id:`${e.nativeTag}-${t.timestamp}`,timestamp:t.timestamp,cause:t,renderNumber:e.renderCount,capturedProps:this.settings.capturePropsOnRender?s:void 0,capturedState:this.settings.captureStateOnRender?i:void 0};e.renderHistory.push(n);const r=Math.max(5,Math.min(50,this.settings.maxRenderHistoryPerComponent));e.renderHistory.length>r&&(e.renderHistory=e.renderHistory.slice(-r)),this.notifyRenderEventCallbacks(n,e)}notifyRenderEventCallbacks(e,t){this.renderEventCallbacks.forEach(s=>{try{s(e,t)}catch{}})}emitRenderEvent(e){if(0===this.renderEventCallbacks.size)return;const t=Date.now(),s=e.renderCause||{type:"unknown",timestamp:t},i={id:String(e.nativeTag),nativeTag:e.nativeTag,viewType:e.viewType,displayName:getComponentDisplayName(e.viewType),testID:e.testID,nativeID:e.nativeID,accessibilityLabel:e.accessibilityLabel,componentName:e.componentName,renderCount:e.count,firstRenderTime:t,lastRenderTime:t,measurements:e.measurements,color:e.color,lastRenderCause:s,renderHistory:[]},n={id:`${e.nativeTag}-${t}-${e.count}`,timestamp:t,renderNumber:e.count,cause:s,capturedProps:void 0,capturedState:void 0};this.notifyRenderEventCallbacks(n,i)}startBatch(){this.isBatchMode=!0,this.batchDirty=!1}endBatch(){this.isBatchMode=!1,this.batchDirty&&(this.batchDirty=!1,this.notifyListeners())}getRenders(){return Array.from(this.renders.values()).map(e=>({...e}))}passesFilters(e){if(this.filters.includePatterns.length>0&&!this.matchesAnyPatternForInfo(e,this.filters.includePatterns))return!1;if(this.filters.excludePatterns.length>0&&this.matchesAnyPatternForInfo(e,this.filters.excludePatterns))return!1;if(this.filters.includeViewType.size>0){const t=e.viewType||"",s=e.displayName||"";if(!this.matchesPattern(t,this.filters.includeViewType)&&!this.matchesPattern(s,this.filters.includeViewType))return!1}if(this.filters.includeTestID.size>0&&(!e.testID||!this.matchesPattern(e.testID,this.filters.includeTestID)))return!1;if(this.filters.includeNativeID.size>0&&(!e.nativeID||!this.matchesPattern(e.nativeID,this.filters.includeNativeID)))return!1;if(this.filters.includeComponent.size>0&&(!e.componentName||!this.matchesPattern(e.componentName,this.filters.includeComponent)))return!1;if(this.filters.excludeViewType.size>0){const t=e.viewType||"",s=e.displayName||"";if(this.matchesPattern(t,this.filters.excludeViewType)||this.matchesPattern(s,this.filters.excludeViewType))return!1}return!(this.filters.excludeTestID.size>0&&e.testID&&this.matchesPattern(e.testID,this.filters.excludeTestID)||this.filters.excludeNativeID.size>0&&e.nativeID&&this.matchesPattern(e.nativeID,this.filters.excludeNativeID)||this.filters.excludeComponent.size>0&&e.componentName&&this.matchesPattern(e.componentName,this.filters.excludeComponent))}matchesAnyPatternForInfo(e,t){for(const s of t){const t=s.value.toLowerCase();switch(s.type){case"any":if(e.viewType.toLowerCase().includes(t)||e.displayName?.toLowerCase().includes(t)||e.testID?.toLowerCase().includes(t)||e.nativeID?.toLowerCase().includes(t)||e.componentName?.toLowerCase().includes(t)||e.accessibilityLabel?.toLowerCase().includes(t))return!0;break;case"viewType":if(e.viewType.toLowerCase().includes(t)||e.displayName?.toLowerCase().includes(t))return!0;break;case"testID":if(e.testID?.toLowerCase().includes(t))return!0;break;case"nativeID":if(e.nativeID?.toLowerCase().includes(t))return!0;break;case"component":if(e.componentName?.toLowerCase().includes(t))return!0;break;case"accessibilityLabel":if(e.accessibilityLabel?.toLowerCase().includes(t))return!0}}return!1}hasActiveFilters(){return this.filters.includePatterns.length>0||this.filters.excludePatterns.length>0||this.filters.includeViewType.size>0||this.filters.includeTestID.size>0||this.filters.includeNativeID.size>0||this.filters.includeComponent.size>0||this.filters.excludeViewType.size>0||this.filters.excludeTestID.size>0||this.filters.excludeNativeID.size>0||this.filters.excludeComponent.size>0||void 0!==this.filters.minRenderCount||void 0!==this.filters.maxRenderCount}getFilteredRenders(e=""){let t=this.getRenders();if(e.trim()){const s=e.toLowerCase();t=t.filter(e=>e.viewType.toLowerCase().includes(s)||e.displayName.toLowerCase().includes(s)||e.testID?.toLowerCase().includes(s)||e.nativeID?.toLowerCase().includes(s)||e.accessibilityLabel?.toLowerCase().includes(s)||e.componentName?.toLowerCase().includes(s)||String(e.nativeTag).includes(s))}return t=t.filter(e=>this.passesFilters(e)),void 0!==this.filters.minRenderCount&&(t=t.filter(e=>e.renderCount>=this.filters.minRenderCount)),void 0!==this.filters.maxRenderCount&&(t=t.filter(e=>e.renderCount<=this.filters.maxRenderCount)),t.sort((e,t)=>t.lastRenderTime-e.lastRenderTime)}matchesPattern(e,t){const s=e.toLowerCase();for(const e of t){const t=e.toLowerCase();if(s.includes(t))return!0}return!1}getRender(e){return this.renders.get(e)}clear(){this.renders.clear(),this.notifyListeners()}resetRenderCount(e){const t=this.renders.get(e);t&&(t.renderCount=0,this.notifyListeners())}start(){this.isTracking=!0,this.isPaused=!1,this.notifyStateListeners()}stop(){this.isTracking=!1,this.isPaused=!1,this.notifyStateListeners()}pause(){this.isPaused=!0,this.notifyStateListeners()}resume(){this.isPaused=!1,this.notifyStateListeners()}togglePause(){this.isPaused=!this.isPaused,this.notifyStateListeners()}getState(){return{isTracking:this.isTracking,isPaused:this.isPaused}}getFilters(){return this.filters}setFilters(e){this.filters={...this.filters,...e},this.notifyListeners(),this.notifyFilterListeners()}addIncludePattern(e,t){const s=`include${e.charAt(0).toUpperCase()+e.slice(1)}`;this.filters[s].add(t),this.notifyListeners()}removeIncludePattern(e,t){const s=`include${e.charAt(0).toUpperCase()+e.slice(1)}`;this.filters[s].delete(t),this.notifyListeners()}addExcludePattern(e,t){const s=`exclude${e.charAt(0).toUpperCase()+e.slice(1)}`;this.filters[s].add(t),this.notifyListeners()}removeExcludePattern(e,t){const s=`exclude${e.charAt(0).toUpperCase()+e.slice(1)}`;this.filters[s].delete(t),this.notifyListeners()}clearFilters(){this.filters={includeTestID:new Set,includeNativeID:new Set,includeViewType:new Set,includeComponent:new Set,excludeTestID:new Set,excludeNativeID:new Set,excludeViewType:new Set,excludeComponent:new Set,includePatterns:[],excludePatterns:[],minRenderCount:void 0,maxRenderCount:void 0},this.notifyListeners()}getAvailableProps(){const e=new Set,t=new Set,s=new Set,i=new Set,n=new Set;for(const r of this.renders.values())r.viewType&&e.add(r.viewType),r.testID&&t.add(r.testID),r.nativeID&&s.add(r.nativeID),r.componentName&&i.add(r.componentName),r.accessibilityLabel&&n.add(r.accessibilityLabel);return{viewTypes:Array.from(e).sort(),testIDs:Array.from(t).sort(),nativeIDs:Array.from(s).sort(),componentNames:Array.from(i).sort(),accessibilityLabels:Array.from(n).sort()}}getStats(){let e=0;for(const t of this.renders.values())e+=t.renderCount;return{totalComponents:this.renders.size,totalRenders:e}}subscribe(e){return this.listeners.add(e),e(this.getRenders()),()=>{this.listeners.delete(e)}}subscribeToState(e){return this.stateListeners.add(e),e(this.getState()),()=>{this.stateListeners.delete(e)}}subscribeToSettings(e){return this.settingsListeners.add(e),e(this.settings),()=>{this.settingsListeners.delete(e)}}subscribeToFilters(e){return this.filterListeners.add(e),e(this.filters),()=>{this.filterListeners.delete(e)}}onRenderEvent(e){return this.renderEventCallbacks.add(e),()=>{this.renderEventCallbacks.delete(e)}}getRenderEventCallbackCount(){return this.renderEventCallbacks.size}shouldShowRender(e){const t=this.filters;return!(t.includePatterns.length>0&&!this.matchesAnyPatternForInfo(e,t.includePatterns))&&(!(t.excludePatterns.length>0&&this.matchesAnyPatternForInfo(e,t.excludePatterns))&&(!((t.includeViewType.size>0||t.includeTestID.size>0||t.includeNativeID.size>0||t.includeComponent.size>0)&&!(t.includeViewType.size>0&&t.includeViewType.has(e.viewType)||t.includeTestID.size>0&&e.testID&&t.includeTestID.has(e.testID)||t.includeNativeID.size>0&&e.nativeID&&t.includeNativeID.has(e.nativeID)||t.includeComponent.size>0&&e.componentName&&t.includeComponent.has(e.componentName)))&&!(t.excludeViewType.has(e.viewType)||e.testID&&t.excludeTestID.has(e.testID)||e.nativeID&&t.excludeNativeID.has(e.nativeID)||e.componentName&&t.excludeComponent.has(e.componentName))))}getSettings(){return{...this.settings}}setSettings(e){void 0!==e.batchSize&&(e.batchSize=Math.max(10,Math.min(500,e.batchSize))),void 0!==e.maxRenderHistoryPerComponent&&(e.maxRenderHistoryPerComponent=Math.max(5,Math.min(50,e.maxRenderHistoryPerComponent))),e.enableRenderHistory&&!this.settings.trackRenderCauses&&(e.trackRenderCauses=!0),!1===e.trackRenderCauses&&this.settings.enableRenderHistory&&(e.enableRenderHistory=!1),this.settings={...this.settings,...e},void 0!==e.performanceLogging&&PerformanceLogger.setEnabled(e.performanceLogging),this.notifySettingsListeners()}clearAllRenderHistory(){for(const e of this.renders.values())e.renderHistory=[];this.notifyListeners()}clearRenderHistory(e){const t=this.renders.get(e);t&&(t.renderHistory=[],this.notifyListeners())}getRenderHistoryStats(){let e=0,t=0;for(const s of this.renders.values())s.renderHistory&&s.renderHistory.length>0&&(e+=s.renderHistory.length,t++);return{totalEvents:e,componentsWithHistory:t,averageEventsPerComponent:t>0?e/t:0}}getBatchSize(){return this.settings.batchSize}setBatchSize(e){this.setSettings({batchSize:e})}notifyListeners(){const e=this.getRenders();for(const t of this.listeners)try{t(e)}catch(e){console.error("[RenderTracker] Error in listener:",e)}}notifyStateListeners(){const e=this.getState();for(const t of this.stateListeners)try{t(e)}catch(e){console.error("[RenderTracker] Error in state listener:",e)}}notifySettingsListeners(){const e=this.getSettings();for(const t of this.settingsListeners)try{t(e)}catch(e){console.error("[RenderTracker] Error in settings listener:",e)}}notifyFilterListeners(){const e=this.getFilters();for(const t of this.filterListeners)try{t(e)}catch(e){console.error("[RenderTracker] Error in filter listener:",e)}}}export const RenderTracker=new RenderTrackerSingleton;export default RenderTracker;
|