@@ -36,6 +36,7 @@ const {
36
36
ObjectPrototypeHasOwnProperty,
37
37
ObjectPrototypePropertyIsEnumerable,
38
38
ObjectSeal,
39
+ Promise,
39
40
RegExp,
40
41
RegExpPrototypeToString,
41
42
Set,
@@ -73,7 +74,8 @@ const {
73
74
74
75
const {
75
76
codes : {
76
- ERR_INVALID_ARG_TYPE
77
+ ERR_INVALID_ARG_TYPE ,
78
+ ERR_PREVIEW_FAILURE
77
79
} ,
78
80
isStackOverflowError
79
81
} = require ( 'internal/errors' ) ;
@@ -120,6 +122,12 @@ const assert = require('internal/assert');
120
122
121
123
const { NativeModule } = require ( 'internal/bootstrap/loaders' ) ;
122
124
125
+ const { hasInspector } = internalBinding ( 'config' ) ;
126
+
127
+ let inspector ;
128
+ let privateInspectionSession ;
129
+ let privatePreviewSessionCount = 0 ;
130
+
123
131
const setSizeGetter = uncurryThis (
124
132
ObjectGetOwnPropertyDescriptor ( SetPrototype , 'size' ) . get ) ;
125
133
const mapSizeGetter = uncurryThis (
@@ -2027,11 +2035,202 @@ function stripVTControlCharacters(str) {
2027
2035
return str . replace ( ansi , '' ) ;
2028
2036
}
2029
2037
2038
+ async function previewValue ( target , opts ) {
2039
+ const ctx = {
2040
+ budget : { } ,
2041
+ indentationLvl : 0 ,
2042
+ currentDepth : 0 ,
2043
+ stylize : stylizeNoColor ,
2044
+ compact : inspectDefaultOptions . compact ,
2045
+ breakLength : inspectDefaultOptions . breakLength ,
2046
+ } ;
2047
+ if ( opts ) {
2048
+ const optKeys = ObjectKeys ( opts ) ;
2049
+ for ( const key of optKeys ) {
2050
+ if ( ObjectPrototypeHasOwnProperty ( inspectDefaultOptions , key ) ) {
2051
+ ctx [ key ] = opts [ key ] ;
2052
+ } else if ( ctx . userOptions === undefined ) {
2053
+ // This is required to pass through the actual user input.
2054
+ ctx . userOptions = opts ;
2055
+ }
2056
+ }
2057
+ }
2058
+ if ( ctx . colors ) {
2059
+ ctx . stylize = stylizeWithColor ;
2060
+ }
2061
+
2062
+ return previewRaw ( ctx , target ) ;
2063
+ }
2064
+
2065
+ async function previewRaw ( ctx , target ) {
2066
+ if ( typeof target !== 'object' &&
2067
+ typeof target !== 'function' &&
2068
+ ! isUndetectableObject ( target ) ) {
2069
+ return formatPrimitive ( ctx . stylize , target , ctx ) ;
2070
+ }
2071
+ if ( target === null ) {
2072
+ return ctx . stylize ( 'null' , 'null' ) ;
2073
+ }
2074
+ if ( typeof target === 'function' ) {
2075
+ return formatRaw ( ctx , target ) ;
2076
+ }
2077
+
2078
+ const preview = await getValuePreviewAsync ( target ) ;
2079
+ if ( preview == null ) {
2080
+ return undefined ;
2081
+ }
2082
+ return formatInspectorPropertyPreview ( ctx , target , preview ) ;
2083
+ }
2084
+
2085
+ function previewPrelude ( ) {
2086
+ if ( ! hasInspector ) {
2087
+ return false ;
2088
+ }
2089
+ if ( privateInspectionSession == null ) {
2090
+ inspector = require ( 'inspector' ) ;
2091
+ privateInspectionSession = new inspector . Session ( ) ;
2092
+ privateInspectionSession . connect ( ) ;
2093
+ }
2094
+ return true ;
2095
+ }
2096
+
2097
+ function getValuePreviewAsync ( target ) {
2098
+ if ( ! previewPrelude ( ) ) {
2099
+ return undefined ;
2100
+ }
2101
+ if ( privatePreviewSessionCount === 0 ) {
2102
+ privateInspectionSession . post ( 'Runtime.enable' ) ;
2103
+ privateInspectionSession . post ( 'Debugger.enable' ) ;
2104
+ }
2105
+ privatePreviewSessionCount ++ ;
2106
+
2107
+ return new Promise ( ( resolve , reject ) => {
2108
+ privateInspectionSession . once ( 'Debugger.paused' , ( pausedContext ) => {
2109
+ const callFrames = pausedContext . params . callFrames ;
2110
+ /** USED */ target ;
2111
+ privateInspectionSession . post (
2112
+ 'Debugger.evaluateOnCallFrame' , {
2113
+ /**
2114
+ * 0. inspector.dispatch,
2115
+ * 1. inspector.post,
2116
+ * 2. new Promise,
2117
+ * 3. previewValueAsync
2118
+ */
2119
+ callFrameId : callFrames [ 3 ] . callFrameId ,
2120
+ expression : 'target' ,
2121
+ } , ( err , result ) => {
2122
+ if ( err ) {
2123
+ return reject ( err ) ;
2124
+ }
2125
+ const { result : evalResult } = result ;
2126
+
2127
+ if ( evalResult . type !== 'object' ) {
2128
+ return resolve ( ) ;
2129
+ }
2130
+ if ( evalResult . subtype === 'error' ) {
2131
+ return reject ( ERR_PREVIEW_FAILURE ( evalResult . description ) ) ;
2132
+ }
2133
+ resolve ( getValuePreviewByObjectIdAsync ( evalResult . objectId ) ) ;
2134
+ } ) ;
2135
+ } ) ;
2136
+ privateInspectionSession . post ( 'Debugger.pause' , ( ) => {
2137
+ /* pause doesn't return anything. */
2138
+ } ) ;
2139
+ } ) . finally ( ( ) => {
2140
+ privatePreviewSessionCount -- ;
2141
+ if ( privatePreviewSessionCount === 0 ) {
2142
+ privateInspectionSession . post ( 'Runtime.disable' ) ;
2143
+ privateInspectionSession . post ( 'Debugger.disable' ) ;
2144
+ }
2145
+ } ) ;
2146
+ }
2147
+
2148
+ function getValuePreviewByObjectIdAsync ( objectId , options = { } ) {
2149
+ return new Promise ( ( resolve , reject ) => {
2150
+ privateInspectionSession . post ( 'Runtime.getProperties' , {
2151
+ objectId : objectId ,
2152
+ ownProperties : true ,
2153
+ ...options
2154
+ } , ( err , result ) => {
2155
+ if ( err ) {
2156
+ return reject ( err ) ;
2157
+ }
2158
+ resolve ( result ) ;
2159
+ } ) ;
2160
+ } ) ;
2161
+ }
2162
+
2163
+ async function formatInspectorPropertyPreview (
2164
+ ctx , target , preview , isJsValue = true
2165
+ ) {
2166
+ const { result, privateProperties } = preview ;
2167
+
2168
+ const output = [ ] ;
2169
+ for ( var item of [
2170
+ // __proto__ is an own property
2171
+ ...result . filter ( ( it ) => it . name !== '__proto__' ) ,
2172
+ ...( privateProperties ?? [ ] )
2173
+ ] ) {
2174
+ const valueDescriptor = item . value ;
2175
+ const valueType = valueDescriptor . type ;
2176
+ const subtype = valueDescriptor . subtype ;
2177
+ const stylize = ctx . stylize ;
2178
+ let str ;
2179
+ if ( valueType === 'string' ) {
2180
+ str = stylize ( strEscape ( valueDescriptor . value ) , valueType ) ;
2181
+ } else if ( valueType === 'number' ) {
2182
+ str = stylize (
2183
+ // -0 is unserializable to JSON representation
2184
+ valueDescriptor . value ?? valueDescriptor . unserializableValue ,
2185
+ valueType ) ;
2186
+ } else if ( valueType === 'boolean' ) {
2187
+ str = stylize ( valueDescriptor . value , valueType ) ;
2188
+ } else if ( valueType === 'symbol' ) {
2189
+ str = stylize ( valueDescriptor . description , valueType ) ;
2190
+ } else if ( valueType === 'undefined' ) {
2191
+ str = stylize ( valueType , valueType ) ;
2192
+ } else if ( subtype === 'null' ) {
2193
+ str = stylize ( subtype , 'null' ) ;
2194
+ } else if ( valueType === 'bigint' ) {
2195
+ str = stylize ( valueDescriptor . unserializableValue , valueType ) ;
2196
+ } else if ( valueType === 'function' ) {
2197
+ str = await previewFunction ( ctx , valueDescriptor . objectId ) ;
2198
+ } else if ( valueType === 'object' ) {
2199
+ // TODO(legendecas): Inspect the whole object, not only the class name.
2200
+ str = `#[${ valueDescriptor . className } ]` ;
2201
+ } else {
2202
+ /* c8 ignore next */
2203
+ assert . fail ( `Unknown value type '${ valueType } '` ) ;
2204
+ }
2205
+ output . push ( `${ item . name } : ${ str } ` ) ;
2206
+ }
2207
+
2208
+ const constructor = isJsValue ?
2209
+ getConstructorName ( target , ctx , 0 ) :
2210
+ target . className ;
2211
+ const braces = [ `${ getPrefix ( constructor , '' , 'Object' ) } {` , '}' ] ;
2212
+ const base = '' ;
2213
+ const res = reduceToSingleString (
2214
+ ctx , output , base , braces , kObjectType , ctx . currentDepth ) ;
2215
+ return res ;
2216
+ }
2217
+
2218
+ async function previewFunction ( ctx , objectId ) {
2219
+ const { result } = await getValuePreviewByObjectIdAsync (
2220
+ objectId , { ownProperties : false } ) ;
2221
+ const nameDesc = result . find ( ( it ) => it . name === 'name' ) ;
2222
+ if ( nameDesc == null ) {
2223
+ return ctx . stylize ( '[Function]' , 'function' ) ;
2224
+ }
2225
+ return ctx . stylize ( `[Function: ${ nameDesc . value . value } ]` , 'function' ) ;
2226
+ }
2227
+
2030
2228
module . exports = {
2031
2229
inspect,
2032
2230
format,
2033
2231
formatWithOptions,
2034
2232
getStringWidth,
2035
2233
inspectDefaultOptions,
2234
+ previewValue,
2036
2235
stripVTControlCharacters
2037
2236
} ;
0 commit comments