@react-devtools-plus/scan 0.2.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/.turbo/turbo-build.log +23 -0
- package/.turbo/turbo-prepare$colon$type.log +6 -0
- package/LICENSE +21 -0
- package/README.md +172 -0
- package/dist/index.cjs +18394 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +304 -0
- package/dist/index.d.ts +304 -0
- package/dist/index.js +18350 -0
- package/dist/index.js.map +1 -0
- package/package.json +50 -0
- package/src/adapter.ts +1257 -0
- package/src/index.ts +134 -0
- package/src/plugin.ts +638 -0
- package/src/types.ts +271 -0
- package/tsconfig.json +9 -0
- package/tsup.config.ts +22 -0
package/src/plugin.ts
ADDED
|
@@ -0,0 +1,638 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React Scan Plugin for React DevTools
|
|
3
|
+
*
|
|
4
|
+
* This plugin integrates React Scan into the React DevTools plugin system,
|
|
5
|
+
* providing performance monitoring and analysis capabilities.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { ReactDevtoolsScanOptions, ScanInstance } from './types'
|
|
9
|
+
import { getDisplayName, getFiberId } from 'bippy'
|
|
10
|
+
import { getScanInstance, resetScanInstance } from './adapter'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* React Scan plugin configuration
|
|
14
|
+
*/
|
|
15
|
+
export interface ScanPluginConfig extends ReactDevtoolsScanOptions {
|
|
16
|
+
/**
|
|
17
|
+
* Whether to auto-start scan on plugin load
|
|
18
|
+
* @default true
|
|
19
|
+
*/
|
|
20
|
+
autoStart?: boolean
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Create React Scan plugin
|
|
25
|
+
*
|
|
26
|
+
* @param config - Plugin configuration
|
|
27
|
+
* @returns DevTools plugin instance
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```typescript
|
|
31
|
+
* import { createScanPlugin } from '@react-devtools-plus/scan/plugin';
|
|
32
|
+
*
|
|
33
|
+
* const scanPlugin = createScanPlugin({
|
|
34
|
+
* enabled: true,
|
|
35
|
+
* showToolbar: true,
|
|
36
|
+
* autoStart: true,
|
|
37
|
+
* });
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export function createScanPlugin(config: ScanPluginConfig = {}): any {
|
|
41
|
+
let scanInstance: ScanInstance | null = null
|
|
42
|
+
let context: any = null
|
|
43
|
+
|
|
44
|
+
const {
|
|
45
|
+
autoStart = true,
|
|
46
|
+
...scanOptions
|
|
47
|
+
} = config
|
|
48
|
+
|
|
49
|
+
// Event emitter for plugin events
|
|
50
|
+
const eventHandlers: Map<string, Set<(data: any) => void>> = new Map()
|
|
51
|
+
|
|
52
|
+
const emit = (eventName: string, data: any) => {
|
|
53
|
+
const handlers = eventHandlers.get(eventName)
|
|
54
|
+
if (handlers) {
|
|
55
|
+
handlers.forEach((handler) => {
|
|
56
|
+
if (typeof handler === 'function') {
|
|
57
|
+
try {
|
|
58
|
+
handler(data)
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
// Ignore event handler errors
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
})
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const subscribe = (eventName: string, handler: (data: any) => void) => {
|
|
69
|
+
if (!eventHandlers.has(eventName)) {
|
|
70
|
+
eventHandlers.set(eventName, new Set())
|
|
71
|
+
}
|
|
72
|
+
eventHandlers.get(eventName)!.add(handler)
|
|
73
|
+
|
|
74
|
+
// Return unsubscribe function
|
|
75
|
+
return () => {
|
|
76
|
+
const handlers = eventHandlers.get(eventName)
|
|
77
|
+
if (handlers) {
|
|
78
|
+
handlers.delete(handler)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
id: 'react-scan',
|
|
85
|
+
name: 'React Scan',
|
|
86
|
+
description: 'Performance monitoring and analysis for React applications',
|
|
87
|
+
version: '1.0.0',
|
|
88
|
+
|
|
89
|
+
// Expose subscribe method for event subscriptions
|
|
90
|
+
subscribe,
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Plugin setup
|
|
94
|
+
*/
|
|
95
|
+
async setup(ctx: any) {
|
|
96
|
+
context = ctx
|
|
97
|
+
|
|
98
|
+
// Always initialize the scan instance, and start by default
|
|
99
|
+
const initOptions = {
|
|
100
|
+
enabled: autoStart !== false,
|
|
101
|
+
...scanOptions,
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Always create the scan instance so RPC methods work
|
|
105
|
+
scanInstance = getScanInstance(initOptions)
|
|
106
|
+
|
|
107
|
+
// Always call scan() to start scanning by default (unless autoStart explicitly false)
|
|
108
|
+
if (autoStart !== false) {
|
|
109
|
+
// Use adapter's start which handles globals correctly
|
|
110
|
+
scanInstance.start()
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Set up inspect state change listener
|
|
114
|
+
try {
|
|
115
|
+
const scan = getScanInstance()
|
|
116
|
+
if (scan) {
|
|
117
|
+
// Track the last hovered component during inspection
|
|
118
|
+
let lastInspectedComponent: { componentName: string, componentId?: string } | null = null
|
|
119
|
+
|
|
120
|
+
scan.onInspectStateChange((state: any) => {
|
|
121
|
+
// Emit inspect state change event
|
|
122
|
+
// Sanitize state for RPC
|
|
123
|
+
const sanitizedState = {
|
|
124
|
+
kind: state.kind,
|
|
125
|
+
// Include component name if available
|
|
126
|
+
componentName: state.fiber ? (state.fiber.type?.displayName || state.fiber.type?.name || 'Unknown') : undefined,
|
|
127
|
+
}
|
|
128
|
+
emit('inspect-state-changed', sanitizedState)
|
|
129
|
+
|
|
130
|
+
// Track component during inspecting state - save hovered component info
|
|
131
|
+
if (state.kind === 'inspecting' && state.fiber) {
|
|
132
|
+
const componentName = getDisplayName(state.fiber.type) || 'Unknown'
|
|
133
|
+
const componentId = String(getFiberId(state.fiber))
|
|
134
|
+
lastInspectedComponent = { componentName, componentId }
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// If a component is focused, emit focused component info and set up tracking
|
|
138
|
+
if (state.kind === 'focused') {
|
|
139
|
+
const focusedComponent = scan.getFocusedComponent()
|
|
140
|
+
if (focusedComponent) {
|
|
141
|
+
// Sanitize for RPC - remove non-serializable fields
|
|
142
|
+
const { fiber, domElement, ...serializableComponent } = focusedComponent as any
|
|
143
|
+
emit('component-focused', serializableComponent)
|
|
144
|
+
// Set up render tracking
|
|
145
|
+
scan.setFocusedComponentByName(focusedComponent.componentName)
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// When inspection ends (inspect-off), emit the last inspected component
|
|
150
|
+
if (state.kind === 'inspect-off' && lastInspectedComponent) {
|
|
151
|
+
emit('component-focused', lastInspectedComponent)
|
|
152
|
+
scan.setFocusedComponentByName(lastInspectedComponent.componentName)
|
|
153
|
+
lastInspectedComponent = null
|
|
154
|
+
}
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
// Subscribe to focused component render changes
|
|
158
|
+
scan.onFocusedComponentChange((info) => {
|
|
159
|
+
emit('focused-component-render', info)
|
|
160
|
+
})
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
// Ignore errors setting up inspect state listener
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Listen for component tree changes if context supports it
|
|
168
|
+
// Listen to component tree changes if supported
|
|
169
|
+
if (ctx.on) {
|
|
170
|
+
ctx.on('component-tree-changed', (_event: any) => {
|
|
171
|
+
// Component tree changed, could update UI here
|
|
172
|
+
})
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Register RPC functions if context supports it
|
|
176
|
+
if (ctx.registerRPC) {
|
|
177
|
+
ctx.registerRPC('getScanOptions', () => {
|
|
178
|
+
try {
|
|
179
|
+
const scan = getScanInstance()
|
|
180
|
+
return scan?.getOptions() || null
|
|
181
|
+
}
|
|
182
|
+
catch {
|
|
183
|
+
return null
|
|
184
|
+
}
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
ctx.registerRPC('setScanOptions', (options: Partial<ReactDevtoolsScanOptions>) => {
|
|
188
|
+
try {
|
|
189
|
+
const scan = getScanInstance()
|
|
190
|
+
if (scan) {
|
|
191
|
+
scan.setOptions(options)
|
|
192
|
+
return true
|
|
193
|
+
}
|
|
194
|
+
return false
|
|
195
|
+
}
|
|
196
|
+
catch {
|
|
197
|
+
return false
|
|
198
|
+
}
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
ctx.registerRPC('startScan', () => {
|
|
202
|
+
try {
|
|
203
|
+
const scan = getScanInstance()
|
|
204
|
+
if (scan) {
|
|
205
|
+
scan.start()
|
|
206
|
+
return true
|
|
207
|
+
}
|
|
208
|
+
return false
|
|
209
|
+
}
|
|
210
|
+
catch {
|
|
211
|
+
return false
|
|
212
|
+
}
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
ctx.registerRPC('stopScan', () => {
|
|
216
|
+
try {
|
|
217
|
+
const scan = getScanInstance()
|
|
218
|
+
if (scan) {
|
|
219
|
+
scan.stop()
|
|
220
|
+
return true
|
|
221
|
+
}
|
|
222
|
+
return false
|
|
223
|
+
}
|
|
224
|
+
catch {
|
|
225
|
+
return false
|
|
226
|
+
}
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
ctx.registerRPC('isScanActive', () => {
|
|
230
|
+
try {
|
|
231
|
+
const scan = getScanInstance()
|
|
232
|
+
return scan?.isActive() || false
|
|
233
|
+
}
|
|
234
|
+
catch {
|
|
235
|
+
return false
|
|
236
|
+
}
|
|
237
|
+
})
|
|
238
|
+
}
|
|
239
|
+
},
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Plugin teardown
|
|
243
|
+
*/
|
|
244
|
+
async teardown() {
|
|
245
|
+
if (scanInstance) {
|
|
246
|
+
scanInstance.stop()
|
|
247
|
+
scanInstance = null
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
resetScanInstance()
|
|
251
|
+
context = null
|
|
252
|
+
},
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* RPC methods exposed to other plugins
|
|
256
|
+
*/
|
|
257
|
+
rpc: {
|
|
258
|
+
/**
|
|
259
|
+
* Get current scan options
|
|
260
|
+
*/
|
|
261
|
+
getOptions: () => {
|
|
262
|
+
try {
|
|
263
|
+
const scan = getScanInstance()
|
|
264
|
+
return scan?.getOptions() || null
|
|
265
|
+
}
|
|
266
|
+
catch {
|
|
267
|
+
return null
|
|
268
|
+
}
|
|
269
|
+
},
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Set scan options
|
|
273
|
+
*/
|
|
274
|
+
setOptions: (options: Partial<ReactDevtoolsScanOptions>) => {
|
|
275
|
+
try {
|
|
276
|
+
const scan = getScanInstance()
|
|
277
|
+
if (scan) {
|
|
278
|
+
scan.setOptions(options)
|
|
279
|
+
return true
|
|
280
|
+
}
|
|
281
|
+
return false
|
|
282
|
+
}
|
|
283
|
+
catch {
|
|
284
|
+
return false
|
|
285
|
+
}
|
|
286
|
+
},
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Start scan
|
|
290
|
+
*/
|
|
291
|
+
start: () => {
|
|
292
|
+
try {
|
|
293
|
+
const scanInst = getScanInstance()
|
|
294
|
+
if (scanInst) {
|
|
295
|
+
scanInst.start()
|
|
296
|
+
return true
|
|
297
|
+
}
|
|
298
|
+
// Auto-initialize if not started
|
|
299
|
+
if (!scanInstance) {
|
|
300
|
+
scanInstance = getScanInstance(config)
|
|
301
|
+
scanInstance.start()
|
|
302
|
+
return true
|
|
303
|
+
}
|
|
304
|
+
return false
|
|
305
|
+
}
|
|
306
|
+
catch {
|
|
307
|
+
return false
|
|
308
|
+
}
|
|
309
|
+
},
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Stop scan
|
|
313
|
+
*/
|
|
314
|
+
stop: () => {
|
|
315
|
+
try {
|
|
316
|
+
const scan = getScanInstance()
|
|
317
|
+
if (scan) {
|
|
318
|
+
scan.stop()
|
|
319
|
+
return true
|
|
320
|
+
}
|
|
321
|
+
return false
|
|
322
|
+
}
|
|
323
|
+
catch {
|
|
324
|
+
return false
|
|
325
|
+
}
|
|
326
|
+
},
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Check if scan is active
|
|
330
|
+
*/
|
|
331
|
+
isActive: () => {
|
|
332
|
+
try {
|
|
333
|
+
const scan = getScanInstance()
|
|
334
|
+
return scan?.isActive() || false
|
|
335
|
+
}
|
|
336
|
+
catch {
|
|
337
|
+
return false
|
|
338
|
+
}
|
|
339
|
+
},
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Hide the React Scan toolbar
|
|
343
|
+
*/
|
|
344
|
+
hideToolbar: () => {
|
|
345
|
+
try {
|
|
346
|
+
const scan = getScanInstance()
|
|
347
|
+
if (scan) {
|
|
348
|
+
scan.hideToolbar()
|
|
349
|
+
return true
|
|
350
|
+
}
|
|
351
|
+
return false
|
|
352
|
+
}
|
|
353
|
+
catch {
|
|
354
|
+
return false
|
|
355
|
+
}
|
|
356
|
+
},
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Show the React Scan toolbar
|
|
360
|
+
*/
|
|
361
|
+
showToolbar: () => {
|
|
362
|
+
try {
|
|
363
|
+
const scan = getScanInstance()
|
|
364
|
+
if (scan) {
|
|
365
|
+
scan.showToolbar()
|
|
366
|
+
return true
|
|
367
|
+
}
|
|
368
|
+
return false
|
|
369
|
+
}
|
|
370
|
+
catch {
|
|
371
|
+
return false
|
|
372
|
+
}
|
|
373
|
+
},
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* Get toolbar visibility state
|
|
377
|
+
*/
|
|
378
|
+
getToolbarVisibility: () => {
|
|
379
|
+
try {
|
|
380
|
+
const scan = getScanInstance()
|
|
381
|
+
return scan?.getToolbarVisibility() || false
|
|
382
|
+
}
|
|
383
|
+
catch {
|
|
384
|
+
return false
|
|
385
|
+
}
|
|
386
|
+
},
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Get performance data for all components
|
|
390
|
+
*/
|
|
391
|
+
getPerformanceData: () => {
|
|
392
|
+
try {
|
|
393
|
+
const scan = getScanInstance()
|
|
394
|
+
return scan?.getPerformanceData() || []
|
|
395
|
+
}
|
|
396
|
+
catch {
|
|
397
|
+
return []
|
|
398
|
+
}
|
|
399
|
+
},
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Get aggregated performance summary
|
|
403
|
+
*/
|
|
404
|
+
getPerformanceSummary: () => {
|
|
405
|
+
try {
|
|
406
|
+
const scan = getScanInstance()
|
|
407
|
+
if (!scan) {
|
|
408
|
+
return {
|
|
409
|
+
totalRenders: 0,
|
|
410
|
+
totalComponents: 0,
|
|
411
|
+
unnecessaryRenders: 0,
|
|
412
|
+
averageRenderTime: 0,
|
|
413
|
+
slowestComponents: [],
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
return scan.getPerformanceSummary()
|
|
417
|
+
}
|
|
418
|
+
catch {
|
|
419
|
+
return {
|
|
420
|
+
totalRenders: 0,
|
|
421
|
+
totalComponents: 0,
|
|
422
|
+
unnecessaryRenders: 0,
|
|
423
|
+
averageRenderTime: 0,
|
|
424
|
+
slowestComponents: [],
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
},
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Clear all performance data
|
|
431
|
+
*/
|
|
432
|
+
clearPerformanceData: () => {
|
|
433
|
+
try {
|
|
434
|
+
const scan = getScanInstance()
|
|
435
|
+
if (scan) {
|
|
436
|
+
scan.clearPerformanceData()
|
|
437
|
+
return true
|
|
438
|
+
}
|
|
439
|
+
return false
|
|
440
|
+
}
|
|
441
|
+
catch {
|
|
442
|
+
return false
|
|
443
|
+
}
|
|
444
|
+
},
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* Get current FPS
|
|
448
|
+
*/
|
|
449
|
+
getFPS: () => {
|
|
450
|
+
try {
|
|
451
|
+
const scan = getScanInstance()
|
|
452
|
+
return scan?.getFPS() || 0
|
|
453
|
+
}
|
|
454
|
+
catch {
|
|
455
|
+
return 0
|
|
456
|
+
}
|
|
457
|
+
},
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* Start component inspection mode
|
|
461
|
+
*/
|
|
462
|
+
startInspecting: () => {
|
|
463
|
+
try {
|
|
464
|
+
const scan = getScanInstance()
|
|
465
|
+
if (scan) {
|
|
466
|
+
scan.startInspecting()
|
|
467
|
+
return true
|
|
468
|
+
}
|
|
469
|
+
return false
|
|
470
|
+
}
|
|
471
|
+
catch {
|
|
472
|
+
return false
|
|
473
|
+
}
|
|
474
|
+
},
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
* Stop component inspection mode
|
|
478
|
+
*/
|
|
479
|
+
stopInspecting: () => {
|
|
480
|
+
try {
|
|
481
|
+
const scan = getScanInstance()
|
|
482
|
+
if (scan) {
|
|
483
|
+
scan.stopInspecting()
|
|
484
|
+
return true
|
|
485
|
+
}
|
|
486
|
+
return false
|
|
487
|
+
}
|
|
488
|
+
catch {
|
|
489
|
+
return false
|
|
490
|
+
}
|
|
491
|
+
},
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* Check if inspection mode is active
|
|
495
|
+
*/
|
|
496
|
+
isInspecting: () => {
|
|
497
|
+
try {
|
|
498
|
+
const scan = getScanInstance()
|
|
499
|
+
return scan?.isInspecting() || false
|
|
500
|
+
}
|
|
501
|
+
catch {
|
|
502
|
+
return false
|
|
503
|
+
}
|
|
504
|
+
},
|
|
505
|
+
|
|
506
|
+
/**
|
|
507
|
+
* Focus on a specific component
|
|
508
|
+
*/
|
|
509
|
+
focusComponent: (fiber: any) => {
|
|
510
|
+
try {
|
|
511
|
+
const scan = getScanInstance()
|
|
512
|
+
if (scan && fiber) {
|
|
513
|
+
scan.focusComponent(fiber)
|
|
514
|
+
return true
|
|
515
|
+
}
|
|
516
|
+
return false
|
|
517
|
+
}
|
|
518
|
+
catch {
|
|
519
|
+
return false
|
|
520
|
+
}
|
|
521
|
+
},
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* Get currently focused component
|
|
525
|
+
*/
|
|
526
|
+
getFocusedComponent: () => {
|
|
527
|
+
try {
|
|
528
|
+
const scan = getScanInstance()
|
|
529
|
+
const component = scan?.getFocusedComponent() || null
|
|
530
|
+
if (component) {
|
|
531
|
+
// Sanitize for RPC - remove non-serializable fields
|
|
532
|
+
const { fiber, domElement, ...serializableComponent } = component as any
|
|
533
|
+
return serializableComponent
|
|
534
|
+
}
|
|
535
|
+
return null
|
|
536
|
+
}
|
|
537
|
+
catch {
|
|
538
|
+
return null
|
|
539
|
+
}
|
|
540
|
+
},
|
|
541
|
+
|
|
542
|
+
/**
|
|
543
|
+
* Get focused component render info with changes
|
|
544
|
+
*/
|
|
545
|
+
getFocusedComponentRenderInfo: () => {
|
|
546
|
+
try {
|
|
547
|
+
const scan = getScanInstance()
|
|
548
|
+
return scan?.getFocusedComponentRenderInfo() || null
|
|
549
|
+
}
|
|
550
|
+
catch {
|
|
551
|
+
return null
|
|
552
|
+
}
|
|
553
|
+
},
|
|
554
|
+
|
|
555
|
+
/**
|
|
556
|
+
* Clear focused component changes
|
|
557
|
+
*/
|
|
558
|
+
clearFocusedComponentChanges: () => {
|
|
559
|
+
try {
|
|
560
|
+
const scan = getScanInstance()
|
|
561
|
+
if (scan) {
|
|
562
|
+
scan.clearFocusedComponentChanges()
|
|
563
|
+
return true
|
|
564
|
+
}
|
|
565
|
+
return false
|
|
566
|
+
}
|
|
567
|
+
catch {
|
|
568
|
+
return false
|
|
569
|
+
}
|
|
570
|
+
},
|
|
571
|
+
|
|
572
|
+
/**
|
|
573
|
+
* Set focused component by name for render tracking
|
|
574
|
+
*/
|
|
575
|
+
setFocusedComponentByName: (componentName: string) => {
|
|
576
|
+
try {
|
|
577
|
+
const scan = getScanInstance()
|
|
578
|
+
if (scan && componentName) {
|
|
579
|
+
scan.setFocusedComponentByName(componentName)
|
|
580
|
+
return true
|
|
581
|
+
}
|
|
582
|
+
return false
|
|
583
|
+
}
|
|
584
|
+
catch {
|
|
585
|
+
return false
|
|
586
|
+
}
|
|
587
|
+
},
|
|
588
|
+
|
|
589
|
+
/**
|
|
590
|
+
* Get the component tree with render counts
|
|
591
|
+
*/
|
|
592
|
+
getComponentTree: () => {
|
|
593
|
+
try {
|
|
594
|
+
const scan = getScanInstance()
|
|
595
|
+
return scan?.getComponentTree() || []
|
|
596
|
+
}
|
|
597
|
+
catch {
|
|
598
|
+
return []
|
|
599
|
+
}
|
|
600
|
+
},
|
|
601
|
+
|
|
602
|
+
/**
|
|
603
|
+
* Clear component tree render counts
|
|
604
|
+
*/
|
|
605
|
+
clearComponentTree: () => {
|
|
606
|
+
try {
|
|
607
|
+
const scan = getScanInstance()
|
|
608
|
+
if (scan) {
|
|
609
|
+
scan.clearComponentTree()
|
|
610
|
+
return true
|
|
611
|
+
}
|
|
612
|
+
return false
|
|
613
|
+
}
|
|
614
|
+
catch {
|
|
615
|
+
return false
|
|
616
|
+
}
|
|
617
|
+
},
|
|
618
|
+
},
|
|
619
|
+
|
|
620
|
+
/**
|
|
621
|
+
* Event handlers
|
|
622
|
+
*/
|
|
623
|
+
on: {
|
|
624
|
+
'component-mounted': (_event: any) => {
|
|
625
|
+
// React Scan automatically tracks component mounts
|
|
626
|
+
},
|
|
627
|
+
|
|
628
|
+
'component-updated': (_event: any) => {
|
|
629
|
+
// React Scan automatically tracks component updates
|
|
630
|
+
},
|
|
631
|
+
},
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
/**
|
|
636
|
+
* Default React Scan plugin instance
|
|
637
|
+
*/
|
|
638
|
+
export const scanPlugin = createScanPlugin()
|