import * as path from 'path' ; import yaml = require ( 'js-yaml' ) ; import { JsonFile , JsonSchema , PackageName , FileSystem , NewlineKind , InternalError } from '@rushstack/node-core-library' ; import { StringBuilder , DocSection , DocComment , DocBlock , StandardTags } from '@microsoft/tsdoc' ; import { ApiModel , ApiItem , ApiItemKind , ApiDocumentedItem , ApiReleaseTagMixin , ReleaseTag , ApiPropertyItem , ApiItemContainerMixin , ApiPackage , ApiEnumMember , ApiClass , ApiInterface , ApiMethod , ApiMethodSignature , ApiConstructor , ApiFunction , ApiReturnTypeMixin , ApiTypeParameterListMixin , Excerpt , ExcerptToken , ExcerptTokenKind , HeritageType , ApiVariable , ApiTypeAlias } from '@microsoft/api-extractor-model' ; import { DeclarationReference , Navigation , Meaning } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference' ; import { IYamlApiFile , IYamlItem , IYamlSyntax , IYamlParameter , IYamlReference , IYamlReferenceSpec , IYamlInheritanceTree } from '../yaml/IYamlApiFile' ; import { IYamlTocFile , IYamlTocItem } from '../yaml/IYamlTocFile' ; import { Utilities } from '../utils/Utilities' ; import { CustomMarkdownEmitter } from '../markdown/CustomMarkdownEmitter' ; import { convertUDPYamlToSDP } from '../utils/ToSdpConvertHelper' ; const yamlApiSchema = JsonSchema . fromFile ( path . join ( __dirname , '..' , 'yaml' , 'typescript.schema.json' ) ) ; interface IYamlReferences { references : IYamlReference [ ] ; typeNameToUid : Map < string , string > ; uidTypeReferenceCounters : Map < string , number > ; } const enum FlattenMode { /** Include entries for nested namespaces and non-namespace children. */ NestedNamespacesAndChildren , /** Include entries for nested namespaces only. */ NestedNamespacesOnly , /** Include entries for non-namespace immediate children. */ ImmediateChildren , /** Include entries for nested non-namespace children. */ NestedChildren } interface INameOptions { includeSignature ? : boolean ; includeNamespace ? : boolean ; } /**
 * Writes documentation in the Universal Reference YAML file format, as defined by typescript.schema.json.
 */ export class YamlDocumenter { protected readonly newDocfxNamespaces : boolean ; private readonly _apiModel : ApiModel ; private readonly _markdownEmitter : CustomMarkdownEmitter ; private _apiItemsByCanonicalReference : Map < string , ApiItem > ; private _yamlReferences : IYamlReferences | undefined ; public constructor ( apiModel , newDocfxNamespaces = false ) { this . _apiModel = apiModel ; this . newDocfxNamespaces = newDocfxNamespaces ; this . _markdownEmitter = new CustomMarkdownEmitter ( this . _apiModel ) ; this . _apiItemsByCanonicalReference = new Map < string , ApiItem > ( ) ; this . _initApiItems ( ) ; } virtual public generateFiles ( outputFolder ) { console . log ( ) ; this . _deleteOldOutputFiles ( outputFolder ) ; for ( const apiPackage of this . _apiModel . packages ) { console . log ( `Writing ${ apiPackage . name } package` ) ; this . _visitApiItems ( outputFolder , apiPackage , undefined ) ; } convertUDPYamlToSDP ( outputFolder ) ; this . _writeTocFile ( outputFolder , this . _apiModel . packages ) ; } virtual protected onGetTocRoot ( ) { return { name : 'SharePoint Framework reference' , href : '~/overview/sharepoint.md' , items : [ ] } ; } virtual protected onCustomizeYamlItem ( yamlItem ) { } private _visitApiItems ( outputFolder , apiItem , parentYamlFile ) { let savedYamlReferences ; if ( ! this . _shouldEmbed ( apiItem . kind ) ) { savedYamlReferences = this . _yamlReferences ; this . _yamlReferences = undefined ; } const yamlItem = this . _generateYamlItem ( apiItem ) ; if ( ! yamlItem ) { return false ; } this . onCustomizeYamlItem ( yamlItem ) ; if ( this . _shouldEmbed ( apiItem . kind ) ) { if ( ! parentYamlFile ) { throw new InternalError ( 'Missing file context' ) ; } parentYamlFile . items . push ( yamlItem ) ; } else { const newYamlFile = { items : [ ] } ; newYamlFile . items . push ( yamlItem ) ; const children = this . _getLogicalChildren ( apiItem ) ; for ( const child of children ) { if ( child instanceof ApiDocumentedItem ) { if ( this . _visitApiItems ( outputFolder , child , newYamlFile ) ) { if ( ! yamlItem . children ) { yamlItem . children = [ ] ; } yamlItem . children . push ( this . _getUid ( child ) ) ; } } } if ( this . _yamlReferences && this . _yamlReferences . references . length > 0 ) { newYamlFile . references = this . _yamlReferences . references ; } this . _yamlReferences = savedYamlReferences ; const yamlFilePath = this . _getYamlFilePath ( outputFolder , apiItem ) ; if ( apiItem . kind === ApiItemKind . Package ) { console . log ( 'Writing ' + yamlFilePath ) ; } this . _writeYamlFile ( newYamlFile , yamlFilePath , 'UniversalReference' , yamlApiSchema ) ; if ( parentYamlFile ) { this . _recordYamlReference ( this . _ensureYamlReferences ( ) , this . _getUid ( apiItem ) , this . _getYamlItemName ( apiItem , { includeNamespace : ! this . newDocfxNamespaces , includeSignature : true } ) , this . _getYamlItemName ( apiItem , { includeNamespace : true , includeSignature : true } ) ) ; } } return true ; } protected _getLogicalChildren ( apiItem ) { const children = [ ] ; if ( apiItem . kind === ApiItemKind . Package ) { this . _flattenNamespaces ( apiItem . members [ 0 ] . members , children , this . newDocfxNamespaces ? FlattenMode . NestedNamespacesAndChildren : FlattenMode . NestedChildren ) ; } else { this . _flattenNamespaces ( apiItem . members , children , this . newDocfxNamespaces ? FlattenMode . ImmediateChildren : FlattenMode . NestedChildren ) ; } return children ; } private _flattenNamespaces ( items , childrenOut , mode ) { let hasNonNamespaceChildren = false ; for ( const item of items ) { if ( item . kind === ApiItemKind . Namespace ) { switch ( mode ) { case FlattenMode . NestedChildren : this . _flattenNamespaces ( item . members , childrenOut , FlattenMode . NestedChildren ) ; break ; case FlattenMode . NestedNamespacesOnly : case FlattenMode . NestedNamespacesAndChildren : const index = childrenOut . length ; childrenOut . push ( item ) ; if ( ! this . _flattenNamespaces ( item . members , childrenOut , FlattenMode . NestedNamespacesOnly ) ) { childrenOut . splice ( index , 1 ) ; } break ; } } else if ( this . _shouldInclude ( item . kind ) ) { switch ( mode ) { case FlattenMode . NestedChildren : case FlattenMode . NestedNamespacesAndChildren : case FlattenMode . ImmediateChildren : childrenOut . push ( item ) ; break ; } hasNonNamespaceChildren = true ; } } return hasNonNamespaceChildren ; } /**
     * Write the table of contents
     */ private _writeTocFile ( outputFolder , apiItems ) { const tocFile = this . buildYamlTocFile ( apiItems ) ; const tocFilePath = path . join ( outputFolder , 'toc.yml' ) ; console . log ( 'Writing ' + tocFilePath ) ; this . _writeYamlFile ( tocFile , tocFilePath , '' , undefined ) ; } virtual protected buildYamlTocFile ( apiItems ) { const tocFile = { items : [ ] } ; const rootItem = this . onGetTocRoot ( ) ; tocFile . items . push ( rootItem ) ; rootItem . items ! . push ( ... this . _buildTocItems ( apiItems ) ) ; return tocFile ; } private _buildTocItems ( apiItems ) { const tocItems = [ ] ; for ( const apiItem of apiItems ) { let tocItem ; if ( apiItem . kind === ApiItemKind . Namespace && ! this . newDocfxNamespaces ) { tocItem = { name : this . _getTocItemName ( apiItem ) } ; } else { if ( this . _shouldEmbed ( apiItem . kind ) ) { continue ; } tocItem = { name : this . _getTocItemName ( apiItem ) , uid : this . _getUid ( apiItem ) } ; } tocItems . push ( tocItem ) ; const children = this . _getLogicalChildren ( apiItem ) ; const childItems = this . _buildTocItems ( children ) ; if ( childItems . length > 0 ) { tocItem . items = childItems ; } } return tocItems ; } virtual protected _getTocItemName ( apiItem ) { let name ; if ( apiItem . kind === ApiItemKind . Package ) { name = PackageName . getUnscopedName ( apiItem . displayName ) ; } else { name = this . _getYamlItemName ( apiItem ) ; } if ( name === apiItem . displayName && apiItem . getMergedSiblings ( ) . length > 1 ) { name += ` (${ apiItem . kind })` ; } return name ; } protected _shouldEmbed ( apiItemKind ) { switch ( apiItemKind ) { case ApiItemKind . Class : case ApiItemKind . Package : case ApiItemKind . Interface : case ApiItemKind . Enum : return false ; case ApiItemKind . Namespace : return ! this . newDocfxNamespaces ; } return true ; } protected _shouldInclude ( apiItemKind ) { switch ( apiItemKind ) { case ApiItemKind . CallSignature : case ApiItemKind . ConstructSignature : case ApiItemKind . IndexSignature : return false ; } return true ; } private _generateYamlItem ( apiItem ) { if ( ! this . _shouldInclude ( apiItem . kind ) ) { return undefined ; } const uid = this . _getUidObject ( apiItem ) ; const yamlItem = { uid : uid . toString ( ) } ; if ( apiItem . tsdocComment ) { const tsdocComment = apiItem . tsdocComment ; if ( tsdocComment . summarySection ) { const summary = this . _renderMarkdown ( tsdocComment . summarySection , apiItem ) ; if ( summary ) { yamlItem . summary = summary ; } } if ( tsdocComment . remarksBlock ) { const remarks = this . _renderMarkdown ( tsdocComment . remarksBlock . content , apiItem ) ; if ( remarks ) { yamlItem . remarks = remarks ; } } if ( tsdocComment ) { const exampleBlocks = tsdocComment . customBlocks . filter ( ( x ) => x . blockTag . tagNameWithUpperCase === StandardTags . example . tagNameWithUpperCase ) ; for ( const exampleBlock of exampleBlocks ) { const example = this . _renderMarkdown ( exampleBlock . content , apiItem ) ; if ( example ) { yamlItem . example = [ ... ( yamlItem . example || [ ] ) , example ] ; } } } if ( tsdocComment . deprecatedBlock ) { const deprecatedMessage = this . _renderMarkdown ( tsdocComment . deprecatedBlock . content , apiItem ) ; if ( deprecatedMessage . length > 0 ) { yamlItem . deprecated = { content : deprecatedMessage } ; } } } if ( ApiReleaseTagMixin . isBaseClassOf ( apiItem ) ) { if ( apiItem . releaseTag === ReleaseTag . Beta ) { yamlItem . isPreview = true ; } } yamlItem . name = this . _getYamlItemName ( apiItem , { includeSignature : true , includeNamespace : ! this . newDocfxNamespaces } ) ; yamlItem . fullName = this . _getYamlItemName ( apiItem , { includeSignature : true , includeNamespace : true } ) ; yamlItem . langs = [ 'typeScript' ] ; if ( apiItem . kind !== ApiItemKind . Namespace && apiItem . parent && apiItem . parent . kind === ApiItemKind . Namespace && this . newDocfxNamespaces ) { yamlItem . namespace = apiItem . parent . canonicalReference . toString ( ) ; } switch ( apiItem . kind ) { case ApiItemKind . Enum : yamlItem . type = 'enum' ; break ; case ApiItemKind . EnumMember : yamlItem . type = 'field' ; const enumMember = apiItem as ApiEnumMember ; if ( enumMember . initializerExcerpt . text . length > 0 ) { yamlItem . numericValue = enumMember . initializerExcerpt . text ; } break ; case ApiItemKind . Class : yamlItem . type = 'class' ; this . _populateYamlClassOrInterface ( uid , yamlItem , apiItem as ApiClass ) ; break ; case ApiItemKind . Interface : yamlItem . type = 'interface' ; this . _populateYamlClassOrInterface ( uid , yamlItem , apiItem as ApiInterface ) ; break ; case ApiItemKind . Method : case ApiItemKind . MethodSignature : yamlItem . type = 'method' ; this . _populateYamlFunctionLike ( uid , yamlItem , apiItem as ApiMethod | ApiMethodSignature ) ; break ; case ApiItemKind . Constructor : yamlItem . type = 'constructor' ; this . _populateYamlFunctionLike ( uid , yamlItem , apiItem as ApiConstructor ) ; break ; case ApiItemKind . Package : yamlItem . type = 'package' ; break ; case ApiItemKind . Namespace : yamlItem . type = 'namespace' ; break ; case ApiItemKind . Property : case ApiItemKind . PropertySignature : const apiProperty = apiItem as ApiPropertyItem ; if ( apiProperty . isEventProperty ) { yamlItem . type = 'event' ; } else { yamlItem . type = 'property' ; } this . _populateYamlProperty ( uid , yamlItem , apiProperty ) ; break ; case ApiItemKind . Function : yamlItem . type = 'function' ; this . _populateYamlFunctionLike ( uid , yamlItem , apiItem as ApiFunction ) ; break ; case ApiItemKind . Variable : yamlItem . type = 'variable' ; this . _populateYamlVariable ( uid , yamlItem , apiItem as ApiVariable ) ; break ; case ApiItemKind . TypeAlias : yamlItem . type = 'typealias' ; this . _populateYamlTypeAlias ( uid , yamlItem , apiItem as ApiTypeAlias ) ; break ; default : throw new Error ( 'Unimplemented item kind: ' + apiItem . kind ) ; } if ( apiItem . kind !== ApiItemKind . Package && ! this . _shouldEmbed ( apiItem . kind ) ) { const associatedPackage = apiItem . getAssociatedPackage ( ) ; if ( ! associatedPackage ) { throw new Error ( 'Unable to determine associated package for ' + apiItem . displayName ) ; } yamlItem . package = this . _getUid ( associatedPackage ) ; } return yamlItem as IYamlItem ; } private _populateYamlTypeParameters ( contextUid , apiItem ) { const typeParameters = [ ] ; for ( const apiTypeParameter of apiItem . typeParameters ) { const typeParameter = { id : apiTypeParameter . name } ; if ( apiTypeParameter . tsdocTypeParamBlock ) { typeParameter . description = this . _renderMarkdown ( apiTypeParameter . tsdocTypeParamBlock . content , apiItem ) ; } if ( ! apiTypeParameter . constraintExcerpt . isEmpty ) { typeParameter . type = [ this . _renderType ( contextUid , apiTypeParameter . constraintExcerpt ) ] ; } typeParameters . push ( typeParameter ) ; } return typeParameters ; } private _populateYamlClassOrInterface ( uid , yamlItem , apiItem ) { if ( apiItem instanceof ApiClass ) { if ( apiItem . extendsType ) { yamlItem . extends = [ this . _renderType ( uid , apiItem . extendsType . excerpt ) ] ; yamlItem . inheritance = this . _renderInheritance ( uid , [ apiItem . extendsType ] ) ; } if ( apiItem . implementsTypes . length > 0 ) { yamlItem . implements = [ ] ; for ( const implementsType of apiItem . implementsTypes ) { yamlItem . implements . push ( this . _renderType ( uid , implementsType . excerpt ) ) ; } } } else if ( apiItem instanceof ApiInterface ) { if ( apiItem . extendsTypes . length > 0 ) { yamlItem . extends = [ ] ; for ( const extendsType of apiItem . extendsTypes ) { yamlItem . extends . push ( this . _renderType ( uid , extendsType . excerpt ) ) ; } yamlItem . inheritance = this . _renderInheritance ( uid , apiItem . extendsTypes ) ; } const typeParameters = this . _populateYamlTypeParameters ( uid , apiItem ) ; if ( typeParameters . length ) { yamlItem . syntax = { typeParameters } ; } } if ( apiItem . tsdocComment ) { if ( apiItem . tsdocComment . modifierTagSet . isSealed ( ) ) { let sealedMessage ; if ( apiItem . kind === ApiItemKind . Class ) { sealedMessage = 'This class is marked as `@sealed`. Subclasses should not extend it.' ; } else { sealedMessage = 'This interface is marked as `@sealed`. Other interfaces should not extend it.' ; } if ( ! yamlItem . remarks ) { yamlItem . remarks = sealedMessage ; } else { yamlItem . remarks = sealedMessage + '\n\n' + yamlItem . remarks ; } } } } private _populateYamlFunctionLike ( uid , yamlItem , apiItem ) { const syntax = { content : apiItem . getExcerptWithModifiers ( ) } ; yamlItem . syntax = syntax ; if ( ApiReturnTypeMixin . isBaseClassOf ( apiItem ) ) { const returnType = this . _renderType ( uid , apiItem . returnTypeExcerpt ) ; let returnDescription = '' ; if ( apiItem . tsdocComment && apiItem . tsdocComment . returnsBlock ) { returnDescription = this . _renderMarkdown ( apiItem . tsdocComment . returnsBlock . content , apiItem ) ; returnDescription = returnDescription . replace ( /^\s*-\s+/ , '' ) ; } if ( returnType || returnDescription ) { syntax . return = { type : [ returnType ] , description : returnDescription } ; } } const parameters = [ ] ; for ( const apiParameter of apiItem . parameters ) { let parameterDescription = '' ; if ( apiParameter . tsdocParamBlock ) { parameterDescription = this . _renderMarkdown ( apiParameter . tsdocParamBlock . content , apiItem ) ; } parameters . push ( { id : apiParameter . name , description : parameterDescription , type : [ this . _renderType ( uid , apiParameter . parameterTypeExcerpt ) ] } as IYamlParameter ) ; } if ( parameters . length ) { syntax . parameters = parameters ; } if ( ApiTypeParameterListMixin . isBaseClassOf ( apiItem ) ) { const typeParameters = this . _populateYamlTypeParameters ( uid , apiItem ) ; if ( typeParameters . length ) { syntax . typeParameters = typeParameters ; } } } private _populateYamlProperty ( uid , yamlItem , apiItem ) { const syntax = { content : apiItem . getExcerptWithModifiers ( ) } ; yamlItem . syntax = syntax ; if ( apiItem . propertyTypeExcerpt . text ) { syntax . return = { type : [ this . _renderType ( uid , apiItem . propertyTypeExcerpt ) ] } ; } } private _populateYamlVariable ( uid , yamlItem , apiItem ) { const syntax = { content : apiItem . getExcerptWithModifiers ( ) } ; yamlItem . syntax = syntax ; if ( apiItem . variableTypeExcerpt . text ) { syntax . return = { type : [ this . _renderType ( uid , apiItem . variableTypeExcerpt ) ] } ; } } private _populateYamlTypeAlias ( uid , yamlItem , apiItem ) { const syntax = { content : apiItem . getExcerptWithModifiers ( ) } ; yamlItem . syntax = syntax ; const typeParameters = this . _populateYamlTypeParameters ( uid , apiItem ) ; if ( typeParameters . length ) { syntax . typeParameters = typeParameters ; } if ( apiItem . typeExcerpt . text ) { syntax . return = { type : [ this . _renderType ( uid , apiItem . typeExcerpt ) ] } ; } } private _renderMarkdown ( docSection , contextApiItem ) { const stringBuilder = new StringBuilder ( ) ; this . _markdownEmitter . emit ( stringBuilder , docSection , { contextApiItem , onGetFilenameForApiItem : ( apiItem ) => { return encodeURI ( `xref:${ this . _getUid ( apiItem ) }` ) . replace ( /[#?]/g , ( s ) => encodeURIComponent ( s ) ) . replace ( /(\([^(]*\))|[()]/g , ( s , balanced ) => balanced || '\\' + s ) ; } } ) ; return stringBuilder . toString ( ) . trim ( ) ; } private _writeYamlFile ( dataObject , filePath , yamlMimeType , schema ) { JsonFile . validateNoUndefinedMembers ( dataObject ) ; let stringified = yaml . safeDump ( dataObject , { lineWidth : 120 } ) ; if ( yamlMimeType ) { stringified = `### YamlMime:${ yamlMimeType }\n` + stringified ; } FileSystem . writeFile ( filePath , stringified , { convertLineEndings : NewlineKind . CrLf , ensureFolderExists : true } ) ; if ( schema ) { schema . validateObject ( dataObject , filePath ) ; } } /**
     * Calculate the DocFX "uid" for the ApiItem
     * Example:  `node-core-library!JsonFile#load`
     */ protected _getUid ( apiItem ) { return this . _getUidObject ( apiItem ) . toString ( ) ; } protected _getUidObject ( apiItem ) { return apiItem . canonicalReference ; } /**
     * Initialize the _apiItemsByCanonicalReference data structure.
     */ private _initApiItems ( ) { this . _initApiItemsRecursive ( this . _apiModel ) ; } /**
     * Helper for _initApiItems()
     */ private _initApiItemsRecursive ( apiItem ) { if ( apiItem . canonicalReference && ! apiItem . canonicalReference . isEmpty ) { this . _apiItemsByCanonicalReference . set ( apiItem . canonicalReference . toString ( ) , apiItem ) ; } if ( ApiItemContainerMixin . isBaseClassOf ( apiItem ) ) { for ( const apiMember of apiItem . members ) { this . _initApiItemsRecursive ( apiMember ) ; } } } private _ensureYamlReferences ( ) { if ( ! this . _yamlReferences ) { this . _yamlReferences = { references : [ ] , typeNameToUid : new Map ( ) , uidTypeReferenceCounters : new Map ( ) } ; } return this . _yamlReferences ; } private _renderInheritance ( contextUid , heritageTypes ) { const result = [ ] ; for ( const heritageType of heritageTypes ) { const type = this . _renderType ( contextUid , heritageType . excerpt ) ; const yamlInheritance = { type } ; const apiItem = this . _apiItemsByCanonicalReference . get ( type ) ; if ( apiItem ) { if ( apiItem instanceof ApiClass ) { if ( apiItem . extendsType ) { yamlInheritance . inheritance = this . _renderInheritance ( this . _getUidObject ( apiItem ) , [ apiItem . extendsType ] ) ; } } else if ( apiItem instanceof ApiInterface ) { if ( apiItem . extendsTypes . length > 0 ) { yamlInheritance . inheritance = this . _renderInheritance ( this . _getUidObject ( apiItem ) , apiItem . extendsTypes ) ; } } } result . push ( yamlInheritance ) ; } return result ; } private _renderType ( contextUid , typeExcerpt ) { const excerptTokens = [ ... typeExcerpt . spannedTokens ] ; if ( excerptTokens . length === 0 ) { return '' ; } const lastToken = excerptTokens [ excerptTokens . length - 1 ] ; if ( lastToken . kind === ExcerptTokenKind . Content && ! lastToken . text . trim ( ) ) { excerptTokens . pop ( ) ; if ( excerptTokens . length === 0 ) { return '' ; } } const typeName = typeExcerpt . text . trim ( ) ; if ( ! excerptTokens . some ( ( tok ) => tok . kind === ExcerptTokenKind . Reference && ! ! tok . canonicalReference ) ) { return typeName ; } const yamlReferences = this . _ensureYamlReferences ( ) ; const existingUid = yamlReferences . typeNameToUid . get ( typeName ) ; if ( existingUid ) { return existingUid ; } if ( excerptTokens . length === 1 && excerptTokens [ 0 ] . kind === ExcerptTokenKind . Reference && excerptTokens [ 0 ] . canonicalReference ) { const excerptRef = excerptTokens [ 0 ] . canonicalReference . toString ( ) ; const apiItem = this . _apiItemsByCanonicalReference . get ( excerptRef ) ; return this . _recordYamlReference ( yamlReferences , excerptTokens [ 0 ] . canonicalReference . toString ( ) , apiItem ? this . _getYamlItemName ( apiItem ) : typeName , apiItem ? this . _getYamlItemName ( apiItem , { includeNamespace : true } ) : typeName ) ; } const baseUid = contextUid . withMeaning ( undefined ) . withOverloadIndex ( undefined ) . toString ( ) ; const counter = yamlReferences . uidTypeReferenceCounters . get ( baseUid ) || 0 ; yamlReferences . uidTypeReferenceCounters . set ( baseUid , counter + 1 ) ; const uid = contextUid . addNavigationStep ( Navigation . Locals , `${ counter }` ) . withMeaning ( Meaning . ComplexType ) . withOverloadIndex ( undefined ) . toString ( ) ; return this . _recordYamlReference ( yamlReferences , uid , typeName , typeName , excerptTokens ) ; } private _recordYamlReference ( yamlReferences , uid , name , fullName , excerptTokens ? ) { if ( yamlReferences . references . some ( ( ref ) => ref . uid === uid ) ) { return uid ; } const specs = [ ] ; if ( excerptTokens ) { for ( const token of excerptTokens ) { if ( token . kind === ExcerptTokenKind . Reference ) { const spec = { } ; const specUid = token . canonicalReference && token . canonicalReference . toString ( ) ; const apiItem = specUid ? this . _apiItemsByCanonicalReference . get ( specUid ) : undefined ; if ( specUid ) { spec . uid = specUid ; } spec . name = token . text ; spec . fullName = apiItem ? apiItem . getScopedNameWithinPackage ( ) : token . canonicalReference ? token . canonicalReference . withSource ( undefined ) . withMeaning ( undefined ) . withOverloadIndex ( undefined ) . toString ( ) : token . text ; specs . push ( spec ) ; } else { specs . push ( { name : token . text , fullName : token . text } ) ; } } } const yamlReference = { uid } ; if ( specs . length > 0 ) { yamlReference . name = specs . map ( ( s ) => s . name ) . join ( '' ) . trim ( ) ; yamlReference . fullName = specs . map ( ( s ) => s . fullName || s . name ) . join ( '' ) . trim ( ) ; yamlReference [ 'spec.typeScript' ] = specs ; } else { if ( name !== uid ) { yamlReference . name = name ; } if ( fullName !== uid && fullName !== name ) { yamlReference . fullName = fullName ; } } yamlReferences . references . push ( yamlReference ) ; return uid ; } private _getYamlItemName ( apiItem , options = { } ) { const { includeSignature , includeNamespace } = options ; const baseName = includeSignature ? Utilities . getConciseSignature ( apiItem ) : apiItem . displayName ; if ( ( includeNamespace || apiItem . kind === ApiItemKind . Namespace ) && apiItem . parent && apiItem . parent . kind === ApiItemKind . Namespace ) { const nameParts = [ baseName ] ; for ( let current = apiItem . parent ; current ; current = current . parent ) { if ( current . kind !== ApiItemKind . Namespace ) { break ; } nameParts . unshift ( current . displayName ) ; } return nameParts . join ( '.' ) ; } else { return baseName ; } } private _getYamlFilePath ( outputFolder , apiItem ) { let result = '' ; for ( const current of apiItem . getHierarchy ( ) ) { switch ( current . kind ) { case ApiItemKind . Model : case ApiItemKind . EntryPoint : break ; case ApiItemKind . Package : result += Utilities . getSafeFilenameForName ( PackageName . getUnscopedName ( current . displayName ) ) ; break ; default : if ( current . parent && current . parent . kind === ApiItemKind . EntryPoint ) { result += '/' ; } else { result += '.' ; } result += Utilities . getSafeFilenameForName ( current . displayName ) ; break ; } } let disambiguator = '' ; if ( apiItem . getMergedSiblings ( ) . length > 1 ) { disambiguator = `-${ apiItem . kind . toLowerCase ( ) }` ; } return path . join ( outputFolder , result + disambiguator + '.yml' ) ; } private _deleteOldOutputFiles ( outputFolder ) { console . log ( 'Deleting old output from ' + outputFolder ) ; FileSystem . ensureEmptyFolder ( outputFolder ) ; } }