Skip to content

Commit

Permalink
feat(hooks): Revert refactoring into separate hooks (PR #37) (#57)
Browse files Browse the repository at this point in the history
BREAKING CHANGES: This reverts back to the usage of v0.4.0 and before.
  • Loading branch information
bertho-zero authored and daffl committed Nov 12, 2020
1 parent d1a3883 commit 56a44be
Show file tree
Hide file tree
Showing 11 changed files with 428 additions and 264 deletions.
174 changes: 158 additions & 16 deletions packages/hooks/src/base.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import { Middleware } from './compose';
import { copyToSelf } from './utils';

export const HOOKS: string = Symbol('@feathersjs/hooks') as any;

export type HookContextData = { [key: string]: any };

/**
* The base hook context.
*/
export class HookContext<T = any, C = any> {
result?: T;
method?: string;
Expand All @@ -18,57 +22,195 @@ export class HookContext<T = any, C = any> {

export type HookContextConstructor = new (data?: { [key: string]: any }) => HookContext;

export type HookDefaultsInitializer = (context: HookContext) => HookContextData;
export type HookDefaultsInitializer = (self?: any, args?: any[], context?: HookContext) => HookContextData;

export class HookManager {
_parent?: this|null = null;
_middleware: Middleware[] = [];
_params: string[]|null = null;
_middleware: Middleware[]|null = null;
_props: HookContextData|null = null;
_defaults: HookDefaultsInitializer;

parent (parent: this) {
this._parent = parent;

return this;
}

middleware (middleware: Middleware[]) {
this._middleware = middleware;
middleware (middleware?: Middleware[]) {
this._middleware = middleware?.length ? middleware : null;

return this;
}

getContextClass (): HookContextConstructor {
return HookContext;
}
getMiddleware (): Middleware[]|null {
if (this._parent) {
const previous = this._parent.getMiddleware();

if (previous) {
if (this._middleware) {
return this._parent.getMiddleware().concat(this._middleware);
}

getMiddleware (): Middleware[] {
const previous = this._parent ? this._parent.getMiddleware() : [];
return previous;
}
}

return previous.concat(this._middleware);
return this._middleware;
}

collectMiddleware (self: any, _args: any[]): Middleware[] {
collectMiddleware (self: any, _args: any[]): Middleware[] {
const otherMiddleware = getMiddleware(self);
const middleware = this.getMiddleware();

if (otherMiddleware) {
if (middleware) {
return otherMiddleware.concat(middleware);
}

return otherMiddleware;
}

return this.getMiddleware();
}

props (props: HookContextData) {
if (!this._props) {
this._props = {};
}

Object.assign(this._props, props);

return this;
}

getProps (): HookContextData {
if (this._parent) {
const previous = this._parent.getProps();

if (previous) {
if (this._props) {
return Object.assign({}, previous, this._props);
}

return previous;
}
}

return this._props;
}

params (...params: string[]) {
this._params = params;

return this;
}

return otherMiddleware.concat(this.getMiddleware());
getParams (): string[] {
if (this._parent) {
const previous = this._parent.getParams();

if (previous) {
if (this._params) {
return previous.concat(this._params);
}

return previous;
}
}

return this._params;
}

defaults (defaults: HookDefaultsInitializer) {
this._defaults = defaults;

return this;
}

getDefaults (self: any, args: any[], context: HookContext): HookContextData {
const defaults = typeof this._defaults === 'function' ? this._defaults(self, args, context) : null;

if (this._parent) {
const previous = this._parent.getDefaults(self, args, context);

if (previous) {
if (this._props) {
return Object.assign({}, previous, this._props);
}

return previous;
}
}

return defaults;
}

getContextClass (Base: HookContextConstructor = HookContext): HookContextConstructor {
const ContextClass = class ContextClass extends Base {
constructor (data: any) {
super(data);

copyToSelf(this);
}
};
const params = this.getParams();
const props = this.getProps();

if (params) {
params.forEach((name, index) => {
if (props?.[name]) {
throw new Error(`Hooks can not have a property and param named '${name}'. Use .defaults instead.`);
}

Object.defineProperty(ContextClass.prototype, name, {
enumerable: true,
get () {
return this?.arguments[index];
},
set (value: any) {
this.arguments[index] = value;
}
});
});
}

if (props) {
Object.assign(ContextClass.prototype, props);
}

return ContextClass;
}

initializeContext (self: any, args: any[], context: HookContext): HookContext {
const ctx = this._parent ? this._parent.initializeContext(self, args, context) : context;
const defaults = this.getDefaults(self, args, ctx);

if (self) {
ctx.self = self;
}

ctx.arguments = args;

if (defaults) {
for (const name of Object.keys(defaults)) {
if (ctx[name] === undefined) {
ctx[name] = defaults[name];
}
}
}

return ctx;
}
}

export type HookOptions = HookManager|Middleware[];
export type HookOptions = HookManager|Middleware[]|null;

export function convertOptions (options: HookOptions = null) {
if (!options) {
return new HookManager()
}

export function convertOptions (options: HookOptions = []) {
return Array.isArray(options) ? new HookManager().middleware(options) : options;
}

Expand All @@ -84,10 +226,10 @@ export function setManager<T> (target: T, manager: HookManager) {
return target;
}

export function getMiddleware (target: any): Middleware[] {
export function getMiddleware (target: any): Middleware[]|null {
const manager = getManager(target);

return manager ? manager.getMiddleware() : [];
return manager ? manager.getMiddleware() : null;
}

export function setMiddleware<T> (target: T, middleware: Middleware[]) {
Expand Down
63 changes: 0 additions & 63 deletions packages/hooks/src/context.ts

This file was deleted.

44 changes: 23 additions & 21 deletions packages/hooks/src/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { compose, Middleware } from './compose';
import {
HookContext, setManager, HookContextData, HookOptions, convertOptions, setMiddleware
} from './base';
import { properties } from './context';

export function getOriginal (fn: any): any {
return typeof fn.original === 'function' ? getOriginal(fn.original) : fn;
Expand Down Expand Up @@ -40,23 +39,29 @@ export function functionHooks <F> (fn: F, managerOrMiddleware: HookOptions) {
// Assemble the hook chain
const hookChain: Middleware[] = [
// Return `ctx.result` or the context
(ctx, next) => next().then(() => returnContext ? ctx : ctx.result),
// Create the hook chain by calling the `collectMiddleware function
...manager.collectMiddleware(this, args),
// Runs the actual original method if `ctx.result` is not already set
(ctx, next) => {
if (ctx.result === undefined) {
return Promise.resolve(original.apply(this, ctx.arguments)).then(result => {
ctx.result = result;

return next();
});
}

return next();
}
(ctx, next) => next().then(() => returnContext ? ctx : ctx.result)
];

// Create the hook chain by calling the `collectMiddleware function
const mw = manager.collectMiddleware(this, args);

if (mw) {
Array.prototype.push.apply(hookChain, mw);
}

// Runs the actual original method if `ctx.result` is not already set
hookChain.push((ctx, next) => {
if (ctx.result === undefined) {
return Promise.resolve(original.apply(this, ctx.arguments)).then(result => {
ctx.result = result;

return next();
});
}

return next();
});

return compose(hookChain).call(this, context);
};

Expand Down Expand Up @@ -92,9 +97,7 @@ export function objectHooks (_obj: any, hooks: HookMap|Middleware[]) {

const manager = convertOptions(hooks[method]);

manager._middleware.unshift(properties({ method }));

result[method] = functionHooks(fn, manager);
result[method] = functionHooks(fn, manager.props({ method }));

return result;
}, obj);
Expand All @@ -116,8 +119,7 @@ export const hookDecorator = (managerOrMiddleware?: HookOptions) => {
throw new Error(`Can not apply hooks. '${method}' is not a function`);
}

manager._middleware.unshift(properties({ method }));
descriptor.value = functionHooks(fn, manager);
descriptor.value = functionHooks(fn, manager.props({ method }));

return descriptor;
};
Expand Down
Loading

0 comments on commit 56a44be

Please sign in to comment.