mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
docs: MatMenu api docs are not generated (#16219)
Currently the `MatMenu` API docs are not generated because the `MatMenu` class is no longer treated as directive/component because there is no decorator w/ directive metadata. In order to fix this in a flexible way, a new JSdoc tag has been introduced that allows enforcing the public state of a symbol (with the possibility of having a public symbol name). This allows us to actually expose the real `_MatMenu` class w/ directive metadata in the API docs under the `MatMenu` symbol name. Fixes #16198
This commit is contained in:
committed by
Joey Perrott
parent
8daaf4d4bc
commit
98b880b77d
@@ -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
|
||||
@@ -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',
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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'},
|
||||
]);
|
||||
});
|
||||
@@ -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;
|
||||
});
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -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') {
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
@@ -35,29 +11,25 @@ export class DocsPrivateFilter implements Processor {
|
||||
$runBefore = ['categorizer'];
|
||||
|
||||
$process(docs: DocCollection) {
|
||||
return docs.filter(doc => this._isPublicDoc(doc));
|
||||
}
|
||||
return docs.filter(doc => {
|
||||
const isPublic = 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;
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
/** Whether the given method member is listed as an internal member. */
|
||||
private _isInternalMember(memberDoc: MemberDoc) {
|
||||
return INTERNAL_METHODS.includes(memberDoc.name);
|
||||
}
|
||||
// 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));
|
||||
}
|
||||
|
||||
/** 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 isPublic;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user