import debug from 'debug' ; import logger from 'redspot/logger' ; import { createNetwork } from '../../provider' ; import { Artifacts as IArtifacts , EnvironmentExtender , Network , ParamDefinition , RedspotArguments , RedspotConfig , RedspotRuntimeEnvironment , RunSuperFunction , RunTaskFunction , RuntimeEnvironment , TaskArguments , TaskDefinition , TasksMap } from '../../types' ; import { Artifacts } from '../artifacts' ; import { lazyObject } from '../util/lazy' ; import { analyzeModuleNotFoundError } from './config/config-loading' ; import { RedspotError } from './errors' ; import { ERRORS } from './errors-list' ; import { OverriddenTaskDefinition } from './tasks/task-definitions' ; const log = debug ( 'redspot:core:hre' ) ; export class Environment implements RedspotRuntimeEnvironment { private static readonly _BLACKLISTED_PROPERTIES : string [ ] = [ 'injectToGlobal' , '_runTaskDefinition' ] ; public network : Network ; public artifacts : IArtifacts ; private readonly _extenders : EnvironmentExtender [ ] ; remarks param config param redspotArguments param tasks param extenders constructor ( public readonly config , public readonly redspotArguments , public readonly tasks , extenders = [ ] ) { log ( 'Creating RedspotRuntimeEnvironment' ) ; const networkName = redspotArguments . network !== undefined ? redspotArguments . network : config . defaultNetwork ; const networkConfig = config . networks [ networkName ] ; if ( networkConfig === undefined ) { throw new RedspotError ( ERRORS . NETWORK . CONFIG_NOT_FOUND , { network : networkName } ) ; } this . artifacts = new Artifacts ( config . paths . artifacts ) ; this . network = createNetwork ( networkName , config . networks [ networkName ] ) ; if ( Number ( redspotArguments . logLevel ) ) { logger . level = Number ( redspotArguments . logLevel ) ; } this . _extenders = extenders ; extenders . forEach ( ( extender ) => extender ( ( this as any ) as RuntimeEnvironment ) ) ; } param name param taskArguments throws returns public readonly run : RunTaskFunction = async ( name , taskArguments = { } ) => { const taskDefinition = this . tasks [ name ] ; log ( 'Running task %s' , name ) ; if ( taskDefinition === undefined ) { throw new RedspotError ( ERRORS . ARGUMENTS . UNRECOGNIZED_TASK , { task : name } ) ; } const resolvedTaskArguments = this . _resolveValidTaskArguments ( taskDefinition , taskArguments ) ; try { return await this . _runTaskDefinition ( taskDefinition , resolvedTaskArguments ) ; } catch ( e ) { analyzeModuleNotFoundError ( e , this . config . paths . configFile ) ; throw e ; } } ; param blacklist returns public injectToGlobal ( blacklist = Environment . _BLACKLISTED_PROPERTIES ) { const globalAsAny = global as any ; const previousValues = { } ; globalAsAny . hre = this ; for ( const [ key , value ] of Object . entries ( this ) ) { if ( blacklist . includes ( key ) ) { continue ; } previousValues [ key ] = globalAsAny [ key ] ; globalAsAny [ key ] = value ; } return ( ) => { for ( const [ key , _ ] of Object . entries ( this ) ) { if ( blacklist . includes ( key ) ) { continue ; } globalAsAny . hre = previousValues . hre ; globalAsAny [ key ] = previousValues [ key ] ; } } ; } private async _runTaskDefinition ( taskDefinition , taskArguments ) { let runSuperFunction ; if ( taskDefinition instanceof OverriddenTaskDefinition ) { runSuperFunction = async ( _taskArguments = taskArguments ) => { log ( "Running %s's super" , taskDefinition . name ) ; return this . _runTaskDefinition ( taskDefinition . parentTaskDefinition , _taskArguments ) ; } ; runSuperFunction . isDefined = true ; } else { runSuperFunction = async ( ) => { throw new RedspotError ( ERRORS . TASK_DEFINITIONS . RUNSUPER_NOT_AVAILABLE , { taskName : taskDefinition . name } ) ; } ; runSuperFunction . isDefined = false ; } const runSuper = runSuperFunction ; const globalAsAny = global as any ; const previousRunSuper = globalAsAny . runSuper ; globalAsAny . runSuper = runSuper ; const uninjectFromGlobal = this . injectToGlobal ( ) ; try { return await taskDefinition . action ( taskArguments , this , runSuper ) ; } finally { uninjectFromGlobal ( ) ; globalAsAny . runSuper = previousRunSuper ; } } private throws param taskDefinition param taskArguments returns private _resolveValidTaskArguments ( taskDefinition , taskArguments ) { const { paramDefinitions , positionalParamDefinitions } = taskDefinition ; const nonPositionalParamDefinitions = Object . values ( paramDefinitions ) ; const allTaskParamDefinitions = [ ... nonPositionalParamDefinitions , ... positionalParamDefinitions ] ; const initResolvedArguments = { errors : [ ] , values : { } } ; const resolvedArguments = allTaskParamDefinitions . reduce ( ( { errors , values } , paramDefinition ) => { try { const paramName = paramDefinition . name ; const argumentValue = taskArguments [ paramName ] ; const resolvedArgumentValue = this . _resolveArgument ( paramDefinition , argumentValue ) ; if ( resolvedArgumentValue !== undefined ) { values [ paramName ] = resolvedArgumentValue ; } } catch ( error ) { errors . push ( error ) ; } return { errors , values } ; } , initResolvedArguments ) ; const { errors : resolveErrors , values : resolvedValues } = resolvedArguments ; if ( resolveErrors . length > 0 ) { throw resolveErrors [ 0 ] ; } const resolvedTaskArguments = { ... taskArguments , ... resolvedValues } ; return resolvedTaskArguments ; } param paramDefinition param argumentValue private private _resolveArgument ( paramDefinition , argumentValue ) { const { defaultValue , isOptional , name , type } = paramDefinition ; if ( argumentValue === undefined ) { if ( isOptional ) { return defaultValue ; } throw new RedspotError ( ERRORS . ARGUMENTS . MISSING_TASK_ARGUMENT , { param : name } ) ; } this . _checkTypeValidation ( paramDefinition , argumentValue ) ; return argumentValue ; } param paramDefinition { ParamDefinition } param argumentValue private throws private _checkTypeValidation ( paramDefinition , argumentValue ) { const { isVariadic , name : paramName , type } = paramDefinition ; const argumentValueContainer = isVariadic ? argumentValue : [ argumentValue ] ; for ( const value of argumentValueContainer ) { type . validate ( paramName , value ) ; } } }