diff --git a/package.json b/package.json index 2a9c050a4519..800e3836482d 100644 --- a/package.json +++ b/package.json @@ -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", @@ -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", @@ -112,4 +115,4 @@ ], "**/*.{ts,js,md,json}": "prettier --write" } -} \ No newline at end of file +} diff --git a/packages/client-documentation-generator/src/index.ts b/packages/client-documentation-generator/src/index.ts index b2ea484bfbbe..534dc4b435ce 100644 --- a/packages/client-documentation-generator/src/index.ts +++ b/packages/client-documentation-generator/src/index.ts @@ -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) + ); }; diff --git a/packages/client-documentation-generator/src/sdk-client-comment-update.ts b/packages/client-documentation-generator/src/sdk-client-comment-update.ts new file mode 100644 index 000000000000..b88872466a9a --- /dev/null +++ b/packages/client-documentation-generator/src/sdk-client-comment-update.ts @@ -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; + } +} diff --git a/packages/client-documentation-generator/src/sdk-client-rename-global.ts b/packages/client-documentation-generator/src/sdk-client-rename-global.ts deleted file mode 100644 index 75d57da101dc..000000000000 --- a/packages/client-documentation-generator/src/sdk-client-rename-global.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { Component, RendererComponent } from "typedoc/dist/lib/output/components"; -import { RendererEvent } from "typedoc/dist/lib/output/events"; -import { NavigationPlugin } from "typedoc/dist/lib/output/plugins"; - -@Component({ name: "SdkClientRenameGlobal" }) -export class SdkClientRenameGlobalPlugin extends RendererComponent { - private navigationPlugin: NavigationPlugin; - initialize() { - this.navigationPlugin = this.owner.application.renderer.getComponent("navigation"); - this.listenTo(this.owner, { - [RendererEvent.BEGIN]: this.onRenderedBegin, - }); - } - - onRenderedBegin() { - const navigationItem = this.navigationPlugin.navigation; - if (!navigationItem) { - return; - } - - navigationItem.children.forEach((item) => { - if (item.isGlobals && item.title === "Globals") { - item.title = "Public Exports"; - } - }); - } -} diff --git a/packages/client-documentation-generator/src/sdk-client-rename-project.ts b/packages/client-documentation-generator/src/sdk-client-rename-project.ts new file mode 100644 index 000000000000..3f92f79d4a29 --- /dev/null +++ b/packages/client-documentation-generator/src/sdk-client-rename-project.ts @@ -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; + } +} diff --git a/packages/client-documentation-generator/src/sdk-client-source-update.ts b/packages/client-documentation-generator/src/sdk-client-source-update.ts deleted file mode 100644 index 6c91bfe09d60..000000000000 --- a/packages/client-documentation-generator/src/sdk-client-source-update.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { sep } from "path"; -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 { SourceFile } from "typedoc/dist/lib/models"; -import ts from "typescript"; - -const basePathsToIgnore = ["model"]; - -@Component({ name: "SdkClientSourceUpdatePlugin" }) -export class SdkClientSourceUpdatePlugin extends ConverterComponent { - initialize() { - this.listenTo(this.owner, { - [Converter.EVENT_BEGIN]: this.onBegin, - }); - } - - /** - * Purge source files we wish to ignore. - * @param context - */ - onBegin(context: Context) { - const program = context.program; - const basePath = (program as any).getCommonSourceDirectory(); - const sourceFiles: ts.SourceFile[] = program.getSourceFiles(); - for (let i = sourceFiles.length - 1; i >= 0; i--) { - const sourceFile = sourceFiles[i]; - if (sourceFile.fileName.indexOf(basePath) !== 0) { - continue; - } - - let localPath = sourceFile.fileName.substring(basePath.length); - localPath = localPath.split(sep)[0]; - if (!basePathsToIgnore.every((basePath) => basePath !== localPath)) { - // the file should be ignored, so remove it - sourceFiles.splice(i, 1); - } - } - } -} diff --git a/packages/client-documentation-generator/src/sdk-client-toc-plugin.ts b/packages/client-documentation-generator/src/sdk-client-toc-plugin.ts index b14e11d3ca3e..e14bb321d535 100644 --- a/packages/client-documentation-generator/src/sdk-client-toc-plugin.ts +++ b/packages/client-documentation-generator/src/sdk-client-toc-plugin.ts @@ -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 = new Map(); private commandsNavigationItem?: NavigationItem; - private exceptionsNavigationItem?: NavigationItem; + private clientsNavigationItem?: NavigationItem; + private paginatorsNavigationItem?: NavigationItem; initialize() { // disable existing toc plugin @@ -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")) + ); } /** @@ -92,7 +102,7 @@ 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; } @@ -100,49 +110,25 @@ export class SdkClientTocPlugin extends RendererComponent { 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)); } } } diff --git a/packages/core-packages-documentation-generator/LICENSE b/packages/core-packages-documentation-generator/LICENSE new file mode 100644 index 000000000000..dd65ae06be7a --- /dev/null +++ b/packages/core-packages-documentation-generator/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/core-packages-documentation-generator/README.md b/packages/core-packages-documentation-generator/README.md new file mode 100644 index 000000000000..ecc568c0954e --- /dev/null +++ b/packages/core-packages-documentation-generator/README.md @@ -0,0 +1,10 @@ +# @aws-sdk/client-documentation-generator + +[![NPM version](https://img.shields.io/npm/v/@aws-sdk/client-documentation-generator/rc.svg)](https://www.npmjs.com/package/@aws-sdk/client-documentation-generator) +[![NPM downloads](https://img.shields.io/npm/dm/@aws-sdk/client-documentation-generator.svg)](https://www.npmjs.com/package/@aws-sdk/client-documentation-generator) + +> An internal package + +## Usage + +You probably shouldn't, at least directly. diff --git a/packages/core-packages-documentation-generator/package.json b/packages/core-packages-documentation-generator/package.json new file mode 100644 index 000000000000..957a7d2ce8c2 --- /dev/null +++ b/packages/core-packages-documentation-generator/package.json @@ -0,0 +1,40 @@ +{ + "name": "@aws-sdk/core-packages-documentation-generator", + "version": "1.0.0-rc.3", + "scripts": { + "prepublishOnly": "yarn build:cjs && yarn build:es", + "pretest": "yarn build:cjs", + "build:cjs": "tsc -p tsconfig.cjs.json", + "build:es": "tsc -p tsconfig.es.json", + "build": "yarn build:es && yarn build:cjs", + "test": "exit 0" + }, + "main": "./dist/cjs/index.js", + "module": "./dist/es/index.js", + "types": "./dist/cjs/index.d.ts", + "author": { + "name": "AWS SDK for JavaScript Team", + "url": "https://aws.amazon.com/javascript/" + }, + "license": "Apache-2.0", + "keywords": [ + "typedocplugin" + ], + "dependencies": { + "tslib": "^1.8.0" + }, + "devDependencies": { + "@types/jest": "^26.0.4", + "@types/node": "^10.0.0", + "jest": "^26.1.0", + "typedoc": "^0.17.8", + "typescript": "~4.0.2" + }, + "private": true, + "homepage": "https://github.com/aws/aws-sdk-js-v3/tree/master/packages/core-packages-documentation-generator", + "repository": { + "type": "git", + "url": "https://github.com/aws/aws-sdk-js-v3.git", + "directory": "packages/core-packages-documentation-generator" + } +} diff --git a/packages/core-packages-documentation-generator/src/index.ts b/packages/core-packages-documentation-generator/src/index.ts new file mode 100644 index 000000000000..5e69e24259b1 --- /dev/null +++ b/packages/core-packages-documentation-generator/src/index.ts @@ -0,0 +1,21 @@ +import { ParameterType, PluginHost } from "typedoc/dist/lib/utils"; + +import { SdkIndexLinkClientPlugin } from "./sdk-index-link-client"; + +/** + * @param pluginHost An instance of PluginHost. + */ +module.exports = function load(pluginHost: PluginHost) { + const application = pluginHost.owner; + + // Core packages doc generator plugins. + application.options.addDeclaration({ + name: "clientDocs", + help: + 'The path pattern denotes the location of individual service client doc. "{{CLIENT}}" will be replaced with the ' + + "client name. For example: `path/{{CLIENT}}/docs` will target s3 docs at `path/client-s3/docs`", + defaultValue: "clients/{{CLIENT}}/docs", + type: ParameterType.String, + }); + application.renderer.addComponent("SdkIndexLinkClientPlugin", new SdkIndexLinkClientPlugin(application.renderer)); +}; diff --git a/packages/core-packages-documentation-generator/src/sdk-index-link-client.ts b/packages/core-packages-documentation-generator/src/sdk-index-link-client.ts new file mode 100644 index 000000000000..933927e8b89f --- /dev/null +++ b/packages/core-packages-documentation-generator/src/sdk-index-link-client.ts @@ -0,0 +1,50 @@ +import { isAbsolute, join, relative, resolve, sep } from "path"; +import { BindOption } from "typedoc"; +import { Component, RendererComponent } from "typedoc/dist/lib/output/components"; +import { PageEvent } from "typedoc/dist/lib/output/events"; + +const PROJECT_ROOT = join(__dirname, "..", "..", "..", ".."); +@Component({ name: "SdkIndexLinkClientPlugin" }) +export class SdkIndexLinkClientPlugin extends RendererComponent { + @BindOption("out") + readonly out!: string; + /** + * The path pattern denotes the location of individual service client doc. + * "{{CLIENT}}" will be replaced with the client name. + * For example: `path/{{CLIENT}}/docs` will target s3 docs at `path/client-s3/docs` + */ + @BindOption("clientDocs") + readonly clientDocs!: string; + + initialize() { + this.listenTo(this.owner, { + [PageEvent.BEGIN]: this.onPageBegin, + }); + } + + onPageBegin(page: PageEvent) { + const out = isAbsolute(this.out) ? this.out : resolve(PROJECT_ROOT, this.out); + const clientDocs = isAbsolute(this.clientDocs) ? this.clientDocs : resolve(PROJECT_ROOT, this.clientDocs); + + // Get relative path from core packages doc to clients' doc. + const clientDocsPattern = relative(out, clientDocs); + if (page.model === page.project) { + // Entry point index.html and global.html page. + page.project.children + .filter((child) => child.sources[0].fileName.startsWith(`clients${sep}`)) + .forEach((child) => { + // "clients/client-s3" => "client-s3" + const clientName = child.sources[0].fileName.split(sep)[1]; + const clientDocDir = clientDocsPattern.replace(/{{CLIENT}}/g, clientName); + child.url = join(clientDocDir, "index.html"); + // @ts-ignore attach temporary flag. + child._skipRendering = true; + }); + } + + // Skip rendering empty landing page for each client. + if (page.model._skipRendering) { + page.preventDefault(); + } + } +} diff --git a/packages/core-packages-documentation-generator/tsconfig.cjs.json b/packages/core-packages-documentation-generator/tsconfig.cjs.json new file mode 100644 index 000000000000..0f2edde70413 --- /dev/null +++ b/packages/core-packages-documentation-generator/tsconfig.cjs.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "strict": false, + "rootDir": "./src", + "outDir": "./dist/cjs", + "experimentalDecorators": true, + "pretty": true, + "baseUrl": "." + }, + "extends": "../../tsconfig.cjs.json", + "include": ["src/"] +} diff --git a/packages/core-packages-documentation-generator/tsconfig.es.json b/packages/core-packages-documentation-generator/tsconfig.es.json new file mode 100644 index 000000000000..0bbb80454647 --- /dev/null +++ b/packages/core-packages-documentation-generator/tsconfig.es.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "strict": false, + "lib": ["es2015"], + "rootDir": "./src", + "outDir": "./dist/es", + "experimentalDecorators": true, + "pretty": true, + "baseUrl": "." + }, + "extends": "../../tsconfig.es.json", + "include": ["src/"] +} diff --git a/tsconfig.json b/tsconfig.json index 4cfcbf91d092..d03036c5263f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -25,6 +25,19 @@ "@aws-sdk/lib-*": ["lib/*"] } }, - "include": ["packages/"], - "exclude": ["node_modules/"] + "include": ["packages/", "lib/"], + "exclude": ["node_modules/"], + "typedocOptions": { + "readme": "README.md", + "mode": "modules", + "out": "docs", + "exclude": ["**/node_modules/**", "**/*.spec.ts"], + "excludeExternals": true, + "name": "AWS SDK for JavaScript v3", + "ignoreCompilerErrors": true, + "theme": "minimal", + "hideGenerator": true, + "clientDocs": "clients/{{CLIENT}}/docs", + "plugin": ["typedoc-plugin-lerna-packages", "@aws-sdk/core-packages-documentation-generator"] + } } diff --git a/yarn.lock b/yarn.lock index 4380a7be3b15..73119cd3205c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5631,9 +5631,9 @@ he@1.2.0: integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== highlight.js@^10.0.0: - version "10.3.2" - resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.3.2.tgz#135fd3619a00c3cbb8b4cd6dbc78d56bfcbc46f1" - integrity sha512-3jRT7OUYsVsKvukNKZCtnvRcFyCJqSEIuIMsEybAXRiFSwpt65qjPd/Pr+UOdYt7WJlt+lj3+ypUsHiySBp/Jw== + version "10.3.1" + resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.3.1.tgz#3ca6bf007377faae347e8135ff25900aac734b9a" + integrity sha512-jeW8rdPdhshYKObedYg5XGbpVgb1/DT4AHvDFXhkU7UnGSIjy9kkJ7zHG7qplhFHMitTSzh5/iClKQk3Kb2RFQ== hmac-drbg@^1.0.0: version "1.0.1" @@ -7620,9 +7620,9 @@ lunr-mutable-indexes@2.3.2: lunr ">= 2.3.0 < 2.4.0" "lunr@>= 2.3.0 < 2.4.0", lunr@^2.3.8: - version "2.3.9" - resolved "https://registry.yarnpkg.com/lunr/-/lunr-2.3.9.tgz#18b123142832337dd6e964df1a5a7707b25d35e1" - integrity sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow== + version "2.3.8" + resolved "https://registry.yarnpkg.com/lunr/-/lunr-2.3.8.tgz#a8b89c31f30b5a044b97d2d28e2da191b6ba2072" + integrity sha512-oxMeX/Y35PNFuZoHp+jUj5OSEmLCaIH4KTFJh7a93cHBoFmpw2IoPs22VIz7vyO2YUnx2Tn9dzIwO2P/4quIRg== macos-release@^2.2.0: version "2.4.1" @@ -11053,6 +11053,11 @@ typedoc-default-themes@^0.10.2: dependencies: lunr "^2.3.8" +typedoc-plugin-lerna-packages@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/typedoc-plugin-lerna-packages/-/typedoc-plugin-lerna-packages-0.3.1.tgz#3e65068e6c6ef987fc4c4553416af3fb22c8a5e6" + integrity sha512-azeP5DVv4Me+C32RoGbMAzXo7JeYmeEstMAx4mdtVGHLtrXjitlaf0pS562vogofwyIcyVnjL6BlZWvbPQ3hmw== + typedoc@^0.17.8: version "0.17.8" resolved "https://registry.yarnpkg.com/typedoc/-/typedoc-0.17.8.tgz#96b67e9454aa7853bfc4dc9a55c8a07adfd5478e"