build(aio): ensure decorators with shared interface types are found (#19361)
Closes #19358
This commit is contained in:
parent
0f5c70d563
commit
4ae546be1f
|
@ -48,9 +48,9 @@ module.exports = function mergeDecoratorDocs(log) {
|
||||||
$runAfter: ['processing-docs'],
|
$runAfter: ['processing-docs'],
|
||||||
$runBefore: ['docs-processed'],
|
$runBefore: ['docs-processed'],
|
||||||
makeDecoratorCalls: [
|
makeDecoratorCalls: [
|
||||||
{type: '', description: 'toplevel'},
|
{type: '', description: 'toplevel', functionName: 'makeDecorator'},
|
||||||
{type: 'Prop', description: 'property'},
|
{type: 'Prop', description: 'property', functionName: 'makePropDecorator'},
|
||||||
{type: 'Param', description: 'parameter'},
|
{type: 'Param', description: 'parameter', functionName: 'makeParamDecorator'},
|
||||||
],
|
],
|
||||||
$process: function(docs) {
|
$process: function(docs) {
|
||||||
|
|
||||||
|
@ -58,24 +58,25 @@ module.exports = function mergeDecoratorDocs(log) {
|
||||||
var docsToMerge = Object.create(null);
|
var docsToMerge = Object.create(null);
|
||||||
|
|
||||||
docs.forEach(function(doc) {
|
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<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<X>(...)`.
|
||||||
|
doc.decoratorType = initializer.arguments[0].text;
|
||||||
|
// clear the symbol type named since it is not needed
|
||||||
|
doc.symbolTypeName = undefined;
|
||||||
|
|
||||||
makeDecoratorCalls.forEach(function(call) {
|
// keep track of the names of the metadata interface that will need to be merged into this decorator doc
|
||||||
// find all the decorators, signified by a call to `make...Decorator<Decorator>(metadata)`
|
docsToMerge[doc.name + 'Decorator'] = doc;
|
||||||
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<X>(...)`.
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// merge the metadata docs into the decorator docs
|
// merge the metadata docs into the decorator docs
|
||||||
|
@ -106,27 +107,19 @@ module.exports = function mergeDecoratorDocs(log) {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
function getMakeDecoratorCall(doc, type) {
|
function getInitializer(doc) {
|
||||||
var makeDecoratorFnName = 'make' + (type || '') + 'Decorator';
|
var initializer = doc.symbol && doc.symbol.valueDeclaration && doc.symbol.valueDeclaration.initializer;
|
||||||
|
// There appear to be two forms of initializer:
|
||||||
var initializer = doc.declaration &&
|
// export var Injectable: InjectableFactory =
|
||||||
doc.declaration.initializer;
|
// <InjectableFactory>makeDecorator(InjectableMetadata);
|
||||||
|
// and
|
||||||
if (initializer) {
|
// export var RouteConfig: (configs: RouteDefinition[]) => ClassDecorator =
|
||||||
// There appear to be two forms of initializer:
|
// makeDecorator(RouteConfigAnnotation);
|
||||||
// export var Injectable: InjectableFactory =
|
// In the first case, the type assertion `<InjectableFactory>` causes the AST to contain an
|
||||||
// <InjectableFactory>makeDecorator(InjectableMetadata);
|
// extra level of expression
|
||||||
// and
|
// to hold the new type of the expression.
|
||||||
// export var RouteConfig: (configs: RouteDefinition[]) => ClassDecorator =
|
if (initializer && initializer.expression && initializer.expression.expression) {
|
||||||
// makeDecorator(RouteConfigAnnotation);
|
initializer = initializer.expression;
|
||||||
// In the first case, the type assertion `<InjectableFactory>` 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return initializer;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,11 +2,11 @@ var testPackage = require('../../helpers/test-package');
|
||||||
var Dgeni = require('dgeni');
|
var Dgeni = require('dgeni');
|
||||||
|
|
||||||
describe('mergeDecoratorDocs processor', () => {
|
describe('mergeDecoratorDocs processor', () => {
|
||||||
var dgeni, injector, processor, moduleDoc, decoratorDoc, metadataDoc, decoratorDocWithTypeAssertion, otherDoc;
|
let processor, moduleDoc, decoratorDoc, metadataDoc, otherDoc;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
dgeni = new Dgeni([testPackage('angular-api-package')]);
|
const dgeni = new Dgeni([testPackage('angular-api-package')]);
|
||||||
injector = dgeni.configureInjector();
|
const injector = dgeni.configureInjector();
|
||||||
processor = injector.get('mergeDecoratorDocs');
|
processor = injector.get('mergeDecoratorDocs');
|
||||||
|
|
||||||
moduleDoc = {};
|
moduleDoc = {};
|
||||||
|
@ -15,7 +15,9 @@ describe('mergeDecoratorDocs processor', () => {
|
||||||
name: 'Component',
|
name: 'Component',
|
||||||
docType: 'const',
|
docType: 'const',
|
||||||
description: 'A description of the metadata for the Component decorator',
|
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: [
|
members: [
|
||||||
{ name: 'templateUrl', description: 'A description of the templateUrl property' }
|
{ name: 'templateUrl', description: 'A description of the templateUrl property' }
|
||||||
],
|
],
|
||||||
|
@ -40,45 +42,45 @@ describe('mergeDecoratorDocs processor', () => {
|
||||||
moduleDoc
|
moduleDoc
|
||||||
};
|
};
|
||||||
|
|
||||||
decoratorDocWithTypeAssertion = {
|
|
||||||
name: 'Y',
|
|
||||||
docType: 'const',
|
|
||||||
declaration: { initializer: { expression: {type: {}, expression: {text: 'makeDecorator'}, arguments: [{text: 'Y'}]} } },
|
|
||||||
moduleDoc
|
|
||||||
};
|
|
||||||
otherDoc = {
|
otherDoc = {
|
||||||
name: 'Y',
|
name: 'Y',
|
||||||
docType: 'const',
|
docType: 'const',
|
||||||
declaration: {initializer: {expression: {text: 'otherCall'}, arguments: [{text: 'param1'}]}},
|
symbol: {
|
||||||
|
valueDeclaration: { initializer: { expression: { text: 'otherCall' }, arguments: [{ text: 'param1' }] } }
|
||||||
|
},
|
||||||
moduleDoc
|
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', () => {
|
it('should change the docType of only the docs that are initialized by a call to makeDecorator', () => {
|
||||||
processor.$process([decoratorDoc, metadataDoc, decoratorDocWithTypeAssertion, otherDoc]);
|
processor.$process([decoratorDoc, metadataDoc, otherDoc]);
|
||||||
expect(decoratorDoc.docType).toEqual('decorator');
|
expect(decoratorDoc.docType).toEqual('decorator');
|
||||||
expect(decoratorDocWithTypeAssertion.docType).toEqual('decorator');
|
|
||||||
expect(otherDoc.docType).toEqual('const');
|
expect(otherDoc.docType).toEqual('const');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should extract the "type" of the decorator meta data', () => {
|
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(decoratorDoc.decoratorType).toEqual('X');
|
||||||
expect(decoratorDocWithTypeAssertion.decoratorType).toEqual('Y');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should copy across properties from the call signature doc', () => {
|
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.description).toEqual('The actual description of the call signature');
|
||||||
expect(decoratorDoc.whatItDoes).toEqual('Does something cool...');
|
expect(decoratorDoc.whatItDoes).toEqual('Does something cool...');
|
||||||
expect(decoratorDoc.howToUse).toEqual('Use it like this...');
|
expect(decoratorDoc.howToUse).toEqual('Use it like this...');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should remove the metadataDoc from the module exports', () => {
|
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);
|
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');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
{% import "lib/memberHelpers.html" as memberHelper -%}
|
{% import "lib/memberHelpers.html" as memberHelper -%}
|
||||||
|
|
||||||
|
{% if doc.members.length %}
|
||||||
<section class="decorator-overview">
|
<section class="decorator-overview">
|
||||||
<h2>Metadata Overview</h2>
|
<h2>Metadata Overview</h2>
|
||||||
<code-example language="ts" hideCopy="true">
|
<code-example language="ts" hideCopy="true">
|
||||||
@{$ doc.name $}{$ doc.typeParams | escape $}({ {$ memberHelper.renderMembers(doc) $}
|
@{$ doc.name $}{$ doc.typeParams | escape $}({ {$ memberHelper.renderMembers(doc) $}
|
||||||
})
|
})
|
||||||
</code-example>
|
</code-example>
|
||||||
</section>
|
</section>
|
||||||
|
{% endif %}
|
Loading…
Reference in New Issue