import * as path from 'path' ; import { PackageName , FileSystem , NewlineKind } from '@rushstack/node-core-library' ; import { DocSection , DocPlainText , DocLinkTag , TSDocConfiguration , StringBuilder , DocNodeKind , DocParagraph , DocCodeSpan , DocFencedCode , StandardTags , DocBlock , DocComment , DocNodeContainer } from '@microsoft/tsdoc' ; import { ApiModel , ApiItem , ApiEnum , ApiPackage , ApiItemKind , ApiReleaseTagMixin , ApiDocumentedItem , ApiClass , ReleaseTag , ApiStaticMixin , ApiPropertyItem , ApiInterface , Excerpt , ApiParameterListMixin , ApiReturnTypeMixin , ApiDeclaredItem , ApiNamespace , ExcerptTokenKind , IResolveDeclarationReferenceResult , ApiTypeAlias , ExcerptToken , ApiOptionalMixin } 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 { IMarkdownDocumenterFeatureOnBeforeWritePageArgs , MarkdownDocumenterFeatureContext } from '../plugin/MarkdownDocumenterFeature' ; import { DocumenterConfig } from './DocumenterConfig' ; import { MarkdownDocumenterAccessor } from '../plugin/MarkdownDocumenterAccessor' ; export interface IMarkdownDocumenterOptions { apiModel : ApiModel ; documenterConfig : DocumenterConfig | undefined ; outputFolder : string ; } /**
 * Renders API documentation in the Markdown file format.
 * For more info:  https://en.wikipedia.org/wiki/Markdown
 */ export class MarkdownDocumenter { private readonly _apiModel : ApiModel ; private readonly _documenterConfig : DocumenterConfig | undefined ; private readonly _tsdocConfiguration : TSDocConfiguration ; private readonly _markdownEmitter : CustomMarkdownEmitter ; private readonly _outputFolder : string ; private readonly _pluginLoader : PluginLoader ; public 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 ( ) ; } public 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 ( { } ) ; } } private _writeApiItemPage ( apiItem ) { const configuration = this . _tsdocConfiguration ; const output = new DocSection ( { configuration : this . _tsdocConfiguration } ) ; 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 . 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 : this . _tsdocConfiguration } , [ new DocParagraph ( { configuration : this . _tsdocConfiguration } , [ new DocPlainText ( { configuration : this . _tsdocConfiguration , 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 as ApiClass ) ; break ; case ApiItemKind . Enum : this . _writeEnumTables ( output , apiItem as ApiEnum ) ; break ; case ApiItemKind . Interface : this . _writeInterfaceTables ( output , apiItem as ApiInterface ) ; break ; case ApiItemKind . Constructor : case ApiItemKind . ConstructSignature : case ApiItemKind . Method : case ApiItemKind . MethodSignature : case ApiItemKind . Function : this . _writeParameterTables ( output , apiItem as ApiParameterListMixin ) ; this . _writeThrowsSection ( output , apiItem ) ; break ; case ApiItemKind . Namespace : this . _writePackageOrNamespaceTables ( output , apiItem as ApiNamespace ) ; break ; case ApiItemKind . Model : this . _writeModelTable ( output , apiItem as ApiModel ) ; break ; case ApiItemKind . Package : this . _writePackageOrNamespaceTables ( output , apiItem as ApiPackage ) ; 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 ) { 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 } ) ; } private _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 extendsParagraph = new DocParagraph ( { configuration } , [ new DocEmphasisSpan ( { configuration , bold : true } , [ new DocPlainText ( { configuration , text : 'Implements: ' } ) ] ) ] ) ; let needsComma = false ; for ( const implementsType of apiItem . implementsTypes ) { if ( needsComma ) { extendsParagraph . appendNode ( new DocPlainText ( { configuration , text : ', ' } ) ) ; } this . _appendExcerptWithHyperlinks ( extendsParagraph , implementsType . excerpt ) ; needsComma = true ; } output . appendNode ( extendsParagraph ) ; } } 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 ) ; } } } private _writeRemarksSection ( output , apiItem ) { if ( apiItem instanceof ApiDocumentedItem ) { const tsdocComment = apiItem . tsdocComment ; if ( tsdocComment ) { if ( tsdocComment . remarksBlock ) { output . appendNode ( new DocHeading ( { configuration : this . _tsdocConfiguration , title : 'Remarks' } ) ) ; this . _appendSection ( output , tsdocComment . remarksBlock . content ) ; } 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 : this . _tsdocConfiguration , title : heading } ) ) ; this . _appendSection ( output , exampleBlock . content ) ; ++ exampleNumber ; } } } } private _writeThrowsSection ( output , apiItem ) { if ( apiItem instanceof ApiDocumentedItem ) { const tsdocComment = apiItem . tsdocComment ; if ( tsdocComment ) { const throwsBlocks = tsdocComment . customBlocks . filter ( ( x ) => x . blockTag . tagNameWithUpperCase === StandardTags . throws . tagNameWithUpperCase ) ; if ( throwsBlocks . length > 0 ) { const heading = 'Exceptions' ; output . appendNode ( new DocHeading ( { configuration : this . _tsdocConfiguration , title : heading } ) ) ; for ( const throwsBlock of throwsBlocks ) { this . _appendSection ( output , throwsBlock . content ) ; } } } } } /**
     * GENERATE PAGE: MODEL
     */ private _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 : this . _tsdocConfiguration , title : 'Packages' } ) ) ; output . appendNode ( packagesTable ) ; } } /**
     * GENERATE PAGE: PACKAGE or NAMESPACE
     */ private _writePackageOrNamespaceTables ( output , apiContainer ) { const configuration = this . _tsdocConfiguration ; 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 as ApiPackage ) . entryPoints [ 0 ] . members : ( apiContainer as ApiNamespace ) . members ; for ( const apiMember of apiMembers ) { const row = new DocTableRow ( { configuration } , [ this . _createTitleCell ( apiMember ) , this . _createDescriptionCell ( apiMember ) ] ) ; switch ( apiMember . kind ) { case ApiItemKind . Class : 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 : this . _tsdocConfiguration , title : 'Classes' } ) ) ; output . appendNode ( classesTable ) ; } if ( enumerationsTable . rows . length > 0 ) { output . appendNode ( new DocHeading ( { configuration : this . _tsdocConfiguration , title : 'Enumerations' } ) ) ; output . appendNode ( enumerationsTable ) ; } if ( functionsTable . rows . length > 0 ) { output . appendNode ( new DocHeading ( { configuration : this . _tsdocConfiguration , title : 'Functions' } ) ) ; output . appendNode ( functionsTable ) ; } if ( interfacesTable . rows . length > 0 ) { output . appendNode ( new DocHeading ( { configuration : this . _tsdocConfiguration , title : 'Interfaces' } ) ) ; output . appendNode ( interfacesTable ) ; } if ( namespacesTable . rows . length > 0 ) { output . appendNode ( new DocHeading ( { configuration : this . _tsdocConfiguration , title : 'Namespaces' } ) ) ; output . appendNode ( namespacesTable ) ; } if ( variablesTable . rows . length > 0 ) { output . appendNode ( new DocHeading ( { configuration : this . _tsdocConfiguration , title : 'Variables' } ) ) ; output . appendNode ( variablesTable ) ; } if ( typeAliasesTable . rows . length > 0 ) { output . appendNode ( new DocHeading ( { configuration : this . _tsdocConfiguration , title : 'Type Aliases' } ) ) ; output . appendNode ( typeAliasesTable ) ; } } /**
     * GENERATE PAGE: CLASS
     */ private _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' ] } ) ; for ( const apiMember of apiClass . members ) { switch ( apiMember . kind ) { case ApiItemKind . Constructor : { constructorsTable . addRow ( new DocTableRow ( { configuration } , [ this . _createTitleCell ( apiMember ) , this . _createModifiersCell ( apiMember ) , this . _createDescriptionCell ( apiMember ) ] ) ) ; this . _writeApiItemPage ( apiMember ) ; break ; } case ApiItemKind . Method : { methodsTable . addRow ( new DocTableRow ( { configuration } , [ this . _createTitleCell ( apiMember ) , this . _createModifiersCell ( apiMember ) , this . _createDescriptionCell ( apiMember ) ] ) ) ; this . _writeApiItemPage ( apiMember ) ; break ; } case ApiItemKind . Property : { if ( ( apiMember as ApiPropertyItem ) . isEventProperty ) { eventsTable . addRow ( new DocTableRow ( { configuration } , [ this . _createTitleCell ( apiMember ) , this . _createModifiersCell ( apiMember ) , this . _createPropertyTypeCell ( apiMember ) , this . _createDescriptionCell ( apiMember ) ] ) ) ; } else { propertiesTable . addRow ( new DocTableRow ( { configuration } , [ this . _createTitleCell ( apiMember ) , this . _createModifiersCell ( apiMember ) , this . _createPropertyTypeCell ( apiMember ) , this . _createDescriptionCell ( apiMember ) ] ) ) ; } this . _writeApiItemPage ( apiMember ) ; break ; } } } if ( eventsTable . rows . length > 0 ) { output . appendNode ( new DocHeading ( { configuration : this . _tsdocConfiguration , title : 'Events' } ) ) ; output . appendNode ( eventsTable ) ; } if ( constructorsTable . rows . length > 0 ) { output . appendNode ( new DocHeading ( { configuration : this . _tsdocConfiguration , title : 'Constructors' } ) ) ; output . appendNode ( constructorsTable ) ; } if ( propertiesTable . rows . length > 0 ) { output . appendNode ( new DocHeading ( { configuration : this . _tsdocConfiguration , title : 'Properties' } ) ) ; output . appendNode ( propertiesTable ) ; } if ( methodsTable . rows . length > 0 ) { output . appendNode ( new DocHeading ( { configuration : this . _tsdocConfiguration , title : 'Methods' } ) ) ; output . appendNode ( methodsTable ) ; } } /**
     * GENERATE PAGE: ENUM
     */ private _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 ) } ) ] ) ] ) , new DocTableCell ( { configuration } , [ new DocParagraph ( { configuration } , [ new DocCodeSpan ( { configuration , code : apiEnumMember . initializerExcerpt . text } ) ] ) ] ) , this . _createDescriptionCell ( apiEnumMember ) ] ) ) ; } if ( enumMembersTable . rows . length > 0 ) { output . appendNode ( new DocHeading ( { configuration : this . _tsdocConfiguration , title : 'Enumeration Members' } ) ) ; output . appendNode ( enumMembersTable ) ; } } /**
     * GENERATE PAGE: INTERFACE
     */ private _writeInterfaceTables ( output , apiClass ) { const configuration = this . _tsdocConfiguration ; const eventsTable = new DocTable ( { configuration , headerTitles : [ 'Property' , 'Type' , 'Description' ] } ) ; const propertiesTable = new DocTable ( { configuration , headerTitles : [ 'Property' , 'Type' , 'Description' ] } ) ; const methodsTable = new DocTable ( { configuration , headerTitles : [ 'Method' , 'Description' ] } ) ; for ( const apiMember of apiClass . members ) { switch ( apiMember . kind ) { case ApiItemKind . ConstructSignature : case ApiItemKind . MethodSignature : { methodsTable . addRow ( new DocTableRow ( { configuration } , [ this . _createTitleCell ( apiMember ) , this . _createDescriptionCell ( apiMember ) ] ) ) ; this . _writeApiItemPage ( apiMember ) ; break ; } case ApiItemKind . PropertySignature : { if ( ( apiMember as ApiPropertyItem ) . isEventProperty ) { eventsTable . addRow ( new DocTableRow ( { configuration } , [ this . _createTitleCell ( apiMember ) , this . _createPropertyTypeCell ( apiMember ) , this . _createDescriptionCell ( apiMember ) ] ) ) ; } else { propertiesTable . addRow ( new DocTableRow ( { configuration } , [ this . _createTitleCell ( apiMember ) , this . _createPropertyTypeCell ( apiMember ) , this . _createDescriptionCell ( apiMember ) ] ) ) ; } this . _writeApiItemPage ( apiMember ) ; break ; } } } if ( eventsTable . rows . length > 0 ) { output . appendNode ( new DocHeading ( { configuration : this . _tsdocConfiguration , title : 'Events' } ) ) ; output . appendNode ( eventsTable ) ; } if ( propertiesTable . rows . length > 0 ) { output . appendNode ( new DocHeading ( { configuration : this . _tsdocConfiguration , title : 'Properties' } ) ) ; output . appendNode ( propertiesTable ) ; } if ( methodsTable . rows . length > 0 ) { output . appendNode ( new DocHeading ( { configuration : this . _tsdocConfiguration , title : 'Methods' } ) ) ; output . appendNode ( methodsTable ) ; } } /**
     * GENERATE PAGE: FUNCTION-LIKE
     */ private _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 . tsdocParamBlock ) { this . _appendSection ( 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 : this . _tsdocConfiguration , 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 ) ; } } } } private _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 ; } private _appendExcerptWithHyperlinks ( docNodeContainer , excerpt ) { for ( const token of excerpt . spannedTokens ) { this . _appendExcerptTokenWithHyperlinks ( docNodeContainer , token ) ; } } private _appendExcerptTokenWithHyperlinks ( docNodeContainer , token ) { const configuration = this . _tsdocConfiguration ; const unwrappedTokenText = token . text . replace ( /[\r\n]+/g , ' ' ) ; 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 ; } } docNodeContainer . appendNode ( new DocPlainText ( { configuration , text : unwrappedTokenText } ) ) ; } private _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 ) } ) ] ) ] ) ; } remarks private _createDescriptionCell ( apiItem ) { const configuration = this . _tsdocConfiguration ; const section = new DocSection ( { configuration } ) ; if ( ApiReleaseTagMixin . isBaseClassOf ( apiItem ) ) { if ( apiItem . releaseTag === ReleaseTag . Beta ) { section . appendNodesInParagraph ( [ new DocEmphasisSpan ( { configuration , bold : true , italic : true } , [ new DocPlainText ( { configuration , text : '(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 ) ; } } return new DocTableCell ( { configuration } , section . nodes ) ; } private _createModifiersCell ( apiItem ) { const configuration = this . _tsdocConfiguration ; const section = new DocSection ( { configuration } ) ; if ( ApiStaticMixin . isBaseClassOf ( apiItem ) ) { if ( apiItem . isStatic ) { section . appendNodeInParagraph ( new DocCodeSpan ( { configuration , code : 'static' } ) ) ; } } return new DocTableCell ( { configuration } , section . nodes ) ; } private _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 ) ; } private _writeBreadcrumb ( output , apiItem ) { output . appendNodeInParagraph ( new DocLinkTag ( { configuration : this . _tsdocConfiguration , tagName : '@link' , linkText : 'Home' , urlDestination : this . _getLinkFilenameForApiItem ( this . _apiModel ) } ) ) ; for ( const hierarchyItem of apiItem . getHierarchy ( ) ) { switch ( hierarchyItem . kind ) { case ApiItemKind . Model : case ApiItemKind . EntryPoint : break ; default : output . appendNodesInParagraph ( [ new DocPlainText ( { configuration : this . _tsdocConfiguration , text : ' > ' } ) , new DocLinkTag ( { configuration : this . _tsdocConfiguration , tagName : '@link' , linkText : hierarchyItem . displayName , urlDestination : this . _getLinkFilenameForApiItem ( hierarchyItem ) } ) ] ) ; } } } private _writeBetaWarning ( output ) { const configuration = this . _tsdocConfiguration ; const betaWarning = 'This API is provided as a 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 } ) ] ) ] ) ) ; } private _appendSection ( output , docSection ) { for ( const node of docSection . nodes ) { output . appendNode ( node ) ; } } private _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 ) ; } } private _getFilenameForApiItem ( apiItem ) { if ( apiItem . kind === ApiItemKind . Model ) { return 'index.md' ; } let baseName = '' ; for ( const hierarchyItem of apiItem . getHierarchy ( ) ) { let qualifiedName = Utilities . getSafeFilenameForName ( hierarchyItem . displayName ) ; if ( ApiParameterListMixin . isBaseClassOf ( hierarchyItem ) ) { if ( hierarchyItem . overloadIndex > 1 ) { 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' ; } private _getLinkFilenameForApiItem ( apiItem ) { return './' + this . _getFilenameForApiItem ( apiItem ) ; } private _deleteOldOutputFiles ( ) { console . log ( 'Deleting old output from ' + this . _outputFolder ) ; FileSystem . ensureEmptyFolder ( this . _outputFolder ) ; } }