diff --git a/aio/tools/transforms/angular-api-package/processors/mergeDecoratorDocs.js b/aio/tools/transforms/angular-api-package/processors/mergeDecoratorDocs.js index 414e742b62..3ee25a8cb0 100644 --- a/aio/tools/transforms/angular-api-package/processors/mergeDecoratorDocs.js +++ b/aio/tools/transforms/angular-api-package/processors/mergeDecoratorDocs.js @@ -56,7 +56,6 @@ module.exports = function mergeDecoratorDocs(log) { {type: 'Param', description: 'parameter', functionName: 'makeParamDecorator'}, ], $process(docs) { - const decoratorDocs = Object.create(null); // find all the decorators, signified by a call to `make...Decorator(metadata)` @@ -69,7 +68,8 @@ module.exports = function mergeDecoratorDocs(log) { // For example the `X` of `createDecorator(...)`. const decoratorType = initializer.arguments[0].text; - log.debug('mergeDecoratorDocs: found decorator', doc.docType, doc.name, decoratorType); + log.debug( + 'mergeDecoratorDocs: found decorator', doc.docType, doc.name, decoratorType); doc.docType = 'decorator'; doc.decoratorLocation = call.description; @@ -84,8 +84,8 @@ module.exports = function mergeDecoratorDocs(log) { // merge the info from the associated metadata interfaces into the decorator docs docs = docs.filter(doc => { if (decoratorDocs[doc.name]) { - - // We have found an `XxxDecorator` document that will hold the call signature of the decorator + // We have found an `XxxDecorator` document that will hold the call signature of the + // decorator var decoratorDoc = decoratorDocs[doc.name]; var callMember = doc.members.find(member => member.isCallMember); @@ -109,18 +109,44 @@ module.exports = function mergeDecoratorDocs(log) { }; function getInitializer(doc) { - var initializer = doc.symbol && doc.symbol.valueDeclaration && doc.symbol.valueDeclaration.initializer; + const declaration = doc.symbol && doc.symbol.valueDeclaration; + if (!declaration || !declaration.initializer || !declaration.initializer.expression) { + return; + } + + let initializer = declaration.initializer; + // There appear to be two forms of initializer: - // export var Injectable: InjectableFactory = - // makeDecorator(InjectableMetadata); + // + // ``` + // export const Injectable: InjectableFactory = + // makeDecorator(InjectableMetadata); + // ``` + // // and - // export var RouteConfig: (configs: RouteDefinition[]) => ClassDecorator = - // makeDecorator(RouteConfigAnnotation); - // In the first case, the type assertion `` causes the AST to contain an - // extra level of expression - // to hold the new type of the expression. - if (initializer && initializer.expression && initializer.expression.expression) { + // + // ``` + // export const RouteConfig: (configs: RouteDefinition[]) => ClassDecorator = + // makeDecorator(RouteConfigAnnotation); + // ``` + // + // In the first case, the type assertion `` causes the AST to contain an extra + // level of expression to hold the new type of the expression. + if (initializer.type && initializer.expression.expression) { initializer = initializer.expression; } + + // It is also possible that the decorator call is wrapped in a call to `attachInjectFlag()`: + // + // ``` + // const Optional: OptionalDecorator = + // attachInjectFlag(makeParamDecorator('Optional'), InternalInjectFlags.Optional); + // ``` + // + // If so, use the first argument of the call. + if (initializer.arguments && initializer.expression.text === 'attachInjectFlag') { + initializer = initializer.arguments[0]; + } + return initializer; } diff --git a/aio/tools/transforms/angular-api-package/processors/mergeDecoratorDocs.spec.js b/aio/tools/transforms/angular-api-package/processors/mergeDecoratorDocs.spec.js index 77bc9f8141..9940fb9d9e 100644 --- a/aio/tools/transforms/angular-api-package/processors/mergeDecoratorDocs.spec.js +++ b/aio/tools/transforms/angular-api-package/processors/mergeDecoratorDocs.spec.js @@ -1,5 +1,5 @@ -var testPackage = require('../../helpers/test-package'); -var Dgeni = require('dgeni'); +const testPackage = require('../../helpers/test-package'); +const Dgeni = require('dgeni'); describe('mergeDecoratorDocs processor', () => { let processor, moduleDoc, decoratorDoc, metadataDoc, otherDoc; @@ -20,11 +20,10 @@ describe('mergeDecoratorDocs processor', () => { shortDescription: 'decorator - short description', description: 'decorator - description', symbol: { - valueDeclaration: { initializer: { expression: { text: 'makeDecorator' }, arguments: [{ text: 'X' }] } } + valueDeclaration: + {initializer: {expression: {text: 'makeDecorator'}, arguments: [{text: 'X'}]}} }, - members: [ - { name: 'templateUrl', description: 'templateUrl - description' } - ], + members: [{name: 'templateUrl', description: 'templateUrl - description'}], moduleDoc }; @@ -38,9 +37,7 @@ describe('mergeDecoratorDocs processor', () => { description: 'call interface - call member - description', usageNotes: 'call interface - call member - usageNotes', }, - { - description: 'call interface - non call member - description' - } + {description: 'call interface - non call member - description'} ], moduleDoc }; @@ -49,7 +46,8 @@ describe('mergeDecoratorDocs processor', () => { name: 'Y', docType: 'const', symbol: { - valueDeclaration: { initializer: { expression: { text: 'otherCall' }, arguments: [{ text: 'param1' }] } } + valueDeclaration: + {initializer: {expression: {text: 'otherCall'}, arguments: [{text: 'param1'}]}} }, moduleDoc }; @@ -58,11 +56,12 @@ describe('mergeDecoratorDocs processor', () => { }); - it('should change the docType of only the docs that are initialized by a call to makeDecorator', () => { - processor.$process([decoratorDoc, metadataDoc, otherDoc]); - expect(decoratorDoc.docType).toEqual('decorator'); - expect(otherDoc.docType).toEqual('const'); - }); + it('should change the docType of only the docs that are initialized by a call to makeDecorator', + () => { + processor.$process([decoratorDoc, metadataDoc, otherDoc]); + expect(decoratorDoc.docType).toEqual('decorator'); + expect(otherDoc.docType).toEqual('const'); + }); it('should extract the "type" of the decorator meta data', () => { processor.$process([decoratorDoc, metadataDoc, otherDoc]); @@ -88,4 +87,23 @@ describe('mergeDecoratorDocs processor', () => { processor.$process([decoratorDoc, metadataDoc, otherDoc]); expect(decoratorDoc.docType).toEqual('decorator'); }); + + it('should handle a type cast before the "make decorator" call', () => { + decoratorDoc.symbol.valueDeclaration.initializer = { + expression: decoratorDoc.symbol.valueDeclaration.initializer, + type: {}, + }; + processor.$process([decoratorDoc, metadataDoc, otherDoc]); + expect(decoratorDoc.docType).toEqual('decorator'); + }); + + it('should handle the "make decorator" call being wrapped in a call to `attachInjectFlag()`', + () => { + decoratorDoc.symbol.valueDeclaration.initializer = { + expression: {text: 'attachInjectFlag'}, + arguments: [decoratorDoc.symbol.valueDeclaration.initializer] + }; + processor.$process([decoratorDoc, metadataDoc, otherDoc]); + expect(decoratorDoc.docType).toEqual('decorator'); + }); });