import { Protocol as Crdp } from 'devtools-protocol' ; import * as Color from 'color' ; import * as variables from './variables' ; export function formatExceptionDetails ( e ) { if ( ! e . exception ) { return `${ e . text || 'Uncaught Error' }\n${ stackTraceToString ( e . stackTrace ) }` ; } return ( e . exception . className && e . exception . className . endsWith ( 'Error' ) && e . exception . description ) || ( `Error: ${ variables . getRemoteObjectPreview ( e . exception ) }\n${ stackTraceToString ( e . stackTrace ) }` ) ; } export const clearConsoleCode = '\u001b[2J' ; export function formatConsoleArguments ( type , args , stackTrace ? ) { switch ( type ) { case 'log' : case 'debug' : case 'info' : case 'error' : case 'warning' : case 'dir' : case 'timeEnd' : case 'count' : args = resolveParams ( args ) ; break ; case 'assert' : const formattedParams = args . length ? resolveParams ( args , true ) : [ ] ; const assertMsg = ( formattedParams [ 0 ] && formattedParams [ 0 ] . type === 'string' ) ? formattedParams . shift ( ) . value : '' ; let outputText = `Assertion failed: ${ assertMsg }\n` + stackTraceToString ( stackTrace ) ; args = [ { type : 'string' , value : outputText } , ... formattedParams ] ; break ; case 'startGroup' : case 'startGroupCollapsed' : let startMsg = '‹Start group›' ; const formattedGroupParams = resolveParams ( args ) ; const previewMessage = formattedGroupParams . find ( x => x && x . type === 'string' ) ; if ( previewMessage ) { startMsg += ': ' + previewMessage . value ; } args = [ { type : 'string' , value : startMsg } , ... formattedGroupParams ] ; break ; case 'endGroup' : args = [ { type : 'string' , value : '‹End group›' } ] ; break ; case 'trace' : args = [ { type : 'string' , value : 'console.trace()\n' + stackTraceToString ( stackTrace ) } ] ; break ; case 'clear' : args = [ { type : 'string' , value : clearConsoleCode } ] ; break ; default : return null ; } const isError = type === 'assert' || type === 'error' ; return { args , isError } ; } /**
 * Collapse non-object arguments, and apply format specifiers (%s, %d, etc). Return a reduced a formatted list of RemoteObjects.
 */ function resolveParams ( args , skipFormatSpecifiers ? ) { if ( ! args . length || args [ 0 ] . objectId ) { return args ; } let formatSpecifiers ; const firstTextArg = args . shift ( ) ; let currentCollapsedStringArg = variables . getRemoteObjectPreview ( firstTextArg , false ) + '' ; if ( firstTextArg . type === 'string' && ! skipFormatSpecifiers ) { formatSpecifiers = ( currentCollapsedStringArg . match ( /\%[sidfoOc]/g ) || [ ] ) . map ( spec => spec [ 1 ] ) ; } else { formatSpecifiers = [ ] ; } const processedArgs = [ ] ; const pushStringArg = ( strArg ) => { if ( typeof strArg === 'string' ) { processedArgs . push ( { type : 'string' , value : strArg } ) ; } } ; for ( let argIdx = 0 ; argIdx < args . length ; argIdx ++ ) { const arg = args [ argIdx ] ; const formatSpec = formatSpecifiers . shift ( ) ; const formatted = formatArg ( formatSpec , arg ) ; currentCollapsedStringArg = currentCollapsedStringArg || '' ; if ( typeof formatted === 'string' ) { if ( formatSpec ) { currentCollapsedStringArg = currentCollapsedStringArg . replace ( '%' + formatSpec , formatted ) ; } else { currentCollapsedStringArg += ( currentCollapsedStringArg ? ' ' + formatted : formatted ) ; } } else if ( formatSpec ) { const curSpecIdx = currentCollapsedStringArg . indexOf ( '%' + formatSpec ) ; const processedPart = currentCollapsedStringArg . slice ( 0 , curSpecIdx ) ; if ( processedPart ) { pushStringArg ( processedPart ) ; } currentCollapsedStringArg = currentCollapsedStringArg . slice ( curSpecIdx + 2 ) ; processedArgs . push ( formatted ) ; } else { pushStringArg ( currentCollapsedStringArg ) ; currentCollapsedStringArg = null ; processedArgs . push ( formatted ) ; } } pushStringArg ( currentCollapsedStringArg ) ; return processedArgs ; } function formatArg ( formatSpec , arg ) { const paramValue = String ( typeof arg . value !== 'undefined' ? arg . value : arg . description ) ; if ( formatSpec === 's' ) { return paramValue ; } else if ( [ 'i' , 'd' ] . indexOf ( formatSpec ) >= 0 ) { return Math . floor ( + paramValue ) + '' ; } else if ( formatSpec === 'f' ) { return + paramValue + '' ; } else if ( formatSpec === 'c' ) { return formatColorArg ( arg ) ; } else if ( formatSpec === 'O' ) { if ( arg . objectId ) { return arg ; } else { return paramValue ; } } else { if ( arg . objectId ) { return arg ; } else { return paramValue ; } } } function formatColorArg ( arg ) { const cssRegex = /\s*(.*?)\s*:\s*(.*?)\s*(?:;|$)/g ; let escapedSequence ; let match = cssRegex . exec ( arg . value ) ; while ( match != null ) { if ( match . length === 3 ) { if ( escapedSequence === undefined ) { escapedSequence = '' ; } if ( match [ 2 ] ) { switch ( match [ 1 ] ) { case 'color' : const color = getAnsi16Color ( match [ 2 ] ) ; if ( color ) { escapedSequence += `;${ color }` ; } break ; case 'background' : const background = getAnsi16Color ( match [ 2 ] ) ; if ( background ) { escapedSequence += `;${ background + 10 }` ; } break ; case 'font-weight' : if ( match [ 2 ] === 'bold' ) { escapedSequence += ';1' ; } break ; case 'text-decoration' : if ( match [ 2 ] === 'underline' ) { escapedSequence += ';4' ; } break ; default : } } } match = cssRegex . exec ( arg . value ) ; } if ( typeof escapedSequence === 'string' ) { escapedSequence = `\x1b[0${ escapedSequence }m` ; } return escapedSequence ; } function stackTraceToString ( stackTrace ) { if ( ! stackTrace ) { return '' ; } return stackTrace . callFrames . map ( frame => { const fnName = frame . functionName || ( frame . url ? '(anonymous)' : '(eval)' ) ; const fileName = frame . url ? frame . url : 'eval' ; return `    at ${ fnName } (${ fileName }:${ frame . lineNumber + 1 }:${ frame . columnNumber })` ; } ) . join ( '\n' ) ; } function getAnsi16Color ( colorString ) { try { const color = new Color ( colorString ) ; return color . ansi16 ( ) . object ( ) . ansi16 ; } catch ( ex ) { } return undefined ; }