Skip to content

docs: MatMenu api docs are not generated #16219

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
2 changes: 2 additions & 0 deletions src/material/menu/menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,7 @@ export class _MatMenuBase implements AfterContentInit, MatMenuPanel<MatMenuItem>
}
}

/** @docs-private We show the "_MatMenu" class as "MatMenu" in the docs. */
export class MatMenu extends _MatMenuBase {}

// Note on the weird inheritance setup: we need three classes, because the MDC-based menu has to
Expand All @@ -429,6 +430,7 @@ export class MatMenu extends _MatMenuBase {}
// * _MatMenu - the actual menu component implementation with the Angular metadata that should
// be tree shaken away for MDC.

/** @docs-public MatMenu */
@Component({
moduleId: module.id,
selector: 'mat-menu',
Expand Down
8 changes: 5 additions & 3 deletions tools/dgeni/bazel-bin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,11 @@ if (require.main === module) {
// Run the docs generation. The process will be automatically kept alive until Dgeni
// completed. In case the returned promise has been rejected, we need to manually exit the
// process with the proper exit code because Dgeni doesn't use native promises which would
// automatically cause the error to propagate. The error message will be automatically
// printed internally by Dgeni (so we don't want to repeat here)
new Dgeni([apiDocsPackage]).generate().catch(() => process.exit(1));
// automatically cause the error to propagate.
new Dgeni([apiDocsPackage]).generate().catch((e: any) => {
console.error(e);
process.exit(1);
});
}


Expand Down
2 changes: 1 addition & 1 deletion tools/dgeni/common/dgeni-definitions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export interface CategorizedClassDoc extends ClassExportDoc, CategorizedClassLik
directiveExportAs?: string | null;
directiveSelectors?: string[];
directiveMetadata: Map<string, any> | null;
extendedDoc: ClassLikeExportDoc | null;
extendedDoc: ClassLikeExportDoc | undefined;
}

/** Extended Dgeni property-member document that includes extracted Angular metadata. */
Expand Down
69 changes: 69 additions & 0 deletions tools/dgeni/common/private-docs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import {ApiDoc} from 'dgeni-packages/typescript/api-doc-types/ApiDoc';
import {MemberDoc} from 'dgeni-packages/typescript/api-doc-types/MemberDoc';

const INTERNAL_METHODS = [
// Lifecycle methods
'ngOnInit',
'ngOnChanges',
'ngDoCheck',
'ngAfterContentInit',
'ngAfterContentChecked',
'ngAfterViewInit',
'ngAfterViewChecked',
'ngOnDestroy',

// ControlValueAccessor methods
'writeValue',
'registerOnChange',
'registerOnTouched',
'setDisabledState',

// Don't ever need to document constructors
'constructor',

// tabIndex exists on all elements, no need to document it
'tabIndex',
];

/** Checks whether the given API document is public. */
export function isPublicDoc(doc: ApiDoc) {
if (_isEnforcedPublicDoc(doc)) {
return true;
}
if (_hasDocsPrivateTag(doc) || doc.name.startsWith('_')) {
return false;
} else if (doc instanceof MemberDoc) {
return !_isInternalMember(doc);
}
return true;
}

/** Gets the @docs-public tag from the given document if present. */
export function getDocsPublicTag(doc: any): {tagName: string, description: string}|undefined {
const tags = doc.tags && doc.tags.tags;
return tags ? tags.find((d: any) => d.tagName == 'docs-public') : undefined;
}

/** Whether the given method member is listed as an internal member. */
function _isInternalMember(memberDoc: MemberDoc) {
return INTERNAL_METHODS.includes(memberDoc.name);
}

/** Whether the given doc has a @docs-private tag set. */
function _hasDocsPrivateTag(doc: any) {
const tags = doc.tags && doc.tags.tags;
return tags ? tags.find((d: any) => d.tagName == 'docs-private') : false;
}

/**
* Whether the given doc has the @docs-public tag specified and should be enforced as
* public document. This allows symbols which are usually private to show up in the docs.
*
* Additionally symbols with "@docs-public" tag can specify a public name under which the
* document should show up in the docs. This is useful for cases where a class needs to be
* split up into several base classes to support the MDC prototypes. e.g. "_MatMenu" should
* show up in the docs as "MatMenu".
*/
function _isEnforcedPublicDoc(doc: any): boolean {
return getDocsPublicTag(doc) !== undefined;
}
2 changes: 1 addition & 1 deletion tools/dgeni/docs-package.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ apiDocsPackage.config(function(computePathsProcessor: any) {
apiDocsPackage.config(function(parseTagsProcessor: any) {
parseTagsProcessor.tagDefinitions = parseTagsProcessor.tagDefinitions.concat([
{name: 'docs-private'},
{name: 'docs-public'},
{name: 'breaking-change'},
]);
});
Expand All @@ -89,7 +90,6 @@ apiDocsPackage.config(function(checkAnchorLinksProcessor: any) {

// Configure the processor for understanding TypeScript.
apiDocsPackage.config(function(readTypeScriptModules: ReadTypeScriptModules) {
readTypeScriptModules.ignoreExportsMatching = [/^_/];
readTypeScriptModules.hidePrivateMembers = true;
});

Expand Down
44 changes: 23 additions & 21 deletions tools/dgeni/processors/categorizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
} from '../common/dgeni-definitions';
import {getDirectiveMetadata} from '../common/directive-metadata';
import {normalizeFunctionParameters} from '../common/normalize-function-parameters';
import {isPublicDoc} from '../common/private-docs';
import {getInputBindingData, getOutputBindingData} from '../common/property-bindings';
import {sortCategorizedMethodMembers, sortCategorizedPropertyMembers} from '../common/sort-members';

Expand All @@ -37,21 +38,16 @@ export class Categorizer implements Processor {
$runBefore = ['docs-processed'];

$process(docs: DocCollection) {
docs
.filter(doc => doc.docType === 'class' || doc.docType === 'interface')
.forEach(doc => this._decorateClassLikeDoc(doc));
docs.filter(doc => doc.docType === 'class' || doc.docType === 'interface')
.forEach(doc => this._decorateClassLikeDoc(doc));

docs
.filter(doc => doc.docType === 'function')
.forEach(doc => this._decorateFunctionExportDoc(doc));
docs.filter(doc => doc.docType === 'function')
.forEach(doc => this._decorateFunctionExportDoc(doc));

docs
.filter(doc => doc.docType === 'const')
.forEach(doc => this._decorateConstExportDoc(doc));
docs.filter(doc => doc.docType === 'const').forEach(doc => this._decorateConstExportDoc(doc));

docs
.filter(doc => doc.docType === 'type-alias')
.forEach(doc => this._decorateTypeAliasExportDoc(doc));
docs.filter(doc => doc.docType === 'type-alias')
.forEach(doc => this._decorateTypeAliasExportDoc(doc));
}

/**
Expand All @@ -60,13 +56,12 @@ export class Categorizer implements Processor {
*/
private _decorateClassLikeDoc(classLikeDoc: CategorizedClassLikeDoc) {
// Resolve all methods and properties from the classDoc.
classLikeDoc.methods = classLikeDoc.members
.filter(isMethod)
.filter(filterDuplicateMembers) as CategorizedMethodMemberDoc[];
classLikeDoc.methods = classLikeDoc.members.filter(isMethod).filter(filterDuplicateMembers) as
CategorizedMethodMemberDoc[];

classLikeDoc.properties = classLikeDoc.members
.filter(isProperty)
.filter(filterDuplicateMembers) as CategorizedPropertyMemberDoc[];
classLikeDoc.properties =
classLikeDoc.members.filter(isProperty).filter(filterDuplicateMembers) as
CategorizedPropertyMemberDoc[];

// Special decorations for real class documents that don't apply for interfaces.
if (classLikeDoc.docType === 'class') {
Expand Down Expand Up @@ -94,9 +89,16 @@ export class Categorizer implements Processor {
// Classes can only extend a single class. This means that there can't be multiple extend
// clauses for the Dgeni document. To make the template syntax simpler and more readable,
// store the extended class in a variable.
classDoc.extendedDoc = classDoc.extendsClauses[0] ? classDoc.extendsClauses[0].doc! : null;
classDoc.extendedDoc = classDoc.extendsClauses[0] ? classDoc.extendsClauses[0].doc! : undefined;
classDoc.directiveMetadata = getDirectiveMetadata(classDoc);

// In case the extended document is not public, we don't want to print it in the
// rendered class API doc. This causes confusion and also is not helpful as the
// extended document is not part of the docs and cannot be viewed.
if (classDoc.extendedDoc !== undefined && !isPublicDoc(classDoc.extendedDoc)) {
classDoc.extendedDoc = undefined;
}

// Categorize the current visited classDoc into its Angular type.
if (isDirective(classDoc) && classDoc.directiveMetadata) {
classDoc.isDirective = true;
Expand Down Expand Up @@ -151,7 +153,8 @@ export class Categorizer implements Processor {
decorateDeprecatedDoc(propertyDoc);

const metadata = propertyDoc.containerDoc.docType === 'class' ?
(propertyDoc.containerDoc as CategorizedClassDoc).directiveMetadata : null;
(propertyDoc.containerDoc as CategorizedClassDoc).directiveMetadata :
null;

const inputMetadata = metadata ? getInputBindingData(propertyDoc, metadata) : null;
const outputMetadata = metadata ? getOutputBindingData(propertyDoc, metadata) : null;
Expand All @@ -172,7 +175,6 @@ export class Categorizer implements Processor {

classDoc.methods.forEach((methodDoc, index) => {
if (methodDoc.overloads.length > 0) {

// Add each method overload to the methods that will be shown in the docs.
// Note that we cannot add the overloads immediately to the methods array because
// that would cause the iteration to visit the new overloads.
Expand Down
72 changes: 22 additions & 50 deletions tools/dgeni/processors/docs-private-filter.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,7 @@
import {DocCollection, Processor} from 'dgeni';
import {ApiDoc} from 'dgeni-packages/typescript/api-doc-types/ApiDoc';
import {BaseApiDoc} from 'dgeni-packages/typescript/api-doc-types/ApiDoc';
import {ClassExportDoc} from 'dgeni-packages/typescript/api-doc-types/ClassExportDoc';
import {MemberDoc} from 'dgeni-packages/typescript/api-doc-types/MemberDoc';

const INTERNAL_METHODS = [
// Lifecycle methods
'ngOnInit',
'ngOnChanges',
'ngDoCheck',
'ngAfterContentInit',
'ngAfterContentChecked',
'ngAfterViewInit',
'ngAfterViewChecked',
'ngOnDestroy',

// ControlValueAccessor methods
'writeValue',
'registerOnChange',
'registerOnTouched',
'setDisabledState',

// Don't ever need to document constructors
'constructor',

// tabIndex exists on all elements, no need to document it
'tabIndex',
];
import {getDocsPublicTag, isPublicDoc} from '../common/private-docs';

/**
* Processor to filter out symbols that should not be shown in the Material docs.
Expand All @@ -35,29 +11,25 @@ export class DocsPrivateFilter implements Processor {
$runBefore = ['categorizer'];

$process(docs: DocCollection) {
return docs.filter(doc => this._isPublicDoc(doc));
}

/** Marks the given API doc with a property that describes its public state. */
private _isPublicDoc(doc: ApiDoc) {
if (this._hasDocsPrivateTag(doc) || doc.name.startsWith('_')) {
return false;
} else if (doc instanceof MemberDoc) {
return !this._isInternalMember(doc);
} else if (doc instanceof ClassExportDoc) {
doc.members = doc.members.filter(memberDoc => this._isPublicDoc(memberDoc));
}
return true;
}

/** Whether the given method member is listed as an internal member. */
private _isInternalMember(memberDoc: MemberDoc) {
return INTERNAL_METHODS.includes(memberDoc.name);
}

/** Whether the given doc has a @docs-private tag set. */
private _hasDocsPrivateTag(doc: any) {
const tags = doc.tags && doc.tags.tags;
return tags ? tags.find((d: any) => d.tagName == 'docs-private') : false;
return docs.filter(doc => {
const isPublic = isPublicDoc(doc);

// Update the API document name in case the "@docs-public" tag is used
// with an alias name.
if (isPublic && doc instanceof BaseApiDoc) {
const docsPublicTag = getDocsPublicTag(doc);
if (docsPublicTag !== undefined && docsPublicTag.description) {
doc.name = docsPublicTag.description;
}
}

// Filter out private class members which could be annotated
// with the "@docs-private" tag.
if (isPublic && doc instanceof ClassExportDoc) {
doc.members = doc.members.filter(memberDoc => isPublicDoc(memberDoc));
}

return isPublic;
});
}
}