import { Console } from 'console' ; import { Context , Script } from 'vm' ; import semver from 'semver' ; import { LooseObject } from './util' ; import { createContext , EvaluateReturnType , runtime } from './inspector' ; export interface ErrorResults { error : Error ; } export interface OutputResults { output : any ; } export type EvaluationResults = ErrorResults | OutputResults ; export type Evaluator = ( line : string ) => Promise < EvaluationResults > ; export type PureEvaluator = ( line : string ) => Promise < string | undefined > ; function prettyPrintEvaluateResults ( result ) { if ( result . type === 'undefined' ) { return 'undefined' ; } if ( result . type === 'string' ) { return `"${ result . value }"` ; } if ( result . type === 'number' ) { if ( result . value ) { return result . value . toString ( ) ; } return result . description ; } if ( result . type === 'boolean' ) { return result . value . toString ( ) ; } if ( result . type === 'symbol' ) { return result . description ; } if ( result . type === 'function' ) { return 'function' ; } if ( result . type === 'object' ) { if ( result . subtype === 'null' ) { return 'null' ; } if ( result . subtype === 'array' ) { return result . description ; } if ( result . subtype === 'regexp' ) { return result . description ; } if ( result . subtype === 'date' && result . description ) { return new Date ( result . description ) . toISOString ( ) ; } if ( [ 'map' , 'set' ] . includes ( result . subtype || '' ) && result . preview && result . preview . entries ) { return `${ result . className } { ${ result . preview . entries . length } elements }` ; } if ( result . subtype === 'error' ) { return result . className ; } return 'Object' ; } } export function createEvaluator ( ctx = { } ) { const template = { } ; Object . getOwnPropertyNames ( global ) . forEach ( name => { if ( name === 'global' ) { return ; } const descriptor = Object . getOwnPropertyDescriptor ( global , name ) ; if ( typeof descriptor !== 'undefined' ) { Object . defineProperty ( template , name , descriptor ) ; } } ) ; template . _ = undefined ; template . console = new Console ( process . stdout , process . stdin ) ; Object . keys ( ctx ) . forEach ( key => { template [ key ] = ctx [ key ] ; } ) ; template . global = template ; const { context , contextIdPromise } = createContext ( template ) ; const wrapIfNeeded = ( line ) => line . startsWith ( '{' ) && line . endsWith ( '}' ) ? `(${ line })` : line ; const evaluate = async ( line ) => { line = wrapIfNeeded ( line ) ; const script = new Script ( line ) ; try { const output = await script . runInContext ( context ) ; context . _ = output ; return { output } ; } catch ( error ) { return { error } ; } } ; const pureEvaluate = async ( line ) => { if ( semver . lt ( process . version , '12.3.0' ) ) { return undefined ; } line = wrapIfNeeded ( line ) ; let output ; try { output = await runtime . evaluate ( { awaitPromise : true , contextId : await contextIdPromise , expression : line , generatePreview : true , silent : true , throwOnSideEffect : true , timeout : 500 , userGesture : true } ) ; } catch { return undefined ; } if ( output . exceptionDetails ) { return undefined ; } const { result } = output as EvaluateReturnType ; return prettyPrintEvaluateResults ( result ) ; } ; return { context , contextIdPromise , evaluate , pureEvaluate } ; }