UNPKG

@microsoft/api-documenter

Version:

Read JSON files from api-extractor, generate documentation pages

987 lines 45.9 kB
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. import * as path from 'node:path'; import { PackageName, FileSystem, NewlineKind } from '@rushstack/node-core-library'; import { DocSection, DocPlainText, DocLinkTag, StringBuilder, DocNodeKind, DocParagraph, DocCodeSpan, DocFencedCode, StandardTags } from '@microsoft/tsdoc'; import { ApiItemKind, ApiReleaseTagMixin, ApiDocumentedItem, ApiClass, ReleaseTag, ApiStaticMixin, ApiPropertyItem, ApiInterface, ApiAbstractMixin, ApiParameterListMixin, ApiReturnTypeMixin, ApiDeclaredItem, ExcerptTokenKind, ApiTypeAlias, ApiOptionalMixin, ApiInitializerMixin, ApiProtectedMixin, ApiReadonlyMixin } from '@microsoft/api-extractor-model'; import { CustomDocNodes } from '../nodes/CustomDocNodeKind'; import { DocHeading } from '../nodes/DocHeading'; import { DocTable } from '../nodes/DocTable'; import { DocEmphasisSpan } from '../nodes/DocEmphasisSpan'; import { DocTableRow } from '../nodes/DocTableRow'; import { DocTableCell } from '../nodes/DocTableCell'; import { DocNoteBox } from '../nodes/DocNoteBox'; import { Utilities } from '../utils/Utilities'; import { CustomMarkdownEmitter } from '../markdown/CustomMarkdownEmitter'; import { PluginLoader } from '../plugin/PluginLoader'; import { MarkdownDocumenterFeatureContext } from '../plugin/MarkdownDocumenterFeature'; import { MarkdownDocumenterAccessor } from '../plugin/MarkdownDocumenterAccessor'; /** * Renders API documentation in the Markdown file format. * For more info: https://en.wikipedia.org/wiki/Markdown */ export class MarkdownDocumenter { constructor(options) { this._apiModel = options.apiModel; this._documenterConfig = options.documenterConfig; this._outputFolder = options.outputFolder; this._tsdocConfiguration = CustomDocNodes.configuration; this._markdownEmitter = new CustomMarkdownEmitter(this._apiModel); this._pluginLoader = new PluginLoader(); } generateFiles() { if (this._documenterConfig) { this._pluginLoader.load(this._documenterConfig, () => { return new MarkdownDocumenterFeatureContext({ apiModel: this._apiModel, outputFolder: this._outputFolder, documenter: new MarkdownDocumenterAccessor({ getLinkForApiItem: (apiItem) => { return this._getLinkFilenameForApiItem(apiItem); } }) }); }); } console.log(); this._deleteOldOutputFiles(); this._writeApiItemPage(this._apiModel); if (this._pluginLoader.markdownDocumenterFeature) { this._pluginLoader.markdownDocumenterFeature.onFinished({}); } } _writeApiItemPage(apiItem) { const configuration = this._tsdocConfiguration; const output = new DocSection({ configuration }); this._writeBreadcrumb(output, apiItem); const scopedName = apiItem.getScopedNameWithinPackage(); switch (apiItem.kind) { case ApiItemKind.Class: output.appendNode(new DocHeading({ configuration, title: `${scopedName} class` })); break; case ApiItemKind.Enum: output.appendNode(new DocHeading({ configuration, title: `${scopedName} enum` })); break; case ApiItemKind.Interface: output.appendNode(new DocHeading({ configuration, title: `${scopedName} interface` })); break; case ApiItemKind.Constructor: case ApiItemKind.ConstructSignature: output.appendNode(new DocHeading({ configuration, title: scopedName })); break; case ApiItemKind.Method: case ApiItemKind.MethodSignature: output.appendNode(new DocHeading({ configuration, title: `${scopedName} method` })); break; case ApiItemKind.Function: output.appendNode(new DocHeading({ configuration, title: `${scopedName} function` })); break; case ApiItemKind.Model: output.appendNode(new DocHeading({ configuration, title: `API Reference` })); break; case ApiItemKind.Namespace: output.appendNode(new DocHeading({ configuration, title: `${scopedName} namespace` })); break; case ApiItemKind.Package: console.log(`Writing ${apiItem.displayName} package`); const unscopedPackageName = PackageName.getUnscopedName(apiItem.displayName); output.appendNode(new DocHeading({ configuration, title: `${unscopedPackageName} package` })); break; case ApiItemKind.Property: case ApiItemKind.PropertySignature: output.appendNode(new DocHeading({ configuration, title: `${scopedName} property` })); break; case ApiItemKind.TypeAlias: output.appendNode(new DocHeading({ configuration, title: `${scopedName} type` })); break; case ApiItemKind.Variable: output.appendNode(new DocHeading({ configuration, title: `${scopedName} variable` })); break; default: throw new Error('Unsupported API item kind: ' + apiItem.kind); } if (ApiReleaseTagMixin.isBaseClassOf(apiItem)) { if (apiItem.releaseTag === ReleaseTag.Alpha) { this._writeAlphaWarning(output); } else if (apiItem.releaseTag === ReleaseTag.Beta) { this._writeBetaWarning(output); } } const decoratorBlocks = []; if (apiItem instanceof ApiDocumentedItem) { const tsdocComment = apiItem.tsdocComment; if (tsdocComment) { decoratorBlocks.push(...tsdocComment.customBlocks.filter((block) => block.blockTag.tagNameWithUpperCase === StandardTags.decorator.tagNameWithUpperCase)); if (tsdocComment.deprecatedBlock) { output.appendNode(new DocNoteBox({ configuration }, [ new DocParagraph({ configuration }, [ new DocPlainText({ configuration, text: 'Warning: This API is now obsolete. ' }) ]), ...tsdocComment.deprecatedBlock.content.nodes ])); } this._appendSection(output, tsdocComment.summarySection); } } if (apiItem instanceof ApiDeclaredItem) { if (apiItem.excerpt.text.length > 0) { output.appendNode(new DocParagraph({ configuration }, [ new DocEmphasisSpan({ configuration, bold: true }, [ new DocPlainText({ configuration, text: 'Signature:' }) ]) ])); output.appendNode(new DocFencedCode({ configuration, code: apiItem.getExcerptWithModifiers(), language: 'typescript' })); } this._writeHeritageTypes(output, apiItem); } if (decoratorBlocks.length > 0) { output.appendNode(new DocParagraph({ configuration }, [ new DocEmphasisSpan({ configuration, bold: true }, [ new DocPlainText({ configuration, text: 'Decorators:' }) ]) ])); for (const decoratorBlock of decoratorBlocks) { output.appendNodes(decoratorBlock.content.nodes); } } let appendRemarks = true; switch (apiItem.kind) { case ApiItemKind.Class: case ApiItemKind.Interface: case ApiItemKind.Namespace: case ApiItemKind.Package: this._writeRemarksSection(output, apiItem); appendRemarks = false; break; } switch (apiItem.kind) { case ApiItemKind.Class: this._writeClassTables(output, apiItem); break; case ApiItemKind.Enum: this._writeEnumTables(output, apiItem); break; case ApiItemKind.Interface: this._writeInterfaceTables(output, apiItem); break; case ApiItemKind.Constructor: case ApiItemKind.ConstructSignature: case ApiItemKind.Method: case ApiItemKind.MethodSignature: case ApiItemKind.Function: this._writeParameterTables(output, apiItem); this._writeThrowsSection(output, apiItem); break; case ApiItemKind.Namespace: this._writePackageOrNamespaceTables(output, apiItem); break; case ApiItemKind.Model: this._writeModelTable(output, apiItem); break; case ApiItemKind.Package: this._writePackageOrNamespaceTables(output, apiItem); break; case ApiItemKind.Property: case ApiItemKind.PropertySignature: break; case ApiItemKind.TypeAlias: break; case ApiItemKind.Variable: break; default: throw new Error('Unsupported API item kind: ' + apiItem.kind); } if (appendRemarks) { this._writeRemarksSection(output, apiItem); } const filename = path.join(this._outputFolder, this._getFilenameForApiItem(apiItem)); const stringBuilder = new StringBuilder(); stringBuilder.append('<!-- Do not edit this file. It is automatically generated by API Documenter. -->\n\n'); this._markdownEmitter.emit(stringBuilder, output, { contextApiItem: apiItem, onGetFilenameForApiItem: (apiItemForFilename) => { return this._getLinkFilenameForApiItem(apiItemForFilename); } }); let pageContent = stringBuilder.toString(); if (this._pluginLoader.markdownDocumenterFeature) { // Allow the plugin to customize the pageContent const eventArgs = { apiItem: apiItem, outputFilename: filename, pageContent: pageContent }; this._pluginLoader.markdownDocumenterFeature.onBeforeWritePage(eventArgs); pageContent = eventArgs.pageContent; } FileSystem.writeFile(filename, pageContent, { convertLineEndings: this._documenterConfig ? this._documenterConfig.newlineKind : NewlineKind.CrLf }); } _writeHeritageTypes(output, apiItem) { const configuration = this._tsdocConfiguration; if (apiItem instanceof ApiClass) { if (apiItem.extendsType) { const extendsParagraph = new DocParagraph({ configuration }, [ new DocEmphasisSpan({ configuration, bold: true }, [ new DocPlainText({ configuration, text: 'Extends: ' }) ]) ]); this._appendExcerptWithHyperlinks(extendsParagraph, apiItem.extendsType.excerpt); output.appendNode(extendsParagraph); } if (apiItem.implementsTypes.length > 0) { const implementsParagraph = new DocParagraph({ configuration }, [ new DocEmphasisSpan({ configuration, bold: true }, [ new DocPlainText({ configuration, text: 'Implements: ' }) ]) ]); let needsComma = false; for (const implementsType of apiItem.implementsTypes) { if (needsComma) { implementsParagraph.appendNode(new DocPlainText({ configuration, text: ', ' })); } this._appendExcerptWithHyperlinks(implementsParagraph, implementsType.excerpt); needsComma = true; } output.appendNode(implementsParagraph); } } if (apiItem instanceof ApiInterface) { if (apiItem.extendsTypes.length > 0) { const extendsParagraph = new DocParagraph({ configuration }, [ new DocEmphasisSpan({ configuration, bold: true }, [ new DocPlainText({ configuration, text: 'Extends: ' }) ]) ]); let needsComma = false; for (const extendsType of apiItem.extendsTypes) { if (needsComma) { extendsParagraph.appendNode(new DocPlainText({ configuration, text: ', ' })); } this._appendExcerptWithHyperlinks(extendsParagraph, extendsType.excerpt); needsComma = true; } output.appendNode(extendsParagraph); } } if (apiItem instanceof ApiTypeAlias) { const refs = apiItem.excerptTokens.filter((token) => token.kind === ExcerptTokenKind.Reference && token.canonicalReference && this._apiModel.resolveDeclarationReference(token.canonicalReference, undefined).resolvedApiItem); if (refs.length > 0) { const referencesParagraph = new DocParagraph({ configuration }, [ new DocEmphasisSpan({ configuration, bold: true }, [ new DocPlainText({ configuration, text: 'References: ' }) ]) ]); let needsComma = false; const visited = new Set(); for (const ref of refs) { if (visited.has(ref.text)) { continue; } visited.add(ref.text); if (needsComma) { referencesParagraph.appendNode(new DocPlainText({ configuration, text: ', ' })); } this._appendExcerptTokenWithHyperlinks(referencesParagraph, ref); needsComma = true; } output.appendNode(referencesParagraph); } } } _writeRemarksSection(output, apiItem) { const configuration = this._tsdocConfiguration; if (apiItem instanceof ApiDocumentedItem) { const tsdocComment = apiItem.tsdocComment; if (tsdocComment) { // Write the @remarks block if (tsdocComment.remarksBlock) { output.appendNode(new DocHeading({ configuration, title: 'Remarks' })); this._appendSection(output, tsdocComment.remarksBlock.content); } // Write the @example blocks const exampleBlocks = tsdocComment.customBlocks.filter((x) => x.blockTag.tagNameWithUpperCase === StandardTags.example.tagNameWithUpperCase); let exampleNumber = 1; for (const exampleBlock of exampleBlocks) { const heading = exampleBlocks.length > 1 ? `Example ${exampleNumber}` : 'Example'; output.appendNode(new DocHeading({ configuration, title: heading })); this._appendSection(output, exampleBlock.content); ++exampleNumber; } } } } _writeThrowsSection(output, apiItem) { const configuration = this._tsdocConfiguration; if (apiItem instanceof ApiDocumentedItem) { const tsdocComment = apiItem.tsdocComment; if (tsdocComment) { // Write the @throws blocks const throwsBlocks = tsdocComment.customBlocks.filter((x) => x.blockTag.tagNameWithUpperCase === StandardTags.throws.tagNameWithUpperCase); if (throwsBlocks.length > 0) { const heading = 'Exceptions'; output.appendNode(new DocHeading({ configuration, title: heading })); for (const throwsBlock of throwsBlocks) { this._appendSection(output, throwsBlock.content); } } } } } /** * GENERATE PAGE: MODEL */ _writeModelTable(output, apiModel) { const configuration = this._tsdocConfiguration; const packagesTable = new DocTable({ configuration, headerTitles: ['Package', 'Description'] }); for (const apiMember of apiModel.members) { const row = new DocTableRow({ configuration }, [ this._createTitleCell(apiMember), this._createDescriptionCell(apiMember) ]); switch (apiMember.kind) { case ApiItemKind.Package: packagesTable.addRow(row); this._writeApiItemPage(apiMember); break; } } if (packagesTable.rows.length > 0) { output.appendNode(new DocHeading({ configuration, title: 'Packages' })); output.appendNode(packagesTable); } } /** * GENERATE PAGE: PACKAGE or NAMESPACE */ _writePackageOrNamespaceTables(output, apiContainer) { const configuration = this._tsdocConfiguration; const abstractClassesTable = new DocTable({ configuration, headerTitles: ['Abstract Class', 'Description'] }); const classesTable = new DocTable({ configuration, headerTitles: ['Class', 'Description'] }); const enumerationsTable = new DocTable({ configuration, headerTitles: ['Enumeration', 'Description'] }); const functionsTable = new DocTable({ configuration, headerTitles: ['Function', 'Description'] }); const interfacesTable = new DocTable({ configuration, headerTitles: ['Interface', 'Description'] }); const namespacesTable = new DocTable({ configuration, headerTitles: ['Namespace', 'Description'] }); const variablesTable = new DocTable({ configuration, headerTitles: ['Variable', 'Description'] }); const typeAliasesTable = new DocTable({ configuration, headerTitles: ['Type Alias', 'Description'] }); const apiMembers = apiContainer.kind === ApiItemKind.Package ? apiContainer.entryPoints[0].members : apiContainer.members; for (const apiMember of apiMembers) { const row = new DocTableRow({ configuration }, [ this._createTitleCell(apiMember), this._createDescriptionCell(apiMember) ]); switch (apiMember.kind) { case ApiItemKind.Class: if (ApiAbstractMixin.isBaseClassOf(apiMember) && apiMember.isAbstract) { abstractClassesTable.addRow(row); } else { classesTable.addRow(row); } this._writeApiItemPage(apiMember); break; case ApiItemKind.Enum: enumerationsTable.addRow(row); this._writeApiItemPage(apiMember); break; case ApiItemKind.Interface: interfacesTable.addRow(row); this._writeApiItemPage(apiMember); break; case ApiItemKind.Namespace: namespacesTable.addRow(row); this._writeApiItemPage(apiMember); break; case ApiItemKind.Function: functionsTable.addRow(row); this._writeApiItemPage(apiMember); break; case ApiItemKind.TypeAlias: typeAliasesTable.addRow(row); this._writeApiItemPage(apiMember); break; case ApiItemKind.Variable: variablesTable.addRow(row); this._writeApiItemPage(apiMember); break; } } if (classesTable.rows.length > 0) { output.appendNode(new DocHeading({ configuration, title: 'Classes' })); output.appendNode(classesTable); } if (abstractClassesTable.rows.length > 0) { output.appendNode(new DocHeading({ configuration, title: 'Abstract Classes' })); output.appendNode(abstractClassesTable); } if (enumerationsTable.rows.length > 0) { output.appendNode(new DocHeading({ configuration, title: 'Enumerations' })); output.appendNode(enumerationsTable); } if (functionsTable.rows.length > 0) { output.appendNode(new DocHeading({ configuration, title: 'Functions' })); output.appendNode(functionsTable); } if (interfacesTable.rows.length > 0) { output.appendNode(new DocHeading({ configuration, title: 'Interfaces' })); output.appendNode(interfacesTable); } if (namespacesTable.rows.length > 0) { output.appendNode(new DocHeading({ configuration, title: 'Namespaces' })); output.appendNode(namespacesTable); } if (variablesTable.rows.length > 0) { output.appendNode(new DocHeading({ configuration, title: 'Variables' })); output.appendNode(variablesTable); } if (typeAliasesTable.rows.length > 0) { output.appendNode(new DocHeading({ configuration, title: 'Type Aliases' })); output.appendNode(typeAliasesTable); } } /** * GENERATE PAGE: CLASS */ _writeClassTables(output, apiClass) { const configuration = this._tsdocConfiguration; const eventsTable = new DocTable({ configuration, headerTitles: ['Property', 'Modifiers', 'Type', 'Description'] }); const constructorsTable = new DocTable({ configuration, headerTitles: ['Constructor', 'Modifiers', 'Description'] }); const propertiesTable = new DocTable({ configuration, headerTitles: ['Property', 'Modifiers', 'Type', 'Description'] }); const methodsTable = new DocTable({ configuration, headerTitles: ['Method', 'Modifiers', 'Description'] }); const apiMembers = this._getMembersAndWriteIncompleteWarning(apiClass, output); for (const apiMember of apiMembers) { const isInherited = apiMember.parent !== apiClass; switch (apiMember.kind) { case ApiItemKind.Constructor: { constructorsTable.addRow(new DocTableRow({ configuration }, [ this._createTitleCell(apiMember), this._createModifiersCell(apiMember), this._createDescriptionCell(apiMember, isInherited) ])); this._writeApiItemPage(apiMember); break; } case ApiItemKind.Method: { methodsTable.addRow(new DocTableRow({ configuration }, [ this._createTitleCell(apiMember), this._createModifiersCell(apiMember), this._createDescriptionCell(apiMember, isInherited) ])); this._writeApiItemPage(apiMember); break; } case ApiItemKind.Property: { if (apiMember.isEventProperty) { eventsTable.addRow(new DocTableRow({ configuration }, [ this._createTitleCell(apiMember), this._createModifiersCell(apiMember), this._createPropertyTypeCell(apiMember), this._createDescriptionCell(apiMember, isInherited) ])); } else { propertiesTable.addRow(new DocTableRow({ configuration }, [ this._createTitleCell(apiMember), this._createModifiersCell(apiMember), this._createPropertyTypeCell(apiMember), this._createDescriptionCell(apiMember, isInherited) ])); } this._writeApiItemPage(apiMember); break; } } } if (eventsTable.rows.length > 0) { output.appendNode(new DocHeading({ configuration, title: 'Events' })); output.appendNode(eventsTable); } if (constructorsTable.rows.length > 0) { output.appendNode(new DocHeading({ configuration, title: 'Constructors' })); output.appendNode(constructorsTable); } if (propertiesTable.rows.length > 0) { output.appendNode(new DocHeading({ configuration, title: 'Properties' })); output.appendNode(propertiesTable); } if (methodsTable.rows.length > 0) { output.appendNode(new DocHeading({ configuration, title: 'Methods' })); output.appendNode(methodsTable); } } /** * GENERATE PAGE: ENUM */ _writeEnumTables(output, apiEnum) { const configuration = this._tsdocConfiguration; const enumMembersTable = new DocTable({ configuration, headerTitles: ['Member', 'Value', 'Description'] }); for (const apiEnumMember of apiEnum.members) { enumMembersTable.addRow(new DocTableRow({ configuration }, [ new DocTableCell({ configuration }, [ new DocParagraph({ configuration }, [ new DocPlainText({ configuration, text: Utilities.getConciseSignature(apiEnumMember) }) ]) ]), this._createInitializerCell(apiEnumMember), this._createDescriptionCell(apiEnumMember) ])); } if (enumMembersTable.rows.length > 0) { output.appendNode(new DocHeading({ configuration, title: 'Enumeration Members' })); output.appendNode(enumMembersTable); } } /** * GENERATE PAGE: INTERFACE */ _writeInterfaceTables(output, apiInterface) { const configuration = this._tsdocConfiguration; const eventsTable = new DocTable({ configuration, headerTitles: ['Property', 'Modifiers', 'Type', 'Description'] }); const propertiesTable = new DocTable({ configuration, headerTitles: ['Property', 'Modifiers', 'Type', 'Description'] }); const methodsTable = new DocTable({ configuration, headerTitles: ['Method', 'Description'] }); const apiMembers = this._getMembersAndWriteIncompleteWarning(apiInterface, output); for (const apiMember of apiMembers) { const isInherited = apiMember.parent !== apiInterface; switch (apiMember.kind) { case ApiItemKind.ConstructSignature: case ApiItemKind.MethodSignature: { methodsTable.addRow(new DocTableRow({ configuration }, [ this._createTitleCell(apiMember), this._createDescriptionCell(apiMember, isInherited) ])); this._writeApiItemPage(apiMember); break; } case ApiItemKind.PropertySignature: { if (apiMember.isEventProperty) { eventsTable.addRow(new DocTableRow({ configuration }, [ this._createTitleCell(apiMember), this._createModifiersCell(apiMember), this._createPropertyTypeCell(apiMember), this._createDescriptionCell(apiMember, isInherited) ])); } else { propertiesTable.addRow(new DocTableRow({ configuration }, [ this._createTitleCell(apiMember), this._createModifiersCell(apiMember), this._createPropertyTypeCell(apiMember), this._createDescriptionCell(apiMember, isInherited) ])); } this._writeApiItemPage(apiMember); break; } } } if (eventsTable.rows.length > 0) { output.appendNode(new DocHeading({ configuration, title: 'Events' })); output.appendNode(eventsTable); } if (propertiesTable.rows.length > 0) { output.appendNode(new DocHeading({ configuration, title: 'Properties' })); output.appendNode(propertiesTable); } if (methodsTable.rows.length > 0) { output.appendNode(new DocHeading({ configuration, title: 'Methods' })); output.appendNode(methodsTable); } } /** * GENERATE PAGE: FUNCTION-LIKE */ _writeParameterTables(output, apiParameterListMixin) { const configuration = this._tsdocConfiguration; const parametersTable = new DocTable({ configuration, headerTitles: ['Parameter', 'Type', 'Description'] }); for (const apiParameter of apiParameterListMixin.parameters) { const parameterDescription = new DocSection({ configuration }); if (apiParameter.isOptional) { parameterDescription.appendNodesInParagraph([ new DocEmphasisSpan({ configuration, italic: true }, [ new DocPlainText({ configuration, text: '(Optional)' }) ]), new DocPlainText({ configuration, text: ' ' }) ]); } if (apiParameter.tsdocParamBlock) { this._appendAndMergeSection(parameterDescription, apiParameter.tsdocParamBlock.content); } parametersTable.addRow(new DocTableRow({ configuration }, [ new DocTableCell({ configuration }, [ new DocParagraph({ configuration }, [ new DocPlainText({ configuration, text: apiParameter.name }) ]) ]), new DocTableCell({ configuration }, [ this._createParagraphForTypeExcerpt(apiParameter.parameterTypeExcerpt) ]), new DocTableCell({ configuration }, parameterDescription.nodes) ])); } if (parametersTable.rows.length > 0) { output.appendNode(new DocHeading({ configuration, title: 'Parameters' })); output.appendNode(parametersTable); } if (ApiReturnTypeMixin.isBaseClassOf(apiParameterListMixin)) { const returnTypeExcerpt = apiParameterListMixin.returnTypeExcerpt; output.appendNode(new DocParagraph({ configuration }, [ new DocEmphasisSpan({ configuration, bold: true }, [ new DocPlainText({ configuration, text: 'Returns:' }) ]) ])); output.appendNode(this._createParagraphForTypeExcerpt(returnTypeExcerpt)); if (apiParameterListMixin instanceof ApiDocumentedItem) { if (apiParameterListMixin.tsdocComment && apiParameterListMixin.tsdocComment.returnsBlock) { this._appendSection(output, apiParameterListMixin.tsdocComment.returnsBlock.content); } } } } _createParagraphForTypeExcerpt(excerpt) { const configuration = this._tsdocConfiguration; const paragraph = new DocParagraph({ configuration }); if (!excerpt.text.trim()) { paragraph.appendNode(new DocPlainText({ configuration, text: '(not declared)' })); } else { this._appendExcerptWithHyperlinks(paragraph, excerpt); } return paragraph; } _appendExcerptWithHyperlinks(docNodeContainer, excerpt) { for (const token of excerpt.spannedTokens) { this._appendExcerptTokenWithHyperlinks(docNodeContainer, token); } } _appendExcerptTokenWithHyperlinks(docNodeContainer, token) { const configuration = this._tsdocConfiguration; // Markdown doesn't provide a standardized syntax for hyperlinks inside code spans, so we will render // the type expression as DocPlainText. Instead of creating multiple DocParagraphs, we can simply // discard any newlines and let the renderer do normal word-wrapping. const unwrappedTokenText = token.text.replace(/[\r\n]+/g, ' '); // If it's hyperlinkable, then append a DocLinkTag if (token.kind === ExcerptTokenKind.Reference && token.canonicalReference) { const apiItemResult = this._apiModel.resolveDeclarationReference(token.canonicalReference, undefined); if (apiItemResult.resolvedApiItem) { docNodeContainer.appendNode(new DocLinkTag({ configuration, tagName: '@link', linkText: unwrappedTokenText, urlDestination: this._getLinkFilenameForApiItem(apiItemResult.resolvedApiItem) })); return; } } // Otherwise append non-hyperlinked text docNodeContainer.appendNode(new DocPlainText({ configuration, text: unwrappedTokenText })); } _createTitleCell(apiItem) { const configuration = this._tsdocConfiguration; let linkText = Utilities.getConciseSignature(apiItem); if (ApiOptionalMixin.isBaseClassOf(apiItem) && apiItem.isOptional) { linkText += '?'; } return new DocTableCell({ configuration }, [ new DocParagraph({ configuration }, [ new DocLinkTag({ configuration, tagName: '@link', linkText: linkText, urlDestination: this._getLinkFilenameForApiItem(apiItem) }) ]) ]); } /** * This generates a DocTableCell for an ApiItem including the summary section and "(BETA)" annotation. * * @remarks * We mostly assume that the input is an ApiDocumentedItem, but it's easier to perform this as a runtime * check than to have each caller perform a type cast. */ _createDescriptionCell(apiItem, isInherited = false) { const configuration = this._tsdocConfiguration; const section = new DocSection({ configuration }); if (ApiReleaseTagMixin.isBaseClassOf(apiItem)) { if (apiItem.releaseTag === ReleaseTag.Alpha || apiItem.releaseTag === ReleaseTag.Beta) { section.appendNodesInParagraph([ new DocEmphasisSpan({ configuration, bold: true, italic: true }, [ new DocPlainText({ configuration, text: `(${apiItem.releaseTag === ReleaseTag.Alpha ? 'ALPHA' : 'BETA'})` }) ]), new DocPlainText({ configuration, text: ' ' }) ]); } } if (ApiOptionalMixin.isBaseClassOf(apiItem) && apiItem.isOptional) { section.appendNodesInParagraph([ new DocEmphasisSpan({ configuration, italic: true }, [ new DocPlainText({ configuration, text: '(Optional)' }) ]), new DocPlainText({ configuration, text: ' ' }) ]); } if (apiItem instanceof ApiDocumentedItem) { if (apiItem.tsdocComment !== undefined) { this._appendAndMergeSection(section, apiItem.tsdocComment.summarySection); } } if (isInherited && apiItem.parent) { section.appendNode(new DocParagraph({ configuration }, [ new DocPlainText({ configuration, text: '(Inherited from ' }), new DocLinkTag({ configuration, tagName: '@link', linkText: apiItem.parent.displayName, urlDestination: this._getLinkFilenameForApiItem(apiItem.parent) }), new DocPlainText({ configuration, text: ')' }) ])); } return new DocTableCell({ configuration }, section.nodes); } _createModifiersCell(apiItem) { const configuration = this._tsdocConfiguration; const section = new DocSection({ configuration }); // Output modifiers in syntactically correct order: first access modifier (here: `protected`), then // `static` or `abstract` (no member can be both, so the order between the two of them does not matter), // last `readonly`. If `override` was supported, it would go directly before `readonly`. if (ApiProtectedMixin.isBaseClassOf(apiItem)) { if (apiItem.isProtected) { section.appendNode(new DocParagraph({ configuration }, [new DocCodeSpan({ configuration, code: 'protected' })])); } } if (ApiStaticMixin.isBaseClassOf(apiItem)) { if (apiItem.isStatic) { section.appendNode(new DocParagraph({ configuration }, [new DocCodeSpan({ configuration, code: 'static' })])); } } if (ApiAbstractMixin.isBaseClassOf(apiItem)) { if (apiItem.isAbstract) { section.appendNode(new DocParagraph({ configuration }, [new DocCodeSpan({ configuration, code: 'abstract' })])); } } if (ApiReadonlyMixin.isBaseClassOf(apiItem)) { if (apiItem.isReadonly) { section.appendNode(new DocParagraph({ configuration }, [new DocCodeSpan({ configuration, code: 'readonly' })])); } } return new DocTableCell({ configuration }, section.nodes); } _createPropertyTypeCell(apiItem) { const configuration = this._tsdocConfiguration; const section = new DocSection({ configuration }); if (apiItem instanceof ApiPropertyItem) { section.appendNode(this._createParagraphForTypeExcerpt(apiItem.propertyTypeExcerpt)); } return new DocTableCell({ configuration }, section.nodes); } _createInitializerCell(apiItem) { const configuration = this._tsdocConfiguration; const section = new DocSection({ configuration }); if (ApiInitializerMixin.isBaseClassOf(apiItem)) { if (apiItem.initializerExcerpt) { section.appendNodeInParagraph(new DocCodeSpan({ configuration, code: apiItem.initializerExcerpt.text })); } } return new DocTableCell({ configuration }, section.nodes); } _writeBreadcrumb(output, apiItem) { const configuration = this._tsdocConfiguration; output.appendNodeInParagraph(new DocLinkTag({ configuration, tagName: '@link', linkText: 'Home', urlDestination: this._getLinkFilenameForApiItem(this._apiModel) })); for (const hierarchyItem of apiItem.getHierarchy()) { switch (hierarchyItem.kind) { case ApiItemKind.Model: case ApiItemKind.EntryPoint: // We don't show the model as part of the breadcrumb because it is the root-level container. // We don't show the entry point because today API Extractor doesn't support multiple entry points; // this may change in the future. break; default: output.appendNodesInParagraph([ new DocPlainText({ configuration, text: ' > ' }), new DocLinkTag({ configuration, tagName: '@link', linkText: hierarchyItem.displayName, urlDestination: this._getLinkFilenameForApiItem(hierarchyItem) }) ]); } } } _writeAlphaWarning(output) { const configuration = this._tsdocConfiguration; const betaWarning = 'This API is provided as an alpha preview for developers and may change' + ' based on feedback that we receive. Do not use this API in a production environment.'; output.appendNode(new DocNoteBox({ configuration }, [ new DocParagraph({ configuration }, [new DocPlainText({ configuration, text: betaWarning })]) ])); } _writeBetaWarning(output) { const configuration = this._tsdocConfiguration; const betaWarning = 'This API is provided as a beta preview for developers and may change' + ' based on feedback that we receive. Do not use this API in a production environment.'; output.appendNode(new DocNoteBox({ configuration }, [ new DocParagraph({ configuration }, [new DocPlainText({ configuration, text: betaWarning })]) ])); } _appendSection(output, docSection) { for (const node of docSection.nodes) { output.appendNode(node); } } _appendAndMergeSection(output, docSection) { let firstNode = true; for (const node of docSection.nodes) { if (firstNode) { if (node.kind === DocNodeKind.Paragraph) { output.appendNodesInParagraph(node.getChildNodes()); firstNode = false; continue; } } firstNode = false; output.appendNode(node); } } _getMembersAndWriteIncompleteWarning(apiClassOrInterface, output) { var _a; const configuration = this._tsdocConfiguration; const showInheritedMembers = !!((_a = this._documenterConfig) === null || _a === void 0 ? void 0 : _a.configFile.showInheritedMembers); if (!showInheritedMembers) { return apiClassOrInterface.members; } const result = apiClassOrInterface.findMembersWithInheritance(); // If the result is potentially incomplete, write a short warning communicating this. if (result.maybeIncompleteResult) { output.appendNode(new DocParagraph({ configuration }, [ new DocEmphasisSpan({ configuration, italic: true }, [ new DocPlainText({ configuration, text: '(Some inherited members may not be shown because they are not represented in the documentation.)' }) ]) ])); } // Log the messages for diagnostic purposes. for (const message of result.messages) { console.log(`Diagnostic message for findMembersWithInheritance: ${message.text}`); } return result.items; } _getFilenameForApiItem(apiItem) { if (apiItem.kind === ApiItemKind.Model) { return 'index.md'; } let baseName = ''; for (const hierarchyItem of apiItem.getHierarchy()) { // For overloaded methods, add a suffix such as "MyClass.myMethod_2". let qualifiedName = Utilities.getSafeFilenameForName(hierarchyItem.displayName); if (ApiParameterListMixin.isBaseClassOf(hierarchyItem)) { if (hierarchyItem.overloadIndex > 1) { // Subtract one for compatibility with earlier releases of API Documenter. // (This will get revamped when we fix GitHub issue #1308) qualifiedName += `_${hierarchyItem.overloadIndex - 1}`; } } switch (hierarchyItem.kind) { case ApiItemKind.Model: case ApiItemKind.EntryPoint: case ApiItemKind.EnumMember: break; case ApiItemKind.Package: baseName = Utilities.getSafeFilenameForName(PackageName.getUnscopedName(hierarchyItem.displayName)); break; default: baseName += '.' + qualifiedName; } } return baseName + '.md'; } _getLinkFilenameForApiItem(apiItem) { return './' + this._getFilenameForApiItem(apiItem); } _deleteOldOutputFiles() { console.log('Deleting old output from ' + this._outputFolder); FileSystem.ensureEmptyFolder(this._outputFolder); } } //# sourceMappingURL=MarkdownDocumenter.js.map