/
plugin.ts
147 lines (129 loc) · 4.64 KB
/
plugin.ts
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
import { Reflection, ReflectionKind, ProjectReflection } from 'typedoc/dist/lib/models/reflections';
import { Component } from 'typedoc/dist/lib/converter/components';
import { Options } from 'typedoc/dist/lib/utils/options';
import { PageEvent } from 'typedoc/dist/lib/output/events';
import { NavigationItem } from 'typedoc/dist/lib/output/models/NavigationItem';
import { Converter } from 'typedoc/dist/lib/converter/converter';
import { Context } from 'typedoc/dist/lib/converter/context';
import { TocPlugin } from 'typedoc/dist/lib/output/plugins/TocPlugin';
export const PLUGIN_NAME = 'toc-group';
export const PLUGIN_SHORT_NAME = 'tocg';
const DEFAULT_UNGROUPED_NAME = 'Others';
const DEPRECATED_REGEXP = new RegExp(/^@deprecated$/);
/**
* This plugin will generate a group menu for toc list.
*/
@Component({ name: PLUGIN_NAME })
export class TocGroupPlugin extends TocPlugin {
private regexp: RegExp;
private defaultTags = ['group', 'kind', 'platform'];
initialize() {
super.initialize();
this.listenTo(this.owner, {
[Converter.EVENT_BEGIN]: this.onBegin,
[Converter.EVENT_RESOLVE_BEGIN]: this.onBeginResolve,
[PageEvent.BEGIN]: this.onBeginRendererPage,
});
}
isHomePage(page: PageEvent) {
if (page && page.url && page.project) {
try {
if (page.url.indexOf(page.project[PLUGIN_NAME].homePath) > -1) {
return true;
}
} catch (e) {
console.log(e);
}
}
return false;
}
private onBegin() {
const options: Options = this.application.options;
const userTags = (options.getValue(PLUGIN_NAME) || '').split(',');
const groupTags = this.defaultTags.concat(userTags).filter(item => item.length);
this.regexp = new RegExp(`@(${groupTags.join('|')})`);
}
private onBeginResolve(context: Context) {
const groupedData = [];
const deprecatedData = new Set();
const mapedTocData = {};
const reflections = context.project.reflections;
for (const key in reflections) {
const ref = reflections[key];
const comment = ref.comment;
if (!comment || !comment.tags) continue;
for (const tag of comment.tags) {
// add deprecated item names
if (DEPRECATED_REGEXP.test(`@${tag.tagName}`)) deprecatedData.add(ref.name);
// add special tags
if (this.regexp.test(`@${tag.tagName}`)) {
groupedData.push(ref.name);
const groupKey = tag.text.split(/\r\n?|\n/)[0];
if (!mapedTocData[groupKey]) mapedTocData[groupKey] = [];
mapedTocData[groupKey].push(ref.name);
break;
}
}
}
const homePath = this.application.options.getValue('homePath') || `modules/_index_.${context.project.name.replace(/\-/g, '')}.html`;
// put them into context.project.
context.project[PLUGIN_NAME] = { groupedData, deprecatedData, mapedTocData, homePath, regexp: this.regexp };
}
/**
* Triggered before a document will be rendered.
*
* @param page An event object describing the current render operation.
*/
private onBeginRendererPage(page: PageEvent) {
let model = page.model;
if (!(model instanceof Reflection)) {
return;
}
const trail: Reflection[] = [];
while (!(model instanceof ProjectReflection) && !model.kindOf(ReflectionKind.SomeModule)) {
trail.unshift(model);
model = model.parent;
}
const tocRestriction = this.owner.toc;
page.toc = new NavigationItem();
TocPlugin.buildToc(model, trail, page.toc, tocRestriction);
this.buildGroupTocContent(page);
}
private buildGroupTocContent(page: PageEvent) {
if (this.isHomePage(page)) {
const { groupedData, deprecatedData, mapedTocData, homePath, regexp } = page.project[PLUGIN_NAME];
if (typeof mapedTocData === 'object' && Object.keys(mapedTocData).length) {
// set ungrouped and remove grouped data.
if (!mapedTocData[DEFAULT_UNGROUPED_NAME]) {
const defaultGroups = [];
page.toc.children.forEach((item: NavigationItem) => {
if (groupedData.indexOf(item.title) === -1) {
defaultGroups.push(item.title);
}
});
if (defaultGroups.length) mapedTocData[DEFAULT_UNGROUPED_NAME] = defaultGroups;
}
const updatedToc = Object.keys(mapedTocData).map((key: string) => {
const groupedValue = mapedTocData[key];
const root = new NavigationItem(key, homePath);
root['groupTitle'] = key;
root.children = page.toc.children.filter((item: NavigationItem) => {
if (regexp.test(`@!${item.reflection.kind}`)) return false;
if (deprecatedData.has(item.title)) {
item['deprecated'] = true;
}
if (groupedValue.indexOf(item.title) > -1) {
item.parent = root;
return true;
}
return false;
});
return root;
});
if (updatedToc && updatedToc.length) {
page.toc.children = updatedToc;
}
}
}
}
}