Skip to content

Commit

Permalink
Revert pull request #37
Browse files Browse the repository at this point in the history
  • Loading branch information
bertho-zero committed Oct 24, 2020
1 parent d61a574 commit 56819d1
Show file tree
Hide file tree
Showing 13 changed files with 349 additions and 238 deletions.
90 changes: 84 additions & 6 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,11 +22,14 @@ 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;
_params: string[] = [];
_middleware: Middleware[] = [];
_props: HookContextData = {};
_defaults: HookDefaultsInitializer;

parent (parent: this) {
this._parent = parent;
Expand All @@ -36,32 +43,103 @@ export class HookManager {
return this;
}

getContextClass (): HookContextConstructor {
return HookContext;
}

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

return previous.concat(this._middleware);
}

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

return otherMiddleware.concat(this.getMiddleware());
}

props (props: HookContextData) {
Object.assign(this._props, props);

return this;
}

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

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

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

return this;
}

getParams (): string[] {
const previous = this._parent ? this._parent.getParams() : [];

return previous.concat(this._params);
}

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

return this;
}

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

return Object.assign({}, previous, 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();

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;
}
});
});

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;

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

return ctx;
}
}
Expand Down
63 changes: 0 additions & 63 deletions packages/hooks/src/context.ts

This file was deleted.

26 changes: 26 additions & 0 deletions packages/hooks/src/decorator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { functionHooks } from './hooks';
import { setManager, HookOptions, convertOptions } from './base';

export const hookDecorator = (managerOrMiddleware?: HookOptions) => {
const wrapper: any = (_target: any, method: string, descriptor: TypedPropertyDescriptor<any>): TypedPropertyDescriptor<any> => {
const manager = convertOptions(managerOrMiddleware);

if (!descriptor) {
setManager(_target.prototype, manager);

return _target;
}

const fn = descriptor.value;

if (typeof fn !== 'function') {
throw new Error(`Can not apply hooks. '${method}' is not a function`);
}

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

return descriptor;
};

return wrapper;
};
10 changes: 3 additions & 7 deletions packages/hooks/src/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@ 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;
}

function copyProperties <F> (target: F, original: any) {
const originalProps = (Object.keys(original) as any)
.concat(Object.getOwnPropertySymbols(original));
.concat(Object.getOwnPropertySymbols(original));

for (const prop of originalProps) {
const propDescriptor = Object.getOwnPropertyDescriptor(original, prop);
Expand Down Expand Up @@ -92,9 +91,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 +113,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
1 change: 0 additions & 1 deletion packages/hooks/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import {
} from './base';
import { functionHooks, hookDecorator, objectHooks, HookMap } from './hooks';

export * as setContext from './context';
export * from './hooks';
export * from './compose';
export * from './base';
Expand Down
29 changes: 29 additions & 0 deletions packages/hooks/src/object.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Middleware } from './compose';
import { functionHooks } from './hooks';
import { setMiddleware, convertOptions, HookOptions } from './base';

export type HookMap<O = any> = {
[L in keyof O]?: HookOptions;
}

export function objectHooks (_obj: any, hooks: HookMap|Middleware[]) {
const obj = typeof _obj === 'function' ? _obj.prototype : _obj;

if (Array.isArray(hooks)) {
return setMiddleware(obj, hooks);
}

return Object.keys(hooks).reduce((result, method) => {
const fn = obj[method];

if (typeof fn !== 'function') {
throw new Error(`Can not apply hooks. '${method}' is not a function`);
}

const manager = convertOptions(hooks[method]);

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

return result;
}, obj);
};
25 changes: 25 additions & 0 deletions packages/hooks/src/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
const proto = Object.prototype as any;
// These are non-standard but offer a more reliable prototype based
// lookup for properties
const hasProtoDefinitions = typeof proto.__lookupGetter__ === 'function' &&
typeof proto.__defineGetter__ === 'function' &&
typeof proto.__defineSetter__ === 'function';

export function copyToSelf (target: any) {
// tslint:disable-next-line
for (const key in target) {
if (!target.hasOwnProperty(key)) {
const getter = hasProtoDefinitions ? target.constructor.prototype.__lookupGetter__(key)
: Object.getOwnPropertyDescriptor(target, key);

if (getter && hasProtoDefinitions) {
target.__defineGetter__(key, getter);
target.__defineSetter__(key, target.constructor.prototype.__lookupSetter__(key));
} else if (getter) {
Object.defineProperty(target, key, getter);
} else {
target[key] = target[key];
}
}
}
}
12 changes: 2 additions & 10 deletions packages/hooks/test/benchmark.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,5 @@
import { strict as assert } from 'assert';
import {
hooks,
HookContext,
NextFunction,
middleware,
setContext
} from '../src/';
import { hooks, HookContext, NextFunction, middleware } from '../src/';

const CYCLES = 100000;
const getRuntime = async (callback: () => Promise<any>) => {
Expand Down Expand Up @@ -50,12 +44,10 @@ describe('hook benchmark', () => {

it('single hook, withParams and props', async () => {
const hookHello = hooks(hello, middleware([
setContext.params('name'),
setContext.properties({ dave: true }),
async (_ctx: HookContext, next: NextFunction) => {
await next();
}
]));
]).params('name').props({ dave: true }));

const runtime = await getRuntime(() => hookHello('Dave'));

Expand Down
Loading

0 comments on commit 56819d1

Please sign in to comment.