Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(api-reference): add typedoc plugins for api reference #1694

Merged
merged 16 commits into from
Nov 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@
"clean": "yarn clear-build-cache && yarn clear-build-info && lerna clean",
"clear-build-cache": "rimraf ./packages/*/build ./packages/*/build-es ./clients/*/dist",
"clear-build-info": "rimraf ./packages/*/*.tsbuildinfo ./clients/*/*/*.tsbuildinfo",
"remove-documentation": "rimraf ./docs",
"build:crypto-dependencies": "lerna run --scope '@aws-sdk/{types,util-utf8-browser,util-locate-window,hash-node}' --include-dependencies pretest",
"build:protocols": "yarn build:crypto-dependencies && lerna run --scope '@aws-sdk/aws-*' --include-dependencies pretest",
"build:smithy-client": "yarn build:crypto-dependencies && lerna run --scope '@aws-sdk/client-rds-data' --include-dependencies pretest",
"build:all": "yarn build:crypto-dependencies && lerna run build",
"build-documentation": "yarn remove-documentation && typedoc",
"pretest:all": "yarn build:all",
"test:all": "jest --coverage --passWithNoTests && lerna run test --scope '@aws-sdk/{fetch-http-handler,hash-blob-browser}'",
"test:functional": "jest --config tests/functional/jest.config.js --passWithNoTests",
Expand Down Expand Up @@ -79,6 +81,7 @@
"prettier": "2.1.0",
"puppeteer": "^5.1.0",
"ts-loader": "^7.0.5",
"typedoc-plugin-lerna-packages": "^0.3.1",
"typescript": "~4.0.2",
"verdaccio": "^4.7.2",
"webpack": "^4.43.0",
Expand Down Expand Up @@ -112,4 +115,4 @@
],
"**/*.{ts,js,md,json}": "prettier --write"
}
}
}
22 changes: 11 additions & 11 deletions packages/client-documentation-generator/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import { PluginHost } from "typedoc/dist/lib/utils";

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

/**
*
* @param pluginHost An instance of PluginHost.
*/
module.exports = function load(pluginHost: PluginHost) {
const application = pluginHost.owner;

// Add renderer plugins
application.renderer.addComponent("SdkClientTocPlugin", SdkClientTocPlugin as any);
application.renderer.addComponent("SdkClientRenameGlobalPlugin", SdkClientRenameGlobalPlugin as any);
application.converter.addComponent(
"SdkClientCommentUpdatePlugin",
new SdkClientCommentUpdatePlugin(application.converter)
);

// Add converter plugins
application.converter.addComponent("SdkClientSourceUpdatePlugin", SdkClientSourceUpdatePlugin as any);
application.renderer.addComponent("SdkClientTocPlugin", new SdkClientTocPlugin(application.renderer));
application.renderer.addComponent(
"SdkClientRenameProjectPlugin",
new SdkClientRenameProjectPlugin(application.renderer)
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { Converter } from "typedoc/dist/lib/converter";
import { Component, ConverterComponent } from "typedoc/dist/lib/converter/components";
import { Context } from "typedoc/dist/lib/converter/context";
import { getRawComment, parseComment } from "typedoc/dist/lib/converter/factories/comment";
import { Reflection } from "typedoc/dist/lib/models/reflections";
import ts from "typescript";

/**
* Best effort make the service docs markdown looks better.
*/
@Component({ name: "SdkClientCommentUpdatePlugin" })
export class SdkClientCommentUpdatePlugin extends ConverterComponent {
initialize() {
this.listenTo(this.owner, {
[Converter.EVENT_CREATE_DECLARATION]: this.onDeclaration,
});
}

private onDeclaration(context: Context, reflection: Reflection, node?: ts.Node) {
if (!node) return;
const rawComment = getRawComment(node);
if (!rawComment) return;
const comment = parseComment(this.cleanEmptyCommentLines(rawComment));
reflection.comment = comment;
}

/**
* Update documentation block to exclude empty lines.
*/
private cleanEmptyCommentLines(comment: string): string {
return comment.startsWith("/*") && comment.endsWith("*/")
? comment
.split("\n")
.filter((line) => line.substr(line.indexOf("*") + 1).trim().length !== 0)
.join("\n")
: comment;
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { readFileSync } from "fs";
import { Component, RendererComponent } from "typedoc/dist/lib/output/components";
import { RendererEvent } from "typedoc/dist/lib/output/events";

/**
* Correct the package name in the navigator.
*/
@Component({ name: "SdkClientRenameProject" })
export class SdkClientRenameProjectPlugin extends RendererComponent {
initialize() {
this.listenTo(this.owner, {
[RendererEvent.BEGIN]: this.onRenderedBegin,
});
}

onRenderedBegin(event: RendererEvent) {
const { fullFileName } = event.project.files.filter((sourceFile) =>
sourceFile.fileName.endsWith("/package.json")
)[0];
const { name } = JSON.parse(readFileSync(fullFileName).toString());
event.project.name = name;
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@ import { Component, RendererComponent } from "typedoc/dist/lib/output/components
import { PageEvent } from "typedoc/dist/lib/output/events";
import { NavigationItem } from "typedoc/dist/lib/output/models/NavigationItem";

/**
* Group the ToC for easier observability.
*/
@Component({ name: "SdkClientTocPlugin" })
export class SdkClientTocPlugin extends RendererComponent {
private commandToNavigationItems: Map<string, NavigationItem> = new Map();
private commandsNavigationItem?: NavigationItem;
private exceptionsNavigationItem?: NavigationItem;
private clientsNavigationItem?: NavigationItem;
private paginatorsNavigationItem?: NavigationItem;

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

if (!model.parent && !trail.length) {
this.clientsNavigationItem = new NavigationItem("Clients", void 0, page.toc);
this.commandsNavigationItem = new NavigationItem("Commands", void 0, page.toc);
this.exceptionsNavigationItem = new NavigationItem("Exceptions", void 0, page.toc);
this.paginatorsNavigationItem = new NavigationItem("Paginators", void 0, page.toc);
}

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

private isCommand({ implementedTypes = [] }: DeclarationReflection): boolean {
private isClient(model: DeclarationReflection): boolean {
const { extendedTypes = [] } = model;
return (
implementedTypes.length === 1 &&
implementedTypes[0].type === "reference" &&
(implementedTypes[0] as ReferenceType).name === "Command"
model.kindOf(ReflectionKind.Class) &&
model.getFullName() !== "Client" && // Exclude the Smithy Client class.
(model.name.endsWith("Client") /* Modular client like S3Client */ ||
(extendedTypes.length === 1 &&
(extendedTypes[0] as ReferenceType).name.endsWith("Client"))) /* Legacy client like S3 */
);
}

private isException(model: DeclarationReflection): boolean {
const extendedTypes = model.extendedTypes || [];
private isCommand(model: DeclarationReflection): boolean {
return (
extendedTypes.length === 1 &&
extendedTypes[0].type === "reference" &&
(extendedTypes[0] as ReferenceType).name === "ServiceException"
model.kindOf(ReflectionKind.Class) &&
model.getFullName() !== "Command" && // Exclude the Smithy Command class.
model.name.endsWith("Command") &&
model.children?.some((child) => child.name === "resolveMiddleware")
);
}

private isUnion(model: DeclarationReflection): boolean {
return model.type?.type === "union";
private isPaginator(model: DeclarationReflection): boolean {
return model.name.startsWith("paginate") && model.kindOf(ReflectionKind.Function);
}

private isInputOrOutput(model: DeclarationReflection): boolean {
return model.kindString === "Interface" && (model.name.endsWith("Input") || model.name.endsWith("Output"));
return (
model.kindOf(ReflectionKind.TypeAlias) &&
(model.name.endsWith("CommandInput") || model.name.endsWith("CommandOutput"))
);
}

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

if (child.kindOf(ReflectionKind.SomeModule)) {
return;
}

if (trail.length) {
const item = NavigationItem.create(child, parent, true);
if (trail.indexOf(child) !== -1) {
item.isInPath = true;
item.isCurrent = trail[trail.length - 1] === child;
this.buildToc(child, trail, item);
}
return;
}

if (this.isCommand(child)) {
const item = NavigationItem.create(child, this.commandsNavigationItem, true);
// create an entry for the command
const commandName = child.name.toLowerCase();
if (!this.commandToNavigationItems.get(commandName)) {
this.commandToNavigationItems.set(commandName, item);
}
} else if (this.isException(child)) {
NavigationItem.create(child, this.exceptionsNavigationItem, true);
} else if (
this.isUnion(child) &&
(child as any).type.types.every((type: ReferenceType) => {
return type.reflection && this.isException(type.reflection as DeclarationReflection);
})
) {
// get command from name
const commandName = child.name.replace("ExceptionsUnion", "").toLowerCase() + "command";
NavigationItem.create(child, this.commandToNavigationItems.get(commandName), true);
if (this.isClient(child)) {
NavigationItem.create(child, this.clientsNavigationItem, true);
} else if (this.isCommand(child)) {
NavigationItem.create(child, this.commandsNavigationItem, true);
} else if (this.isPaginator(child)) {
NavigationItem.create(child, this.paginatorsNavigationItem, true);
} else if (this.isInputOrOutput(child)) {
// get command from name
const commandName = child.name.replace(/Input|Output/, "").toLowerCase() + "command";
NavigationItem.create(child, this.commandToNavigationItems.get(commandName), true);
} else if (child.name.startsWith("_")) {
return;
NavigationItem.create(child, this.commandsNavigationItem, true);
} else {
const item = NavigationItem.create(child, parent, true);
if (trail.indexOf(child) !== -1) {
if (trail.includes(child)) {
item.isInPath = true;
item.isCurrent = trail[trail.length - 1] === child;
this.buildToc(child, trail, item);
}
}
});
// Group commands and input/output interface of each command.
this.commandsNavigationItem?.children.sort((childA, childB) => childA.title.localeCompare(childB.title));
}
}
}
Loading