diff --git a/package.json b/package.json index 441cc90b05d..5cb6ad70e2a 100644 --- a/package.json +++ b/package.json @@ -54,8 +54,7 @@ "docgen:exp": "ts-node-script scripts/exp/docgen.ts", "postinstall": "yarn --cwd repo-scripts/changelog-generator build", "sa": "ts-node-script repo-scripts/size-analysis/cli.ts", - "api-documenter-devsite": "ts-node-script repo-scripts/api-documenter/src/start.ts", - "toc-devsite": "ts-node-script scripts/exp/generate-devsite-toc.ts -i temp" + "api-documenter-devsite": "ts-node-script repo-scripts/api-documenter/src/start.ts" }, "repository": { "type": "git", diff --git a/repo-scripts/api-documenter/README.md b/repo-scripts/api-documenter/README.md index 8c57fc065e8..302cdd25bd8 100644 --- a/repo-scripts/api-documenter/README.md +++ b/repo-scripts/api-documenter/README.md @@ -3,3 +3,14 @@ It is a fork of [API Documenter](https://github.com/microsoft/rushstack/tree/master/apps/api-documenter) It reads the *.api.json data files produced by [API Extractor](https://api-extractor.com/), and then generates files in [Markdown](https://en.wikipedia.org/wiki/Markdown) format suitable for displaying in Firebase Devsite. + +## Generate toc for Firebase devsite +`api-documenter-fire toc -i temp -p "/docs/reference/js/v9"` + +`-i` and `-p` (`--host-path`) are required parameters. +Use `-i` to specify the folder that contains api.json files. +Use `-p` to specify the g3 path that contains the reference docs. + +By default, the command will create `toc.yaml` in folder `/toc`. To change the output folder, use the flag `-o`. + +To generate toc for the Firebase JS SDK, also set the flag `-j` to create the top level `firebase` toc item. \ No newline at end of file diff --git a/repo-scripts/api-documenter/package.json b/repo-scripts/api-documenter/package.json index deba19c0281..60a6b0dbb18 100644 --- a/repo-scripts/api-documenter/package.json +++ b/repo-scripts/api-documenter/package.json @@ -27,9 +27,11 @@ "@rushstack/ts-command-line": "4.7.8", "colors": "~1.2.1", "resolve": "~1.17.0", - "tslib": "^2.1.0" + "tslib": "^2.1.0", + "js-yaml": "4.0.0" }, "devDependencies": { + "@types/js-yaml": "4.0.0", "@types/resolve": "1.17.1", "mocha-chai-jest-snapshot": "1.1.1" } diff --git a/repo-scripts/api-documenter/src/cli/ApiDocumenterCommandLine.ts b/repo-scripts/api-documenter/src/cli/ApiDocumenterCommandLine.ts index ac10e970edd..4e98f53df13 100644 --- a/repo-scripts/api-documenter/src/cli/ApiDocumenterCommandLine.ts +++ b/repo-scripts/api-documenter/src/cli/ApiDocumenterCommandLine.ts @@ -20,6 +20,7 @@ import { CommandLineParser } from '@rushstack/ts-command-line'; import { MarkdownAction } from './MarkdownAction'; +import { TocAction } from './TocAction'; export class ApiDocumenterCommandLine extends CommandLineParser { public constructor() { @@ -39,5 +40,6 @@ export class ApiDocumenterCommandLine extends CommandLineParser { private _populateActions(): void { this.addAction(new MarkdownAction(this)); + this.addAction(new TocAction(this)); } } diff --git a/repo-scripts/api-documenter/src/cli/TocAction.ts b/repo-scripts/api-documenter/src/cli/TocAction.ts new file mode 100644 index 00000000000..316be83a0d2 --- /dev/null +++ b/repo-scripts/api-documenter/src/cli/TocAction.ts @@ -0,0 +1,77 @@ +/** + * @license + * Copyright 2021 Google LLC + * + * 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. + */ + +import { + CommandLineFlagParameter, + CommandLineStringParameter +} from '@rushstack/ts-command-line'; +import { ApiDocumenterCommandLine } from './ApiDocumenterCommandLine'; +import { BaseAction } from './BaseAction'; +import { generateToc } from '../toc'; + +export class TocAction extends BaseAction { + private _g3PathParameter!: CommandLineStringParameter; + private _jsSDKParameter!: CommandLineFlagParameter; + public constructor(parser: ApiDocumenterCommandLine) { + super({ + actionName: 'toc', + summary: 'Generate TOC(table of content) for Firebase devsite.', + documentation: 'Generate TOC(table of content) for Firebase devsite.' + }); + } + + protected onDefineParameters(): void { + super.onDefineParameters(); + + this._g3PathParameter = this.defineStringParameter({ + parameterLongName: '--host-path', + parameterShortName: '-p', + argumentName: 'HOSTPATH', + description: `Specifies the path where the reference docs resides (e.g. g3). + Used to generate paths in the toc` + }); + + this._jsSDKParameter = this.defineFlagParameter({ + parameterLongName: '--js-sdk', + parameterShortName: '-j', + description: + `Generating toc for the Firebase JS SDK.` + + `It will create an artificial top level toc item "firebase".` + }); + } + + protected async onExecute(): Promise { + // override + const { apiModel, outputFolder, addFileNameSuffix } = this.buildApiModel(); + const g3Path: string | undefined = this._g3PathParameter.value; + const jsSdk: boolean = this._jsSDKParameter.value; + + if (!g3Path) { + throw new Error( + '--g3-path is a required to generate toc, but it is not provided' + ); + } + + generateToc({ + apiModel, + outputFolder, + addFileNameSuffix, + g3Path, + jsSdk + }); + } +} diff --git a/repo-scripts/api-documenter/src/toc.ts b/repo-scripts/api-documenter/src/toc.ts new file mode 100644 index 00000000000..2b71f3c8922 --- /dev/null +++ b/repo-scripts/api-documenter/src/toc.ts @@ -0,0 +1,107 @@ +/** + * @license + * Copyright 2021 Google LLC + * + * 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. + */ + +import yaml from 'js-yaml'; +import { ApiItem, ApiItemKind, ApiModel } from 'api-extractor-model-me'; +import { getFilenameForApiItem } from './documenters/MarkdownDocumenterHelpers'; +import { ModuleSource } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference'; +import { writeFileSync } from 'fs'; +import { resolve } from 'path'; + +export interface ITocGenerationOptions { + apiModel: ApiModel; + g3Path: string; + outputFolder: string; + addFileNameSuffix: boolean; + jsSdk: boolean; +} + +interface ITocItem { + title: string; + path: string; + section?: ITocItem[]; +} + +export function generateToc({ + apiModel, + g3Path, + outputFolder, + addFileNameSuffix, + jsSdk +}: ITocGenerationOptions) { + const toc = []; + + if (jsSdk) { + const firebaseToc: ITocItem = { + title: 'firebase', + path: `${g3Path}/index`, + section: [] + }; + toc.push(firebaseToc); + } + + generateTocRecursively(apiModel, g3Path, addFileNameSuffix, toc); + + writeFileSync( + resolve(outputFolder, 'toc.yaml'), + yaml.dump( + { toc }, + { + quotingType: '"' + } + ) + ); +} + +function generateTocRecursively( + apiItem: ApiItem, + g3Path: string, + addFileNameSuffix: boolean, + toc: ITocItem[] +) { + if (apiItem.kind === ApiItemKind.EntryPoint) { + // Entry point + const entryPointName = (apiItem.canonicalReference + .source! as ModuleSource).escapedPath.replace('@firebase/', ''); + const entryPointToc: ITocItem = { + title: entryPointName, + path: `${g3Path}/${getFilenameForApiItem(apiItem, addFileNameSuffix)}`, + section: [] + }; + + for (const member of apiItem.members) { + // only classes and interfaces have dedicated pages + if ( + member.kind === ApiItemKind.Class || + member.kind === ApiItemKind.Interface + ) { + const fileName = getFilenameForApiItem(member, addFileNameSuffix); + entryPointToc.section!.push({ + title: member.displayName, + path: `${g3Path}/${fileName}` + }); + } + } + + toc.push(entryPointToc); + } else { + // travel the api tree to find the next entry point + for (const member of apiItem.members) { + generateTocRecursively(member, g3Path, addFileNameSuffix, toc); + } + } +} diff --git a/scripts/exp/generate-devsite-toc.ts b/scripts/exp/generate-devsite-toc.ts deleted file mode 100644 index 93ab5352dc0..00000000000 --- a/scripts/exp/generate-devsite-toc.ts +++ /dev/null @@ -1,134 +0,0 @@ -/** - * @license - * Copyright 2021 Google LLC - * - * 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. - */ - -import * as yargs from 'yargs'; -import * as fs from 'fs'; -import * as path from 'path'; -import * as yaml from 'js-yaml'; - -interface TocItem { - title: string; - path: string; - section?: TocItem[]; -} - -const argv = yargs - .options({ - input: { - alias: 'i', - type: 'string', - default: 'temp' - } - }) - .help().argv; - -const REF_DOC_DIR = '/docs/reference/js'; -const REPORT_DIR = path.resolve(process.cwd(), argv.input); - -const js: TocItem = { - title: 'firebase', - path: `${REF_DOC_DIR}/index` -}; -const toc = [js]; - -for (const fileName of fs.readdirSync(REPORT_DIR)) { - const report = require(`${REPORT_DIR}/${fileName}`); - - /** - * Entry point - */ - for (const entryPoint of report.members) { - const entryPointName = entryPoint.canonicalReference - .replace('!', '') - .replace('@firebase/', ''); - const entryPointToc: Required = { - title: entryPointName, - path: `${REF_DOC_DIR}/${getFileName( - entryPoint, - entryPoint, - report.members.length > 1 - )}`, - section: [] - }; - - for (const member of entryPoint.members) { - const fileName = getFileName( - member, - entryPoint, - report.members.length > 1 - ); - - if (fileName) { - entryPointToc.section.push({ - title: member.name, - path: `${REF_DOC_DIR}/${fileName}` - }); - } - } - toc.push(entryPointToc); - } -} - -console.log( - yaml.dump( - { toc }, - { - quotingType: '"' - } - ) -); - -function getFileName( - apiMember: any, - entryPoint: any, - multipleEntryPoints = false -) { - const entryPointName = entryPoint.canonicalReference.replace('!', ''); - const unscopedName = getUnscopedName(entryPointName); - let entryPointPrefix = unscopedName; - - if (multipleEntryPoints) { - const nameParts = unscopedName.split('/'); - if (nameParts.length === 1) { - entryPointPrefix = `${unscopedName}_`; - } else { - // > 1 - entryPointPrefix = nameParts.join('_'); - } - } - - switch (apiMember.kind) { - case 'EntryPoint': - return entryPointPrefix; - case 'Class': - case 'Interface': - return `${entryPointPrefix}.${apiMember.name.toLowerCase()}`; - default: - // other kinds don't have their own files - return null; - } -} - -function getUnscopedName(packageName: string) { - const parts = packageName.split('/'); - if (parts.length === 1) { - return packageName; - } - - parts.shift(); - return parts.join('/'); -}