UNPKG

@feathersjs/hooks

Version:

Async middleware for JavaScript and TypeScript

98 lines (97 loc) 3.9 kB
import { compose } from './compose.js'; import { convertOptions, setManager, setMiddleware } from './base.js'; import { copyFnProperties, copyProperties } from './utils.js'; export function getOriginal(fn) { return typeof fn.original === 'function' ? getOriginal(fn.original) : fn; } export function functionHooks(fn, managerOrMiddleware) { if (typeof fn !== 'function') { throw new Error('Can not apply hooks to non-function'); } const manager = convertOptions(managerOrMiddleware); const wrapper = function (...args) { const { Context, original } = wrapper; // If we got passed an existing HookContext instance, we want to return it as well const returnContext = args[args.length - 1] instanceof Context; // Use existing context or default const base = returnContext ? args.pop() : new Context(); // Initialize the context const context = manager.initializeContext(this, args, base); // Assemble the hook chain const hookChain = [ // Return `ctx.result` or the context (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 (!Object.prototype.hasOwnProperty.call(context, 'result')) { return Promise.resolve(original.apply(this, ctx.arguments)).then((result) => { ctx.result = result; return next(); }); } return next(); }); return compose(hookChain).call(this, context); }; copyFnProperties(wrapper, fn); copyProperties(wrapper, fn); setManager(wrapper, manager); return Object.assign(wrapper, { original: getOriginal(fn), Context: manager.getContextClass(), createContext: (data = {}) => { return new wrapper.Context(data); } }); } export function objectHooks(obj, hooks) { if (Array.isArray(hooks)) { return setMiddleware(obj, hooks); } for (const method of Object.keys(hooks)) { const target = typeof obj[method] === 'function' ? obj : obj.prototype; const fn = target && target[method]; if (typeof fn !== 'function') { throw new Error(`Can not apply hooks. '${method}' is not a function`); } const manager = convertOptions(hooks[method]); target[method] = functionHooks(fn, manager.props({ method })); } return obj; } export const hookDecorator = (managerOrMiddleware) => { return (target, context) => { const manager = convertOptions(managerOrMiddleware); if (context.kind === 'class') { setManager(target.prototype, manager); return target; } else if (context.kind === 'method') { const method = String(context.name); return functionHooks(target, manager.props({ method })); } throw new Error('Can not apply hooks.'); }; }; export const legacyDecorator = (managerOrMiddleware) => { const wrapper = (_target, method, descriptor) => { 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; };