18d307f52SEvan Baconimport { CommandError, UnimplementedError } from '../../utils/errors'; 28d307f52SEvan Baconimport { memoize } from '../../utils/fn'; 38d307f52SEvan Bacon 48d307f52SEvan Bacon/** An error that is memoized and asserted whenever a Prerequisite.assertAsync is subsequently called. */ 58d307f52SEvan Baconexport class PrerequisiteCommandError extends CommandError { 68d307f52SEvan Bacon constructor(code: string, message: string = '') { 78d307f52SEvan Bacon super(message ? 'VALIDATE_' + code : code, message); 88d307f52SEvan Bacon } 98d307f52SEvan Bacon} 108d307f52SEvan Bacon 11c4ef02aeSEvan Baconexport class Prerequisite<T = void, TProps = void> { 128d307f52SEvan Bacon /** Memoized results of `assertImplementation` */ 13c4ef02aeSEvan Bacon private _assertAsync: (props: TProps) => Promise<T>; 148d307f52SEvan Bacon 158d307f52SEvan Bacon constructor() { 168d307f52SEvan Bacon this._assertAsync = memoize(this.assertImplementation.bind(this)); 178d307f52SEvan Bacon } 188d307f52SEvan Bacon 198d307f52SEvan Bacon /** An optional warning to call before running the memoized assertion. */ 208d307f52SEvan Bacon protected cachedError?: PrerequisiteCommandError; 218d307f52SEvan Bacon 228d307f52SEvan Bacon /** Reset the assertion memo and warning message. */ 23c4ef02aeSEvan Bacon public resetAssertion(props: TProps) { 248d307f52SEvan Bacon this.cachedError = undefined; 258d307f52SEvan Bacon this._assertAsync = memoize(this.assertImplementation.bind(this)); 268d307f52SEvan Bacon } 278d307f52SEvan Bacon 28c4ef02aeSEvan Bacon async assertAsync(props: TProps): Promise<T> { 298d307f52SEvan Bacon if (this.cachedError) { 308d307f52SEvan Bacon throw this.cachedError; 318d307f52SEvan Bacon } 328d307f52SEvan Bacon try { 33c4ef02aeSEvan Bacon return await this._assertAsync(props); 348d307f52SEvan Bacon } catch (error) { 358d307f52SEvan Bacon if (error instanceof PrerequisiteCommandError) { 368d307f52SEvan Bacon this.cachedError = error; 378d307f52SEvan Bacon } 388d307f52SEvan Bacon throw error; 398d307f52SEvan Bacon } 408d307f52SEvan Bacon } 418d307f52SEvan Bacon 428d307f52SEvan Bacon /** Exposed for testing. */ 43c4ef02aeSEvan Bacon async assertImplementation(props: TProps): Promise<T> { 448d307f52SEvan Bacon throw new UnimplementedError(); 458d307f52SEvan Bacon } 468d307f52SEvan Bacon} 478d307f52SEvan Bacon 488d307f52SEvan Bacon/** A prerequisite that is project specific. */ 49*33643b60SEvan Baconexport class ProjectPrerequisite<T = void, TProps = void> extends Prerequisite<T, TProps> { 508d307f52SEvan Bacon constructor(protected projectRoot: string) { 518d307f52SEvan Bacon super(); 528d307f52SEvan Bacon } 538d307f52SEvan Bacon} 54