From d442eb285a3d69aee1ec17d28c60297fb08279fa Mon Sep 17 00:00:00 2001 From: Feiyang Date: Thu, 22 Apr 2021 15:39:05 -0700 Subject: [PATCH] generate links for namespaces correctly (#4809) * generate link for namespaces correctly * add addFileNameSuffix option * add suffix to namespace files * prepare for publishing --- repo-scripts/api-documenter/package.json | 11 ++-- .../api-documenter/src/cli/BaseAction.ts | 18 ++++- .../api-documenter/src/cli/MarkdownAction.ts | 5 +- .../src/documenters/MarkdownDocumenter.ts | 66 +++++++++++++++---- .../documenters/MarkdownDocumenterHelpers.ts | 40 +++++++++-- repo-scripts/api-documenter/src/start.ts | 2 + 6 files changed, 114 insertions(+), 28 deletions(-) diff --git a/repo-scripts/api-documenter/package.json b/repo-scripts/api-documenter/package.json index 8e702c69ef5..312d6fd8352 100644 --- a/repo-scripts/api-documenter/package.json +++ b/repo-scripts/api-documenter/package.json @@ -1,7 +1,6 @@ { "name": "@firebase/api-documenter", "version": "0.1.0", - "private": true, "description": "Read JSON files from api-extractor, generate documentation pages", "repository": { "directory": "repo-scripts/documenter", @@ -14,9 +13,12 @@ "test": "TS_NODE_COMPILER_OPTIONS='{\"module\":\"commonjs\"}' nyc --reporter lcovonly -- mocha src/**/*.test.ts --config ../../config/mocharc.node.js" }, "bin": { - "api-documenter": "./bin/api-documenter" + "api-documenter-fire": "./dist/start.js" }, - "main": "lib/index.js", + "files": [ + "dist" + ], + "main": "dist/index.js", "typings": "dist/rollup.d.ts", "dependencies": { "api-extractor-model-me": "0.1.1", @@ -24,7 +26,8 @@ "@rushstack/node-core-library": "3.36.0", "@rushstack/ts-command-line": "4.7.8", "colors": "~1.2.1", - "resolve": "~1.17.0" + "resolve": "~1.17.0", + "tslib": "^2.1.0" }, "devDependencies": { "@types/resolve": "1.17.1", diff --git a/repo-scripts/api-documenter/src/cli/BaseAction.ts b/repo-scripts/api-documenter/src/cli/BaseAction.ts index 8dfbc40770e..0bed34178d3 100644 --- a/repo-scripts/api-documenter/src/cli/BaseAction.ts +++ b/repo-scripts/api-documenter/src/cli/BaseAction.ts @@ -24,6 +24,7 @@ import colors from 'colors'; import { CommandLineAction, + CommandLineFlagParameter, CommandLineStringParameter } from '@rushstack/ts-command-line'; import { FileSystem } from '@rushstack/node-core-library'; @@ -39,11 +40,13 @@ export interface IBuildApiModelResult { apiModel: ApiModel; inputFolder: string; outputFolder: string; + addFileNameSuffix: boolean; } export abstract class BaseAction extends CommandLineAction { private _inputFolderParameter!: CommandLineStringParameter; private _outputFolderParameter!: CommandLineStringParameter; + private _fileNameSuffixParameter!: CommandLineFlagParameter; protected onDefineParameters(): void { // override @@ -65,6 +68,17 @@ export abstract class BaseAction extends CommandLineAction { ` ANY EXISTING CONTENTS WILL BE DELETED!` + ` If omitted, the default is "./${this.actionName}"` }); + + this._fileNameSuffixParameter = this.defineFlagParameter({ + parameterLongName: '--name-suffix', + parameterShortName: '-s', + description: + `Add suffix to interface and class names in the file path.` + + `For example, packageA.myinterface_i.md for MyInterface interface, ` + + `Add packageA.myclass_c.md for MyClass class.` + + `This is to avoid name conflict in case packageA also has, for example, an entry point with the same name in lowercase.` + + `This option is specifically designed for the Admin SDK where such case occurs.` + }); } protected buildApiModel(): IBuildApiModelResult { @@ -79,6 +93,8 @@ export abstract class BaseAction extends CommandLineAction { this._outputFolderParameter.value || `./${this.actionName}`; FileSystem.ensureFolder(outputFolder); + const addFileNameSuffix: boolean = this._fileNameSuffixParameter.value; + for (const filename of FileSystem.readFolder(inputFolder)) { if (filename.match(/\.api\.json$/i)) { console.log(`Reading ${filename}`); @@ -89,7 +105,7 @@ export abstract class BaseAction extends CommandLineAction { this._applyInheritDoc(apiModel, apiModel); - return { apiModel, inputFolder, outputFolder }; + return { apiModel, inputFolder, outputFolder, addFileNameSuffix }; } // TODO: This is a temporary workaround. The long term plan is for API Extractor's DocCommentEnhancer diff --git a/repo-scripts/api-documenter/src/cli/MarkdownAction.ts b/repo-scripts/api-documenter/src/cli/MarkdownAction.ts index 274e0ed48d8..e28e9ec6854 100644 --- a/repo-scripts/api-documenter/src/cli/MarkdownAction.ts +++ b/repo-scripts/api-documenter/src/cli/MarkdownAction.ts @@ -35,12 +35,13 @@ export class MarkdownAction extends BaseAction { protected async onExecute(): Promise { // override - const { apiModel, outputFolder } = this.buildApiModel(); + const { apiModel, outputFolder, addFileNameSuffix } = this.buildApiModel(); const markdownDocumenter: MarkdownDocumenter = new MarkdownDocumenter({ apiModel, documenterConfig: undefined, - outputFolder + outputFolder, + addFileNameSuffix }); markdownDocumenter.generateFiles(); } diff --git a/repo-scripts/api-documenter/src/documenters/MarkdownDocumenter.ts b/repo-scripts/api-documenter/src/documenters/MarkdownDocumenter.ts index 2f79de6e9ad..3163b88d536 100644 --- a/repo-scripts/api-documenter/src/documenters/MarkdownDocumenter.ts +++ b/repo-scripts/api-documenter/src/documenters/MarkdownDocumenter.ts @@ -91,6 +91,7 @@ export interface IMarkdownDocumenterOptions { apiModel: ApiModel; documenterConfig: DocumenterConfig | undefined; outputFolder: string; + addFileNameSuffix: boolean; } /** @@ -104,11 +105,13 @@ export class MarkdownDocumenter { private readonly _markdownEmitter: CustomMarkdownEmitter; private readonly _outputFolder: string; private readonly _pluginLoader: PluginLoader; + private readonly _addFileNameSuffix: boolean; public constructor(options: IMarkdownDocumenterOptions) { this._apiModel = options.apiModel; this._documenterConfig = options.documenterConfig; this._outputFolder = options.outputFolder; + this._addFileNameSuffix = options.addFileNameSuffix; this._tsdocConfiguration = CustomDocNodes.configuration; this._markdownEmitter = new CustomMarkdownEmitter(this._apiModel); @@ -123,7 +126,7 @@ export class MarkdownDocumenter { outputFolder: this._outputFolder, documenter: new MarkdownDocumenterAccessor({ getLinkForApiItem: (apiItem: ApiItem) => { - return getLinkForApiItem(apiItem); + return getLinkForApiItem(apiItem, this._addFileNameSuffix); } }) }); @@ -156,7 +159,7 @@ export class MarkdownDocumenter { // write to file const filename: string = path.join( this._outputFolder, - getFilenameForApiItem(apiItem) + getFilenameForApiItem(apiItem, this._addFileNameSuffix) ); const stringBuilder: StringBuilder = new StringBuilder(); @@ -172,7 +175,7 @@ export class MarkdownDocumenter { this._markdownEmitter.emit(stringBuilder, output, { contextApiItem: apiItem, onGetFilenameForApiItem: (apiItemForFilename: ApiItem) => { - return getLinkForApiItem(apiItemForFilename); + return getLinkForApiItem(apiItemForFilename, this._addFileNameSuffix); } }); @@ -393,7 +396,11 @@ export class MarkdownDocumenter { case ApiItemKind.Constructor: { constructorsTable.addRow( new DocTableRow({ configuration }, [ - createTitleCell(apiMember, configuration), + createTitleCell( + apiMember, + configuration, + this._addFileNameSuffix + ), createModifiersCell(apiMember, configuration), createDescriptionCell(apiMember, configuration) ]) @@ -407,7 +414,11 @@ export class MarkdownDocumenter { case ApiItemKind.Method: { methodsTable.addRow( new DocTableRow({ configuration }, [ - createTitleCell(apiMember, configuration), + createTitleCell( + apiMember, + configuration, + this._addFileNameSuffix + ), createModifiersCell(apiMember, configuration), createDescriptionCell(apiMember, configuration) ]) @@ -422,7 +433,11 @@ export class MarkdownDocumenter { if ((apiMember as ApiPropertyItem).isEventProperty) { eventsTable.addRow( new DocTableRow({ configuration }, [ - createTitleCell(apiMember, configuration), + createTitleCell( + apiMember, + configuration, + this._addFileNameSuffix + ), createModifiersCell(apiMember, configuration), this._createPropertyTypeCell(apiMember), createDescriptionCell(apiMember, configuration) @@ -435,7 +450,11 @@ export class MarkdownDocumenter { } else { propertiesTable.addRow( new DocTableRow({ configuration }, [ - createTitleCell(apiMember, configuration), + createTitleCell( + apiMember, + configuration, + this._addFileNameSuffix + ), createModifiersCell(apiMember, configuration), this._createPropertyTypeCell(apiMember), createDescriptionCell(apiMember, configuration) @@ -509,7 +528,11 @@ export class MarkdownDocumenter { case ApiItemKind.MethodSignature: { methodsTable.addRow( new DocTableRow({ configuration }, [ - createTitleCell(apiMember, configuration), + createTitleCell( + apiMember, + configuration, + this._addFileNameSuffix + ), createDescriptionCell(apiMember, configuration) ]) ); @@ -523,7 +546,11 @@ export class MarkdownDocumenter { if ((apiMember as ApiPropertyItem).isEventProperty) { eventsTable.addRow( new DocTableRow({ configuration }, [ - createTitleCell(apiMember, configuration), + createTitleCell( + apiMember, + configuration, + this._addFileNameSuffix + ), this._createPropertyTypeCell(apiMember), createDescriptionCell(apiMember, configuration) ]) @@ -534,7 +561,11 @@ export class MarkdownDocumenter { } else { propertiesTable.addRow( new DocTableRow({ configuration }, [ - createTitleCell(apiMember, configuration), + createTitleCell( + apiMember, + configuration, + this._addFileNameSuffix + ), this._createPropertyTypeCell(apiMember), createDescriptionCell(apiMember, configuration) ]) @@ -686,7 +717,10 @@ export class MarkdownDocumenter { configuration, tagName: '@link', linkText: unwrappedTokenText, - urlDestination: getLinkForApiItem(apiItemResult.resolvedApiItem) + urlDestination: getLinkForApiItem( + apiItemResult.resolvedApiItem, + this._addFileNameSuffix + ) }) ); continue; @@ -714,7 +748,7 @@ export class MarkdownDocumenter { for (const apiMember of apiModel.members) { const row: DocTableRow = new DocTableRow({ configuration }, [ - createTitleCell(apiMember, configuration), + createTitleCell(apiMember, configuration, this._addFileNameSuffix), createDescriptionCell(apiMember, configuration) ]); @@ -755,7 +789,11 @@ export class MarkdownDocumenter { for (const entryPoint of apiContainer.entryPoints) { const row: DocTableRow = new DocTableRow({ configuration }, [ - createEntryPointTitleCell(entryPoint, configuration), + createEntryPointTitleCell( + entryPoint, + configuration, + this._addFileNameSuffix + ), createDescriptionCell(entryPoint, configuration) ]); @@ -828,7 +866,7 @@ export class MarkdownDocumenter { for (const apiMember of apiMembers) { const row: DocTableRow = new DocTableRow({ configuration }, [ - createTitleCell(apiMember, configuration), + createTitleCell(apiMember, configuration, this._addFileNameSuffix), createDescriptionCell(apiMember, configuration) ]); diff --git a/repo-scripts/api-documenter/src/documenters/MarkdownDocumenterHelpers.ts b/repo-scripts/api-documenter/src/documenters/MarkdownDocumenterHelpers.ts index 30dec957eeb..590393aa895 100644 --- a/repo-scripts/api-documenter/src/documenters/MarkdownDocumenterHelpers.ts +++ b/repo-scripts/api-documenter/src/documenters/MarkdownDocumenterHelpers.ts @@ -49,13 +49,19 @@ import { DocNoteBox } from '../nodes/DocNoteBox'; import { DocTableRow } from '../nodes/DocTableRow'; import { DocTableCell } from '../nodes/DocTableCell'; -export function getLinkForApiItem(apiItem: ApiItem) { - const fileName = getFilenameForApiItem(apiItem); +export function getLinkForApiItem( + apiItem: ApiItem, + addFileNameSuffix: boolean +) { + const fileName = getFilenameForApiItem(apiItem, addFileNameSuffix); const headingAnchor = getHeadingAnchorForApiItem(apiItem); return `./${fileName}#${headingAnchor}`; } -export function getFilenameForApiItem(apiItem: ApiItem): string { +export function getFilenameForApiItem( + apiItem: ApiItem, + addFileNameSuffix: boolean +): string { if (apiItem.kind === ApiItemKind.Model) { return 'index.md'; } @@ -96,9 +102,27 @@ export function getFilenameForApiItem(apiItem: ApiItem): string { multipleEntryPoints = true; } break; + case ApiItemKind.Namespace: + baseName += '.' + qualifiedName; + if (addFileNameSuffix) { + baseName += '_n'; + } + break; + // append the file name with the first letter of the ApiItemKind to avoid name collision. + // Sometimes we could have a class/interface and an entry point that have the same name. + // This happened in the admin SDK where the App interface and the app namespace write to the same file. case ApiItemKind.Class: + baseName += '.' + qualifiedName; + if (addFileNameSuffix) { + baseName += '_c'; + } + break; case ApiItemKind.Interface: baseName += '.' + qualifiedName; + if (addFileNameSuffix) { + baseName += '_i'; + } + break; } } return baseName + '.md'; @@ -224,7 +248,8 @@ export function createExampleSection( export function createTitleCell( apiItem: ApiItem, - configuration: TSDocConfiguration + configuration: TSDocConfiguration, + addFileNameSuffix: boolean ): DocTableCell { return new DocTableCell({ configuration }, [ new DocParagraph({ configuration }, [ @@ -232,7 +257,7 @@ export function createTitleCell( configuration, tagName: '@link', linkText: Utilities.getConciseSignature(apiItem), - urlDestination: getLinkForApiItem(apiItem) + urlDestination: getLinkForApiItem(apiItem, addFileNameSuffix) }) ]) ]); @@ -339,7 +364,8 @@ export function createThrowsSection( export function createEntryPointTitleCell( apiItem: ApiEntryPoint, - configuration: TSDocConfiguration + configuration: TSDocConfiguration, + addFileNameSuffix: boolean ): DocTableCell { return new DocTableCell({ configuration }, [ new DocParagraph({ configuration }, [ @@ -347,7 +373,7 @@ export function createEntryPointTitleCell( configuration, tagName: '@link', linkText: `/${apiItem.displayName}`, - urlDestination: getLinkForApiItem(apiItem) + urlDestination: getLinkForApiItem(apiItem, addFileNameSuffix) }) ]) ]); diff --git a/repo-scripts/api-documenter/src/start.ts b/repo-scripts/api-documenter/src/start.ts index 3ba945be7c7..55d50fb21f2 100644 --- a/repo-scripts/api-documenter/src/start.ts +++ b/repo-scripts/api-documenter/src/start.ts @@ -1,3 +1,5 @@ +#!/usr/bin/env node + /** * @license * Copyright 2020 Google LLC