import { createError, IPackageBuilderCommandHandler, IPackageBuilderCommandHandlerHooks, MrBuildSpecBuilderCommandHandlerArgs, MrPackageBuilderCommandHandlerArgs, MrPackageBuilderCommandResults, } from '@cirrusct/mr-core'; import { AsyncParallelHook } from 'tapable'; const DefaultBuilderName = 'default'; export abstract class PackageBuilderCommandHandlerBase implements IPackageBuilderCommandHandler { private _hooks: IPackageBuilderCommandHandlerHooks; protected constructor( protected readonly commandName: string, protected readonly args: MrPackageBuilderCommandHandlerArgs ) {} public get hooks(): IPackageBuilderCommandHandlerHooks { if (!this._hooks) { this._hooks = { beforeRunPackageSpecBuilderCommand: new AsyncParallelHook(['command', 'buildSpec', 'args']), afterRunPackageSpecBuilderCommand: new AsyncParallelHook(['command', 'buildSpec', 'args']), }; } return this._hooks; } protected get titleCaseCommandName(): string { return this.commandName.charAt(0).toUpperCase() + this.commandName.substr(1); } protected get inflectedCommandName(): string { return `${this.titleCaseCommandName}ing`; } public async run(): Promise { const { buildService, currentPackage, logger } = this.args; let exitCode = 0; // Assert current package (should always exist since build commands define 'isPackageHandler') if (!currentPackage) { throw createError(`Expected currentPackage in BuildService.enumerateSpecs`); } try { // get the buildConfig for the currentPackage const buildConfig = currentPackage.config.buildConfig; logger.info( { package: currentPackage.name, }, `Running Builder Command: ${this.commandName}` ); // determine which build specs to run // TODO: pull this off of command options, then command's (i.e. build/start/clean) config, then // default specs defined as part of buildConfig const specConfigs = buildConfig.defaultSpecConfigs; if (specConfigs) { // loop over each spec we're building for (const specConfig of specConfigs) { // get the name of builder from the spec, fall back to default builder const builderName = specConfig.value.builder || DefaultBuilderName; // get the registered builder with this name const builder = buildService.specBuilders[builderName]; if (!builder) { throw createError(`Cannot build package. Builder '${builderName}' not found`); } // construct args for builder const specBuilderArgs: MrBuildSpecBuilderCommandHandlerArgs = { ...this.args, config: specConfig, }; // tslint:disable: object-literal-sort-keys logger.info( { spec: specConfig.propertyName, builder: specConfig.value.builder, profile: specConfig.value.profile, }, `${this.inflectedCommandName} Spec` ); // call hook to notify before run package builder command await this.hooks.beforeRunPackageSpecBuilderCommand.promise( this.commandName, specConfig, this.args ); // run builder command // TODO: How to handle multiple results? const builderResult = await builder.runCommand(this.commandName, specBuilderArgs); // call hook to notify before run package builder command await this.hooks.afterRunPackageSpecBuilderCommand.promise(this.commandName, specConfig, this.args); } } else { logger.error( { package: currentPackage.name, }, 'Nothing to build. No build specs defined' ); exitCode = 1; } return { exitCode, }; } catch (e) { throw createError(`Package Command Handler Failed`, e); } } }