Skip to content

Commit 2cb016f

Browse files
feat(api-reference): add typedoc plugins for api reference (#1694)
* docs: add typedoc plugins and customizations * docs: support generating typedoc for all packages * docs: consolidate client doc generator plugins * docs: add core packages doc plugin * feat(client-documentation-generator): refactor plugins * chore(core-packages-documentation-generator): generate core packages doc * fix: apply review feedbacks Co-authored-by: Trivikram Kamat <[email protected]>
1 parent 96f61bb commit 2cb016f

16 files changed

+485
-137
lines changed

Diff for: package.json

+4-1
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,12 @@
1111
"clean": "yarn clear-build-cache && yarn clear-build-info && lerna clean",
1212
"clear-build-cache": "rimraf ./packages/*/build ./packages/*/build-es ./clients/*/dist",
1313
"clear-build-info": "rimraf ./packages/*/*.tsbuildinfo ./clients/*/*/*.tsbuildinfo",
14+
"remove-documentation": "rimraf ./docs",
1415
"build:crypto-dependencies": "lerna run --scope '@aws-sdk/{types,util-utf8-browser,util-locate-window,hash-node}' --include-dependencies pretest",
1516
"build:protocols": "yarn build:crypto-dependencies && lerna run --scope '@aws-sdk/aws-*' --include-dependencies pretest",
1617
"build:smithy-client": "yarn build:crypto-dependencies && lerna run --scope '@aws-sdk/client-rds-data' --include-dependencies pretest",
1718
"build:all": "yarn build:crypto-dependencies && lerna run build",
19+
"build-documentation": "yarn remove-documentation && typedoc",
1820
"pretest:all": "yarn build:all",
1921
"test:all": "jest --coverage --passWithNoTests && lerna run test --scope '@aws-sdk/{fetch-http-handler,hash-blob-browser}'",
2022
"test:functional": "jest --config tests/functional/jest.config.js --passWithNoTests",
@@ -79,6 +81,7 @@
7981
"prettier": "2.1.0",
8082
"puppeteer": "^5.1.0",
8183
"ts-loader": "^7.0.5",
84+
"typedoc-plugin-lerna-packages": "^0.3.1",
8285
"typescript": "~4.0.2",
8386
"verdaccio": "^4.7.2",
8487
"webpack": "^4.43.0",
@@ -112,4 +115,4 @@
112115
],
113116
"**/*.{ts,js,md,json}": "prettier --write"
114117
}
115-
}
118+
}

Diff for: packages/client-documentation-generator/src/index.ts

+11-11
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
import { PluginHost } from "typedoc/dist/lib/utils";
22

3-
import { SdkClientRenameGlobalPlugin } from "./sdk-client-rename-global";
4-
import { SdkClientSourceUpdatePlugin } from "./sdk-client-source-update";
3+
import { SdkClientCommentUpdatePlugin } from "./sdk-client-comment-update";
4+
import { SdkClientRenameProjectPlugin } from "./sdk-client-rename-project";
55
import { SdkClientTocPlugin } from "./sdk-client-toc-plugin";
66

7-
/**
8-
*
9-
* @param pluginHost An instance of PluginHost.
10-
*/
117
module.exports = function load(pluginHost: PluginHost) {
128
const application = pluginHost.owner;
139

14-
// Add renderer plugins
15-
application.renderer.addComponent("SdkClientTocPlugin", SdkClientTocPlugin as any);
16-
application.renderer.addComponent("SdkClientRenameGlobalPlugin", SdkClientRenameGlobalPlugin as any);
10+
application.converter.addComponent(
11+
"SdkClientCommentUpdatePlugin",
12+
new SdkClientCommentUpdatePlugin(application.converter)
13+
);
1714

18-
// Add converter plugins
19-
application.converter.addComponent("SdkClientSourceUpdatePlugin", SdkClientSourceUpdatePlugin as any);
15+
application.renderer.addComponent("SdkClientTocPlugin", new SdkClientTocPlugin(application.renderer));
16+
application.renderer.addComponent(
17+
"SdkClientRenameProjectPlugin",
18+
new SdkClientRenameProjectPlugin(application.renderer)
19+
);
2020
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { Converter } from "typedoc/dist/lib/converter";
2+
import { Component, ConverterComponent } from "typedoc/dist/lib/converter/components";
3+
import { Context } from "typedoc/dist/lib/converter/context";
4+
import { getRawComment, parseComment } from "typedoc/dist/lib/converter/factories/comment";
5+
import { Reflection } from "typedoc/dist/lib/models/reflections";
6+
import ts from "typescript";
7+
8+
/**
9+
* Best effort make the service docs markdown looks better.
10+
*/
11+
@Component({ name: "SdkClientCommentUpdatePlugin" })
12+
export class SdkClientCommentUpdatePlugin extends ConverterComponent {
13+
initialize() {
14+
this.listenTo(this.owner, {
15+
[Converter.EVENT_CREATE_DECLARATION]: this.onDeclaration,
16+
});
17+
}
18+
19+
private onDeclaration(context: Context, reflection: Reflection, node?: ts.Node) {
20+
if (!node) return;
21+
const rawComment = getRawComment(node);
22+
if (!rawComment) return;
23+
const comment = parseComment(this.cleanEmptyCommentLines(rawComment));
24+
reflection.comment = comment;
25+
}
26+
27+
/**
28+
* Update documentation block to exclude empty lines.
29+
*/
30+
private cleanEmptyCommentLines(comment: string): string {
31+
return comment.startsWith("/*") && comment.endsWith("*/")
32+
? comment
33+
.split("\n")
34+
.filter((line) => line.substr(line.indexOf("*") + 1).trim().length !== 0)
35+
.join("\n")
36+
: comment;
37+
}
38+
}

Diff for: packages/client-documentation-generator/src/sdk-client-rename-global.ts

-27
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { readFileSync } from "fs";
2+
import { Component, RendererComponent } from "typedoc/dist/lib/output/components";
3+
import { RendererEvent } from "typedoc/dist/lib/output/events";
4+
5+
/**
6+
* Correct the package name in the navigator.
7+
*/
8+
@Component({ name: "SdkClientRenameProject" })
9+
export class SdkClientRenameProjectPlugin extends RendererComponent {
10+
initialize() {
11+
this.listenTo(this.owner, {
12+
[RendererEvent.BEGIN]: this.onRenderedBegin,
13+
});
14+
}
15+
16+
onRenderedBegin(event: RendererEvent) {
17+
const { fullFileName } = event.project.files.filter((sourceFile) =>
18+
sourceFile.fileName.endsWith("/package.json")
19+
)[0];
20+
const { name } = JSON.parse(readFileSync(fullFileName).toString());
21+
event.project.name = name;
22+
}
23+
}

Diff for: packages/client-documentation-generator/src/sdk-client-source-update.ts

-40
This file was deleted.

Diff for: packages/client-documentation-generator/src/sdk-client-toc-plugin.ts

+36-50
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,14 @@ import { Component, RendererComponent } from "typedoc/dist/lib/output/components
44
import { PageEvent } from "typedoc/dist/lib/output/events";
55
import { NavigationItem } from "typedoc/dist/lib/output/models/NavigationItem";
66

7+
/**
8+
* Group the ToC for easier observability.
9+
*/
710
@Component({ name: "SdkClientTocPlugin" })
811
export class SdkClientTocPlugin extends RendererComponent {
9-
private commandToNavigationItems: Map<string, NavigationItem> = new Map();
1012
private commandsNavigationItem?: NavigationItem;
11-
private exceptionsNavigationItem?: NavigationItem;
13+
private clientsNavigationItem?: NavigationItem;
14+
private paginatorsNavigationItem?: NavigationItem;
1215

1316
initialize() {
1417
// disable existing toc plugin
@@ -40,36 +43,43 @@ export class SdkClientTocPlugin extends RendererComponent {
4043
page.toc = new NavigationItem(model.name);
4144

4245
if (!model.parent && !trail.length) {
46+
this.clientsNavigationItem = new NavigationItem("Clients", void 0, page.toc);
4347
this.commandsNavigationItem = new NavigationItem("Commands", void 0, page.toc);
44-
this.exceptionsNavigationItem = new NavigationItem("Exceptions", void 0, page.toc);
48+
this.paginatorsNavigationItem = new NavigationItem("Paginators", void 0, page.toc);
4549
}
4650

4751
this.buildToc(model, trail, page.toc, tocRestriction);
4852
}
4953

50-
private isCommand({ implementedTypes = [] }: DeclarationReflection): boolean {
54+
private isClient(model: DeclarationReflection): boolean {
55+
const { extendedTypes = [] } = model;
5156
return (
52-
implementedTypes.length === 1 &&
53-
implementedTypes[0].type === "reference" &&
54-
(implementedTypes[0] as ReferenceType).name === "Command"
57+
model.kindOf(ReflectionKind.Class) &&
58+
model.getFullName() !== "Client" && // Exclude the Smithy Client class.
59+
(model.name.endsWith("Client") /* Modular client like S3Client */ ||
60+
(extendedTypes.length === 1 &&
61+
(extendedTypes[0] as ReferenceType).name.endsWith("Client"))) /* Legacy client like S3 */
5562
);
5663
}
5764

58-
private isException(model: DeclarationReflection): boolean {
59-
const extendedTypes = model.extendedTypes || [];
65+
private isCommand(model: DeclarationReflection): boolean {
6066
return (
61-
extendedTypes.length === 1 &&
62-
extendedTypes[0].type === "reference" &&
63-
(extendedTypes[0] as ReferenceType).name === "ServiceException"
67+
model.kindOf(ReflectionKind.Class) &&
68+
model.getFullName() !== "Command" && // Exclude the Smithy Command class.
69+
model.name.endsWith("Command") &&
70+
model.children?.some((child) => child.name === "resolveMiddleware")
6471
);
6572
}
6673

67-
private isUnion(model: DeclarationReflection): boolean {
68-
return model.type?.type === "union";
74+
private isPaginator(model: DeclarationReflection): boolean {
75+
return model.name.startsWith("paginate") && model.kindOf(ReflectionKind.Function);
6976
}
7077

7178
private isInputOrOutput(model: DeclarationReflection): boolean {
72-
return model.kindString === "Interface" && (model.name.endsWith("Input") || model.name.endsWith("Output"));
79+
return (
80+
model.kindOf(ReflectionKind.TypeAlias) &&
81+
(model.name.endsWith("CommandInput") || model.name.endsWith("CommandOutput"))
82+
);
7383
}
7484

7585
/**
@@ -92,57 +102,33 @@ export class SdkClientTocPlugin extends RendererComponent {
92102
this.buildToc(child, trail, item);
93103
} else {
94104
children.forEach((child: DeclarationReflection) => {
95-
if (restriction && restriction.length > 0 && restriction.indexOf(child.name) === -1) {
105+
if (restriction && restriction.length > 0 && !restriction.includes(child.name)) {
96106
return;
97107
}
98108

99109
if (child.kindOf(ReflectionKind.SomeModule)) {
100110
return;
101111
}
102112

103-
if (trail.length) {
104-
const item = NavigationItem.create(child, parent, true);
105-
if (trail.indexOf(child) !== -1) {
106-
item.isInPath = true;
107-
item.isCurrent = trail[trail.length - 1] === child;
108-
this.buildToc(child, trail, item);
109-
}
110-
return;
111-
}
112-
113-
if (this.isCommand(child)) {
114-
const item = NavigationItem.create(child, this.commandsNavigationItem, true);
115-
// create an entry for the command
116-
const commandName = child.name.toLowerCase();
117-
if (!this.commandToNavigationItems.get(commandName)) {
118-
this.commandToNavigationItems.set(commandName, item);
119-
}
120-
} else if (this.isException(child)) {
121-
NavigationItem.create(child, this.exceptionsNavigationItem, true);
122-
} else if (
123-
this.isUnion(child) &&
124-
(child as any).type.types.every((type: ReferenceType) => {
125-
return type.reflection && this.isException(type.reflection as DeclarationReflection);
126-
})
127-
) {
128-
// get command from name
129-
const commandName = child.name.replace("ExceptionsUnion", "").toLowerCase() + "command";
130-
NavigationItem.create(child, this.commandToNavigationItems.get(commandName), true);
113+
if (this.isClient(child)) {
114+
NavigationItem.create(child, this.clientsNavigationItem, true);
115+
} else if (this.isCommand(child)) {
116+
NavigationItem.create(child, this.commandsNavigationItem, true);
117+
} else if (this.isPaginator(child)) {
118+
NavigationItem.create(child, this.paginatorsNavigationItem, true);
131119
} else if (this.isInputOrOutput(child)) {
132-
// get command from name
133-
const commandName = child.name.replace(/Input|Output/, "").toLowerCase() + "command";
134-
NavigationItem.create(child, this.commandToNavigationItems.get(commandName), true);
135-
} else if (child.name.startsWith("_")) {
136-
return;
120+
NavigationItem.create(child, this.commandsNavigationItem, true);
137121
} else {
138122
const item = NavigationItem.create(child, parent, true);
139-
if (trail.indexOf(child) !== -1) {
123+
if (trail.includes(child)) {
140124
item.isInPath = true;
141125
item.isCurrent = trail[trail.length - 1] === child;
142126
this.buildToc(child, trail, item);
143127
}
144128
}
145129
});
130+
// Group commands and input/output interface of each command.
131+
this.commandsNavigationItem?.children.sort((childA, childB) => childA.title.localeCompare(childB.title));
146132
}
147133
}
148134
}

0 commit comments

Comments
 (0)