@@ -205,12 +205,59 @@ function getRelativePath(filename) {
205205 return filename ;
206206}
207207
208- function formatPreview ( { properties, subtype } ) {
209- const object = properties . reduce ( ( obj , { name, value } ) => {
210- obj [ name ] = value ;
211- return obj ;
212- } , subtype === 'array' ? [ ] : { } ) ;
213- return object ;
208+ function stylizeWithColor ( str , styleType ) {
209+ const style = util . inspect . styles [ styleType ] ;
210+
211+ if ( style ) {
212+ const [ start , end ] = util . inspect . colors [ style ] ;
213+ return `\u001b[${ start } m${ str } \u001b[${ end } m` ;
214+ }
215+ return str ;
216+ }
217+
218+ function formatFunction ( { className, description } , opts ) {
219+ const fnNameMatch = ( description ) . match ( / ^ (?: f u n c t i o n \* ? ) ? ( [ ^ ( \s ] + ) \( / ) ;
220+ const fnName = fnNameMatch ? `: ${ fnNameMatch [ 1 ] } ` : '' ;
221+ const formatted = `[${ className } ${ fnName } ]` ;
222+ return opts . colors ? stylizeWithColor ( formatted , 'special' ) : formatted ;
223+ }
224+
225+ function formatPropertyValue ( prop ) {
226+ const { value, type, subtype } = prop ;
227+ if ( subtype === 'array' ) {
228+ return stylizeWithColor ( value , 'special' ) ;
229+ } else if ( subtype === 'regexp' ) {
230+ return stylizeWithColor ( value , 'regexp' ) ;
231+ } else if ( subtype === 'date' ) {
232+ const date = new Date ( value ) ;
233+ return stylizeWithColor ( date . toISOString ( ) , 'date' ) ;
234+ } else if ( type === 'object' ) {
235+ return stylizeWithColor ( value , 'special' ) ;
236+ } else if ( type === 'function' ) {
237+ return formatFunction ( { className : 'Foo' , description : 'bar' } , { colors : true } ) ;
238+ }
239+ return util . inspect ( value , { colors : true } ) ;
240+ }
241+
242+ function formatPreview ( { properties, subtype, description } , opts ) {
243+ if ( subtype === 'regexp' ) {
244+ return opts . colors ? stylizeWithColor ( description , 'regexp' ) : description ;
245+ } else if ( subtype === 'date' ) {
246+ const date = new Date ( description ) ;
247+ return opts . colors ? stylizeWithColor ( date . toISOString ( ) , 'date' ) : description ;
248+ }
249+
250+ function formatPropertyPair ( prop ) {
251+ return `${ prop . name } : ${ formatPropertyValue ( prop ) } ` ;
252+ }
253+
254+ const propertyFormatter = subtype === 'array' ? formatPropertyValue : formatPropertyPair ;
255+ const formattedProps = properties . map ( propertyFormatter ) . join ( '\n ' ) ;
256+
257+ if ( subtype === 'array' ) {
258+ return `[ ${ formattedProps } ]` ;
259+ }
260+ return `{ ${ formattedProps } }` ;
214261}
215262
216263function extractErrorMessage ( stack ) {
@@ -219,21 +266,53 @@ function extractErrorMessage(stack) {
219266 return m ? m [ 1 ] : stack ;
220267}
221268
222- function formatValue ( { result, wasThrown } ) {
223- const { className, description, preview, type, value } = result ;
224- if ( wasThrown ) {
225- const err = new Error ( extractErrorMessage ( description ) ) ;
226- err . stack = description ;
227- Object . defineProperty ( err , 'name' , { value : className } ) ;
228- return err ;
229- }
230- if ( type === 'object' ) {
231- if ( preview && preview . properties ) {
232- return formatPreview ( preview ) ;
269+ const REMOTE_OBJ_INSPECT = Symbol ( 'remoteObjectInspect' ) ;
270+ const REMOTE_OBJ_DATA = Symbol ( 'remoteObjectData' ) ;
271+ class RemoteObject {
272+ constructor ( remoteObject ) {
273+ this [ REMOTE_OBJ_DATA ] = remoteObject ;
274+ Object . assign ( this , remoteObject ) ;
275+ }
276+
277+ // Future: [util.inspect.custom]() { ... }
278+ [ REMOTE_OBJ_INSPECT ] ( recurseTimes , ctx ) {
279+ const opts = Object . assign ( { } , ctx , { depth : 0 } ) ;
280+ const { description, type, preview, value } = this [ REMOTE_OBJ_DATA ] ;
281+ if ( type === 'object' ) {
282+ if ( preview && preview . properties ) {
283+ return formatPreview ( preview , opts ) ;
284+ }
285+ return opts . colors ? stylizeWithColor ( description , 'special' ) : description ;
286+ } else if ( type === 'function' ) {
287+ return formatFunction ( this [ REMOTE_OBJ_DATA ] , opts ) ;
233288 }
234- return description ;
289+ return util . inspect ( value , ctx ) ;
235290 }
236- return value ;
291+ }
292+
293+ function createRemoteObjectProxyHandler ( /* client */ ) {
294+ return {
295+ get ( target , name ) {
296+ if ( name === REMOTE_OBJ_INSPECT || name === REMOTE_OBJ_DATA ) return target [ name ] ;
297+ if ( name === 'then' ) return undefined ; // This is gonna be tricky. :(
298+ console . log ( 'use client to get %j' , name ) ;
299+ return undefined ;
300+ } ,
301+
302+ set ( target , name , value ) {
303+ throw new Error ( `Modifying remote objects is not implemented yet; .${ name } = ${ value } ` ) ;
304+ } ,
305+ } ;
306+ }
307+
308+ function createRemoteAwareWriter ( colors ) {
309+ return function remoteAwareWriter ( value , originalOpts ) {
310+ const opts = Object . assign ( { } , originalOpts , { colors } ) ;
311+ if ( value && value [ REMOTE_OBJ_INSPECT ] ) {
312+ return value [ REMOTE_OBJ_INSPECT ] ( 0 , opts ) ;
313+ }
314+ return util . inspect ( value , opts ) ;
315+ } ;
237316}
238317
239318function toCallback ( promise , callback ) {
@@ -408,6 +487,17 @@ function createCommandContext(inspector) {
408487 }
409488 }
410489
490+ function convertResultToRemoteObject ( { result, wasThrown } ) {
491+ const { className, description } = result ;
492+ if ( wasThrown ) {
493+ const err = new Error ( extractErrorMessage ( description ) ) ;
494+ err . stack = description ;
495+ Object . defineProperty ( err , 'name' , { value : className } ) ;
496+ return err ;
497+ }
498+ return new Proxy ( new RemoteObject ( result ) , createRemoteObjectProxyHandler ( inspector . client ) ) ;
499+ }
500+
411501 const ctx = {
412502 debugEval ( code ) {
413503 // Repl asked for scope variables
@@ -423,7 +513,7 @@ function createCommandContext(inspector) {
423513 } ;
424514
425515 return Debugger . evaluateOnCallFrame ( params )
426- . then ( formatValue ) ;
516+ . then ( convertResultToRemoteObject ) ;
427517 } ,
428518
429519 exec ( code ) {
@@ -553,6 +643,12 @@ class Inspector {
553643 [ 'Debugger' , 'Runtime' ] . forEach ( domain => {
554644 this [ domain ] = createAgentProxy ( domain , this ) ;
555645 } ) ;
646+ this . handleDebugEvent = ( fullName , params ) => {
647+ const [ domain , name ] = fullName . split ( '.' ) ;
648+ if ( domain in this ) {
649+ this [ domain ] . emit ( name , params ) ;
650+ }
651+ } ;
556652
557653 // Two eval modes are available: controlEval and debugEval
558654 // But controlEval is used by default
@@ -580,6 +676,8 @@ class Inspector {
580676 opts . useColors = false ;
581677 }
582678
679+ opts . writer = createRemoteAwareWriter ( opts . useColors !== false ) ;
680+
583681 this . repl = Repl . start ( opts ) ;
584682
585683 // Do not print useless warning
@@ -632,24 +730,6 @@ class Inspector {
632730 } ) ;
633731 }
634732
635- handlePaused ( { callFrames /* , reason, hitBreakpoints */ } ) {
636- // this.pause();
637-
638- // Save execution context's data
639- const topFrame = callFrames [ 0 ] ;
640- // const { scriptId, lineNumber } = this.client.currentSourceLocation = topFrame.location;
641- this . client . currentFrame = topFrame . callFrameId ;
642-
643- // const script = this.scripts[scriptId];
644- // const scriptUrl = script ? getRelativePath(script.url) : '[unknown]';
645- // this.print(`${reason} in ${scriptUrl}:${lineNumber + 1}`);
646-
647- // this.watchers(true)
648- // .then(() => this.list(2))
649- // .then(() => {
650- // this.resume();
651- // }, this.error.bind(this));
652- }
653733
654734 controlEval ( code , context , filename , callback ) {
655735 /* eslint no-param-reassign: 0 */
@@ -802,12 +882,6 @@ class Inspector {
802882 }
803883 }
804884
805- handleScriptParsed ( { scriptId, url } ) {
806- if ( url ) {
807- this . scripts [ scriptId ] = { url } ;
808- }
809- }
810-
811885 // Spawns child process (and restores breakpoints)
812886 trySpawn ( cb ) {
813887 const breakpoints = this . breakpoints || [ ] ;
@@ -867,14 +941,7 @@ class Inspector {
867941 const client = this . client = new Client ( ) ;
868942 let connectionAttempts = 0 ;
869943
870- client . on ( 'debugEvent' , ( fullName , params ) => {
871- const [ domain , name ] = fullName . split ( '.' ) ;
872- if ( domain in this ) {
873- this [ domain ] . emit ( name , params ) ;
874- }
875- } ) ;
876-
877- client . on ( 'Debugger.scriptParsed' , this . handleScriptParsed . bind ( this ) ) ;
944+ client . on ( 'debugEvent' , this . handleDebugEvent ) ;
878945
879946 client . once ( 'ready' , ( ) => {
880947 // Restore breakpoints
@@ -895,16 +962,6 @@ class Inspector {
895962 this . resume ( ) ;
896963 } ) ;
897964
898- client . on ( 'unhandledResponse' , res => {
899- this . pause ( ) ;
900- this . print ( `\nunhandled res:${ JSON . stringify ( res ) } ` ) ;
901- this . resume ( ) ;
902- } ) ;
903-
904- client . on ( 'Debugger.paused' , res => {
905- this . handlePaused ( res ) ;
906- } ) ;
907-
908965 const attemptConnect = ( ) => {
909966 ++ connectionAttempts ;
910967 this . stdout . write ( '.' ) ;
0 commit comments