Skip to content

Commit 01bfda8

Browse files
authored
Merge pull request microsoft#1294 from Vitalius1/vibraga/experimentalDocumenter_adjustments
[api-documenter] - ExperimentalYamlDocumenter new filter feature addition
2 parents 0d16e0e + 4c238cb commit 01bfda8

11 files changed

Lines changed: 320 additions & 44 deletions

File tree

apps/api-documenter/src/documenters/ExperimentalYamlDocumenter.ts

Lines changed: 60 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -59,26 +59,7 @@ export class ExperimentalYamlDocumenter extends YamlDocumenter {
5959
name: apiItem.displayName,
6060
uid: this._getUid(apiItem)
6161
};
62-
// Filtering out the api-items as we build the tocItems array.
63-
if (apiItem instanceof ApiDocumentedItem) {
64-
const docInlineTag: DocInlineTag | undefined =
65-
(this._config && this._config.filterByInlineTag)
66-
? this._findInlineTagByName(this._config.filterByInlineTag, apiItem.tsdocComment)
67-
: undefined;
68-
69-
const tagContent: string | undefined =
70-
docInlineTag && docInlineTag.tagContent && docInlineTag.tagContent.trim();
71-
72-
if (tagContent && this._tocPointerMap[tagContent]) {
73-
// null assertion used because when pointer map was created we checked for presence of empty `items` array
74-
this._tocPointerMap[tagContent].items!.push(tocItem);
75-
} else {
76-
if (this._catchAllPointer && this._catchAllPointer.items) {
77-
this._catchAllPointer.items.push(tocItem);
78-
}
79-
}
80-
}
81-
62+
this._filterItem(apiItem, tocItem);
8263
}
8364
}
8465

@@ -102,13 +83,15 @@ export class ExperimentalYamlDocumenter extends YamlDocumenter {
10283

10384
// Parses the tocConfig object to build a pointers map of nodes where we want to sort out the API items
10485
private _generateTocPointersMap(tocConfig: IYamlTocFile | IYamlTocItem): void {
86+
const { catchAllCategory } = this._config;
87+
10588
if (tocConfig.items) {
10689
for (const tocItem of tocConfig.items) {
107-
if (tocItem.items && tocItem.items.length > 0) {
90+
if (tocItem.items && tocItem.items.length > 0 && this._shouldNotIncludeInPointersMap(tocItem)) {
10891
this._generateTocPointersMap(tocItem);
10992
} else {
11093
// check for presence of the `catchAllCategory` config option
111-
if (this._config && this._config.catchAllCategory && tocItem.name === this._config.catchAllCategory) {
94+
if (catchAllCategory && tocItem.name === catchAllCategory) {
11295
this._catchAllPointer = tocItem;
11396
} else {
11497
this._tocPointerMap[tocItem.name] = tocItem;
@@ -118,11 +101,57 @@ export class ExperimentalYamlDocumenter extends YamlDocumenter {
118101
}
119102
}
120103

104+
/**
105+
* Filtering out the api-item by inlineTags or category name presence in the item name.
106+
*/
107+
private _filterItem(apiItem: ApiItem, tocItem: IYamlTocItem): void {
108+
const { categoryInlineTag, categorizeByName } = this._config;
109+
const { name: itemName } = tocItem;
110+
let filtered: boolean = false;
111+
112+
// First we attempt to filter by inline tag if provided.
113+
if (apiItem instanceof ApiDocumentedItem) {
114+
const docInlineTag: DocInlineTag | undefined =
115+
categoryInlineTag
116+
? this._findInlineTagByName(categoryInlineTag, apiItem.tsdocComment)
117+
: undefined;
118+
119+
const tagContent: string | undefined =
120+
docInlineTag && docInlineTag.tagContent && docInlineTag.tagContent.trim();
121+
122+
if (tagContent && this._tocPointerMap[tagContent]) {
123+
// null assertion used because when pointer map was created we checked for presence of empty `items` array
124+
this._tocPointerMap[tagContent].items!.push(tocItem);
125+
filtered = true;
126+
}
127+
}
128+
129+
// If not filtered by inline tag and `categorizeByName` config is enabled attempt to filter it by category name.
130+
if (!filtered && categorizeByName) {
131+
const pointers: string[] = Object.keys(this._tocPointerMap);
132+
for (let i: number = 0, length: number = pointers.length; i < length; i++) {
133+
if (itemName.indexOf(pointers[i]) !== -1) {
134+
// null assertion used because when pointer map was created we checked for presence of empty `items` array
135+
this._tocPointerMap[pointers[i]].items!.push(tocItem);
136+
filtered = true;
137+
break;
138+
}
139+
}
140+
}
141+
142+
// If item still not filtered and a `catchAllCategory` config provided push it to it.
143+
if (!filtered && this._catchAllPointer && this._catchAllPointer.items) {
144+
this._catchAllPointer.items.push(tocItem);
145+
}
146+
}
147+
121148
// This is a direct copy of a @docCategory inline tag finder in office-ui-fabric-react,
122149
// but is generic enough to be used for any inline tag
123150
private _findInlineTagByName(tagName: string, docComment: DocComment | undefined): DocInlineTag | undefined {
151+
const tagNameToCheck: string = `@${tagName}`;
152+
124153
if (docComment instanceof DocInlineTag) {
125-
if (docComment.tagName === tagName) {
154+
if (docComment.tagName === tagNameToCheck) {
126155
return docComment;
127156
}
128157
}
@@ -136,4 +165,12 @@ export class ExperimentalYamlDocumenter extends YamlDocumenter {
136165
}
137166
return undefined;
138167
}
168+
169+
private _shouldNotIncludeInPointersMap(item: IYamlTocItem): boolean {
170+
const { nonEmptyCategoryNodeNames } = this._config;
171+
if (nonEmptyCategoryNodeNames && nonEmptyCategoryNodeNames.length) {
172+
return nonEmptyCategoryNodeNames.indexOf(item.name) === -1;
173+
}
174+
return true;
175+
}
139176
}

apps/api-documenter/src/documenters/IConfigFile.ts

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,37 +9,45 @@ import { IYamlTocFile } from '../yaml/IYamlTocFile';
99
export interface IConfigTableOfContents {
1010
/**
1111
* Represents the tree structure describing the toc.file format.
12-
* Only the nodes that have an empty `items` array will be filled with API items
13-
* that are matched with the filters provided. Everything else will be placed under a catchAll category
14-
* that is highly recommended to be provided.
12+
* Nodes that have an empty `items` array property or their name will be included in the
13+
* {@link IConfigTableOfContents.nonEmptyCategoryNodeNames} will be filled with API items
14+
* that are matched with the filters provided. Everything else will be placed under
15+
* {@link IConfigTableOfContents.catchAllCategory} if provided, which is highly recommended.
1516
*/
1617
tocConfig: IYamlTocFile;
1718

1819
/**
19-
* Optional category name that is recommended to include in the `tocConfig`,
20-
* along with one of the filters: `filterByApiItemName` or `filterByInlineTag`.
21-
* Any items that are not matched to the mentioned filters will be placed under this
20+
* Optional category name that is recommended to be included along with
21+
* one of the configs: {@link IConfigTableOfContents.categorizeByName} or
22+
* {@link IConfigTableOfContents.categoryInlineTag}.
23+
* Any items that are not matched according to the mentioned configuration options will be placed under this
2224
* catchAll category. If none provided the items will not be included in the final toc.yml file.
2325
*/
2426
catchAllCategory?: string;
2527

2628
/**
27-
* When loading more than one api.json files that might include the same API items,
28-
* toggle either to show duplicates or not.
29+
* Toggle either categorization of the API items should be made based on category name presence
30+
* in the API item's name. Useful when there are API items without an inline tag to categorize them,
31+
* but still need to place the items under categories. Note: this type of categorization might place some items
32+
* under wrong categories if the names are similar but belong to different categories.
33+
* In case that {@link IConfigTableOfContents.categoryInlineTag} is provided it will try categorize by
34+
* using it and only if it didn't, it will attempt to categorize by name.
2935
*/
30-
noDuplicateEntries?: boolean;
36+
categorizeByName?: boolean;
3137

3238
/**
33-
* Toggle either sorting of the API items should be made based on category name presence
34-
* in the API item's name.
39+
* Inline tag that will be used to categorize the API items. Will take precedence over the
40+
* {@link IConfigTableOfContents.categorizeByName} flag in trying to place the API item according to the
41+
* custom inline tag present in documentation of the source code.
3542
*/
36-
filterByApiItemName?: boolean;
43+
categoryInlineTag?: string;
3744

3845
/**
39-
* Filter that can be used to sort the API items according to an inline custom tag
40-
* that is present on them.
46+
* Array of node names that might have already items injected at the time of creating the
47+
* {@link IConfigTableOfContents.tocConfig} tree structure but are still needed to be included as category
48+
* nodes where API items will be pushed during the categorization algorithm.
4149
*/
42-
filterByInlineTag?: string;
50+
nonEmptyCategoryNodeNames?: string[];
4351
}
4452

4553
/**

build-tests/api-documenter-test/config/api-documenter.json

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,26 @@
1717
{ "name": "DocClass1", "items": [] }
1818
]
1919
},
20-
{ "name": "References", "items": [] }
20+
{
21+
"name": "Interfaces",
22+
"items": [
23+
{ "name": "Interface5", "items": [] },
24+
{ "name": "Interface6", "items": [{ "name": "InjectedCustomInterface", "uid": "customUid" }] }
25+
]
26+
},
27+
{
28+
"name": "References",
29+
"items": [
30+
{ "name": "InjectedCustomItem", "uid": "customUrl" }
31+
]
32+
}
2133
]
2234
}
2335
]
2436
},
37+
"nonEmptyCategoryNodeNames": ["References", "Interface6"],
2538
"catchAllCategory": "References",
26-
"noDuplicateEntries": true,
27-
"filterByApiItemName": false,
28-
"filterByInlineTag": "@docCategory"
39+
"categorizeByName": true,
40+
"categoryInlineTag": "docCategory"
2941
}
3042
}

build-tests/api-documenter-test/etc/api-documenter-test.api.json

Lines changed: 110 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,21 +37,22 @@
3737
"members": [
3838
{
3939
"kind": "Constructor",
40-
"canonicalReference": "(:constructor,0)",
40+
"canonicalReference": "(:constructor,instance,0)",
4141
"docComment": "/**\n * The simple constructor for `DocBaseClass`\n */\n",
4242
"excerptTokens": [
4343
{
4444
"kind": "Content",
4545
"text": "constructor();"
4646
}
4747
],
48+
"isStatic": false,
4849
"releaseTag": "Public",
4950
"overloadIndex": 0,
5051
"parameters": []
5152
},
5253
{
5354
"kind": "Constructor",
54-
"canonicalReference": "(:constructor,1)",
55+
"canonicalReference": "(:constructor,instance,1)",
5556
"docComment": "/**\n * The overloaded constructor for `DocBaseClass`\n */\n",
5657
"excerptTokens": [
5758
{
@@ -75,6 +76,7 @@
7576
"text": ");"
7677
}
7778
],
79+
"isStatic": false,
7880
"releaseTag": "Public",
7981
"overloadIndex": 1,
8082
"parameters": [
@@ -1182,6 +1184,112 @@
11821184
],
11831185
"extendsTokenRanges": []
11841186
},
1187+
{
1188+
"kind": "Interface",
1189+
"canonicalReference": "(IDocInterface5:interface)",
1190+
"docComment": "/**\n * Interface without inline tag to test custom TOC\n *\n * @public\n */\n",
1191+
"excerptTokens": [
1192+
{
1193+
"kind": "Content",
1194+
"text": "export interface "
1195+
},
1196+
{
1197+
"kind": "Reference",
1198+
"text": "IDocInterface5"
1199+
},
1200+
{
1201+
"kind": "Content",
1202+
"text": " "
1203+
}
1204+
],
1205+
"releaseTag": "Public",
1206+
"name": "IDocInterface5",
1207+
"members": [
1208+
{
1209+
"kind": "PropertySignature",
1210+
"canonicalReference": "regularProperty",
1211+
"docComment": "/**\n * Property of type string that does something\n */\n",
1212+
"excerptTokens": [
1213+
{
1214+
"kind": "Reference",
1215+
"text": "regularProperty"
1216+
},
1217+
{
1218+
"kind": "Content",
1219+
"text": ": "
1220+
},
1221+
{
1222+
"kind": "Content",
1223+
"text": "string"
1224+
},
1225+
{
1226+
"kind": "Content",
1227+
"text": ";"
1228+
}
1229+
],
1230+
"releaseTag": "Public",
1231+
"name": "regularProperty",
1232+
"propertyTypeTokenRange": {
1233+
"startIndex": 2,
1234+
"endIndex": 3
1235+
}
1236+
}
1237+
],
1238+
"extendsTokenRanges": []
1239+
},
1240+
{
1241+
"kind": "Interface",
1242+
"canonicalReference": "(IDocInterface6:interface)",
1243+
"docComment": "/**\n * Interface without inline tag to test custom TOC with injection\n *\n * @public\n */\n",
1244+
"excerptTokens": [
1245+
{
1246+
"kind": "Content",
1247+
"text": "export interface "
1248+
},
1249+
{
1250+
"kind": "Reference",
1251+
"text": "IDocInterface6"
1252+
},
1253+
{
1254+
"kind": "Content",
1255+
"text": " "
1256+
}
1257+
],
1258+
"releaseTag": "Public",
1259+
"name": "IDocInterface6",
1260+
"members": [
1261+
{
1262+
"kind": "PropertySignature",
1263+
"canonicalReference": "regularProperty",
1264+
"docComment": "/**\n * Property of type number that does something\n */\n",
1265+
"excerptTokens": [
1266+
{
1267+
"kind": "Reference",
1268+
"text": "regularProperty"
1269+
},
1270+
{
1271+
"kind": "Content",
1272+
"text": ": "
1273+
},
1274+
{
1275+
"kind": "Content",
1276+
"text": "number"
1277+
},
1278+
{
1279+
"kind": "Content",
1280+
"text": ";"
1281+
}
1282+
],
1283+
"releaseTag": "Public",
1284+
"name": "regularProperty",
1285+
"propertyTypeTokenRange": {
1286+
"startIndex": 2,
1287+
"endIndex": 3
1288+
}
1289+
}
1290+
],
1291+
"extendsTokenRanges": []
1292+
},
11851293
{
11861294
"kind": "Namespace",
11871295
"canonicalReference": "(OuterNamespace:namespace)",

build-tests/api-documenter-test/etc/api-documenter-test.api.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,16 @@ export interface IDocInterface4 {
7676
stringOrNumber: string | number;
7777
}
7878

79+
// @public
80+
export interface IDocInterface5 {
81+
regularProperty: string;
82+
}
83+
84+
// @public
85+
export interface IDocInterface6 {
86+
regularProperty: number;
87+
}
88+
7989
// @public
8090
export namespace OuterNamespace {
8191
export namespace InnerNamespace {

build-tests/api-documenter-test/etc/yaml/api-documenter-test.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ items:
2020
- api-documenter-test.IDocInterface2
2121
- api-documenter-test.IDocInterface3
2222
- api-documenter-test.IDocInterface4
23+
- api-documenter-test.IDocInterface5
24+
- api-documenter-test.IDocInterface6
2325
- api-documenter-test.OuterNamespace.InnerNamespace.nestedFunction
2426
- api-documenter-test.SystemEvent
2527
- uid: api-documenter-test.globalFunction
@@ -75,5 +77,9 @@ references:
7577
name: IDocInterface3
7678
- uid: api-documenter-test.IDocInterface4
7779
name: IDocInterface4
80+
- uid: api-documenter-test.IDocInterface5
81+
name: IDocInterface5
82+
- uid: api-documenter-test.IDocInterface6
83+
name: IDocInterface6
7884
- uid: api-documenter-test.SystemEvent
7985
name: SystemEvent

0 commit comments

Comments
 (0)