@@ -17,6 +17,45 @@ export const cumulativeChanges = {
1717 context : new Map < string , number > ( ) ,
1818} ;
1919
20+ const getProxyValue = ( proxy : any ) => {
21+ try {
22+ if ( ! proxy || typeof proxy !== 'object' ) {
23+ return proxy ;
24+ }
25+
26+ // Handle URLSearchParams-like objects
27+ if ( typeof proxy . entries === 'function' && typeof proxy . get === 'function' ) {
28+ try {
29+ const entries = Array . from ( proxy . entries ( ) as Iterable < [ string , any ] > ) ;
30+ return Object . fromEntries ( entries ) ;
31+ } catch ( err ) {
32+ return proxy ;
33+ }
34+ }
35+
36+ // Handle regular objects more efficiently
37+ try {
38+ const descriptors = Object . getOwnPropertyDescriptors ( proxy ) ;
39+ const result : Record < string , any > = { } ;
40+
41+ // Using for...in is faster than reduce for this case
42+ for ( const key in descriptors ) {
43+ const descriptor = descriptors [ key ] ;
44+ if ( typeof key === 'string' && 'value' in descriptor ) {
45+ result [ key ] = descriptor . value ;
46+ }
47+ }
48+
49+ return result ;
50+ } catch ( err ) {
51+ return proxy ;
52+ }
53+
54+ } catch ( err ) {
55+ return proxy ;
56+ }
57+ } ;
58+
2059export const renderPropsAndState = ( didRender : boolean , fiber : any ) => {
2160 const propContainer = Store . inspectState . value . propContainer ;
2261
@@ -380,6 +419,7 @@ export const createPropertyElement = (
380419 if ( isExpandable ) {
381420 const isExpanded = EXPANDED_PATHS . has ( currentPath ) ;
382421
422+ // Check for circular references first
383423 if ( typeof value === 'object' && value !== null ) {
384424 let paths = objectPathMap . get ( value ) ;
385425 if ( ! paths ) {
@@ -392,6 +432,28 @@ export const createPropertyElement = (
392432 paths . add ( currentPath ) ;
393433 }
394434
435+ const unwrapped = getProxyValue ( value ) ;
436+ const isNonExpandable = ( unwrapped === value &&
437+ value &&
438+ Object . getPrototypeOf ( value ) ?. constructor ?. name === 'Proxy' ) ||
439+ value instanceof Promise ;
440+
441+ // For non-expandable items, render like a simple property
442+ if ( isNonExpandable ) {
443+ const preview = document . createElement ( 'div' ) ;
444+ preview . className = 'react-scan-preview-line' ;
445+ preview . dataset . key = key ;
446+ preview . dataset . section = section ;
447+ preview . innerHTML = `
448+ <span style="width: 8px; display: inline-block"></span>
449+ <span class="react-scan-key">${ key } : </span>
450+ <span class="${ getValueClassName ( value ) } ">${ getValuePreview ( value ) } </span>
451+ ` ;
452+ container . appendChild ( preview ) ;
453+ return container ;
454+ }
455+
456+ // Normal expandable logic for other objects
395457 container . classList . add ( 'react-scan-expandable' ) ;
396458 if ( isExpanded ) {
397459 container . classList . add ( 'react-scan-expanded' ) ;
@@ -469,67 +531,77 @@ export const createPropertyElement = (
469531 }
470532 }
471533
472- arrow . addEventListener ( 'click' , ( e ) => {
473- e . stopPropagation ( ) ;
474-
475- const isExpanding = ! container . classList . contains (
476- 'react-scan-expanded' ,
477- ) ;
478-
479- if ( isExpanding ) {
480- EXPANDED_PATHS . add ( currentPath ) ;
481- container . classList . add ( 'react-scan-expanded' ) ;
482- content . classList . remove ( 'react-scan-hidden' ) ;
483-
484- if ( ! content . hasChildNodes ( ) ) {
485- if ( Array . isArray ( value ) ) {
486- value . forEach ( ( item , index ) => {
487- const el = createPropertyElement (
488- componentName ,
489- didRender ,
490- propsContainer ,
491- fiber ,
492- index . toString ( ) ,
493- item ,
494- section ,
495- level + 1 ,
496- changedKeys ,
497- currentPath ,
498- new WeakMap ( ) ,
499- ) ;
500- if ( ! el ) {
501- return ;
502- }
503- content . appendChild ( el ) ;
504- } ) ;
505- } else {
506- Object . entries ( value ) . forEach ( ( [ k , v ] ) => {
507- const el = createPropertyElement (
508- componentName ,
509- didRender ,
510- propsContainer ,
511- fiber ,
512- k ,
513- v ,
514- section ,
515- level + 1 ,
516- changedKeys ,
517- currentPath ,
518- new WeakMap ( ) ,
519- ) ;
520- if ( ! el ) {
521- return ;
522- }
523- content . appendChild ( el ) ;
524- } ) ;
534+ if ( ! isNonExpandable ) {
535+ arrow . addEventListener ( 'click' , ( e ) => {
536+ e . stopPropagation ( ) ;
537+ const isExpanding = ! container . classList . contains ( 'react-scan-expanded' ) ;
538+
539+ if ( isExpanding ) {
540+ EXPANDED_PATHS . add ( currentPath ) ;
541+ container . classList . add ( 'react-scan-expanded' ) ;
542+ content . classList . remove ( 'react-scan-hidden' ) ;
543+
544+ if ( ! content . hasChildNodes ( ) ) {
545+ if ( Array . isArray ( value ) ) {
546+ const arrayContainer = document . createElement ( 'div' ) ;
547+ arrayContainer . className = 'react-scan-array-container' ;
548+ value . forEach ( ( item , index ) => {
549+ const el = createPropertyElement (
550+ componentName ,
551+ didRender ,
552+ propsContainer ,
553+ fiber ,
554+ index . toString ( ) ,
555+ item ,
556+ section ,
557+ level + 1 ,
558+ changedKeys ,
559+ currentPath ,
560+ new WeakMap ( ) ,
561+ ) ;
562+ if ( ! el ) {
563+ return ;
564+ }
565+ arrayContainer . appendChild ( el ) ;
566+ } ) ;
567+ content . appendChild ( arrayContainer ) ;
568+ } else {
569+ Object . entries ( value ) . forEach ( ( [ k , v ] ) => {
570+ const el = createPropertyElement (
571+ componentName ,
572+ didRender ,
573+ propsContainer ,
574+ fiber ,
575+ k ,
576+ v ,
577+ section ,
578+ level + 1 ,
579+ changedKeys ,
580+ currentPath ,
581+ new WeakMap ( ) ,
582+ ) ;
583+ if ( ! el ) {
584+ return ;
585+ }
586+ content . appendChild ( el ) ;
587+ } ) ;
588+ }
525589 }
590+ } else {
591+ EXPANDED_PATHS . delete ( currentPath ) ;
592+ container . classList . remove ( 'react-scan-expanded' ) ;
593+ content . classList . add ( 'react-scan-hidden' ) ;
526594 }
527- } else {
528- EXPANDED_PATHS . delete ( currentPath ) ;
529- container . classList . remove ( 'react-scan-expanded' ) ;
530- content . classList . add ( 'react-scan-hidden' ) ;
531- }
532- } ) ;
595+
596+ requestAnimationFrame ( ( ) => {
597+ const inspector = propsContainer . firstElementChild as HTMLElement ;
598+ if ( inspector ) {
599+ const contentHeight = inspector . getBoundingClientRect ( ) . height ;
600+ propsContainer . style . maxHeight = `${ contentHeight } px` ;
601+ }
602+ } ) ;
603+ } ) ;
604+ }
533605 } else {
534606 const preview = document . createElement ( 'div' ) ;
535607 preview . className = 'react-scan-preview-line' ;
@@ -663,30 +735,54 @@ export const getValueClassName = (value: any) => {
663735} ;
664736
665737export const getValuePreview = ( value : any ) => {
666- if ( Array . isArray ( value ) ) {
667- return `Array(${ value . length } )` ;
668- }
669738 if ( value === null ) return 'null' ;
670739 if ( value === undefined ) return 'undefined' ;
671- switch ( typeof value ) {
672- case 'string' :
673- return `"${ value } "` ;
674- case 'number' :
675- return value . toString ( ) ;
676- case 'boolean' :
677- return value . toString ( ) ;
678- case 'object' : {
679- if ( value instanceof Promise ) {
680- return 'Promise' ;
681- }
682- const keys = Object . keys ( value ) ;
683- if ( keys . length <= 3 ) {
684- return `{${ keys . join ( ', ' ) } }` ;
740+
741+ try {
742+ if ( Array . isArray ( value ) ) {
743+ return `Array(${ value . length } )` ;
744+ }
745+
746+ switch ( typeof value ) {
747+ case 'string' :
748+ return `"${ value } "` ;
749+ case 'number' :
750+ case 'boolean' :
751+ return String ( value ) ;
752+ case 'object' : {
753+ // Get constructor once for all checks
754+ const constructor = value . constructor ;
755+
756+ // Fast constructor checks
757+ if ( constructor === Promise ) {
758+ return '[Promise]' ;
759+ }
760+ if ( constructor === Set ) {
761+ return `Set(${ value . size ?? '?' } )` ;
762+ }
763+ if ( constructor === Map ) {
764+ return `Map(${ value . size ?? '?' } )` ;
765+ }
766+ if ( constructor === URLSearchParams ) {
767+ return '[URLSearchParams]' ;
768+ }
769+
770+ // Handle regular objects
771+ try {
772+ const keys = Object . keys ( value ) ;
773+ if ( keys . length <= 3 ) {
774+ return `{${ keys . join ( ', ' ) } }` ;
775+ }
776+ return `{${ keys . slice ( 0 , 3 ) . join ( ', ' ) } , ...}` ;
777+ } catch {
778+ return '{...}' ;
779+ }
685780 }
686- return `{${ keys . slice ( 0 , 8 ) . join ( ', ' ) } , ...}` ;
781+ default :
782+ return typeof value ;
687783 }
688- default :
689- return typeof value ;
784+ } catch {
785+ return String ( value ) ;
690786 }
691787} ;
692788
0 commit comments