import * as ts from 'typescript' ; import { NodeWrap } from './convert-ast' ; import { isBlockLike , isPropertyDeclaration , isJsDoc , isImportDeclaration , isTextualLiteral , isImportEqualsDeclaration , isModuleDeclaration , isCallExpression , isExportDeclaration , isLiteralTypeNode , isTypeReferenceNode , isPropertyAssignment , isEntityNameExpression , isNumericOrStringLikeLiteral , isPropertyAccessExpression , isIdentifier , isPrefixUnaryExpression , isNumericLiteral , isCaseClause , isMethodDeclaration , } from '../typeguard/node' ; import { isBigIntLiteral , isUniqueESSymbolType } from '../typeguard/3.2' ; import { isBooleanLiteralType , unionTypeParts , getPropertyNameFromType } from './type' ; export function getChildOfKind < T extends ts . SyntaxKind > ( node , kind , sourceFile ? ) { for ( const child of node . getChildren ( sourceFile ) ) if ( child . kind === kind ) return < ts . Token < T > > child ; } export function isTokenKind ( kind ) { return kind >= ts . SyntaxKind . FirstToken && kind <= ts . SyntaxKind . LastToken ; } export function isNodeKind ( kind ) { return kind >= ts . SyntaxKind . FirstNode ; } export function isAssignmentKind ( kind ) { return kind >= ts . SyntaxKind . FirstAssignment && kind <= ts . SyntaxKind . LastAssignment ; } export function isTypeNodeKind ( kind ) { return kind >= ts . SyntaxKind . FirstTypeNode && kind <= ts . SyntaxKind . LastTypeNode ; } export function isJsDocKind ( kind ) { return kind >= ts . SyntaxKind . FirstJSDocNode && kind <= ts . SyntaxKind . LastJSDocNode ; } export function isKeywordKind ( kind ) { return kind >= ts . SyntaxKind . FirstKeyword && kind <= ts . SyntaxKind . LastKeyword ; } export function isThisParameter ( parameter ) { return parameter . name . kind === ts . SyntaxKind . Identifier && parameter . name . originalKeywordKind === ts . SyntaxKind . ThisKeyword ; } export function getModifier ( node , kind ) { if ( node . modifiers !== undefined ) for ( const modifier of node . modifiers ) if ( modifier . kind === kind ) return modifier ; } export function hasModifier ( modifiers , ... kinds ) { if ( modifiers === undefined ) return false ; for ( const modifier of modifiers ) if ( kinds . includes ( modifier . kind ) ) return true ; return false ; } export function isParameterProperty ( node ) { return hasModifier ( node . modifiers , ts . SyntaxKind . PublicKeyword , ts . SyntaxKind . ProtectedKeyword , ts . SyntaxKind . PrivateKeyword , ts . SyntaxKind . ReadonlyKeyword ) ; } export function hasAccessModifier ( node ) { return isModifierFlagSet ( node , ts . ModifierFlags . AccessibilityModifier ) ; } function isFlagSet ( obj , flag ) { return ( obj . flags & flag ) !== 0 ; } export const isNodeFlagSet = isFlagSet ; export const isTypeFlagSet = isFlagSet ; export const isSymbolFlagSet = isFlagSet ; export function isObjectFlagSet ( objectType , flag ) { return ( objectType . objectFlags & flag ) !== 0 ; } export function isModifierFlagSet ( node , flag ) { return ( ts . getCombinedModifierFlags ( < ts . Declaration > node ) & flag ) !== 0 ; } export function getPreviousStatement ( statement ) { const parent = statement . parent ! ; if ( isBlockLike ( parent ) ) { const index = parent . statements . indexOf ( statement ) ; if ( index > 0 ) return parent . statements [ index - 1 ] ; } } export function getNextStatement ( statement ) { const parent = statement . parent ! ; if ( isBlockLike ( parent ) ) { const index = parent . statements . indexOf ( statement ) ; if ( index < parent . statements . length ) return parent . statements [ index + 1 ] ; } } /** Returns the token before the start of `node` or `undefined` if there is none. */ export function getPreviousToken ( node , sourceFile ? ) { const { pos } = node ; if ( pos === 0 ) return ; do node = node . parent ! ; while ( node . pos === pos ) ; return getTokenAtPositionWorker ( node , pos - 1 , sourceFile ?? node . getSourceFile ( ) , false ) ; } /** Returns the next token that begins after the end of `node`. Returns `undefined` for SourceFile and EndOfFileToken */ export function getNextToken ( node , sourceFile ? ) { if ( node . kind === ts . SyntaxKind . SourceFile || node . kind === ts . SyntaxKind . EndOfFileToken ) return ; const end = node . end ; node = node . parent ! ; while ( node . end === end ) { if ( node . parent === undefined ) return ( < ts . SourceFile > node ) . endOfFileToken ; node = node . parent ; } return getTokenAtPositionWorker ( node , end , sourceFile ?? node . getSourceFile ( ) , false ) ; } /** Returns the token at or following the specified position or undefined if none is found inside `parent`. */ export function getTokenAtPosition ( parent , pos , sourceFile ? , allowJsDoc ? ) { if ( pos < parent . pos || pos >= parent . end ) return ; if ( isTokenKind ( parent . kind ) ) return parent ; return getTokenAtPositionWorker ( parent , pos , sourceFile ?? parent . getSourceFile ( ) , allowJsDoc === true ) ; } function getTokenAtPositionWorker ( node , pos , sourceFile , allowJsDoc ) { if ( ! allowJsDoc ) { node = getAstNodeAtPosition ( node , pos ) ! ; if ( isTokenKind ( node . kind ) ) return node ; } outer : while ( true ) { for ( const child of node . getChildren ( sourceFile ) ) { if ( child . end > pos && ( allowJsDoc || child . kind !== ts . SyntaxKind . JSDocComment ) ) { if ( isTokenKind ( child . kind ) ) return child ; node = child ; continue outer ; } } return ; } } /**
 * Return the comment at the specified position.
 * You can pass an optional `parent` to avoid some work finding the corresponding token starting at `sourceFile`.
 * If the `parent` parameter is passed, `pos` must be between `parent.pos` and `parent.end`.
*/ export function getCommentAtPosition ( sourceFile , pos , parent = sourceFile ) { const token = getTokenAtPosition ( parent , pos , sourceFile ) ; if ( token === undefined || token . kind === ts . SyntaxKind . JsxText || pos >= token . end - ( ts . tokenToString ( token . kind ) || '' ) . length ) return ; const startPos = token . pos === 0 ? ( ts . getShebang ( sourceFile . text ) || '' ) . length : token . pos ; return startPos !== 0 && ts . forEachTrailingCommentRange ( sourceFile . text , startPos , commentAtPositionCallback , pos ) || ts . forEachLeadingCommentRange ( sourceFile . text , startPos , commentAtPositionCallback , pos ) ; } function commentAtPositionCallback ( pos , end , kind , _nl , at ) { return at >= pos && at < end ? { pos , end , kind } : undefined ; } /**
 * Returns whether the specified position is inside a comment.
 * You can pass an optional `parent` to avoid some work finding the corresponding token starting at `sourceFile`.
 * If the `parent` parameter is passed, `pos` must be between `parent.pos` and `parent.end`.
 */ export function isPositionInComment ( sourceFile , pos , parent ? ) { return getCommentAtPosition ( sourceFile , pos , parent ) !== undefined ; } export function commentText ( sourceText , comment ) { return sourceText . substring ( comment . pos + 2 , comment . kind === ts . SyntaxKind . SingleLineCommentTrivia ? comment . end : comment . end - 2 ) ; } /** Returns the deepest AST Node at `pos`. Returns undefined if `pos` is outside of the range of `node` */ export function getAstNodeAtPosition ( node , pos ) { if ( node . pos > pos || node . end <= pos ) return ; while ( isNodeKind ( node . kind ) ) { const nested = ts . forEachChild ( node , ( child ) => child . pos <= pos && child . end > pos ? child : undefined ) ; if ( nested === undefined ) break ; node = nested ; } return node ; } /**
 * Returns the NodeWrap of deepest AST node that contains `pos` between its `pos` and `end`.
 * Only returns undefined if pos is outside of `wrap`
 */ export function getWrappedNodeAtPosition ( wrap , pos ) { if ( wrap . node . pos > pos || wrap . node . end <= pos ) return ; outer : while ( true ) { for ( const child of wrap . children ) { if ( child . node . pos > pos ) return wrap ; if ( child . node . end > pos ) { wrap = child ; continue outer ; } } return wrap ; } } export function getPropertyName ( propertyName ) { if ( propertyName . kind === ts . SyntaxKind . ComputedPropertyName ) { const expression = unwrapParentheses ( propertyName . expression ) ; if ( isPrefixUnaryExpression ( expression ) ) { let negate = false ; switch ( expression . operator ) { case ts . SyntaxKind . MinusToken : negate = true ; case ts . SyntaxKind . PlusToken : return isNumericLiteral ( expression . operand ) ? `${ negate ? '-' : '' }${ expression . operand . text }` : isBigIntLiteral ( expression . operand ) ? `${ negate ? '-' : '' }${ expression . operand . text . slice ( 0 , - 1 ) }` : undefined ; default : return ; } } if ( isBigIntLiteral ( expression ) ) return expression . text . slice ( 0 , - 1 ) ; if ( isNumericOrStringLikeLiteral ( expression ) ) return expression . text ; return ; } return propertyName . kind === ts . SyntaxKind . PrivateIdentifier ? undefined : propertyName . text ; } export function forEachDestructuringIdentifier < T > ( pattern , fn ) { for ( const element of pattern . elements ) { if ( element . kind !== ts . SyntaxKind . BindingElement ) continue ; let result ; if ( element . name . kind === ts . SyntaxKind . Identifier ) { result = fn ( < ts . BindingElement & { name : ts . Identifier ; } > element ) ; } else { result = forEachDestructuringIdentifier ( element . name , fn ) ; } if ( result ) return result ; } } export function forEachDeclaredVariable < T > ( declarationList , cb ) { for ( const declaration of declarationList . declarations ) { let result ; if ( declaration . name . kind === ts . SyntaxKind . Identifier ) { result = cb ( < ts . VariableDeclaration & { name : ts . Identifier ; } > declaration ) ; } else { result = forEachDestructuringIdentifier ( declaration . name , cb ) ; } if ( result ) return result ; } } export const enum VariableDeclarationKind { Var , Let , Const } export function getVariableDeclarationKind ( declarationList ) { if ( declarationList . flags & ts . NodeFlags . Let ) return VariableDeclarationKind . Let ; if ( declarationList . flags & ts . NodeFlags . Const ) return VariableDeclarationKind . Const ; return VariableDeclarationKind . Var ; } export function isBlockScopedVariableDeclarationList ( declarationList ) { return ( declarationList . flags & ts . NodeFlags . BlockScoped ) !== 0 ; } export function isBlockScopedVariableDeclaration ( declaration ) { const parent = declaration . parent ! ; return parent . kind === ts . SyntaxKind . CatchClause || isBlockScopedVariableDeclarationList ( parent ) ; } export function isBlockScopedDeclarationStatement ( statement ) { switch ( statement . kind ) { case ts . SyntaxKind . VariableStatement : return isBlockScopedVariableDeclarationList ( ( < ts . VariableStatement > statement ) . declarationList ) ; case ts . SyntaxKind . ClassDeclaration : case ts . SyntaxKind . EnumDeclaration : case ts . SyntaxKind . InterfaceDeclaration : case ts . SyntaxKind . TypeAliasDeclaration : return true ; default : return false ; } } export function isInSingleStatementContext ( statement ) { switch ( statement . parent ! . kind ) { case ts . SyntaxKind . ForStatement : case ts . SyntaxKind . ForInStatement : case ts . SyntaxKind . ForOfStatement : case ts . SyntaxKind . WhileStatement : case ts . SyntaxKind . DoStatement : case ts . SyntaxKind . IfStatement : case ts . SyntaxKind . WithStatement : case ts . SyntaxKind . LabeledStatement : return true ; default : return false ; } } export const enum ScopeBoundary { None = 0 , Function = 1 , Block = 2 , Type = 4 , ConditionalType = 8 } export const enum ScopeBoundarySelector { Function = ScopeBoundary . Function , Block = ScopeBoundarySelector . Function | ScopeBoundary . Block , Type = ScopeBoundarySelector . Block | ScopeBoundary . Type , InferType = ScopeBoundary . ConditionalType } export function isScopeBoundary ( node ) { return isFunctionScopeBoundary ( node ) || isBlockScopeBoundary ( node ) || isTypeScopeBoundary ( node ) ; } export function isTypeScopeBoundary ( node ) { switch ( node . kind ) { case ts . SyntaxKind . InterfaceDeclaration : case ts . SyntaxKind . TypeAliasDeclaration : case ts . SyntaxKind . MappedType : return ScopeBoundary . Type ; case ts . SyntaxKind . ConditionalType : return ScopeBoundary . ConditionalType ; default : return ScopeBoundary . None ; } } export function isFunctionScopeBoundary ( node ) { switch ( node . kind ) { case ts . SyntaxKind . FunctionExpression : case ts . SyntaxKind . ArrowFunction : case ts . SyntaxKind . Constructor : case ts . SyntaxKind . ModuleDeclaration : case ts . SyntaxKind . ClassDeclaration : case ts . SyntaxKind . ClassExpression : case ts . SyntaxKind . EnumDeclaration : case ts . SyntaxKind . MethodDeclaration : case ts . SyntaxKind . FunctionDeclaration : case ts . SyntaxKind . GetAccessor : case ts . SyntaxKind . SetAccessor : case ts . SyntaxKind . MethodSignature : case ts . SyntaxKind . CallSignature : case ts . SyntaxKind . ConstructSignature : case ts . SyntaxKind . ConstructorType : case ts . SyntaxKind . FunctionType : return ScopeBoundary . Function ; case ts . SyntaxKind . SourceFile : return ts . isExternalModule ( < ts . SourceFile > node ) ? ScopeBoundary . Function : ScopeBoundary . None ; default : return ScopeBoundary . None ; } } export function isBlockScopeBoundary ( node ) { switch ( node . kind ) { case ts . SyntaxKind . Block : const parent = node . parent ! ; return parent . kind !== ts . SyntaxKind . CatchClause && ( parent . kind === ts . SyntaxKind . SourceFile || ! isFunctionScopeBoundary ( parent ) ) ? ScopeBoundary . Block : ScopeBoundary . None ; case ts . SyntaxKind . ForStatement : case ts . SyntaxKind . ForInStatement : case ts . SyntaxKind . ForOfStatement : case ts . SyntaxKind . CaseBlock : case ts . SyntaxKind . CatchClause : case ts . SyntaxKind . WithStatement : return ScopeBoundary . Block ; default : return ScopeBoundary . None ; } } /** Returns true for scope boundaries that have their own `this` reference instead of inheriting it from the containing scope */ export function hasOwnThisReference ( node ) { switch ( node . kind ) { case ts . SyntaxKind . ClassDeclaration : case ts . SyntaxKind . ClassExpression : case ts . SyntaxKind . FunctionExpression : return true ; case ts . SyntaxKind . FunctionDeclaration : return ( < ts . FunctionDeclaration > node ) . body !== undefined ; case ts . SyntaxKind . MethodDeclaration : case ts . SyntaxKind . GetAccessor : case ts . SyntaxKind . SetAccessor : return node . parent ! . kind === ts . SyntaxKind . ObjectLiteralExpression ; default : return false ; } } export function isFunctionWithBody ( node ) { switch ( node . kind ) { case ts . SyntaxKind . GetAccessor : case ts . SyntaxKind . SetAccessor : case ts . SyntaxKind . FunctionDeclaration : case ts . SyntaxKind . MethodDeclaration : case ts . SyntaxKind . Constructor : return ( < ts . FunctionLikeDeclaration > node ) . body !== undefined ; case ts . SyntaxKind . FunctionExpression : case ts . SyntaxKind . ArrowFunction : return true ; default : return false ; } } param node param cb export function forEachToken ( node , cb , sourceFile = node . getSourceFile ( ) ) { const queue = [ ] ; while ( true ) { if ( isTokenKind ( node . kind ) ) { cb ( node ) ; } else if ( node . kind !== ts . SyntaxKind . JSDocComment ) { const children = node . getChildren ( sourceFile ) ; if ( children . length === 1 ) { node = children [ 0 ] ; continue ; } for ( let i = children . length - 1 ; i >= 0 ; -- i ) queue . push ( children [ i ] ) ; } if ( queue . length === 0 ) break ; node = queue . pop ( ) ! ; } } export type ForEachTokenCallback = ( fullText : string , kind : ts . SyntaxKind , range : ts . TextRange , parent : ts . Node ) => void ; description param node param cb export function forEachTokenWithTrivia ( node , cb , sourceFile = node . getSourceFile ( ) ) { const fullText = sourceFile . text ; const scanner = ts . createScanner ( sourceFile . languageVersion , false , sourceFile . languageVariant , fullText ) ; return forEachToken ( node , ( token ) => { const tokenStart = token . kind === ts . SyntaxKind . JsxText || token . pos === token . end ? token . pos : token . getStart ( sourceFile ) ; if ( tokenStart !== token . pos ) { scanner . setTextPos ( token . pos ) ; let kind = scanner . scan ( ) ; let pos = scanner . getTokenPos ( ) ; while ( pos < tokenStart ) { const textPos = scanner . getTextPos ( ) ; cb ( fullText , kind , { pos , end : textPos } , token . parent ! ) ; if ( textPos === tokenStart ) break ; kind = scanner . scan ( ) ; pos = scanner . getTokenPos ( ) ; } } return cb ( fullText , token . kind , { end : token . end , pos : tokenStart } , token . parent ! ) ; } , sourceFile ) ; } export type ForEachCommentCallback = ( fullText : string , comment : ts . CommentRange ) => void ; /** Iterate over all comments owned by `node` or its children */ export function forEachComment ( node , cb , sourceFile = node . getSourceFile ( ) ) { const fullText = sourceFile . text ; const notJsx = sourceFile . languageVariant !== ts . LanguageVariant . JSX ; return forEachToken ( node , ( token ) => { if ( token . pos === token . end ) return ; if ( token . kind !== ts . SyntaxKind . JsxText ) ts . forEachLeadingCommentRange ( fullText , token . pos === 0 ? ( ts . getShebang ( fullText ) || '' ) . length : token . pos , commentCallback ) ; if ( notJsx || canHaveTrailingTrivia ( token ) ) return ts . forEachTrailingCommentRange ( fullText , token . end , commentCallback ) ; } , sourceFile ) ; function commentCallback ( pos , end , kind ) { cb ( fullText , { pos , end , kind } ) ; } } /** Exclude trailing positions that would lead to scanning for trivia inside JsxText */ function canHaveTrailingTrivia ( token ) { switch ( token . kind ) { case ts . SyntaxKind . CloseBraceToken : return token . parent ! . kind !== ts . SyntaxKind . JsxExpression || ! isJsxElementOrFragment ( token . parent ! . parent ! ) ; case ts . SyntaxKind . GreaterThanToken : switch ( token . parent ! . kind ) { case ts . SyntaxKind . JsxOpeningElement : return token . end !== token . parent ! . end ; case ts . SyntaxKind . JsxOpeningFragment : return false ; case ts . SyntaxKind . JsxSelfClosingElement : return token . end !== token . parent ! . end || ! isJsxElementOrFragment ( token . parent ! . parent ! ) ; case ts . SyntaxKind . JsxClosingElement : case ts . SyntaxKind . JsxClosingFragment : return ! isJsxElementOrFragment ( token . parent ! . parent ! . parent ! ) ; } } return true ; } function isJsxElementOrFragment ( node ) { return node . kind === ts . SyntaxKind . JsxElement || node . kind === ts . SyntaxKind . JsxFragment ; } export interface LineRange extends ts . TextRange { contentLength : number ; } export function getLineRanges ( sourceFile ) { const lineStarts = sourceFile . getLineStarts ( ) ; const result = [ ] ; const length = lineStarts . length ; const sourceText = sourceFile . text ; let pos = 0 ; for ( let i = 1 ; i < length ; ++ i ) { const end = lineStarts [ i ] ; let lineEnd = end ; for ( ; lineEnd > pos ; -- lineEnd ) if ( ! ts . isLineBreak ( sourceText . charCodeAt ( lineEnd - 1 ) ) ) break ; result . push ( { pos , end , contentLength : lineEnd - pos , } ) ; pos = end ; } result . push ( { pos , end : sourceFile . end , contentLength : sourceFile . end - pos , } ) ; return result ; } /** Get the line break style used in sourceFile. This function only looks at the first line break. If there is none, \n is assumed. */ export function getLineBreakStyle ( sourceFile ) { const lineStarts = sourceFile . getLineStarts ( ) ; return lineStarts . length === 1 || lineStarts [ 1 ] < 2 || sourceFile . text [ lineStarts [ 1 ] - 2 ] !== '\r' ? '\n' : '\r\n' ; } let cachedScanner ; function scanToken ( text , languageVersion ) { if ( cachedScanner === undefined ) { cachedScanner = ts . createScanner ( languageVersion , false , undefined , text ) ; } else { cachedScanner . setScriptTarget ( languageVersion ) ; cachedScanner . setText ( text ) ; } cachedScanner . scan ( ) ; return cachedScanner ; } /**
 * Determines whether the given text parses as a standalone identifier.
 * This is not a guarantee that it works in every context. The property name in PropertyAccessExpressions for example allows reserved words.
 * Depending on the context it could be parsed as contextual keyword or TypeScript keyword.
 */ export function isValidIdentifier ( text , languageVersion = ts . ScriptTarget . Latest ) { const scan = scanToken ( text , languageVersion ) ; return scan . isIdentifier ( ) && scan . getTextPos ( ) === text . length && scan . getTokenPos ( ) === 0 ; } function charSize ( ch ) { return ch >= 0x10000 ? 2 : 1 ; } /**
 * Determines whether the given text can be used to access a property with a PropertyAccessExpression while preserving the property's name.
 */ export function isValidPropertyAccess ( text , languageVersion = ts . ScriptTarget . Latest ) { if ( text . length === 0 ) return false ; let ch = text . codePointAt ( 0 ) ! ; if ( ! ts . isIdentifierStart ( ch , languageVersion ) ) return false ; for ( let i = charSize ( ch ) ; i < text . length ; i += charSize ( ch ) ) { ch = text . codePointAt ( i ) ! ; if ( ! ts . isIdentifierPart ( ch , languageVersion ) ) return false ; } return true ; } /**
 * Determines whether the given text can be used as unquoted name of a property declaration while preserving the property's name.
 */ export function isValidPropertyName ( text , languageVersion = ts . ScriptTarget . Latest ) { if ( isValidPropertyAccess ( text , languageVersion ) ) return true ; const scan = scanToken ( text , languageVersion ) ; return scan . getTextPos ( ) === text . length && scan . getToken ( ) === ts . SyntaxKind . NumericLiteral && scan . getTokenValue ( ) === text ; } /**
 * Determines whether the given text can be parsed as a numeric literal.
 */ export function isValidNumericLiteral ( text , languageVersion = ts . ScriptTarget . Latest ) { const scan = scanToken ( text , languageVersion ) ; return scan . getToken ( ) === ts . SyntaxKind . NumericLiteral && scan . getTextPos ( ) === text . length && scan . getTokenPos ( ) === 0 ; } /**
 * Determines whether the given text can be used as JSX tag or attribute name while preserving the exact name.
 */ export function isValidJsxIdentifier ( text , languageVersion = ts . ScriptTarget . Latest ) { if ( text . length === 0 ) return false ; let seenNamespaceSeparator = false ; let ch = text . codePointAt ( 0 ) ! ; if ( ! ts . isIdentifierStart ( ch , languageVersion ) ) return false ; for ( let i = charSize ( ch ) ; i < text . length ; i += charSize ( ch ) ) { ch = text . codePointAt ( i ) ! ; if ( ! ts . isIdentifierPart ( ch , languageVersion ) && ch !== 45 ) { if ( ! seenNamespaceSeparator && ch === 58 && i + charSize ( ch ) !== text . length ) { seenNamespaceSeparator = true ; } else { return false ; } } } return true ; } export function isNumericPropertyName ( name ) { return String ( + name ) === name ; } export function isSameLine ( sourceFile , pos1 , pos2 ) { return ts . getLineAndCharacterOfPosition ( sourceFile , pos1 ) . line === ts . getLineAndCharacterOfPosition ( sourceFile , pos2 ) . line ; } export const enum SideEffectOptions { None = 0 , TaggedTemplate = 1 , Constructor = 2 , JsxElement = 4 } export function hasSideEffects ( node , options ? ) { const queue = [ ] ; while ( true ) { switch ( node . kind ) { case ts . SyntaxKind . CallExpression : case ts . SyntaxKind . PostfixUnaryExpression : case ts . SyntaxKind . AwaitExpression : case ts . SyntaxKind . YieldExpression : case ts . SyntaxKind . DeleteExpression : return true ; case ts . SyntaxKind . TypeAssertionExpression : case ts . SyntaxKind . AsExpression : case ts . SyntaxKind . ParenthesizedExpression : case ts . SyntaxKind . NonNullExpression : case ts . SyntaxKind . VoidExpression : case ts . SyntaxKind . TypeOfExpression : case ts . SyntaxKind . PropertyAccessExpression : case ts . SyntaxKind . SpreadElement : case ts . SyntaxKind . PartiallyEmittedExpression : node = ( < ts . AssertionExpression | ts . ParenthesizedExpression | ts . NonNullExpression | ts . VoidExpression | ts . TypeOfExpression | ts . PropertyAccessExpression | ts . SpreadElement | ts . PartiallyEmittedExpression > node ) . expression ; continue ; case ts . SyntaxKind . BinaryExpression : if ( isAssignmentKind ( ( < ts . BinaryExpression > node ) . operatorToken . kind ) ) return true ; queue . push ( ( < ts . BinaryExpression > node ) . right ) ; node = ( < ts . BinaryExpression > node ) . left ; continue ; case ts . SyntaxKind . PrefixUnaryExpression : switch ( ( < ts . PrefixUnaryExpression > node ) . operator ) { case ts . SyntaxKind . PlusPlusToken : case ts . SyntaxKind . MinusMinusToken : return true ; default : node = ( < ts . PrefixUnaryExpression > node ) . operand ; continue ; } case ts . SyntaxKind . ElementAccessExpression : if ( ( < ts . ElementAccessExpression > node ) . argumentExpression !== undefined ) queue . push ( ( < ts . ElementAccessExpression > node ) . argumentExpression ) ; node = ( < ts . ElementAccessExpression > node ) . expression ; continue ; case ts . SyntaxKind . ConditionalExpression : queue . push ( ( < ts . ConditionalExpression > node ) . whenTrue , ( < ts . ConditionalExpression > node ) . whenFalse ) ; node = ( < ts . ConditionalExpression > node ) . condition ; continue ; case ts . SyntaxKind . NewExpression : if ( options ! & SideEffectOptions . Constructor ) return true ; if ( ( < ts . NewExpression > node ) . arguments !== undefined ) queue . push ( ... ( < ts . NewExpression > node ) . arguments ! ) ; node = ( < ts . NewExpression > node ) . expression ; continue ; case ts . SyntaxKind . TaggedTemplateExpression : if ( options ! & SideEffectOptions . TaggedTemplate ) return true ; queue . push ( ( < ts . TaggedTemplateExpression > node ) . tag ) ; node = ( < ts . TaggedTemplateExpression > node ) . template ; if ( node . kind === ts . SyntaxKind . NoSubstitutionTemplateLiteral ) break ; case ts . SyntaxKind . TemplateExpression : for ( const child of ( < ts . TemplateExpression > node ) . templateSpans ) queue . push ( child . expression ) ; break ; case ts . SyntaxKind . ClassExpression : { if ( ( < ts . ClassExpression > node ) . decorators !== undefined ) return true ; for ( const child of ( < ts . ClassExpression > node ) . members ) { if ( child . decorators !== undefined ) return true ; if ( ! hasModifier ( child . modifiers , ts . SyntaxKind . DeclareKeyword ) ) { if ( child . name ?. kind === ts . SyntaxKind . ComputedPropertyName ) queue . push ( child . name . expression ) ; if ( isMethodDeclaration ( child ) ) { for ( const p of child . parameters ) if ( p . decorators !== undefined ) return true ; } else if ( isPropertyDeclaration ( child ) && child . initializer !== undefined && hasModifier ( child . modifiers , ts . SyntaxKind . StaticKeyword ) ) { queue . push ( child . initializer ) ; } } } const base = getBaseOfClassLikeExpression ( < ts . ClassExpression > node ) ; if ( base === undefined ) break ; node = base . expression ; continue ; } case ts . SyntaxKind . ArrayLiteralExpression : queue . push ( ... ( < ts . ArrayLiteralExpression > node ) . elements ) ; break ; case ts . SyntaxKind . ObjectLiteralExpression : for ( const child of ( < ts . ObjectLiteralExpression > node ) . properties ) { if ( child . name ?. kind === ts . SyntaxKind . ComputedPropertyName ) queue . push ( child . name . expression ) ; switch ( child . kind ) { case ts . SyntaxKind . PropertyAssignment : queue . push ( child . initializer ) ; break ; case ts . SyntaxKind . SpreadAssignment : queue . push ( child . expression ) ; } } break ; case ts . SyntaxKind . JsxExpression : if ( ( < ts . JsxExpression > node ) . expression === undefined ) break ; node = ( < ts . JsxExpression > node ) . expression ! ; continue ; case ts . SyntaxKind . JsxElement : case ts . SyntaxKind . JsxFragment : for ( const child of ( < ts . JsxElement | ts . JsxFragment > node ) . children ) if ( child . kind !== ts . SyntaxKind . JsxText ) queue . push ( child ) ; if ( node . kind === ts . SyntaxKind . JsxFragment ) break ; node = ( < ts . JsxElement > node ) . openingElement ; case ts . SyntaxKind . JsxSelfClosingElement : case ts . SyntaxKind . JsxOpeningElement : if ( options ! & SideEffectOptions . JsxElement ) return true ; for ( const child of ( < ts . JsxOpeningLikeElement > node ) . attributes . properties ) { if ( child . kind === ts . SyntaxKind . JsxSpreadAttribute ) { queue . push ( child . expression ) ; } else if ( child . initializer !== undefined ) { queue . push ( child . initializer ) ; } } break ; case ts . SyntaxKind . CommaListExpression : queue . push ( ... ( < ts . CommaListExpression > node ) . elements ) ; } if ( queue . length === 0 ) return false ; node = queue . pop ( ) ! ; } } /** Returns the VariableDeclaration or ParameterDeclaration that contains the BindingElement */ export function getDeclarationOfBindingElement ( node ) { let parent = node . parent ! . parent ! ; while ( parent . kind === ts . SyntaxKind . BindingElement ) parent = parent . parent ! . parent ! ; return parent ; } export function isExpressionValueUsed ( node ) { while ( true ) { const parent = node . parent ! ; switch ( parent . kind ) { case ts . SyntaxKind . CallExpression : case ts . SyntaxKind . NewExpression : case ts . SyntaxKind . ElementAccessExpression : case ts . SyntaxKind . WhileStatement : case ts . SyntaxKind . DoStatement : case ts . SyntaxKind . WithStatement : case ts . SyntaxKind . ThrowStatement : case ts . SyntaxKind . ReturnStatement : case ts . SyntaxKind . JsxExpression : case ts . SyntaxKind . JsxSpreadAttribute : case ts . SyntaxKind . JsxElement : case ts . SyntaxKind . JsxFragment : case ts . SyntaxKind . JsxSelfClosingElement : case ts . SyntaxKind . ComputedPropertyName : case ts . SyntaxKind . ArrowFunction : case ts . SyntaxKind . ExportSpecifier : case ts . SyntaxKind . ExportAssignment : case ts . SyntaxKind . ImportDeclaration : case ts . SyntaxKind . ExternalModuleReference : case ts . SyntaxKind . Decorator : case ts . SyntaxKind . TaggedTemplateExpression : case ts . SyntaxKind . TemplateSpan : case ts . SyntaxKind . ExpressionWithTypeArguments : case ts . SyntaxKind . TypeOfExpression : case ts . SyntaxKind . AwaitExpression : case ts . SyntaxKind . YieldExpression : case ts . SyntaxKind . LiteralType : case ts . SyntaxKind . JsxAttributes : case ts . SyntaxKind . JsxOpeningElement : case ts . SyntaxKind . JsxClosingElement : case ts . SyntaxKind . IfStatement : case ts . SyntaxKind . CaseClause : case ts . SyntaxKind . SwitchStatement : return true ; case ts . SyntaxKind . PropertyAccessExpression : return ( < ts . PropertyAccessExpression > parent ) . expression === node ; case ts . SyntaxKind . QualifiedName : return ( < ts . QualifiedName > parent ) . left === node ; case ts . SyntaxKind . ShorthandPropertyAssignment : return ( < ts . ShorthandPropertyAssignment > parent ) . objectAssignmentInitializer === node || ! isInDestructuringAssignment ( < ts . ShorthandPropertyAssignment > parent ) ; case ts . SyntaxKind . PropertyAssignment : return ( < ts . PropertyAssignment > parent ) . initializer === node && ! isInDestructuringAssignment ( < ts . PropertyAssignment > parent ) ; case ts . SyntaxKind . SpreadAssignment : case ts . SyntaxKind . SpreadElement : case ts . SyntaxKind . ArrayLiteralExpression : return ! isInDestructuringAssignment ( < ts . SpreadAssignment | ts . SpreadElement | ts . ArrayLiteralExpression > parent ) ; case ts . SyntaxKind . ParenthesizedExpression : case ts . SyntaxKind . AsExpression : case ts . SyntaxKind . TypeAssertionExpression : case ts . SyntaxKind . PostfixUnaryExpression : case ts . SyntaxKind . PrefixUnaryExpression : case ts . SyntaxKind . NonNullExpression : node = < ts . Expression > parent ; continue ; case ts . SyntaxKind . ForStatement : return ( < ts . ForStatement > parent ) . condition === node ; case ts . SyntaxKind . ForInStatement : case ts . SyntaxKind . ForOfStatement : return ( < ts . ForInStatement | ts . ForOfStatement > parent ) . expression === node ; case ts . SyntaxKind . ConditionalExpression : if ( ( < ts . ConditionalExpression > parent ) . condition === node ) return true ; node = < ts . Expression > parent ; break ; case ts . SyntaxKind . PropertyDeclaration : case ts . SyntaxKind . BindingElement : case ts . SyntaxKind . VariableDeclaration : case ts . SyntaxKind . Parameter : case ts . SyntaxKind . EnumMember : return ( < ts . HasExpressionInitializer > parent ) . initializer === node ; case ts . SyntaxKind . ImportEqualsDeclaration : return ( < ts . ImportEqualsDeclaration > parent ) . moduleReference === node ; case ts . SyntaxKind . CommaListExpression : if ( ( < ts . CommaListExpression > parent ) . elements [ ( < ts . CommaListExpression > parent ) . elements . length - 1 ] !== node ) return false ; node = < ts . Expression > parent ; break ; case ts . SyntaxKind . BinaryExpression : if ( ( < ts . BinaryExpression > parent ) . right === node ) { if ( ( < ts . BinaryExpression > parent ) . operatorToken . kind === ts . SyntaxKind . CommaToken ) { node = < ts . Expression > parent ; break ; } return true ; } switch ( ( < ts . BinaryExpression > parent ) . operatorToken . kind ) { case ts . SyntaxKind . CommaToken : case ts . SyntaxKind . EqualsToken : return false ; case ts . SyntaxKind . EqualsEqualsEqualsToken : case ts . SyntaxKind . EqualsEqualsToken : case ts . SyntaxKind . ExclamationEqualsEqualsToken : case ts . SyntaxKind . ExclamationEqualsToken : case ts . SyntaxKind . InstanceOfKeyword : case ts . SyntaxKind . PlusToken : case ts . SyntaxKind . MinusToken : case ts . SyntaxKind . AsteriskToken : case ts . SyntaxKind . SlashToken : case ts . SyntaxKind . PercentToken : case ts . SyntaxKind . AsteriskAsteriskToken : case ts . SyntaxKind . GreaterThanToken : case ts . SyntaxKind . GreaterThanGreaterThanToken : case ts . SyntaxKind . GreaterThanGreaterThanGreaterThanToken : case ts . SyntaxKind . GreaterThanEqualsToken : case ts . SyntaxKind . LessThanToken : case ts . SyntaxKind . LessThanLessThanToken : case ts . SyntaxKind . LessThanEqualsToken : case ts . SyntaxKind . AmpersandToken : case ts . SyntaxKind . BarToken : case ts . SyntaxKind . CaretToken : case ts . SyntaxKind . BarBarToken : case ts . SyntaxKind . AmpersandAmpersandToken : case ts . SyntaxKind . QuestionQuestionToken : case ts . SyntaxKind . InKeyword : case ts . SyntaxKind . QuestionQuestionEqualsToken : case ts . SyntaxKind . AmpersandAmpersandEqualsToken : case ts . SyntaxKind . BarBarEqualsToken : return true ; default : node = < ts . Expression > parent ; } break ; default : return false ; } } } function isInDestructuringAssignment ( node ) { switch ( node . kind ) { case ts . SyntaxKind . ShorthandPropertyAssignment : if ( node . objectAssignmentInitializer !== undefined ) return true ; case ts . SyntaxKind . PropertyAssignment : case ts . SyntaxKind . SpreadAssignment : node = < ts . ArrayLiteralExpression | ts . ObjectLiteralExpression > node . parent ; break ; case ts . SyntaxKind . SpreadElement : if ( node . parent ! . kind !== ts . SyntaxKind . ArrayLiteralExpression ) return false ; node = < ts . ArrayLiteralExpression > node . parent ; } while ( true ) { switch ( node . parent ! . kind ) { case ts . SyntaxKind . BinaryExpression : return ( < ts . BinaryExpression > node . parent ) . left === node && ( < ts . BinaryExpression > node . parent ) . operatorToken . kind === ts . SyntaxKind . EqualsToken ; case ts . SyntaxKind . ForOfStatement : return ( < ts . ForOfStatement > node . parent ) . initializer === node ; case ts . SyntaxKind . ArrayLiteralExpression : case ts . SyntaxKind . ObjectLiteralExpression : node = < ts . ArrayLiteralExpression | ts . ObjectLiteralExpression > node . parent ; break ; case ts . SyntaxKind . SpreadAssignment : case ts . SyntaxKind . PropertyAssignment : node = < ts . ObjectLiteralExpression > node . parent ! . parent ; break ; case ts . SyntaxKind . SpreadElement : if ( node . parent ! . parent ! . kind !== ts . SyntaxKind . ArrayLiteralExpression ) return false ; node = < ts . ArrayLiteralExpression > node . parent ! . parent ; break ; default : return false ; } } } export const enum AccessKind { None = 0 , Read = 1 , Write = 2 , Delete = 4 , ReadWrite = Read | Write , Modification = Write | Delete } export function getAccessKind ( node ) { const parent = node . parent ! ; switch ( parent . kind ) { case ts . SyntaxKind . DeleteExpression : return AccessKind . Delete ; case ts . SyntaxKind . PostfixUnaryExpression : return AccessKind . ReadWrite ; case ts . SyntaxKind . PrefixUnaryExpression : return ( < ts . PrefixUnaryExpression > parent ) . operator === ts . SyntaxKind . PlusPlusToken || ( < ts . PrefixUnaryExpression > parent ) . operator === ts . SyntaxKind . MinusMinusToken ? AccessKind . ReadWrite : AccessKind . Read ; case ts . SyntaxKind . BinaryExpression : return ( < ts . BinaryExpression > parent ) . right === node ? AccessKind . Read : ! isAssignmentKind ( ( < ts . BinaryExpression > parent ) . operatorToken . kind ) ? AccessKind . Read : ( < ts . BinaryExpression > parent ) . operatorToken . kind === ts . SyntaxKind . EqualsToken ? AccessKind . Write : AccessKind . ReadWrite ; case ts . SyntaxKind . ShorthandPropertyAssignment : return ( < ts . ShorthandPropertyAssignment > parent ) . objectAssignmentInitializer === node ? AccessKind . Read : isInDestructuringAssignment ( ( < ts . ShorthandPropertyAssignment > parent ) ) ? AccessKind . Write : AccessKind . Read ; case ts . SyntaxKind . PropertyAssignment : return ( < ts . PropertyAssignment > parent ) . name === node ? AccessKind . None : isInDestructuringAssignment ( < ts . PropertyAssignment > parent ) ? AccessKind . Write : AccessKind . Read ; case ts . SyntaxKind . ArrayLiteralExpression : case ts . SyntaxKind . SpreadElement : case ts . SyntaxKind . SpreadAssignment : return isInDestructuringAssignment ( < ts . SpreadElement | ts . SpreadAssignment | ts . ArrayLiteralExpression > parent ) ? AccessKind . Write : AccessKind . Read ; case ts . SyntaxKind . ParenthesizedExpression : case ts . SyntaxKind . NonNullExpression : case ts . SyntaxKind . TypeAssertionExpression : case ts . SyntaxKind . AsExpression : return getAccessKind ( < ts . Expression > parent ) ; case ts . SyntaxKind . ForOfStatement : case ts . SyntaxKind . ForInStatement : return ( < ts . ForInOrOfStatement > parent ) . initializer === node ? AccessKind . Write : AccessKind . Read ; case ts . SyntaxKind . ExpressionWithTypeArguments : return ( < ts . HeritageClause > ( < ts . ExpressionWithTypeArguments > parent ) . parent ) . token === ts . SyntaxKind . ExtendsKeyword && parent . parent ! . parent ! . kind !== ts . SyntaxKind . InterfaceDeclaration ? AccessKind . Read : AccessKind . None ; case ts . SyntaxKind . ComputedPropertyName : case ts . SyntaxKind . ExpressionStatement : case ts . SyntaxKind . TypeOfExpression : case ts . SyntaxKind . ElementAccessExpression : case ts . SyntaxKind . ForStatement : case ts . SyntaxKind . IfStatement : case ts . SyntaxKind . DoStatement : case ts . SyntaxKind . WhileStatement : case ts . SyntaxKind . SwitchStatement : case ts . SyntaxKind . WithStatement : case ts . SyntaxKind . ThrowStatement : case ts . SyntaxKind . CallExpression : case ts . SyntaxKind . NewExpression : case ts . SyntaxKind . TaggedTemplateExpression : case ts . SyntaxKind . JsxExpression : case ts . SyntaxKind . Decorator : case ts . SyntaxKind . TemplateSpan : case ts . SyntaxKind . JsxOpeningElement : case ts . SyntaxKind . JsxSelfClosingElement : case ts . SyntaxKind . JsxSpreadAttribute : case ts . SyntaxKind . VoidExpression : case ts . SyntaxKind . ReturnStatement : case ts . SyntaxKind . AwaitExpression : case ts . SyntaxKind . YieldExpression : case ts . SyntaxKind . ConditionalExpression : case ts . SyntaxKind . CaseClause : case ts . SyntaxKind . JsxElement : return AccessKind . Read ; case ts . SyntaxKind . ArrowFunction : return ( < ts . ArrowFunction > parent ) . body === node ? AccessKind . Read : AccessKind . Write ; case ts . SyntaxKind . PropertyDeclaration : case ts . SyntaxKind . VariableDeclaration : case ts . SyntaxKind . Parameter : case ts . SyntaxKind . EnumMember : case ts . SyntaxKind . BindingElement : case ts . SyntaxKind . JsxAttribute : return ( < ts . PropertyDeclaration | ts . VariableDeclaration | ts . ParameterDeclaration | ts . EnumMember | ts . BindingElement | ts . JsxAttribute > parent ) . initializer === node ? AccessKind . Read : AccessKind . None ; case ts . SyntaxKind . PropertyAccessExpression : return ( < ts . PropertyAccessExpression > parent ) . expression === node ? AccessKind . Read : AccessKind . None ; case ts . SyntaxKind . ExportAssignment : return ( < ts . ExportAssignment > parent ) . isExportEquals ? AccessKind . Read : AccessKind . None ; } return AccessKind . None ; } export function isReassignmentTarget ( node ) { return ( getAccessKind ( node ) & AccessKind . Write ) !== 0 ; } export function canHaveJsDoc ( node ) { const kind = ( < ts . HasJSDoc > node ) . kind ; switch ( kind ) { case ts . SyntaxKind . Parameter : case ts . SyntaxKind . CallSignature : case ts . SyntaxKind . ConstructSignature : case ts . SyntaxKind . MethodSignature : case ts . SyntaxKind . PropertySignature : case ts . SyntaxKind . ArrowFunction : case ts . SyntaxKind . ParenthesizedExpression : case ts . SyntaxKind . SpreadAssignment : case ts . SyntaxKind . ShorthandPropertyAssignment : case ts . SyntaxKind . PropertyAssignment : case ts . SyntaxKind . FunctionExpression : case ts . SyntaxKind . LabeledStatement : case ts . SyntaxKind . ExpressionStatement : case ts . SyntaxKind . VariableStatement : case ts . SyntaxKind . FunctionDeclaration : case ts . SyntaxKind . Constructor : case ts . SyntaxKind . MethodDeclaration : case ts . SyntaxKind . PropertyDeclaration : case ts . SyntaxKind . GetAccessor : case ts . SyntaxKind . SetAccessor : case ts . SyntaxKind . ClassDeclaration : case ts . SyntaxKind . ClassExpression : case ts . SyntaxKind . InterfaceDeclaration : case ts . SyntaxKind . TypeAliasDeclaration : case ts . SyntaxKind . EnumMember : case ts . SyntaxKind . EnumDeclaration : case ts . SyntaxKind . ModuleDeclaration : case ts . SyntaxKind . ImportEqualsDeclaration : case ts . SyntaxKind . ImportDeclaration : case ts . SyntaxKind . NamespaceExportDeclaration : case ts . SyntaxKind . ExportAssignment : case ts . SyntaxKind . IndexSignature : case ts . SyntaxKind . FunctionType : case ts . SyntaxKind . ConstructorType : case ts . SyntaxKind . JSDocFunctionType : case ts . SyntaxKind . ExportDeclaration : case ts . SyntaxKind . NamedTupleMember : case ts . SyntaxKind . EndOfFileToken : return true ; default : return < AssertNever < typeof kind > > false ; } } type AssertNever < T extends never > = T ; /** Gets the JSDoc of a node. For performance reasons this function should only be called when `canHaveJsDoc` returns true. */ export function getJsDoc ( node , sourceFile ? ) { const result = [ ] ; for ( const child of node . getChildren ( sourceFile ) ) { if ( ! isJsDoc ( child ) ) break ; result . push ( child ) ; } return result ; } param considerTrailingComments export function parseJsDocOfNode ( node , considerTrailingComments ? , sourceFile = node . getSourceFile ( ) ) { if ( canHaveJsDoc ( node ) && node . kind !== ts . SyntaxKind . EndOfFileToken ) { const result = getJsDoc ( node , sourceFile ) ; if ( result . length !== 0 || ! considerTrailingComments ) return result ; } return parseJsDocWorker ( node , node . getStart ( sourceFile ) , sourceFile , considerTrailingComments ) ; } function parseJsDocWorker ( node , nodeStart , sourceFile , considerTrailingComments ? ) { const start = ts [ considerTrailingComments && isSameLine ( sourceFile , node . pos , nodeStart ) ? 'forEachTrailingCommentRange' : 'forEachLeadingCommentRange' ] ( sourceFile . text , node . pos , ( pos , _end , kind ) => kind === ts . SyntaxKind . MultiLineCommentTrivia && sourceFile . text [ pos + 2 ] === '*' ? { pos } : undefined ) ; if ( start === undefined ) return [ ] ; const startPos = start . pos ; const text = sourceFile . text . slice ( startPos , nodeStart ) ; const newSourceFile = ts . createSourceFile ( 'jsdoc.ts' , `${ text }var a;` , sourceFile . languageVersion ) ; const result = getJsDoc ( newSourceFile . statements [ 0 ] , newSourceFile ) ; for ( const doc of result ) updateNode ( doc , node ) ; return result ; function updateNode ( n , parent ) { ( < number > n . pos ) += startPos ; ( < number > n . end ) += startPos ; ( < ts . Node > n . parent ) = parent ; return ts . forEachChild ( n , ( child ) => updateNode ( child , n ) , ( children ) => { ( < number > children . pos ) += startPos ; ( < number > children . end ) += startPos ; for ( const child of children ) updateNode ( child , n ) ; } ) ; } } export const enum ImportKind { ImportDeclaration = 1 , ImportEquals = 2 , ExportFrom = 4 , DynamicImport = 8 , Require = 16 , ImportType = 32 , All = ImportDeclaration | ImportEquals | ExportFrom | DynamicImport | Require | ImportType , AllImports = ImportDeclaration | ImportEquals | DynamicImport | Require | ImportType , AllStaticImports = ImportDeclaration | ImportEquals , AllImportExpressions = DynamicImport | Require , AllRequireLike = ImportEquals | Require , AllNestedImports = AllImportExpressions | ImportType , AllTopLevelImports = AllStaticImports | ExportFrom } export function findImports ( sourceFile , kinds , ignoreFileName = true ) { const result = [ ] ; for ( const node of findImportLikeNodes ( sourceFile , kinds , ignoreFileName ) ) { switch ( node . kind ) { case ts . SyntaxKind . ImportDeclaration : addIfTextualLiteral ( node . moduleSpecifier ) ; break ; case ts . SyntaxKind . ImportEqualsDeclaration : addIfTextualLiteral ( node . moduleReference . expression ) ; break ; case ts . SyntaxKind . ExportDeclaration : addIfTextualLiteral ( node . moduleSpecifier ) ; break ; case ts . SyntaxKind . CallExpression : addIfTextualLiteral ( node . arguments [ 0 ] ) ; break ; case ts . SyntaxKind . ImportType : if ( isLiteralTypeNode ( node . argument ) ) addIfTextualLiteral ( node . argument . literal ) ; break ; default : throw < AssertNever < typeof node > > new Error ( 'unexpected node' ) ; } } return result ; function addIfTextualLiteral ( node ) { if ( isTextualLiteral ( node ) ) result . push ( node ) ; } } export type ImportLike = ts . ImportDeclaration | ts . ImportEqualsDeclaration & { moduleReference : ts . ExternalModuleReference ; } | ts . ExportDeclaration & { moduleSpecifier : { } ; } | ts . CallExpression & { expression : ts . Token < ts . SyntaxKind . ImportKeyword > | ts . Identifier & { text : 'require' ; } ; arguments : [ ts . Expression , ... ts . Expression [ ] ] ; } | ts . ImportTypeNode ; export function findImportLikeNodes ( sourceFile , kinds , ignoreFileName = true ) { return new ImportFinder ( sourceFile , kinds , ignoreFileName ) . find ( ) ; } class ImportFinder { constructor ( private _sourceFile , private _options , private _ignoreFileName ) { } private _result : ImportLike [ ] = [ ] ; public find ( ) { if ( this . _sourceFile . isDeclarationFile ) this . _options &= ~ ImportKind . AllImportExpressions ; if ( this . _options & ImportKind . AllTopLevelImports ) this . _findImports ( this . _sourceFile . statements ) ; if ( this . _options & ImportKind . AllNestedImports ) this . _findNestedImports ( ) ; return this . _result ; } private _findImports ( statements ) { for ( const statement of statements ) { if ( isImportDeclaration ( statement ) ) { if ( this . _options & ImportKind . ImportDeclaration ) this . _result . push ( statement ) ; } else if ( isImportEqualsDeclaration ( statement ) ) { if ( this . _options & ImportKind . ImportEquals && statement . moduleReference . kind === ts . SyntaxKind . ExternalModuleReference ) this . _result . push ( < any > statement ) ; } else if ( isExportDeclaration ( statement ) ) { if ( statement . moduleSpecifier !== undefined && this . _options & ImportKind . ExportFrom ) this . _result . push ( < any > statement ) ; } else if ( isModuleDeclaration ( statement ) ) { this . _findImportsInModule ( statement ) ; } } } private _findImportsInModule ( declaration ) { if ( declaration . body === undefined ) return ; if ( declaration . body . kind === ts . SyntaxKind . ModuleDeclaration ) return this . _findImportsInModule ( declaration . body ) ; this . _findImports ( ( < ts . ModuleBlock > declaration . body ) . statements ) ; } private _findNestedImports ( ) { const isJavaScriptFile = this . _ignoreFileName || ( this . _sourceFile . flags & ts . NodeFlags . JavaScriptFile ) !== 0 ; let re ; let includeJsDoc ; if ( ( this . _options & ImportKind . AllNestedImports ) === ImportKind . Require ) { if ( ! isJavaScriptFile ) return ; re = /\brequire\s*[</(]/g ; includeJsDoc = false ; } else if ( this . _options & ImportKind . Require && isJavaScriptFile ) { re = /\b(?:import|require)\s*[</(]/g ; includeJsDoc = ( this . _options & ImportKind . ImportType ) !== 0 ; } else { re = /\bimport\s*[</(]/g ; includeJsDoc = isJavaScriptFile && ( this . _options & ImportKind . ImportType ) !== 0 ; } for ( let match = re . exec ( this . _sourceFile . text ) ; match !== null ; match = re . exec ( this . _sourceFile . text ) ) { const token = getTokenAtPositionWorker ( this . _sourceFile , match . index , this . _sourceFile , match [ 0 ] [ 0 ] === 'i' && includeJsDoc ) ! ; if ( token . kind === ts . SyntaxKind . ImportKeyword ) { if ( token . end - 'import' . length !== match . index ) continue ; switch ( token . parent ! . kind ) { case ts . SyntaxKind . ImportType : this . _result . push ( < ts . ImportTypeNode > token . parent ) ; break ; case ts . SyntaxKind . CallExpression : if ( ( < ts . CallExpression > token . parent ) . arguments . length > 1 ) this . _result . push ( < any > token . parent ) ; } } else if ( token . kind === ts . SyntaxKind . Identifier && token . end - 'require' . length === match . index && token . parent ! . kind === ts . SyntaxKind . CallExpression && ( < ts . CallExpression > token . parent ) . expression === token && ( < ts . CallExpression > token . parent ) . arguments . length === 1 ) { this . _result . push ( < any > token . parent ) ; } } } } /**
 * Ambient context means the statement itself has the `declare` keyword
 * or is inside a `declare namespace`,  `delcare module` or `declare global`.
 */ export function isStatementInAmbientContext ( node ) { while ( node . flags & ts . NodeFlags . NestedNamespace ) node = < ts . NamespaceDeclaration > node . parent ; return hasModifier ( node . modifiers , ts . SyntaxKind . DeclareKeyword ) || isAmbientModuleBlock ( node . parent ! ) ; } /** Includes `declare namespace`, `declare module` and `declare global` and namespace nested in one of the aforementioned. */ export function isAmbientModuleBlock ( node ) { while ( node . kind === ts . SyntaxKind . ModuleBlock ) { do node = node . parent ! ; while ( node . flags & ts . NodeFlags . NestedNamespace ) ; if ( hasModifier ( node . modifiers , ts . SyntaxKind . DeclareKeyword ) ) return true ; node = node . parent ! ; } return false ; } export function getIIFE ( func ) { let node = func . parent ! ; while ( node . kind === ts . SyntaxKind . ParenthesizedExpression ) node = node . parent ! ; return isCallExpression ( node ) && func . end <= node . expression . end ? node : undefined ; } export type StrictCompilerOption = 'noImplicitAny' | 'noImplicitThis' | 'strictNullChecks' | 'strictFunctionTypes' | 'strictPropertyInitialization' | 'alwaysStrict' | 'strictBindCallApply' ; export function isStrictCompilerOptionEnabled ( options , option ) { return ( options . strict ? options [ option ] !== false : options [ option ] === true ) && ( option !== 'strictPropertyInitialization' || isStrictCompilerOptionEnabled ( options , 'strictNullChecks' ) ) ; } export type BooleanCompilerOptions = { [ K in keyof ts . CompilerOptions ] : NonNullable < ts . CompilerOptions [ K ] > extends boolean ? K : never ; } extends { [ _ in keyof ts . CompilerOptions ] : infer U ; } ? U : never ; /**
 * Checks if a given compiler option is enabled.
 * It handles dependencies of options, e.g. `declaration` is implicitly enabled by `composite` or `strictNullChecks` is enabled by `strict`.
 * However, it does not check dependencies that are already checked and reported as errors, e.g. `checkJs` without `allowJs`.
 * This function only handles boolean flags.
 */ export function isCompilerOptionEnabled ( options , option ) { switch ( option ) { case 'stripInternal' : case 'declarationMap' : case 'emitDeclarationOnly' : return options [ option ] === true && isCompilerOptionEnabled ( options , 'declaration' ) ; case 'declaration' : return options . declaration || isCompilerOptionEnabled ( options , 'composite' ) ; case 'incremental' : return options . incremental === undefined ? isCompilerOptionEnabled ( options , 'composite' ) : options . incremental ; case 'skipDefaultLibCheck' : return options . skipDefaultLibCheck || isCompilerOptionEnabled ( options , 'skipLibCheck' ) ; case 'suppressImplicitAnyIndexErrors' : return options . suppressImplicitAnyIndexErrors === true && isCompilerOptionEnabled ( options , 'noImplicitAny' ) ; case 'allowSyntheticDefaultImports' : return options . allowSyntheticDefaultImports !== undefined ? options . allowSyntheticDefaultImports : isCompilerOptionEnabled ( options , 'esModuleInterop' ) || options . module === ts . ModuleKind . System ; case 'noUncheckedIndexedAccess' : return options . noUncheckedIndexedAccess === true && isCompilerOptionEnabled ( options , 'strictNullChecks' ) ; case 'allowJs' : return options . allowJs === undefined ? isCompilerOptionEnabled ( options , 'checkJs' ) : options . allowJs ; case 'noImplicitAny' : case 'noImplicitThis' : case 'strictNullChecks' : case 'strictFunctionTypes' : case 'strictPropertyInitialization' : case 'alwaysStrict' : case 'strictBindCallApply' : type AssertEqual < T , U extends T > = U ; return isStrictCompilerOptionEnabled ( options , < AssertEqual < typeof option , StrictCompilerOption > > option ) ; } return options [ option ] === true ; } returns export function isAmbientModule ( node ) { return node . name . kind === ts . SyntaxKind . StringLiteral || ( node . flags & ts . NodeFlags . GlobalAugmentation ) !== 0 ; } deprecated returns export function getCheckJsDirective ( source ) { return getTsCheckDirective ( source ) ; } returns export function getTsCheckDirective ( source ) { let directive ; ts . forEachLeadingCommentRange ( source , ( ts . getShebang ( source ) || '' ) . length , ( pos , end , kind ) => { if ( kind === ts . SyntaxKind . SingleLineCommentTrivia ) { const text = source . slice ( pos , end ) ; const match = /^\/{2,3}\s*@ts-(no)?check(?:\s|$)/i . exec ( text ) ; if ( match !== null ) directive = { pos , end , enabled : match [ 1 ] === undefined } ; } } ) ; return directive ; } export function isConstAssertion ( node ) { return isTypeReferenceNode ( node . type ) && node . type . typeName . kind === ts . SyntaxKind . Identifier && node . type . typeName . escapedText === 'const' ; } /** Detects whether an expression is affected by an enclosing 'as const' assertion and therefore treated literally. */ export function isInConstContext ( node ) { let current = node ; while ( true ) { const parent = current . parent ! ; outer : switch ( parent . kind ) { case ts . SyntaxKind . TypeAssertionExpression : case ts . SyntaxKind . AsExpression : return isConstAssertion ( < ts . AssertionExpression > parent ) ; case ts . SyntaxKind . PrefixUnaryExpression : if ( current . kind !== ts . SyntaxKind . NumericLiteral ) return false ; switch ( ( < ts . PrefixUnaryExpression > parent ) . operator ) { case ts . SyntaxKind . PlusToken : case ts . SyntaxKind . MinusToken : current = parent ; break outer ; default : return false ; } case ts . SyntaxKind . PropertyAssignment : if ( ( < ts . PropertyAssignment > parent ) . initializer !== current ) return false ; current = parent . parent ! ; break ; case ts . SyntaxKind . ShorthandPropertyAssignment : current = parent . parent ! ; break ; case ts . SyntaxKind . ParenthesizedExpression : case ts . SyntaxKind . ArrayLiteralExpression : case ts . SyntaxKind . ObjectLiteralExpression : case ts . SyntaxKind . TemplateExpression : current = parent ; break ; default : return false ; } } } /** Returns true for `Object.defineProperty(o, 'prop', {value, writable: false})` and  `Object.defineProperty(o, 'prop', {get: () => 1})`*/ export function isReadonlyAssignmentDeclaration ( node , checker ) { if ( ! isBindableObjectDefinePropertyCall ( node ) ) return false ; const descriptorType = checker . getTypeAtLocation ( node . arguments [ 2 ] ) ; if ( descriptorType . getProperty ( 'value' ) === undefined ) return descriptorType . getProperty ( 'set' ) === undefined ; const writableProp = descriptorType . getProperty ( 'writable' ) ; if ( writableProp === undefined ) return false ; const writableType = writableProp . valueDeclaration !== undefined && isPropertyAssignment ( writableProp . valueDeclaration ) ? checker . getTypeAtLocation ( writableProp . valueDeclaration . initializer ) : checker . getTypeOfSymbolAtLocation ( writableProp , node . arguments [ 2 ] ) ; return isBooleanLiteralType ( writableType , false ) ; } /** Determines whether a call to `Object.defineProperty` is statically analyzable. */ export function isBindableObjectDefinePropertyCall ( node ) { return node . arguments . length === 3 && isEntityNameExpression ( node . arguments [ 0 ] ) && isNumericOrStringLikeLiteral ( node . arguments [ 1 ] ) && isPropertyAccessExpression ( node . expression ) && node . expression . name . escapedText === 'defineProperty' && isIdentifier ( node . expression . expression ) && node . expression . expression . escapedText === 'Object' ; } export interface WellKnownSymbolLiteral extends ts . PropertyAccessExpression { expression : ts . Identifier & { text : 'Symbol' ; escapedText : 'symbol' ; } ; } export function isWellKnownSymbolLiterally ( node ) { return ts . isPropertyAccessExpression ( node ) && ts . isIdentifier ( node . expression ) && node . expression . escapedText === 'Symbol' ; } export interface PropertyName { displayName : string ; symbolName : ts . __String ; } deprecated export function getPropertyNameOfWellKnownSymbol ( node ) { return { displayName : `[Symbol.${ node . name . text }]` , symbolName : < ts . __String > ( '__@' + node . name . text ) , } ; } const isTsBefore43 = ( ( [ major , minor ] ) => major < '4' || major === '4' && minor < '3' ) ( ts . versionMajorMinor . split ( '.' ) ) ; export interface LateBoundPropertyNames { /** Whether all constituents are literal names. */ known : boolean ; names : PropertyName [ ] ; } export function getLateBoundPropertyNames ( node , checker ) { const result = { known : true , names : [ ] , } ; node = unwrapParentheses ( node ) ; if ( isTsBefore43 && isWellKnownSymbolLiterally ( node ) ) { result . names . push ( getPropertyNameOfWellKnownSymbol ( node ) ) ; } else { const type = checker . getTypeAtLocation ( node ) ; for ( const key of unionTypeParts ( checker . getBaseConstraintOfType ( type ) || type ) ) { const propertyName = getPropertyNameFromType ( key ) ; if ( propertyName ) { result . names . push ( propertyName ) ; } else { result . known = false ; } } } return result ; } export function getLateBoundPropertyNamesOfPropertyName ( node , checker ) { const staticName = getPropertyName ( node ) ; return staticName !== undefined ? { known : true , names : [ { displayName : staticName , symbolName : ts . escapeLeadingUnderscores ( staticName ) } ] } : node . kind === ts . SyntaxKind . PrivateIdentifier ? { known : true , names : [ { displayName : node . text , symbolName : checker . getSymbolAtLocation ( node ) ! . escapedName } ] } : getLateBoundPropertyNames ( ( < ts . ComputedPropertyName > node ) . expression , checker ) ; } /** Most declarations demand there to be only one statically known name, e.g. class members with computed name. */ export function getSingleLateBoundPropertyNameOfPropertyName ( node , checker ) { const staticName = getPropertyName ( node ) ; if ( staticName !== undefined ) return { displayName : staticName , symbolName : ts . escapeLeadingUnderscores ( staticName ) } ; if ( node . kind === ts . SyntaxKind . PrivateIdentifier ) return { displayName : node . text , symbolName : checker . getSymbolAtLocation ( node ) ! . escapedName } ; const { expression } = < ts . ComputedPropertyName > node ; return isTsBefore43 && isWellKnownSymbolLiterally ( expression ) ? getPropertyNameOfWellKnownSymbol ( expression ) : getPropertyNameFromType ( checker . getTypeAtLocation ( expression ) ) ; } export function unwrapParentheses ( node ) { while ( node . kind === ts . SyntaxKind . ParenthesizedExpression ) node = ( < ts . ParenthesizedExpression > node ) . expression ; return node ; } export function formatPseudoBigInt ( v ) { return `${ v . negative ? '-' : '' }${ v . base10Value }n` ; } /**
 * Determines whether the given `SwitchStatement`'s `case` clauses cover every possible value of the switched expression.
 * The logic is the same as TypeScript's control flow analysis.
 * This does **not** check whether all `case` clauses do a certain action like assign a variable or return a value.
 * This function ignores the `default` clause if present.
 */ export function hasExhaustiveCaseClauses ( node , checker ) { const caseClauses = node . caseBlock . clauses . filter ( isCaseClause ) ; if ( caseClauses . length === 0 ) return false ; const typeParts = unionTypeParts ( checker . getTypeAtLocation ( node . expression ) ) ; if ( typeParts . length > caseClauses . length ) return false ; const types = new Set < string | undefined > ( typeParts . map ( getPrimitiveLiteralFromType ) ) ; if ( types . has ( undefined ) ) return false ; const seen = new Set < string | undefined > ( ) ; for ( const clause of caseClauses ) { const expressionType = checker . getTypeAtLocation ( clause . expression ) ; if ( isTypeFlagSet ( expressionType , ts . TypeFlags . Never ) ) continue ; const type = getPrimitiveLiteralFromType ( expressionType ) ; if ( types . has ( type ) ) { seen . add ( type ) ; } else if ( type !== 'null' && type !== 'undefined' ) { return false ; } } return types . size === seen . size ; } function getPrimitiveLiteralFromType ( t ) { if ( isTypeFlagSet ( t , ts . TypeFlags . Null ) ) return 'null' ; if ( isTypeFlagSet ( t , ts . TypeFlags . Undefined ) ) return 'undefined' ; if ( isTypeFlagSet ( t , ts . TypeFlags . NumberLiteral ) ) return `${ isTypeFlagSet ( t , ts . TypeFlags . EnumLiteral ) ? 'enum:' : '' }${ ( < ts . NumberLiteralType > t ) . value }` ; if ( isTypeFlagSet ( t , ts . TypeFlags . StringLiteral ) ) return `${ isTypeFlagSet ( t , ts . TypeFlags . EnumLiteral ) ? 'enum:' : '' }string:${ ( < ts . StringLiteralType > t ) . value }` ; if ( isTypeFlagSet ( t , ts . TypeFlags . BigIntLiteral ) ) return formatPseudoBigInt ( ( < ts . BigIntLiteralType > t ) . value ) ; if ( isUniqueESSymbolType ( t ) ) return < string > t . escapedName ; if ( isBooleanLiteralType ( t , true ) ) return 'true' ; if ( isBooleanLiteralType ( t , false ) ) return 'false' ; } export function getBaseOfClassLikeExpression ( node ) { if ( node . heritageClauses ?. [ 0 ] . token === ts . SyntaxKind . ExtendsKeyword ) return node . heritageClauses [ 0 ] . types [ 0 ] ; }