license import { formatComment , indent , quotePropertyName } from './formatting' ; import { Node } from './index' ; import { anyType , ParamType , Type } from './types' ; /** An AST node that can appear directly in a document or namespace. */ export type Declaration = GlobalNamespace | Namespace | Class | Interface | Function | ConstValue | Import | Export | TypeAssignment ; export class GlobalNamespace { readonly kind = 'globalNamespace' ; members : Declaration [ ] ; constructor ( members ? ) { this . members = members || [ ] ; } * traverse ( ) { for ( const m of this . members ) { yield * m . traverse ( ) ; } yield this ; } serialize ( depth = 0 ) { const i = indent ( depth ) ; let out = `${ i }` ; if ( depth === 0 ) { out += 'declare ' ; } out += `global {\n` ; for ( const member of this . members ) { out += '\n' + member . serialize ( depth + 1 ) ; } out += `${ i }}\n` ; return out ; } } export class Namespace { readonly kind = 'namespace' ; name : string ; description : string ; style : 'namespace' | 'module' ; members : Declaration [ ] ; constructor ( data ) { this . name = data . name ; this . description = data . description || '' ; this . members = data . members || [ ] ; this . style = data . style || 'namespace' ; } * traverse ( ) { for ( const m of this . members ) { yield * m . traverse ( ) ; } yield this ; } serialize ( depth = 0 ) { let out = '' ; if ( this . description ) { out += formatComment ( this . description , depth ) ; } const i = indent ( depth ) ; out += i ; if ( depth === 0 ) { out += 'declare ' ; } let name = this . name ; if ( this . style === 'module' ) { name = `'${ name }'` ; } out += `${ this . style } ${ name } {\n` ; for ( const member of this . members ) { out += '\n' + member . serialize ( depth + 1 ) ; } out += `${ i }}\n` ; return out ; } } export class Class { readonly kind = 'class' ; name : string ; description : string ; extends : string ; mixins : string [ ] ; properties : Property [ ] ; methods : Method [ ] ; constructorMethod ? : Method ; constructor ( data ) { this . name = data . name ; this . description = data . description || '' ; this . extends = data . extends || '' ; this . mixins = data . mixins || [ ] ; this . properties = data . properties || [ ] ; this . methods = data . methods || [ ] ; this . constructorMethod = data . constructorMethod ; } * traverse ( ) { for ( const p of this . properties ) { yield * p . traverse ( ) ; } if ( this . constructorMethod ) { yield * this . constructorMethod . traverse ( ) ; } for ( const m of this . methods ) { yield * m . traverse ( ) ; } yield this ; } serialize ( depth = 0 ) { let out = '' ; const i = indent ( depth ) ; if ( this . description ) { out += formatComment ( this . description , depth ) ; } out += i ; if ( depth === 0 ) { out += 'declare ' ; } out += `class ${ this . name }` ; if ( this . mixins . length ) { const i2 = indent ( depth + 1 ) ; out += ' extends' ; for ( const mixin of this . mixins ) { out += `\n${ i2 }${ mixin }(` ; } out += `\n${ i2 }${ this . extends || 'Object' }` ; out += ')' . repeat ( this . mixins . length ) ; } else if ( this . extends ) { out += ' extends ' + this . extends ; } out += ' {\n' ; for ( const property of this . properties ) { out += property . serialize ( depth + 1 ) ; } if ( this . constructorMethod ) { out += this . constructorMethod . serialize ( depth + 1 ) ; } for ( const method of this . methods ) { out += method . serialize ( depth + 1 ) ; } if ( ! out . endsWith ( '\n' ) ) { out += '\n' ; } out += `${ i }}\n` ; return out ; } } export class Interface { readonly kind = 'interface' ; name : string ; description : string ; extends : string [ ] ; properties : Property [ ] ; methods : Method [ ] ; constructor ( data ) { this . name = data . name ; this . description = data . description || '' ; this . extends = data . extends || [ ] ; this . properties = data . properties || [ ] ; this . methods = data . methods || [ ] ; } * traverse ( ) { for ( const p of this . properties ) { yield * p . traverse ( ) ; } for ( const m of this . methods ) { yield * m . traverse ( ) ; } yield this ; } serialize ( depth = 0 ) { let out = '' ; const i = indent ( depth ) ; if ( this . description ) { out += formatComment ( this . description , depth ) ; } out += i ; out += `interface ${ this . name }` ; if ( this . extends . length ) { out += ' extends ' + this . extends . join ( ', ' ) ; } out += ' {\n' ; for ( const property of this . properties ) { out += property . serialize ( depth + 1 ) ; } for ( const method of this . methods ) { out += method . serialize ( depth + 1 ) ; } if ( ! out . endsWith ( '\n' ) ) { out += '\n' ; } out += `${ i }}\n` ; return out ; } } export abstract class FunctionLike { kind ? : 'method' | 'function' ; name : string ; description : string ; params : ParamType [ ] ; templateTypes : string [ ] ; returns ? : Type ; returnsDescription : string ; isStatic : boolean ; ignoreTypeCheck : boolean ; constructor ( data ) { this . name = data . name ; this . description = data . description || '' ; this . params = data . params || [ ] ; this . returns = data . returns || anyType ; this . templateTypes = data . templateTypes || [ ] ; this . returnsDescription = data . returnsDescription || '' ; this . isStatic = data . isStatic || false ; this . ignoreTypeCheck = data . ignoreTypeCheck || false ; } serialize ( depth = 0 ) { let out = '' ; const i = indent ( depth ) ; const annotations = [ ] ; for ( const p of this . params ) { if ( p . description ) { annotations . push ( `@param ${ p . name } ${ p . description }` ) ; } } if ( this . returnsDescription ) { annotations . push ( `@returns ${ this . returnsDescription }` ) ; } let combinedDescription = this . description ; if ( annotations . length > 0 ) { if ( combinedDescription ) { combinedDescription += '\n\n' ; } combinedDescription += annotations . join ( '\n' ) ; } if ( combinedDescription ) { out += '\n' + formatComment ( combinedDescription , depth ) ; } if ( this . ignoreTypeCheck ) { out += indent ( depth ) + '// @ts-ignore\n' ; } out += i ; if ( depth === 0 ) { out += 'declare ' ; } if ( this . kind === 'method' && this . isStatic ) { out += 'static ' ; } if ( this . kind === 'function' ) { out += 'function ' ; } out += this . name ; if ( this . templateTypes . length > 0 ) { out += `<${ this . templateTypes . join ( ', ' ) }>` ; } out += '(' ; out += this . params . map ( ( p ) => p . serialize ( ) ) . join ( ', ' ) ; out += `)` ; if ( this . returns ) { out += `: ${ this . returns . serialize ( ) }` ; } out += `;\n` ; return out ; } } export class Function extends FunctionLike { readonly kind = 'function' ; * traverse ( ) { for ( const p of this . params ) { yield * p . traverse ( ) ; } if ( this . returns ) { yield * this . returns . traverse ( ) ; } yield this ; } } export class Method extends FunctionLike { readonly kind = 'method' ; * traverse ( ) { for ( const p of this . params ) { yield * p . traverse ( ) ; } if ( this . returns ) { yield * this . returns . traverse ( ) ; } yield this ; } } export class Property { readonly kind = 'property' ; name : string ; description : string ; type : Type ; readOnly : boolean ; constructor ( data ) { this . name = data . name ; this . description = data . description || '' ; this . type = data . type || anyType ; this . readOnly = data . readOnly || false ; } * traverse ( ) { yield * this . type . traverse ( ) ; yield this ; } serialize ( depth = 0 ) { let out = '' ; const i = indent ( depth ) ; if ( this . description ) { out += '\n' + formatComment ( this . description , depth ) ; } out += i ; if ( this . readOnly ) { out += 'readonly ' ; } out += `${ quotePropertyName ( this . name ) }: ${ this . type . serialize ( ) };\n` ; return out ; } } export class ConstValue { readonly kind = 'constValue' ; name : string ; type : Type ; constructor ( data ) { this . name = data . name ; this . type = data . type ; } * traverse ( ) { yield * this . type . traverse ( ) ; yield this ; } serialize ( depth = 0 ) { return indent ( depth ) + ( depth === 0 ? 'declare ' : '' ) + `const ${ this . name }: ${ this . type . serialize ( ) };\n` ; } } /**
 * The "*" token in an import or export.
 */ export const AllIdentifiers = Symbol ( '*' ) ; export type AllIdentifiers = typeof AllIdentifiers ; /**
 * An identifier that is imported, possibly with a different name.
 */ export interface ImportSpecifier { identifier : string | AllIdentifiers ; alias ? : string ; } /**
 * A JavaScript module import.
 */ export class Import { readonly kind = 'import' ; identifiers : ImportSpecifier [ ] ; fromModuleSpecifier : string ; trailingComment ? : string ; constructor ( data ) { this . identifiers = data . identifiers ; this . fromModuleSpecifier = data . fromModuleSpecifier ; } * traverse ( ) { yield this ; } serialize ( depth = 0 ) { const i = indent ( depth ) ; if ( this . identifiers . some ( ( i ) => i . identifier === AllIdentifiers ) ) { const parts = [ ] ; for ( const identifier of this . identifiers ) { if ( identifier . identifier === 'default' ) { parts . push ( identifier . alias ) ; } else if ( identifier . identifier === AllIdentifiers ) { parts . push ( `* as ${ identifier . alias }` ) ; } } return `${ i }import ${ parts . join ( ', ' ) } ` + `from '${ this . fromModuleSpecifier }';` + `${ this . trailingComment || '' }\n` ; } else { const parts = [ ] ; for ( const { identifier , alias } of this . identifiers ) { if ( identifier === AllIdentifiers ) { continue ; } parts . push ( identifier + ( alias !== undefined && alias !== identifier ? ` as ${ alias }` : '' ) ) ; } return `${ i }import {${ parts . join ( ', ' ) }} ` + `from '${ this . fromModuleSpecifier }';${ this . trailingComment || '' }\n` ; } } } /**
 * An identifier that is imported, possibly with a different name.
 */ export interface ExportSpecifier { identifier : string ; alias ? : string ; } /**
 * A JavaScript module export.
 */ export class Export { readonly kind = 'export' ; identifiers : ExportSpecifier [ ] | AllIdentifiers ; fromModuleSpecifier : string ; trailingComment ? : string ; constructor ( data ) { this . identifiers = data . identifiers ; this . fromModuleSpecifier = data . fromModuleSpecifier || '' ; } * traverse ( ) { yield this ; } serialize ( depth = 0 ) { const i = indent ( depth ) ; let out = `${ i }export ` ; if ( this . identifiers === AllIdentifiers ) { out += '*' ; } else { const specifiers = this . identifiers . map ( ( { identifier , alias } ) => { return identifier + ( alias !== undefined && alias !== identifier ? ` as ${ alias }` : '' ) ; } ) ; out += `{${ specifiers . join ( ', ' ) }}` ; } if ( this . fromModuleSpecifier !== '' ) { out += ` from '${ this . fromModuleSpecifier }'` ; } out += `;${ this . trailingComment || '' }\n` ; return out ; } } export class TypeAssignment { readonly kind = 'typeAssignment' ; name : string ; value : Type ; constructor ( data ) { this . name = data . name ; this . value = data . value ; } * traverse ( ) { yield * this . value . traverse ( ) ; yield this ; } serialize ( depth = 0 ) { const i = indent ( depth ) ; return `${ i }type ${ this . name } = ${ this . value . serialize ( ) };\n` ; } }