From 875b3269f86187012eb73ee88e89f6761f27bcc7 Mon Sep 17 00:00:00 2001 From: Rob Wormald Date: Tue, 18 Jul 2017 15:54:07 -0700 Subject: [PATCH] fix(effects): allow downleveled annotations (#98) Applications using Google Closure Compiler with Angular's ngc can opt into downleveling specifically marked decorators into static properties. This change enables that usage. Closes #93 --- modules/effects/spec/effects_metadata.spec.ts | 18 +++++++++++ modules/effects/src/effects_metadata.ts | 31 +++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/modules/effects/spec/effects_metadata.spec.ts b/modules/effects/spec/effects_metadata.spec.ts index 223927a19f..66e80ff275 100644 --- a/modules/effects/spec/effects_metadata.spec.ts +++ b/modules/effects/spec/effects_metadata.spec.ts @@ -23,6 +23,24 @@ describe('Effect Metadata', () => { ]); }); + it('should get the effects metadata for a downleveled class instance', () => { + class Fixture { + static get propDecorators() { + return { + a: [{ type: Effect, args: [{ dispatch: false }] }], + b: [{ type: Effect, args: [] }], + }; + } + } + + const mock = new Fixture(); + + expect(getSourceMetadata(mock)).toEqual([ + { propertyName: 'a', dispatch: false }, + { propertyName: 'b', dispatch: true }, + ]); + }); + it('should return an empty array if the class has not been decorated', () => { class Fixture { a: any; diff --git a/modules/effects/src/effects_metadata.ts b/modules/effects/src/effects_metadata.ts index 06380f8ec2..0d891faa76 100644 --- a/modules/effects/src/effects_metadata.ts +++ b/modules/effects/src/effects_metadata.ts @@ -11,7 +11,35 @@ export interface EffectMetadata { dispatch: boolean; } +function hasStaticMetadata(sourceType: any): boolean { + return !!(sourceType as any).propDecorators; +} + +function getStaticMetadata(sourceType: any): EffectMetadata[] { + const propDecorators = sourceType.propDecorators; + return Object.keys(propDecorators).reduce( + (all, key) => all.concat(getStaticMetadataEntry(propDecorators[key], key)), + [] + ); +} + +function getStaticMetadataEntry(metadataEntry: any, propertyName: string) { + return metadataEntry + .filter((entry: any) => entry.type === Effect) + .map((entry: any) => { + let dispatch = true; + if (entry.args.length) { + dispatch = !!entry.args[0].dispatch; + } + return { propertyName, dispatch }; + }); +} + function getEffectMetadataEntries(sourceProto: any): EffectMetadata[] { + if (hasStaticMetadata(sourceProto.constructor)) { + return getStaticMetadata(sourceProto.constructor); + } + if (r.hasOwnMetadata(METADATA_KEY, sourceProto)) { return r.getOwnMetadata(METADATA_KEY, sourceProto); } @@ -23,6 +51,9 @@ function setEffectMetadataEntries(sourceProto: any, entries: EffectMetadata[]) { r.defineMetadata(METADATA_KEY, entries, sourceProto); } +/** + * @Annotation + */ export function Effect({ dispatch } = { dispatch: true }): PropertyDecorator { return function(target: any, propertyName: string) { const effects: EffectMetadata[] = getEffectMetadataEntries(target);