Skip to content
This repository was archived by the owner on Feb 2, 2021. It is now read-only.

Allow hooks to provide implementation for decorated methods #1018

Merged
merged 2 commits into from
Nov 2, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 24 additions & 4 deletions helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -281,14 +281,34 @@ export function appendZeroesToVersion(version: string, requiredVersionLength: nu
return version;
}

export function decorateMethod(before: (method1: any, self1: any, args1: any[]) => Promise<void>, after: (method2: any, self2: any, result2: any, args2: any[]) => Promise<any>) {
export function decorateMethod(before: (method1: any, self1: any, args1: any[]) => Promise<any>, after: (method2: any, self2: any, result2: any, args2: any[]) => Promise<any>) {
return (target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor<Function>) => {
const sink = descriptor.value;
descriptor.value = async function (...args: any[]): Promise<any> {
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<ILogger>("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);
}
Expand Down Expand Up @@ -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;
Expand Down
18 changes: 13 additions & 5 deletions services/hooks-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export class HooksService implements IHooksService {
return this.executeHooks(afterHookName, traceMessage, hookArguments);
}

private async executeHooks(hookName: string, traceMessage: string, hookArguments?: IDictionary<any>): Promise<void> {
private async executeHooks(hookName: string, traceMessage: string, hookArguments?: IDictionary<any>): Promise<any> {
if (this.$config.DISABLE_HOOKS || !this.$options.hooks) {
return;
}
Expand All @@ -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<any>): Promise<void> {
private async executeHooksInDirectory(directoryPath: string, hookName: string, hookArguments?: IDictionary<any>): Promise<any[]> {
hookArguments = hookArguments || {};
const results: any[] = [];
const hooks = this.getHooksByName(directoryPath, hookName);
for (let i = 0; i < hooks.length; ++i) {
const hook = hooks[i];
Expand Down Expand Up @@ -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);
Expand All @@ -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[] {
Expand Down