diff --git a/packages/hooks/src/base.ts b/packages/hooks/src/base.ts index 2660f4b..344ed8b 100644 --- a/packages/hooks/src/base.ts +++ b/packages/hooks/src/base.ts @@ -124,23 +124,39 @@ export function withParams (...params: Array) { context[name] = args[index] === undefined ? defaultValue : args[index]; }); - if (!context.arguments) { - if (params.length > 0) { - Object.defineProperty(context, 'arguments', { - get (this: HookContext) { - const result = params.map(param => { - const name = typeof param === 'string' ? param : param[0]; - return this[name]; + if (params.length > 0) { + Object.defineProperty(context, 'arguments', { + enumerable: true, + get (this: HookContext) { + const result: any = []; + + params.forEach((param, index) => { + const name = typeof param === 'string' ? param : param[0]; + + Object.defineProperty(result, index, { + enumerable: true, + configurable: true, + get: () => this[name], + set: (value) => { + this[name] = value; + if (result[index] !== this[name]) { + result[index] = value; + } + } }); - return Object.freeze(result); - } - }); - } else { - context.arguments = args; - } + this[name] = result[index]; + }); + + return result; + } + }); + } else if (!context.arguments) { + context.arguments = args; } + Object.seal(context.arguments); + if (self) { context.self = self; } diff --git a/packages/hooks/test/decorator.test.ts b/packages/hooks/test/decorator.test.ts index 9464d71..b3ffd2d 100644 --- a/packages/hooks/test/decorator.test.ts +++ b/packages/hooks/test/decorator.test.ts @@ -27,6 +27,7 @@ describe('hookDecorator', () => { assert.deepStrictEqual(ctx, new HookContext({ method: 'sayHi', self: instance, + arguments: ['David NameFromTopLevel NameFromDummyClass'], name: expectedName })); diff --git a/packages/hooks/test/function.test.ts b/packages/hooks/test/function.test.ts index fdaab76..7996c42 100644 --- a/packages/hooks/test/function.test.ts +++ b/packages/hooks/test/function.test.ts @@ -205,9 +205,12 @@ describe('functionHooks', () => { assert.equal(await fn('Dave'), 'Hello Changed'); }); - it('with named context ctx.arguments is frozen', async () => { + it('ctx.arguments is configurable with named params', async () => { const modifyArgs = async (ctx: HookContext, next: NextFunction) => { - ctx.arguments[0] = 'Test'; + ctx.arguments[0] = 'Changed'; + ctx.arguments.push('no'); + + assert.equal(ctx.name, ctx.arguments[0]); await next(); }; @@ -217,9 +220,15 @@ describe('functionHooks', () => { context: withParams('name') }); - await assert.rejects(() => fn('There'), { - message: `Cannot assign to read only property '0' of object '[object Array]'` - }); + const customContext = new HookContext({}); + const resultContext = await fn('Daffl', {}, customContext); + + assert.equal(resultContext, customContext); + assert.deepEqual(resultContext, new HookContext({ + arguments: ['Changed'], + name: 'Changed', + result: 'Hello Changed' + })); }); it('can take and return an existing HookContext', async () => { @@ -242,6 +251,34 @@ describe('functionHooks', () => { assert.equal(resultContext, customContext); assert.deepEqual(resultContext, new HookContext({ + arguments: ['Changed'], + message: 'Custom message', + name: 'Changed', + result: 'Hello Changed' + })); + }); + + it('takes parameters with multiple withParams', async () => { + const message = 'Custom message'; + const fn = hooks(hello, { + middleware: [ + async (ctx, next) => { + assert.equal(ctx.name, 'Dave'); + assert.equal(ctx.message, message); + + ctx.name = 'Changed'; + await next(); + } + ], + context: [withParams(), withParams('name'), withParams()] + }); + + const customContext = new HookContext({ message }); + const resultContext: HookContext = await fn('Dave', {}, customContext); + + assert.equal(resultContext, customContext); + assert.deepEqual(resultContext, new HookContext({ + arguments: ['Changed'], message: 'Custom message', name: 'Changed', result: 'Hello Changed' diff --git a/packages/hooks/test/object.test.ts b/packages/hooks/test/object.test.ts index a0222f3..1c873ea 100644 --- a/packages/hooks/test/object.test.ts +++ b/packages/hooks/test/object.test.ts @@ -33,9 +33,9 @@ describe('objectHooks', () => { const hookedObj = hooks(obj, { sayHi: [async (ctx: HookContext, next: NextFunction) => { assert.deepEqual(ctx, new HookContext({ + arguments: [ 'David' ], self: obj, - method: 'sayHi', - arguments: [ 'David' ] + method: 'sayHi' })); await next(); @@ -59,6 +59,7 @@ describe('objectHooks', () => { sayHi: { middleware: [async (ctx: HookContext, next: NextFunction) => { assert.deepStrictEqual(ctx, new HookContext({ + arguments: ['David'], method: 'sayHi', name: 'David', self: obj @@ -122,6 +123,7 @@ describe('objectHooks', () => { sayHi: { middleware: [async (ctx: HookContext, next: NextFunction) => { assert.deepStrictEqual(ctx, new HookContext({ + arguments: ['David'], self: instance, method: 'sayHi', name: 'David' @@ -150,8 +152,8 @@ describe('objectHooks', () => { hooks(DummyClass, { sayHi: [async (ctx: HookContext, next: NextFunction) => { assert.deepStrictEqual(ctx, new HookContext({ - method: 'sayHi', arguments: [ 'David' ], + method: 'sayHi', self: instance })); diff --git a/readme.md b/readme.md index 5e297e4..105f86d 100644 --- a/readme.md +++ b/readme.md @@ -593,7 +593,7 @@ hooks(HelloSayer.prototype, { With decorators and inheritance ```js -import { hooks, params, HookContext, NextFunction } from '@feathersjs/hooks'; +import { hooks, withParams, HookContext, NextFunction } from '@feathersjs/hooks'; @hooks([ async (context: HookContext, next: NextFunction) => {