Skip to content

Commit 68e5566

Browse files
authored
docs: fix inherited docs-private members showing up (#22890)
We recently landed a couple of improvements in the Dgeni pipeline, so that descriptions of inherited members show up. To achieve this, we applied a workaround that basically meant we are parsing the API doc's` string cotntent manually to a JSDoc description (this also involved manually stripping any JSDoc annotations). This commit replaces the workaround with a solution that allows us to still run necessary default Dgeni processors on the inherited API docs, such as the offical `jsdoc` processors. These solve the problem of manually extracting the JSDOc description, and also fix more down-the-line problems where e.g. `@docs-private` is not respected for inherited members. It also fixes cases where the JSDOc annotations are still showing up in the docs.
1 parent 29c631e commit 68e5566

File tree

6 files changed

+116
-14
lines changed

6 files changed

+116
-14
lines changed

tools/dgeni/common/class-inheritance.ts

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,27 @@
11
// tslint:disable:no-bitwise
22

3+
import {ApiDoc} from 'dgeni-packages/typescript/api-doc-types/ApiDoc';
34
import {ClassExportDoc} from 'dgeni-packages/typescript/api-doc-types/ClassExportDoc';
45
import {ClassLikeExportDoc} from 'dgeni-packages/typescript/api-doc-types/ClassLikeExportDoc';
56
import {InterfaceExportDoc} from 'dgeni-packages/typescript/api-doc-types/InterfaceExportDoc';
67
import * as ts from 'typescript';
8+
import {MemberDoc} from 'dgeni-packages/typescript/api-doc-types/MemberDoc';
9+
10+
/** Type describing class like documents which have been created through inheritance. */
11+
export type InheritanceCreatedClassLikeDoc = ClassLikeExportDoc &
12+
{_inheritanceCreated?: true};
13+
14+
/** Whether the given API doc has been created through inheritance. */
15+
export function isInheritanceCreatedDoc(doc: ApiDoc): doc is ClassLikeExportDoc {
16+
// For member docs, we look if the containing API doc has been created through
17+
// inheritance.
18+
if (doc instanceof MemberDoc) {
19+
return isInheritanceCreatedDoc(doc.containerDoc);
20+
}
21+
22+
return doc instanceof ClassLikeExportDoc &&
23+
(doc as InheritanceCreatedClassLikeDoc)._inheritanceCreated === true;
24+
}
725

826
/** Gets all class like export documents which the given doc inherits from. */
927
export function getInheritedDocsOfClass(
@@ -63,15 +81,18 @@ function getClassLikeDocsFromType(
6381
if (exportSymbolsToDocsMap.has(symbol)) {
6482
return [exportSymbolsToDocsMap.get(symbol)!];
6583
}
66-
let createdDoc: ClassLikeExportDoc|null = null;
84+
let createdDoc: InheritanceCreatedClassLikeDoc|null = null;
6785
if ((symbol.flags & ts.SymbolFlags.Class) !== 0) {
6886
createdDoc = new ClassExportDoc(baseDoc.host, baseDoc.moduleDoc, symbol, aliasSymbol);
6987
} else if ((symbol.flags & ts.SymbolFlags.Interface) !== 0) {
7088
createdDoc = new InterfaceExportDoc(baseDoc.host, baseDoc.moduleDoc, symbol, aliasSymbol);
7189
}
72-
// If a new document has been created, add it to the shared symbol
73-
// docs map and return it.
90+
7491
if (createdDoc) {
92+
// Mark the created document. This allows us to distinguish between documents which
93+
// have been resolved by Dgeni automatically, and docs which are manually resolved.
94+
createdDoc._inheritanceCreated = true;
95+
// If a new document has been created, add it to the shared symbol.
7596
exportSymbolsToDocsMap.set(aliasSymbol || symbol, createdDoc);
7697
return [createdDoc];
7798
}

tools/dgeni/common/private-docs.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {ApiDoc} from 'dgeni-packages/typescript/api-doc-types/ApiDoc';
22
import {MemberDoc} from 'dgeni-packages/typescript/api-doc-types/MemberDoc';
3+
import {isInheritanceCreatedDoc} from './class-inheritance';
34

45
const INTERNAL_METHODS = [
56
// Lifecycle methods
@@ -27,9 +28,17 @@ const INTERNAL_METHODS = [
2728

2829
/** Checks whether the given API document is public. */
2930
export function isPublicDoc(doc: ApiDoc) {
31+
// Always skip documents which have been created through inheritance. These docs are
32+
// not exported as they have not been resolved by Dgeni through a module entry-point.
33+
// The `@docs-public` tag is only applicable if a symbol is at least exported.
34+
if (isInheritanceCreatedDoc(doc)) {
35+
return false;
36+
}
37+
3038
if (_isEnforcedPublicDoc(doc)) {
3139
return true;
3240
}
41+
3342
if (_hasDocsPrivateTag(doc) || doc.name.startsWith('_') ||
3443
doc.name.startsWith('ngAcceptInputType_')) {
3544
return false;

tools/dgeni/docs-package.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {DocsPrivateFilter} from './processors/docs-private-filter';
1010
import {EntryPointGrouper} from './processors/entry-point-grouper';
1111
import {FilterDuplicateExports} from './processors/filter-duplicate-exports';
1212
import {mergeInheritedProperties} from './processors/merge-inherited-properties';
13+
import {resolveInheritedDocs} from './processors/resolve-inherited-docs';
1314

1415
// Dgeni packages that the Material docs package depends on.
1516
const jsdocPackage = require('dgeni-packages/jsdoc');
@@ -35,6 +36,11 @@ export const apiDocsPackage = new Package('material2-api-docs', [
3536
typescriptPackage,
3637
]);
3738

39+
// Processor that resolves inherited docs of class docs. The resolved docs will
40+
// be added to the pipeline so that the JSDoc processors can capture these too.
41+
// Note: needs to use a factory function since the processor relies on DI.
42+
apiDocsPackage.processor(resolveInheritedDocs);
43+
3844
// Processor that filters out duplicate exports that should not be shown in the docs.
3945
apiDocsPackage.processor(new FilterDuplicateExports());
4046

tools/dgeni/processors/entry-point-grouper.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ export class EntryPointDoc {
9191
export class EntryPointGrouper implements Processor {
9292
name = 'entryPointGrouper';
9393
$runBefore = ['docs-processed'];
94+
$runAfter = ['docs-private-filter'];
9495
entryPoints: string[] = [];
9596

9697
$process(docs: DocCollection) {

tools/dgeni/processors/merge-inherited-properties.ts

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -53,19 +53,8 @@ export class MergeInheritedProperties implements Processor {
5353
// tslint:disable-next-line:ban Need to use Object.assign to preserve the prototype.
5454
const newMemberDoc = Object.assign(Object.create(memberDoc), memberDoc);
5555

56-
// Dgeni won't add the `description` if the member doc belongs to a class
57-
// that isn't exported. If that's the case, we fall back to assigning it
58-
// ourselves by stripping JSDoc tags from the raw description.
59-
// TODO: figure out a more robust solution that will ensure that the description is
60-
// always added.
61-
newMemberDoc.description = newMemberDoc.description || stripJsDocTags(memberDoc.content);
6256
newMemberDoc.containerDoc = destination;
6357
destination.members.push(newMemberDoc);
6458
}
6559
}
6660
}
67-
68-
/** Strips all of the content after the first JSDoc tag from a string. */
69-
function stripJsDocTags(text: string): string {
70-
return text.split(/\s@[a-zA-Z-]*\s/)[0];
71-
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import {DocCollection, Document, Processor} from 'dgeni';
2+
import {ClassLikeExportDoc} from 'dgeni-packages/typescript/api-doc-types/ClassLikeExportDoc';
3+
import * as ts from 'typescript';
4+
import {getInheritedDocsOfClass, isInheritanceCreatedDoc} from '../common/class-inheritance';
5+
import {ClassExportDoc} from 'dgeni-packages/typescript/api-doc-types/ClassExportDoc';
6+
import {ApiDoc} from 'dgeni-packages/typescript/api-doc-types/ApiDoc';
7+
8+
/**
9+
* Factory function for the "ResolvedInheritedDocs" processor. Dgeni does not support
10+
* dependency injection for classes. The symbol docs map is provided by the TypeScript
11+
* dgeni package.
12+
*/
13+
export function resolveInheritedDocs(
14+
exportSymbolsToDocsMap: Map<ts.Symbol, ClassLikeExportDoc>) {
15+
return new ResolveInheritedDocs(exportSymbolsToDocsMap);
16+
}
17+
18+
/**
19+
* Processor that resolves inherited API docs from class API documents. The resolved
20+
* API documents will be added to the Dgeni document collection so that they can be
21+
* processed by other standard processors in the Dgeni pipeline. This is helpful as
22+
* API documents for inheritance are created manually if not exported, and we'd want
23+
* such docs to be processed by the Dgeni JSDoc processor for example.
24+
*/
25+
export class ResolveInheritedDocs implements Processor {
26+
$runBefore = ['docs-private-filter', 'parsing-tags'];
27+
28+
constructor(
29+
/** Shared map that can be used to resolve docs through symbols. */
30+
private _exportSymbolsToDocsMap: Map<ts.Symbol, ClassLikeExportDoc>) {}
31+
32+
$process(docs: DocCollection) {
33+
const newDocs = new Set<Document>(docs);
34+
35+
docs.forEach(doc => {
36+
if (doc.docType !== 'class') {
37+
return;
38+
}
39+
40+
getInheritedDocsOfClass(doc, this._exportSymbolsToDocsMap).forEach(apiDoc => {
41+
// If the API document has not been resolved through inheritance, then it is already
42+
// part of the Dgeni doc collection. i.e. The doc already been resolved through Dgeni
43+
// itself (which happens if the doc is exported through an entry-point).
44+
if (!isInheritanceCreatedDoc(apiDoc)) {
45+
return;
46+
}
47+
// Add the member docs for the inherited doc to the Dgeni doc collection.
48+
this._getContainingMemberDocs(apiDoc).forEach(d => newDocs.add(d));
49+
// Add the class-like export doc to the Dgeni doc collection.
50+
newDocs.add(apiDoc);
51+
});
52+
});
53+
54+
return Array.from(newDocs);
55+
}
56+
57+
/**
58+
* Gets the nested API documents of the given class-like API document. This
59+
* follows the logic as per:
60+
* dgeni-packages/blob/master/typescript/src/processors/readTypeScriptModules/index.ts#L110-L121
61+
*/
62+
private _getContainingMemberDocs(doc: ClassLikeExportDoc): ApiDoc[] {
63+
const nestedDocs: ApiDoc[] = [...doc.members];
64+
65+
// For classes, also add the static member docs and optional constructor doc.
66+
if (doc instanceof ClassExportDoc) {
67+
nestedDocs.push(...doc.statics);
68+
69+
if (doc.constructorDoc !== undefined) {
70+
nestedDocs.push(doc.constructorDoc);
71+
}
72+
}
73+
74+
return nestedDocs;
75+
}
76+
}

0 commit comments

Comments
 (0)