Skip to content

Commit 5c07322

Browse files
committed
build: show inherited members in dgeni
* Introduced a new Dgeni processor that will take care of the inherited docs for TypeScript classes. * Cleaned up the categorizer processor and made it compatible with the `inherited-docs` processor. * Fixes that the `docs-private-filter` processor doesn't filter the referenced members in the class docs. This basically allows us to extend other classes in our components like in #3036
1 parent cdb3763 commit 5c07322

File tree

4 files changed

+122
-40
lines changed

4 files changed

+122
-40
lines changed

tools/dgeni/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ const dgeniPackageDeps = [
3535

3636
let apiDocsPackage = new DgeniPackage('material2-api-docs', dgeniPackageDeps)
3737

38+
.processor(require('./processors/link-inherited-docs'))
39+
3840
// Processor that filters out symbols that should not be shown in the docs.
3941
.processor(require('./processors/docs-private-filter'))
4042

tools/dgeni/processors/categorizer.js

Lines changed: 59 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -9,46 +9,69 @@
99
module.exports = function categorizer() {
1010
return {
1111
$runBefore: ['docs-processed'],
12-
$process: function(docs) {
13-
docs.forEach(doc => {
14-
// The typescriptPackage groups both methods and parameters into "members".
15-
// Use the presence of `parameters` as a proxy to determine if this is a method.
16-
if (doc.classDoc && doc.hasOwnProperty('parameters')) {
17-
doc.isMethod = true;
18-
19-
// Mark methods with a `void` return type so we can omit show the return type in the docs.
20-
doc.showReturns = doc.returnType && doc.returnType != 'void';
21-
22-
normalizeMethodParameters(doc);
23-
24-
// Maintain a list of methods on the associated class so we can
25-
// iterate through them while rendering.
26-
doc.classDoc.methods ?
27-
doc.classDoc.methods.push(doc) :
28-
doc.classDoc.methods = [doc];
29-
} else if (isDirective(doc)) {
30-
doc.isDirective = true;
31-
doc.directiveExportAs = getDirectiveExportAs(doc);
32-
} else if (isService(doc)) {
33-
doc.isService = true;
34-
} else if (isNgModule(doc)) {
35-
doc.isNgModule = true;
36-
} else if (doc.docType == 'member') {
37-
doc.isDirectiveInput = isDirectiveInput(doc);
38-
doc.directiveInputAlias = getDirectiveInputAlias(doc);
39-
40-
doc.isDirectiveOutput = isDirectiveOutput(doc);
41-
doc.directiveOutputAlias = getDirectiveOutputAlias(doc);
42-
43-
doc.classDoc.properties ?
44-
doc.classDoc.properties.push(doc) :
45-
doc.classDoc.properties = [doc];
46-
}
47-
});
12+
$process: function (docs) {
13+
docs.filter(doc => doc.docType === 'class').forEach(doc => visitClassDoc(doc));
4814
}
4915
};
16+
17+
function visitClassDoc(classDoc) {
18+
// Resolve all methods and properties from the classDoc. Includes inherited docs.
19+
classDoc.methods = resolveMethods(classDoc);
20+
classDoc.properties = resolveProperties(classDoc);
21+
22+
// Call visit hooks that can modify the method and property docs.
23+
classDoc.methods.forEach(doc => visitMethodDoc(doc));
24+
classDoc.properties.forEach(doc => visitPropertyDoc(doc));
25+
26+
// Categorize the current visited classDoc into its Angular type.
27+
if (isDirective(classDoc)) {
28+
classDoc.isDirective = true;
29+
classDoc.directiveExportAs = getDirectiveExportAs(classDoc);
30+
} else if (isService(classDoc)) {
31+
classDoc.isService = true;
32+
} else if (isNgModule(classDoc)) {
33+
classDoc.isNgModule = true;
34+
}
35+
}
36+
37+
function visitMethodDoc(methodDoc) {
38+
normalizeMethodParameters(methodDoc);
39+
40+
// Mark methods with a `void` return type so we can omit show the return type in the docs.
41+
methodDoc.showReturns = methodDoc.returnType && methodDoc.returnType != 'void';
42+
}
43+
44+
function visitPropertyDoc(propertyDoc) {
45+
propertyDoc.isDirectiveInput = isDirectiveInput(propertyDoc);
46+
propertyDoc.directiveInputAlias = getDirectiveInputAlias(propertyDoc);
47+
48+
propertyDoc.isDirectiveOutput = isDirectiveOutput(propertyDoc);
49+
propertyDoc.directiveOutputAlias = getDirectiveOutputAlias(propertyDoc);
50+
}
5051
};
5152

53+
/** Function that walks through all inherited docs and collects public methods. */
54+
function resolveMethods(classDoc) {
55+
let methods = classDoc.members.filter(member => member.hasOwnProperty('parameters'));
56+
57+
if (classDoc.inheritedDoc) {
58+
methods = methods.concat(resolveMethods(classDoc.inheritedDoc));
59+
}
60+
61+
return methods;
62+
}
63+
64+
/** Function that walks through all inherited docs and collects public properties. */
65+
function resolveProperties(classDoc) {
66+
let properties = classDoc.members.filter(member => !member.hasOwnProperty('parameters'));
67+
68+
if (classDoc.inheritedDoc) {
69+
properties = properties.concat(resolveProperties(classDoc.inheritedDoc));
70+
}
71+
72+
return properties;
73+
}
74+
5275

5376
/**
5477
* The `parameters` property are the parameters extracted from TypeScript and are strings

tools/dgeni/processors/docs-private-filter.js

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,25 @@ const INTERNAL_METHODS = [
2828

2929
module.exports = function docsPrivateFilter() {
3030
return {
31-
$runBefore: ['docs-processed'],
32-
$process: function(docs) {
33-
return docs.filter(d => !(hasDocsPrivateTag(d) || INTERNAL_METHODS.includes(d.name)));
34-
}
31+
$runBefore: ['categorizer'],
32+
$process: docs => docs.filter(doc => validateDocEntry(doc))
3533
};
3634
};
3735

36+
function validateDocEntry(doc) {
37+
if (doc.docType === 'member') {
38+
return validateMemberDoc(doc);
39+
} else if (doc.docType === 'class') {
40+
doc.members = doc.members.filter(memberDoc => validateMemberDoc(memberDoc));
41+
}
42+
43+
return true;
44+
}
45+
46+
function validateMemberDoc(memberDoc) {
47+
return !(hasDocsPrivateTag(memberDoc) || INTERNAL_METHODS.includes(memberDoc.name))
48+
}
49+
3850
function hasDocsPrivateTag(doc) {
3951
let tags = doc.tags && doc.tags.tags;
4052
return tags ? tags.find(d => d.tagName == 'docs-private') : false;
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
const ts = require('typescript');
2+
3+
module.exports = function linkInheritedDocs(readTypeScriptModules, tsParser) {
4+
5+
let checker = null;
6+
7+
return {
8+
$runAfter: ['readTypeScriptModules'],
9+
$runBefore: ['categorizer'],
10+
$process: processDocs,
11+
};
12+
13+
function processDocs(docs) {
14+
// Use the configuration from the `readTypeScriptModules` processor.
15+
let {sourceFiles, basePath} = readTypeScriptModules;
16+
17+
// To be able to map the TypeScript Nodes to the according symbols we need to use the same
18+
// TypeScript configuration as in the `readTypeScriptModules` processor.
19+
checker = tsParser.parse(sourceFiles, basePath).typeChecker;
20+
21+
// Iterate through all class docs and resolve the inherited docs.
22+
docs.filter(doc => doc.docType === 'class').forEach(classDoc => visitClassDoc(classDoc, docs));
23+
}
24+
25+
function visitClassDoc(classDoc, docs) {
26+
let inheritedType = resolveInheritedType(classDoc.exportSymbol);
27+
let inheritedSymbol = inheritedType && inheritedType.symbol;
28+
29+
if (inheritedSymbol) {
30+
classDoc.inheritedSymbol = inheritedSymbol;
31+
classDoc.inheritedDoc = docs.find(doc => doc.exportSymbol === inheritedSymbol);
32+
}
33+
}
34+
35+
function resolveInheritedType(classSymbol) {
36+
if (classSymbol.flags & ~ts.SymbolFlags.Class) {
37+
return;
38+
}
39+
40+
let declaration = classSymbol.valueDeclaration || classSymbol.declarations[0];
41+
let typeExpression = ts.getClassExtendsHeritageClauseElement(declaration);
42+
43+
return typeExpression ? checker.getTypeAtLocation(typeExpression) : null;
44+
}
45+
};

0 commit comments

Comments
 (0)