Skip to content

docs: fix inherited docs-private members showing up #22890

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 24 additions & 3 deletions tools/dgeni/common/class-inheritance.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,27 @@
// tslint:disable:no-bitwise

import {ApiDoc} from 'dgeni-packages/typescript/api-doc-types/ApiDoc';
import {ClassExportDoc} from 'dgeni-packages/typescript/api-doc-types/ClassExportDoc';
import {ClassLikeExportDoc} from 'dgeni-packages/typescript/api-doc-types/ClassLikeExportDoc';
import {InterfaceExportDoc} from 'dgeni-packages/typescript/api-doc-types/InterfaceExportDoc';
import * as ts from 'typescript';
import {MemberDoc} from 'dgeni-packages/typescript/api-doc-types/MemberDoc';

/** Type describing class like documents which have been created through inheritance. */
export type InheritanceCreatedClassLikeDoc = ClassLikeExportDoc &
{_inheritanceCreated?: true};

/** Whether the given API doc has been created through inheritance. */
export function isInheritanceCreatedDoc(doc: ApiDoc): doc is ClassLikeExportDoc {
// For member docs, we look if the containing API doc has been created through
// inheritance.
if (doc instanceof MemberDoc) {
return isInheritanceCreatedDoc(doc.containerDoc);
}

return doc instanceof ClassLikeExportDoc &&
(doc as InheritanceCreatedClassLikeDoc)._inheritanceCreated === true;
}

/** Gets all class like export documents which the given doc inherits from. */
export function getInheritedDocsOfClass(
Expand Down Expand Up @@ -63,15 +81,18 @@ function getClassLikeDocsFromType(
if (exportSymbolsToDocsMap.has(symbol)) {
return [exportSymbolsToDocsMap.get(symbol)!];
}
let createdDoc: ClassLikeExportDoc|null = null;
let createdDoc: InheritanceCreatedClassLikeDoc|null = null;
if ((symbol.flags & ts.SymbolFlags.Class) !== 0) {
createdDoc = new ClassExportDoc(baseDoc.host, baseDoc.moduleDoc, symbol, aliasSymbol);
} else if ((symbol.flags & ts.SymbolFlags.Interface) !== 0) {
createdDoc = new InterfaceExportDoc(baseDoc.host, baseDoc.moduleDoc, symbol, aliasSymbol);
}
// If a new document has been created, add it to the shared symbol
// docs map and return it.

if (createdDoc) {
// Mark the created document. This allows us to distinguish between documents which
// have been resolved by Dgeni automatically, and docs which are manually resolved.
createdDoc._inheritanceCreated = true;
// If a new document has been created, add it to the shared symbol.
exportSymbolsToDocsMap.set(aliasSymbol || symbol, createdDoc);
return [createdDoc];
}
Expand Down
9 changes: 9 additions & 0 deletions tools/dgeni/common/private-docs.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {ApiDoc} from 'dgeni-packages/typescript/api-doc-types/ApiDoc';
import {MemberDoc} from 'dgeni-packages/typescript/api-doc-types/MemberDoc';
import {isInheritanceCreatedDoc} from './class-inheritance';

const INTERNAL_METHODS = [
// Lifecycle methods
Expand Down Expand Up @@ -27,9 +28,17 @@ const INTERNAL_METHODS = [

/** Checks whether the given API document is public. */
export function isPublicDoc(doc: ApiDoc) {
// Always skip documents which have been created through inheritance. These docs are
// not exported as they have not been resolved by Dgeni through a module entry-point.
// The `@docs-public` tag is only applicable if a symbol is at least exported.
if (isInheritanceCreatedDoc(doc)) {
return false;
}

if (_isEnforcedPublicDoc(doc)) {
return true;
}

if (_hasDocsPrivateTag(doc) || doc.name.startsWith('_') ||
doc.name.startsWith('ngAcceptInputType_')) {
return false;
Expand Down
6 changes: 6 additions & 0 deletions tools/dgeni/docs-package.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {DocsPrivateFilter} from './processors/docs-private-filter';
import {EntryPointGrouper} from './processors/entry-point-grouper';
import {FilterDuplicateExports} from './processors/filter-duplicate-exports';
import {mergeInheritedProperties} from './processors/merge-inherited-properties';
import {resolveInheritedDocs} from './processors/resolve-inherited-docs';

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

// Processor that resolves inherited docs of class docs. The resolved docs will
// be added to the pipeline so that the JSDoc processors can capture these too.
// Note: needs to use a factory function since the processor relies on DI.
apiDocsPackage.processor(resolveInheritedDocs);

// Processor that filters out duplicate exports that should not be shown in the docs.
apiDocsPackage.processor(new FilterDuplicateExports());

Expand Down
1 change: 1 addition & 0 deletions tools/dgeni/processors/entry-point-grouper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ export class EntryPointDoc {
export class EntryPointGrouper implements Processor {
name = 'entryPointGrouper';
$runBefore = ['docs-processed'];
$runAfter = ['docs-private-filter'];
entryPoints: string[] = [];

$process(docs: DocCollection) {
Expand Down
11 changes: 0 additions & 11 deletions tools/dgeni/processors/merge-inherited-properties.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,19 +53,8 @@ export class MergeInheritedProperties implements Processor {
// tslint:disable-next-line:ban Need to use Object.assign to preserve the prototype.
const newMemberDoc = Object.assign(Object.create(memberDoc), memberDoc);

// Dgeni won't add the `description` if the member doc belongs to a class
// that isn't exported. If that's the case, we fall back to assigning it
// ourselves by stripping JSDoc tags from the raw description.
// TODO: figure out a more robust solution that will ensure that the description is
// always added.
newMemberDoc.description = newMemberDoc.description || stripJsDocTags(memberDoc.content);
newMemberDoc.containerDoc = destination;
destination.members.push(newMemberDoc);
}
}
}

/** Strips all of the content after the first JSDoc tag from a string. */
function stripJsDocTags(text: string): string {
return text.split(/\s@[a-zA-Z-]*\s/)[0];
}
76 changes: 76 additions & 0 deletions tools/dgeni/processors/resolve-inherited-docs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import {DocCollection, Document, Processor} from 'dgeni';
import {ClassLikeExportDoc} from 'dgeni-packages/typescript/api-doc-types/ClassLikeExportDoc';
import * as ts from 'typescript';
import {getInheritedDocsOfClass, isInheritanceCreatedDoc} from '../common/class-inheritance';
import {ClassExportDoc} from 'dgeni-packages/typescript/api-doc-types/ClassExportDoc';
import {ApiDoc} from 'dgeni-packages/typescript/api-doc-types/ApiDoc';

/**
* Factory function for the "ResolvedInheritedDocs" processor. Dgeni does not support
* dependency injection for classes. The symbol docs map is provided by the TypeScript
* dgeni package.
*/
export function resolveInheritedDocs(
exportSymbolsToDocsMap: Map<ts.Symbol, ClassLikeExportDoc>) {
return new ResolveInheritedDocs(exportSymbolsToDocsMap);
}

/**
* Processor that resolves inherited API docs from class API documents. The resolved
* API documents will be added to the Dgeni document collection so that they can be
* processed by other standard processors in the Dgeni pipeline. This is helpful as
* API documents for inheritance are created manually if not exported, and we'd want
* such docs to be processed by the Dgeni JSDoc processor for example.
*/
export class ResolveInheritedDocs implements Processor {
$runBefore = ['docs-private-filter', 'parsing-tags'];

constructor(
/** Shared map that can be used to resolve docs through symbols. */
private _exportSymbolsToDocsMap: Map<ts.Symbol, ClassLikeExportDoc>) {}

$process(docs: DocCollection) {
const newDocs = new Set<Document>(docs);

docs.forEach(doc => {
if (doc.docType !== 'class') {
return;
}

getInheritedDocsOfClass(doc, this._exportSymbolsToDocsMap).forEach(apiDoc => {
// If the API document has not been resolved through inheritance, then it is already
// part of the Dgeni doc collection. i.e. The doc already been resolved through Dgeni
// itself (which happens if the doc is exported through an entry-point).
if (!isInheritanceCreatedDoc(apiDoc)) {
return;
}
// Add the member docs for the inherited doc to the Dgeni doc collection.
this._getContainingMemberDocs(apiDoc).forEach(d => newDocs.add(d));
// Add the class-like export doc to the Dgeni doc collection.
newDocs.add(apiDoc);
});
});

return Array.from(newDocs);
}

/**
* Gets the nested API documents of the given class-like API document. This
* follows the logic as per:
* dgeni-packages/blob/master/typescript/src/processors/readTypeScriptModules/index.ts#L110-L121
*/
private _getContainingMemberDocs(doc: ClassLikeExportDoc): ApiDoc[] {
const nestedDocs: ApiDoc[] = [...doc.members];

// For classes, also add the static member docs and optional constructor doc.
if (doc instanceof ClassExportDoc) {
nestedDocs.push(...doc.statics);

if (doc.constructorDoc !== undefined) {
nestedDocs.push(doc.constructorDoc);
}
}

return nestedDocs;
}
}