-
Notifications
You must be signed in to change notification settings - Fork 682
Expand file tree
/
Copy pathExperimentalYamlDocumenter.ts
More file actions
165 lines (142 loc) · 6.07 KB
/
ExperimentalYamlDocumenter.ts
File metadata and controls
165 lines (142 loc) · 6.07 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.
import { type DocComment, DocInlineTag } from '@microsoft/tsdoc';
import { type ApiModel, type ApiItem, ApiItemKind, ApiDocumentedItem } from '@microsoft/api-extractor-model';
import type { IConfigTableOfContents } from './IConfigFile.ts';
import type { IYamlTocItem, IYamlTocFile } from '../yaml/IYamlTocFile.ts';
import { YamlDocumenter } from './YamlDocumenter.ts';
import type { DocumenterConfig } from './DocumenterConfig.ts';
/**
* EXPERIMENTAL - This documenter is a prototype of a new config file driven mode of operation for
* API Documenter. It is not ready for general usage yet. Its design may change in the future.
*/
export class ExperimentalYamlDocumenter extends YamlDocumenter {
private _config: IConfigTableOfContents;
private _tocPointerMap: { [key: string]: IYamlTocItem };
private _catchAllPointer: IYamlTocItem | undefined;
public constructor(apiModel: ApiModel, documenterConfig: DocumenterConfig) {
super(apiModel, documenterConfig.configFile.newDocfxNamespaces);
this._config = documenterConfig.configFile.tableOfContents!;
this._tocPointerMap = {};
this._generateTocPointersMap(this._config.tocConfig);
}
/** @override */
protected buildYamlTocFile(apiItems: ReadonlyArray<ApiItem>): IYamlTocFile {
this._buildTocItems2(apiItems);
return this._config.tocConfig;
}
private _buildTocItems2(apiItems: ReadonlyArray<ApiItem>): IYamlTocItem[] {
const tocItems: IYamlTocItem[] = [];
for (const apiItem of apiItems) {
let tocItem: IYamlTocItem;
if (apiItem.kind === ApiItemKind.Namespace && !this.newDocfxNamespaces) {
tocItem = {
name: this._getTocItemName(apiItem)
};
} else {
if (this._shouldEmbed(apiItem.kind)) {
// Don't generate table of contents items for embedded definitions
continue;
}
tocItem = {
name: this._getTocItemName(apiItem),
uid: this._getUid(apiItem)
};
if (apiItem.kind !== ApiItemKind.Package) {
this._filterItem(apiItem, tocItem);
}
}
tocItems.push(tocItem);
const children: ApiItem[] = this._getLogicalChildren(apiItem);
const childItems: IYamlTocItem[] = this._buildTocItems2(children);
if (childItems.length > 0) {
tocItem.items = childItems;
}
}
return tocItems;
}
// Parses the tocConfig object to build a pointers map of nodes where we want to sort out the API items
private _generateTocPointersMap(tocConfig: IYamlTocFile | IYamlTocItem): void {
const { catchAllCategory } = this._config;
if (tocConfig.items) {
for (const tocItem of tocConfig.items) {
if (tocItem.items && tocItem.items.length > 0 && this._shouldNotIncludeInPointersMap(tocItem)) {
this._generateTocPointersMap(tocItem);
} else {
// check for presence of the `catchAllCategory` config option
if (catchAllCategory && tocItem.name === catchAllCategory) {
this._catchAllPointer = tocItem;
} else {
this._tocPointerMap[tocItem.name] = tocItem;
}
}
}
}
}
/**
* Filtering out the api-item by inlineTags or category name presence in the item name.
*/
private _filterItem(apiItem: ApiItem, tocItem: IYamlTocItem): void {
const { categoryInlineTag, categorizeByName } = this._config;
const { name: itemName } = tocItem;
let filtered: boolean = false;
// First we attempt to filter by inline tag if provided.
if (apiItem instanceof ApiDocumentedItem) {
const docInlineTag: DocInlineTag | undefined = categoryInlineTag
? this._findInlineTagByName(categoryInlineTag, apiItem.tsdocComment)
: undefined;
const tagContent: string | undefined =
docInlineTag && docInlineTag.tagContent && docInlineTag.tagContent.trim();
if (tagContent && this._tocPointerMap[tagContent]) {
// null assertion used because when pointer map was created we checked for presence of empty `items` array
this._tocPointerMap[tagContent].items!.push(tocItem);
filtered = true;
}
}
// If not filtered by inline tag and `categorizeByName` config is enabled attempt to filter it by category name.
if (!filtered && categorizeByName) {
const pointers: string[] = Object.keys(this._tocPointerMap);
for (let i: number = 0, length: number = pointers.length; i < length; i++) {
if (itemName.indexOf(pointers[i]) !== -1) {
// null assertion used because when pointer map was created we checked for presence of empty `items` array
this._tocPointerMap[pointers[i]].items!.push(tocItem);
filtered = true;
break;
}
}
}
// If item still not filtered and a `catchAllCategory` config provided push it to it.
if (!filtered && this._catchAllPointer && this._catchAllPointer.items) {
this._catchAllPointer.items.push(tocItem);
}
}
// This is a direct copy of a @docCategory inline tag finder in office-ui-fabric-react,
// but is generic enough to be used for any inline tag
private _findInlineTagByName(
tagName: string,
docComment: DocComment | undefined
): DocInlineTag | undefined {
const tagNameToCheck: string = `@${tagName}`;
if (docComment instanceof DocInlineTag) {
if (docComment.tagName === tagNameToCheck) {
return docComment;
}
}
if (docComment) {
for (const childNode of docComment.getChildNodes()) {
const result: DocInlineTag | undefined = this._findInlineTagByName(tagName, childNode as DocComment);
if (result !== undefined) {
return result;
}
}
}
return undefined;
}
private _shouldNotIncludeInPointersMap(item: IYamlTocItem): boolean {
const { nonEmptyCategoryNodeNames } = this._config;
if (nonEmptyCategoryNodeNames && nonEmptyCategoryNodeNames.length) {
return nonEmptyCategoryNodeNames.indexOf(item.name) === -1;
}
return true;
}
}