diff --git a/helpers.ts b/helpers.ts index 74815c59..a4cac82e 100644 --- a/helpers.ts +++ b/helpers.ts @@ -281,14 +281,34 @@ export function appendZeroesToVersion(version: string, requiredVersionLength: nu return version; } -export function decorateMethod(before: (method1: any, self1: any, args1: any[]) => Promise, after: (method2: any, self2: any, result2: any, args2: any[]) => Promise) { +export function decorateMethod(before: (method1: any, self1: any, args1: any[]) => Promise, after: (method2: any, self2: any, result2: any, args2: any[]) => Promise) { return (target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor) => { const sink = descriptor.value; descriptor.value = async function (...args: any[]): Promise { + let newMethods: Function[] = null; if (before) { - await before(sink, this, args); + newMethods = await before(sink, this, args); } - const result = sink.apply(this, args); + + let hasBeenReplaced = false; + let result: any; + if (newMethods && newMethods.length) { + const replacementMethods = _.filter(newMethods, f => _.isFunction(f)); + if (replacementMethods.length > 0) { + if (replacementMethods.length > 1) { + const $logger = $injector.resolve("logger"); + $logger.warn(`Multiple methods detected which try to replace ${sink.name}. Will execute only the first of them.`); + } + + hasBeenReplaced = true; + result = _.head(replacementMethods)(args, sink.bind(this)); + } + } + + if (!hasBeenReplaced) { + result = sink.apply(this, args); + } + if (after) { return await after(sink, this, result, args); } @@ -327,7 +347,7 @@ export function hook(commandName: string) { return decorateMethod( async (method: any, self: any, args: any[]) => { const hooksService = getHooksService(self); - await hooksService.executeBeforeHooks(commandName, prepareArguments(method, args, hooksService)); + return hooksService.executeBeforeHooks(commandName, prepareArguments(method, args, hooksService)); }, async (method: any, self: any, resultPromise: any, args: any[]) => { const result = await resultPromise; diff --git a/services/hooks-service.ts b/services/hooks-service.ts index 14afa480..b041edcb 100644 --- a/services/hooks-service.ts +++ b/services/hooks-service.ts @@ -63,7 +63,7 @@ export class HooksService implements IHooksService { return this.executeHooks(afterHookName, traceMessage, hookArguments); } - private async executeHooks(hookName: string, traceMessage: string, hookArguments?: IDictionary): Promise { + private async executeHooks(hookName: string, traceMessage: string, hookArguments?: IDictionary): Promise { if (this.$config.DISABLE_HOOKS || !this.$options.hooks) { return; } @@ -75,19 +75,22 @@ export class HooksService implements IHooksService { this.initialize(projectDir); this.$logger.trace(traceMessage); - + const results: any[] = []; try { for (const hooksDirectory of this.hooksDirectories) { - await this.executeHooksInDirectory(hooksDirectory, hookName, hookArguments); + results.push(await this.executeHooksInDirectory(hooksDirectory, hookName, hookArguments)); } } catch (err) { this.$logger.trace("Failed during hook execution."); this.$errors.failWithoutHelp(err.message || err); } + + return _.flatten(results); } - private async executeHooksInDirectory(directoryPath: string, hookName: string, hookArguments?: IDictionary): Promise { + private async executeHooksInDirectory(directoryPath: string, hookName: string, hookArguments?: IDictionary): Promise { hookArguments = hookArguments || {}; + const results: any[] = []; const hooks = this.getHooksByName(directoryPath, hookName); for (let i = 0; i < hooks.length; ++i) { const hook = hooks[i]; @@ -129,7 +132,8 @@ export class HooksService implements IHooksService { if (maybePromise) { this.$logger.trace('Hook promises to signal completion'); try { - await maybePromise; + const result = await maybePromise; + results.push(result); } catch (err) { if (err && _.isBoolean(err.stopExecution) && err.errorAsWarning === true) { this.$logger.warn(err.message || err); @@ -144,12 +148,16 @@ export class HooksService implements IHooksService { this.$logger.trace("Executing %s hook at location %s with environment ", hookName, hook.fullPath, environment); const output = await this.$childProcess.spawnFromEvent(command, [hook.fullPath], "close", environment, { throwError: false }); + results.push(output); + if (output.exitCode !== 0) { throw new Error(output.stdout + output.stderr); } } } } + + return results; } private getHooksByName(directoryPath: string, hookName: string): IHook[] {