From 4ae546be1fae2cd84e4e19d31af23352e32da8ba Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Mon, 25 Sep 2017 19:59:21 +0100 Subject: [PATCH] build(aio): ensure decorators with shared interface types are found (#19361) Closes #19358 --- .../processors/mergeDecoratorDocs.js | 77 +++++++++---------- .../processors/mergeDecoratorDocs.spec.js | 40 +++++----- .../api/includes/decorator-overview.html | 6 +- 3 files changed, 60 insertions(+), 63 deletions(-) diff --git a/aio/tools/transforms/angular-api-package/processors/mergeDecoratorDocs.js b/aio/tools/transforms/angular-api-package/processors/mergeDecoratorDocs.js index 02b96f3a87..6227dbb3d8 100644 --- a/aio/tools/transforms/angular-api-package/processors/mergeDecoratorDocs.js +++ b/aio/tools/transforms/angular-api-package/processors/mergeDecoratorDocs.js @@ -48,9 +48,9 @@ module.exports = function mergeDecoratorDocs(log) { $runAfter: ['processing-docs'], $runBefore: ['docs-processed'], makeDecoratorCalls: [ - {type: '', description: 'toplevel'}, - {type: 'Prop', description: 'property'}, - {type: 'Param', description: 'parameter'}, + {type: '', description: 'toplevel', functionName: 'makeDecorator'}, + {type: 'Prop', description: 'property', functionName: 'makePropDecorator'}, + {type: 'Param', description: 'parameter', functionName: 'makeParamDecorator'}, ], $process: function(docs) { @@ -58,24 +58,25 @@ module.exports = function mergeDecoratorDocs(log) { var docsToMerge = Object.create(null); docs.forEach(function(doc) { + const initializer = getInitializer(doc); + if (initializer) { + makeDecoratorCalls.forEach(function(call) { + // find all the decorators, signified by a call to `make...Decorator(metadata)` + if (initializer.expression && initializer.expression.text === call.functionName) { + log.debug('mergeDecoratorDocs: found decorator', doc.docType, doc.name); + doc.docType = 'decorator'; + doc.decoratorLocation = call.description; + // Get the type of the decorator metadata from the first "type" argument of the call. + // For example the `X` of `createDecorator(...)`. + doc.decoratorType = initializer.arguments[0].text; + // clear the symbol type named since it is not needed + doc.symbolTypeName = undefined; - makeDecoratorCalls.forEach(function(call) { - // find all the decorators, signified by a call to `make...Decorator(metadata)` - var makeDecorator = getMakeDecoratorCall(doc, call.type); - if (makeDecorator) { - log.debug('mergeDecoratorDocs: found decorator', doc.docType, doc.name); - doc.docType = 'decorator'; - doc.decoratorLocation = call.description; - // Get the type of the decorator metadata from the first "type" argument of the call. - // For example the `X` of `createDecorator(...)`. - doc.decoratorType = makeDecorator.arguments[0].text; - // clear the symbol type named since it is not needed - doc.symbolTypeName = undefined; - - // keep track of the names of the metadata interface that will need to be merged into this decorator doc - docsToMerge[doc.name + 'Decorator'] = doc; - } - }); + // keep track of the names of the metadata interface that will need to be merged into this decorator doc + docsToMerge[doc.name + 'Decorator'] = doc; + } + }); + } }); // merge the metadata docs into the decorator docs @@ -106,27 +107,19 @@ module.exports = function mergeDecoratorDocs(log) { }; }; -function getMakeDecoratorCall(doc, type) { - var makeDecoratorFnName = 'make' + (type || '') + 'Decorator'; - - var initializer = doc.declaration && - doc.declaration.initializer; - - if (initializer) { - // There appear to be two forms of initializer: - // export var 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.expression && initializer.expression.expression) { - initializer = initializer.expression; - } - if (initializer.expression && initializer.expression.text === makeDecoratorFnName) { - return initializer; - } +function getInitializer(doc) { + var initializer = doc.symbol && doc.symbol.valueDeclaration && doc.symbol.valueDeclaration.initializer; + // There appear to be two forms of initializer: + // export var 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) { + initializer = initializer.expression; } + 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 50add89022..be6cff2e4b 100644 --- a/aio/tools/transforms/angular-api-package/processors/mergeDecoratorDocs.spec.js +++ b/aio/tools/transforms/angular-api-package/processors/mergeDecoratorDocs.spec.js @@ -2,11 +2,11 @@ var testPackage = require('../../helpers/test-package'); var Dgeni = require('dgeni'); describe('mergeDecoratorDocs processor', () => { - var dgeni, injector, processor, moduleDoc, decoratorDoc, metadataDoc, decoratorDocWithTypeAssertion, otherDoc; + let processor, moduleDoc, decoratorDoc, metadataDoc, otherDoc; beforeEach(() => { - dgeni = new Dgeni([testPackage('angular-api-package')]); - injector = dgeni.configureInjector(); + const dgeni = new Dgeni([testPackage('angular-api-package')]); + const injector = dgeni.configureInjector(); processor = injector.get('mergeDecoratorDocs'); moduleDoc = {}; @@ -15,7 +15,9 @@ describe('mergeDecoratorDocs processor', () => { name: 'Component', docType: 'const', description: 'A description of the metadata for the Component decorator', - declaration: {initializer: {expression: {text: 'makeDecorator'}, arguments: [{text: 'X'}]}}, + symbol: { + valueDeclaration: { initializer: { expression: { text: 'makeDecorator' }, arguments: [{ text: 'X' }] } } + }, members: [ { name: 'templateUrl', description: 'A description of the templateUrl property' } ], @@ -40,45 +42,45 @@ describe('mergeDecoratorDocs processor', () => { moduleDoc }; - decoratorDocWithTypeAssertion = { - name: 'Y', - docType: 'const', - declaration: { initializer: { expression: {type: {}, expression: {text: 'makeDecorator'}, arguments: [{text: 'Y'}]} } }, - moduleDoc - }; otherDoc = { name: 'Y', docType: 'const', - declaration: {initializer: {expression: {text: 'otherCall'}, arguments: [{text: 'param1'}]}}, + symbol: { + valueDeclaration: { initializer: { expression: { text: 'otherCall' }, arguments: [{ text: 'param1' }] } } + }, moduleDoc }; - moduleDoc.exports = [decoratorDoc, metadataDoc, decoratorDocWithTypeAssertion, otherDoc]; + moduleDoc.exports = [decoratorDoc, metadataDoc, otherDoc]; }); - it('should change the docType of only the docs that are initialied by a call to makeDecorator', () => { - processor.$process([decoratorDoc, metadataDoc, decoratorDocWithTypeAssertion, otherDoc]); + 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(decoratorDocWithTypeAssertion.docType).toEqual('decorator'); expect(otherDoc.docType).toEqual('const'); }); it('should extract the "type" of the decorator meta data', () => { - processor.$process([decoratorDoc, metadataDoc, decoratorDocWithTypeAssertion, otherDoc]); + processor.$process([decoratorDoc, metadataDoc, otherDoc]); expect(decoratorDoc.decoratorType).toEqual('X'); - expect(decoratorDocWithTypeAssertion.decoratorType).toEqual('Y'); }); it('should copy across properties from the call signature doc', () => { - processor.$process([decoratorDoc, metadataDoc, decoratorDocWithTypeAssertion, otherDoc]); + processor.$process([decoratorDoc, metadataDoc, otherDoc]); expect(decoratorDoc.description).toEqual('The actual description of the call signature'); expect(decoratorDoc.whatItDoes).toEqual('Does something cool...'); expect(decoratorDoc.howToUse).toEqual('Use it like this...'); }); it('should remove the metadataDoc from the module exports', () => { - processor.$process([decoratorDoc, metadataDoc, decoratorDocWithTypeAssertion, otherDoc]); + processor.$process([decoratorDoc, metadataDoc, otherDoc]); expect(moduleDoc.exports).not.toContain(metadataDoc); }); + + it('should cope with decorators that have type params', () => { + decoratorDoc.symbol.valueDeclaration.initializer.expression.type = {}; + processor.$process([decoratorDoc, metadataDoc, otherDoc]); + expect(decoratorDoc.docType).toEqual('decorator'); + }); }); diff --git a/aio/tools/transforms/templates/api/includes/decorator-overview.html b/aio/tools/transforms/templates/api/includes/decorator-overview.html index 840c341f40..7323ef5cc0 100644 --- a/aio/tools/transforms/templates/api/includes/decorator-overview.html +++ b/aio/tools/transforms/templates/api/includes/decorator-overview.html @@ -1,9 +1,11 @@ {% import "lib/memberHelpers.html" as memberHelper -%} +{% if doc.members.length %}
-

Metadata Overview

+

Metadata Overview

@{$ doc.name $}{$ doc.typeParams | escape $}({ {$ memberHelper.renderMembers(doc) $} }) -
\ No newline at end of file + +{% endif %} \ No newline at end of file