1import * as ts from "./_namespaces/ts"; 2import { 3 __String, AccessExpression, AccessorDeclaration, addRange, affectsDeclarationPathOptionDeclarations, 4 affectsEmitOptionDeclarations, AllAccessorDeclarations, AmbientModuleDeclaration, AnnotationPropertyDeclaration, 5 AnyImportOrBareOrAccessedRequire, AnyImportOrReExport, AnyImportSyntax, AnyValidImportOrReExport, arrayFrom, 6 ArrayLiteralExpression, ArrayTypeNode, ArrowFunction, AsExpression, AssertionExpression, assertType, AssignmentDeclarationKind, 7 AssignmentExpression, AssignmentOperatorToken, BinaryExpression, binarySearch, BindableObjectDefinePropertyCall, 8 BindableStaticAccessExpression, BindableStaticElementAccessExpression, BindableStaticNameExpression, BindingElement, 9 Block, BundleFileSection, BundleFileSectionKind, BundleFileTextLike, CallExpression, CallLikeExpression, 10 canHaveDecorators, canHaveIllegalDecorators, canHaveModifiers, CaseBlock, CaseClause, CaseOrDefaultClause, 11 CatchClause, changeAnyExtension, CharacterCodes, CheckFlags, ClassDeclaration, ClassElement, ClassLikeDeclaration, 12 ClassStaticBlockDeclaration, combinePaths, CommaListExpression, CommandLineOption, CommentDirective, 13 CommentDirectivesMap, CommentDirectiveType, CommentRange, compareStringsCaseSensitive, compareValues, Comparison, 14 CompilerOptions, ComputedPropertyName, computeLineAndCharacterOfPosition, computeLineOfPosition, computeLineStarts, 15 concatenate, ConditionalExpression, ConstructorDeclaration, contains, containsPath, createGetCanonicalFileName, 16 createModeAwareCache, createMultiMap, createScanner, createTextSpan, createTextSpanFromBounds, Debug, Declaration, 17 DeclarationName, DeclarationWithTypeParameterChildren, DeclarationWithTypeParameters, Decorator, DefaultClause, 18 DestructuringAssignment, Diagnostic, DiagnosticCollection, DiagnosticMessage, DiagnosticMessageChain, 19 DiagnosticRelatedInformation, Diagnostics, DiagnosticWithDetachedLocation, DiagnosticWithLocation, 20 directorySeparator, DoStatement, DynamicNamedBinaryExpression, DynamicNamedDeclaration, ElementAccessExpression, 21 EmitFlags, EmitHost, EmitResolver, EmitTextWriter, emptyArray, ensurePathIsNonModuleName, 22 ensureTrailingDirectorySeparator, EntityName, EntityNameExpression, EntityNameOrEntityNameExpression, 23 EnumDeclaration, EqualityComparer, equalOwnProperties, EqualsToken, equateValues, escapeLeadingUnderscores, ESMap, 24 EtsComponentExpression, every, ExportAssignment, ExportDeclaration, ExportSpecifier, Expression, 25 ExpressionStatement, ExpressionWithTypeArguments, Extension, ExternalModuleReference, factory, FileExtensionInfo, 26 fileExtensionIs, fileExtensionIsOneOf, FileReference, FileWatcher, filter, find, findAncestor, findBestPatternMatch, 27 findIndex, findLast, firstDefined, firstOrUndefined, flatMap, flatMapToMutable, flatten, forEach, 28 forEachAncestorDirectory, forEachChild, forEachChildRecursively, ForInOrOfStatement, ForInStatement, ForOfStatement, 29 ForStatement, FunctionBody, FunctionDeclaration, FunctionExpression, FunctionLikeDeclaration, 30 GetAccessorDeclaration, getAllDecorators, getBaseFileName, GetCanonicalFileName, getCombinedModifierFlags, 31 getCombinedNodeFlags, getDirectoryPath, getEntries, getJSDocAugmentsTag, getJSDocDeprecatedTagNoCache, 32 getJSDocImplementsTags, getJSDocOverrideTagNoCache, getJSDocParameterTags, getJSDocParameterTagsNoCache, 33 getJSDocPrivateTagNoCache, getJSDocProtectedTagNoCache, getJSDocPublicTagNoCache, getJSDocReadonlyTagNoCache, 34 getJSDocReturnType, getJSDocTags, getJSDocType, getJSDocTypeParameterTags, getJSDocTypeParameterTagsNoCache, 35 getJSDocTypeTag, getLeadingCommentRanges, getLineAndCharacterOfPosition, getLinesBetweenPositions, getLineStarts, 36 getModeForFileReference, getModeForResolutionAtIndex, getNameOfDeclaration, getNormalizedAbsolutePath, 37 getNormalizedPathComponents, getOwnKeys, getParseTreeNode, getPathComponents, getPathFromPathComponents, 38 getRelativePathToDirectoryOrUrl, getRootLength, getStringComparer, getSymbolId, getTrailingCommentRanges, 39 HasExpressionInitializer, hasExtension, hasInitializer, HasInitializer, HasJSDoc, hasJSDocNodes, HasModifiers, 40 hasProperty, HasType, HasTypeArguments, HeritageClause, Identifier, IdentifierTypePredicate, identity, idText, 41 IfStatement, ignoredPaths, ImportCall, ImportClause, ImportDeclaration, ImportEqualsDeclaration, ImportMetaProperty, 42 ImportSpecifier, ImportTypeNode, IndexInfo, indexOfAnyCharCode, InitializedVariableDeclaration, insertSorted, 43 InterfaceDeclaration, isAccessor, isAnnotation, isAnyDirectorySeparator, isArkTsDecorator, isArray, isArrayLiteralExpression, 44 isArrowFunction, isBigIntLiteral, isBinaryExpression, isBindingPattern, isCallExpression, isClassDeclaration, 45 isClassElement, isClassExpression, isClassLike, isClassStaticBlockDeclaration, isCommaListExpression, 46 isComputedPropertyName, isConstructorDeclaration, isDeclaration, isDecorator, isElementAccessExpression, 47 isEnumDeclaration, isEnumMember, isEtsComponentExpression, isExportAssignment, isExportDeclaration, 48 isExpressionStatement, isExpressionWithTypeArguments, isExternalModule, isExternalModuleReference, 49 isFileProbablyExternalModule, isForStatement, isFunctionDeclaration, isFunctionExpression, isFunctionLike, 50 isFunctionLikeDeclaration, isFunctionLikeOrClassStaticBlockDeclaration, isGetAccessorDeclaration, isHeritageClause, 51 isIdentifier, isIdentifierText, isImportTypeNode, isInterfaceDeclaration, isJSDoc, isJSDocFunctionType, 52 isJSDocLinkLike, isJSDocMemberName, isJSDocNameReference, isJSDocNode, isJSDocParameterTag, isJSDocPropertyLikeTag, 53 isJSDocSignature, isJSDocTag, isJSDocTemplateTag, isJSDocTypeExpression, isJSDocTypeLiteral, isJSDocTypeTag, 54 isJsxChild, isJsxFragment, isJsxOpeningLikeElement, isJsxText, isLeftHandSideExpression, isLineBreak, 55 isLiteralTypeNode, isMemberName, isMetaProperty, isMethodDeclaration, isMethodOrAccessor, isModuleDeclaration, 56 isNamedDeclaration, isNamespaceExport, isNamespaceExportDeclaration, isNamespaceImport, 57 isNoSubstitutionTemplateLiteral, isNumericLiteral, isObjectLiteralExpression, isOmittedExpression, isParameter, 58 isParameterPropertyDeclaration, isParenthesizedExpression, isParenthesizedTypeNode, isPrefixUnaryExpression, 59 isPrivateIdentifier, isPropertyAccessExpression, isPropertyAssignment, isPropertyDeclaration, isPropertyName, 60 isPropertySignature, isQualifiedName, isRootedDiskPath, isSendableFunctionOrType, isSetAccessorDeclaration, 61 isShorthandPropertyAssignment, isSourceFile, isString, isStringLiteral, isStringLiteralLike, isStruct, 62 isTypeAliasDeclaration, isTypeElement, isTypeLiteralNode, isTypeNode, isTypeReferenceNode, isVariableDeclaration, 63 isVariableStatement, isVoidExpression, isWhiteSpaceLike, isWhiteSpaceSingleLine, JSDoc, JSDocCallbackTag, 64 JSDocEnumTag, JSDocMemberName, JSDocParameterTag, JSDocSignature, JSDocTag, JSDocTemplateTag, JSDocTypedefTag, 65 JsonSourceFile, JsxChild, JsxElement, JsxEmit, JsxFragment, JsxOpeningElement, JsxOpeningLikeElement, 66 JsxSelfClosingElement, JsxTagNameExpression, KeywordSyntaxKind, LabeledStatement, LanguageVariant, last, 67 lastOrUndefined, LateVisibilityPaintedStatement, length, LiteralImportTypeNode, LiteralLikeElementAccessExpression, 68 LiteralLikeNode, LogicalOrCoalescingAssignmentOperator, map, Map, mapDefined, MapLike, MemberName, 69 MethodDeclaration, ModeAwareCache, ModifierFlags, ModifierLike, ModuleBlock, ModuleDeclaration, ModuleDetectionKind, 70 ModuleKind, ModuleResolutionKind, moduleResolutionOptionDeclarations, MultiMap, NamedDeclaration, NamedExports, 71 NamedImports, NamedImportsOrExports, NamespaceExport, NamespaceImport, NewExpression, NewLineKind, Node, NodeArray, 72 NodeFlags, NonNullExpression, noop, normalizePath, NoSubstitutionTemplateLiteral, NumericLiteral, ObjectFlags, 73 ObjectFlagsType, ObjectLiteralElement, ObjectLiteralExpression, ObjectLiteralExpressionBase, ObjectTypeDeclaration, 74 optionsAffectingProgramStructure, or, OuterExpressionKinds, PackageId, ParameterDeclaration, 75 ParenthesizedExpression, ParenthesizedTypeNode, parseConfigFileTextToJson, PartiallyEmittedExpression, Path, 76 pathIsRelative, Pattern, PostfixUnaryExpression, PrefixUnaryExpression, PrinterOptions, PrintHandlers, 77 PrivateIdentifier, ProjectReference, PrologueDirective, PropertyAccessEntityNameExpression, 78 PropertyAccessExpression, PropertyAssignment, PropertyDeclaration, PropertyName, PropertyNameLiteral, PseudoBigInt, 79 QualifiedName, ReadonlyCollection, ReadonlyESMap, ReadonlyTextRange, removeTrailingDirectorySeparator, 80 RequireOrImportCall, RequireVariableStatement, ResolvedModuleFull, ResolvedTypeReferenceDirective, 81 resolveTripleslashReference, ReturnStatement, SatisfiesExpression, ScriptKind, ScriptTarget, 82 semanticDiagnosticsOptionDeclarations, SetAccessorDeclaration, ShorthandPropertyAssignment, Signature, 83 SignatureDeclaration, SignatureFlags, SignatureKind, singleElementArray, singleOrUndefined, skipOuterExpressions, 84 skipTrivia, some, sort, SortedArray, SourceFile, SourceFileLike, SourceFileMayBeEmittedHost, SourceMapSource, 85 startsWith, startsWithUseStrict, Statement, stringContains, StringLiteral, StringLiteralLike, stringToToken, 86 StructDeclaration, SuperCall, SuperExpression, SuperProperty, SwitchStatement, Symbol, SymbolFlags, SymbolTable, 87 SyntaxKind, SyntaxList, sys, TaggedTemplateExpression, TemplateLiteral, TemplateLiteralLikeNode, 88 TemplateLiteralTypeSpan, TemplateSpan, TextRange, TextSpan, ThisTypePredicate, Token, TokenFlags, tokenToString, 89 toPath, tracing, TransformFlags, TransientSymbol, trimString, trimStringStart, TriviaSyntaxKind, tryCast, 90 tryRemovePrefix, TryStatement, TsConfigSourceFile, TupleTypeNode, Type, TypeAliasDeclaration, TypeAssertion, 91 TypeChecker, TypeCheckerHost, TypeElement, TypeFlags, TypeLiteralNode, TypeNode, TypeNodeSyntaxKind, TypeParameter, 92 TypeParameterDeclaration, TypePredicate, TypePredicateKind, TypeReferenceNode, unescapeLeadingUnderscores, 93 UnionOrIntersectionTypeNode, ValidImportTypeNode, VariableDeclaration, VariableDeclarationInitializedTo, 94 VariableDeclarationList, VariableLikeDeclaration, VariableStatement, version, WhileStatement, WithStatement, 95 WriteFileCallback, WriteFileCallbackData, YieldExpression, 96} from "./_namespaces/ts"; 97 98/** @internal */ 99export const resolvingEmptyArray: never[] = []; 100 101/** @internal */ 102export const externalHelpersModuleNameText = "tslib"; 103 104/** @internal */ 105export const defaultMaximumTruncationLength = 160; 106/** @internal */ 107export const noTruncationMaximumTruncationLength = 1_000_000; 108 109/** @internal */ 110export function getDeclarationOfKind<T extends Declaration>(symbol: Symbol, kind: T["kind"]): T | undefined { 111 const declarations = symbol.declarations; 112 if (declarations) { 113 for (const declaration of declarations) { 114 if (declaration.kind === kind) { 115 return declaration as T; 116 } 117 } 118 } 119 120 return undefined; 121} 122 123/** @internal */ 124export function getDeclarationsOfKind<T extends Declaration>(symbol: Symbol, kind: T["kind"]): T[] { 125 return filter(symbol.declarations || emptyArray, d => d.kind === kind) as T[]; 126} 127 128/** @internal */ 129export function createSymbolTable(symbols?: readonly Symbol[]): SymbolTable { 130 const result = new Map<__String, Symbol>(); 131 if (symbols) { 132 for (const symbol of symbols) { 133 result.set(symbol.escapedName, symbol); 134 } 135 } 136 return result; 137} 138 139/** @internal */ 140export function isTransientSymbol(symbol: Symbol): symbol is TransientSymbol { 141 return (symbol.flags & SymbolFlags.Transient) !== 0; 142} 143 144const stringWriter = createSingleLineStringWriter(); 145 146function createSingleLineStringWriter(): EmitTextWriter { 147 /* eslint-disable no-var */ 148 var str = ""; 149 /* eslint-enable no-var */ 150 const writeText: (text: string) => void = text => str += text; 151 return { 152 getText: () => str, 153 write: writeText, 154 rawWrite: writeText, 155 writeKeyword: writeText, 156 writeOperator: writeText, 157 writePunctuation: writeText, 158 writeSpace: writeText, 159 writeStringLiteral: writeText, 160 writeLiteral: writeText, 161 writeParameter: writeText, 162 writeProperty: writeText, 163 writeSymbol: (s, _) => writeText(s), 164 writeTrailingSemicolon: writeText, 165 writeComment: writeText, 166 getTextPos: () => str.length, 167 getLine: () => 0, 168 getColumn: () => 0, 169 getIndent: () => 0, 170 isAtStartOfLine: () => false, 171 hasTrailingComment: () => false, 172 hasTrailingWhitespace: () => !!str.length && isWhiteSpaceLike(str.charCodeAt(str.length - 1)), 173 174 // Completely ignore indentation for string writers. And map newlines to 175 // a single space. 176 writeLine: () => str += " ", 177 increaseIndent: noop, 178 decreaseIndent: noop, 179 clear: () => str = "", 180 trackSymbol: () => false, 181 reportInaccessibleThisError: noop, 182 reportInaccessibleUniqueSymbolError: noop, 183 reportPrivateInBaseOfClassExpression: noop, 184 }; 185} 186 187/** @internal */ 188export function changesAffectModuleResolution(oldOptions: CompilerOptions, newOptions: CompilerOptions): boolean { 189 return oldOptions.configFilePath !== newOptions.configFilePath || 190 optionsHaveModuleResolutionChanges(oldOptions, newOptions); 191} 192 193/** @internal */ 194export function optionsHaveModuleResolutionChanges(oldOptions: CompilerOptions, newOptions: CompilerOptions) { 195 return optionsHaveChanges(oldOptions, newOptions, moduleResolutionOptionDeclarations); 196} 197 198/** @internal */ 199export function changesAffectingProgramStructure(oldOptions: CompilerOptions, newOptions: CompilerOptions) { 200 return optionsHaveChanges(oldOptions, newOptions, optionsAffectingProgramStructure); 201} 202 203/** @internal */ 204export function optionsHaveChanges(oldOptions: CompilerOptions, newOptions: CompilerOptions, optionDeclarations: readonly CommandLineOption[]) { 205 return oldOptions !== newOptions && optionDeclarations.some(o => 206 !isJsonEqual(getCompilerOptionValue(oldOptions, o), getCompilerOptionValue(newOptions, o))); 207} 208 209/** @internal */ 210export function forEachAncestor<T>(node: Node, callback: (n: Node) => T | undefined | "quit"): T | undefined { 211 while (true) { 212 const res = callback(node); 213 if (res === "quit") return undefined; 214 if (res !== undefined) return res; 215 if (isSourceFile(node)) return undefined; 216 node = node.parent; 217 } 218} 219 220/** 221 * Calls `callback` for each entry in the map, returning the first truthy result. 222 * Use `map.forEach` instead for normal iteration. 223 * 224 * @internal 225 */ 226export function forEachEntry<K, V, U>(map: ReadonlyESMap<K, V>, callback: (value: V, key: K) => U | undefined): U | undefined { 227 const iterator = map.entries(); 228 for (let iterResult = iterator.next(); !iterResult.done; iterResult = iterator.next()) { 229 const [key, value] = iterResult.value; 230 const result = callback(value, key); 231 if (result) { 232 return result; 233 } 234 } 235 return undefined; 236} 237 238/** 239 * `forEachEntry` for just keys. 240 * 241 * @internal 242 */ 243export function forEachKey<K, T>(map: ReadonlyCollection<K>, callback: (key: K) => T | undefined): T | undefined { 244 const iterator = map.keys(); 245 for (let iterResult = iterator.next(); !iterResult.done; iterResult = iterator.next()) { 246 const result = callback(iterResult.value); 247 if (result) { 248 return result; 249 } 250 } 251 return undefined; 252} 253 254/** 255 * Copy entries from `source` to `target`. 256 * 257 * @internal 258 */ 259export function copyEntries<K, V>(source: ReadonlyESMap<K, V>, target: ESMap<K, V>): void { 260 source.forEach((value, key) => { 261 target.set(key, value); 262 }); 263} 264 265/** @internal */ 266export function usingSingleLineStringWriter(action: (writer: EmitTextWriter) => void): string { 267 const oldString = stringWriter.getText(); 268 try { 269 action(stringWriter); 270 return stringWriter.getText(); 271 } 272 finally { 273 stringWriter.clear(); 274 stringWriter.writeKeyword(oldString); 275 } 276} 277 278/** @internal */ 279export function getFullWidth(node: Node) { 280 return node.end - node.pos; 281} 282 283/** @internal */ 284export function getResolvedModule(sourceFile: SourceFile | undefined, moduleNameText: string, mode: ModuleKind.CommonJS | ModuleKind.ESNext | undefined): ResolvedModuleFull | undefined { 285 return sourceFile && sourceFile.resolvedModules && sourceFile.resolvedModules.get(moduleNameText, mode); 286} 287 288/** @internal */ 289export function setResolvedModule(sourceFile: SourceFile, moduleNameText: string, resolvedModule: ResolvedModuleFull | undefined, mode: ModuleKind.CommonJS | ModuleKind.ESNext | undefined): void { 290 if (!sourceFile.resolvedModules) { 291 sourceFile.resolvedModules = createModeAwareCache(); 292 } 293 294 sourceFile.resolvedModules.set(moduleNameText, mode, resolvedModule); 295} 296 297/** @internal */ 298export function setResolvedTypeReferenceDirective(sourceFile: SourceFile, typeReferenceDirectiveName: string, resolvedTypeReferenceDirective?: ResolvedTypeReferenceDirective): void { 299 if (!sourceFile.resolvedTypeReferenceDirectiveNames) { 300 sourceFile.resolvedTypeReferenceDirectiveNames = createModeAwareCache(); 301 } 302 303 sourceFile.resolvedTypeReferenceDirectiveNames.set(typeReferenceDirectiveName, /*mode*/ undefined, resolvedTypeReferenceDirective); 304} 305 306/** @internal */ 307export function projectReferenceIsEqualTo(oldRef: ProjectReference, newRef: ProjectReference) { 308 return oldRef.path === newRef.path && 309 !oldRef.prepend === !newRef.prepend && 310 !oldRef.circular === !newRef.circular; 311} 312 313/** @internal */ 314export function moduleResolutionIsEqualTo(oldResolution: ResolvedModuleFull, newResolution: ResolvedModuleFull): boolean { 315 return oldResolution.isExternalLibraryImport === newResolution.isExternalLibraryImport && 316 oldResolution.extension === newResolution.extension && 317 oldResolution.resolvedFileName === newResolution.resolvedFileName && 318 oldResolution.originalPath === newResolution.originalPath && 319 packageIdIsEqual(oldResolution.packageId, newResolution.packageId); 320} 321 322function packageIdIsEqual(a: PackageId | undefined, b: PackageId | undefined): boolean { 323 return a === b || !!a && !!b && a.name === b.name && a.subModuleName === b.subModuleName && a.version === b.version; 324} 325 326/** @internal */ 327export function packageIdToPackageName({ name, subModuleName }: PackageId): string { 328 return subModuleName ? `${name}/${subModuleName}` : name; 329} 330 331/** @internal */ 332export function packageIdToString(packageId: PackageId): string { 333 return `${packageIdToPackageName(packageId)}@${packageId.version}`; 334} 335 336/** @internal */ 337export function typeDirectiveIsEqualTo(oldResolution: ResolvedTypeReferenceDirective, newResolution: ResolvedTypeReferenceDirective): boolean { 338 return oldResolution.resolvedFileName === newResolution.resolvedFileName 339 && oldResolution.primary === newResolution.primary 340 && oldResolution.originalPath === newResolution.originalPath; 341} 342 343/** @internal */ 344export function hasChangesInResolutions<T>( 345 names: readonly string[] | readonly FileReference[], 346 newResolutions: readonly T[], 347 oldResolutions: ModeAwareCache<T> | undefined, 348 oldSourceFile: SourceFile | undefined, 349 comparer: (oldResolution: T, newResolution: T) => boolean): boolean { 350 Debug.assert(names.length === newResolutions.length); 351 352 for (let i = 0; i < names.length; i++) { 353 const newResolution = newResolutions[i]; 354 const entry = names[i]; 355 // We lower-case all type references because npm automatically lowercases all packages. See GH#9824. 356 const name = !isString(entry) ? entry.fileName.toLowerCase() : entry; 357 const mode = !isString(entry) ? getModeForFileReference(entry, oldSourceFile?.impliedNodeFormat) : oldSourceFile && getModeForResolutionAtIndex(oldSourceFile, i); 358 const oldResolution = oldResolutions && oldResolutions.get(name, mode); 359 const changed = 360 oldResolution 361 ? !newResolution || !comparer(oldResolution, newResolution) 362 : newResolution; 363 if (changed) { 364 return true; 365 } 366 } 367 return false; 368} 369 370// Returns true if this node contains a parse error anywhere underneath it. 371/** @internal */ 372export function containsParseError(node: Node): boolean { 373 aggregateChildData(node); 374 return (node.flags & NodeFlags.ThisNodeOrAnySubNodesHasError) !== 0; 375} 376 377function aggregateChildData(node: Node): void { 378 if (!(node.flags & NodeFlags.HasAggregatedChildData)) { 379 // A node is considered to contain a parse error if: 380 // a) the parser explicitly marked that it had an error 381 // b) any of it's children reported that it had an error. 382 const thisNodeOrAnySubNodesHasError = ((node.flags & NodeFlags.ThisNodeHasError) !== 0) || 383 forEachChild(node, containsParseError); 384 385 // If so, mark ourselves accordingly. 386 if (thisNodeOrAnySubNodesHasError) { 387 (node as Mutable<Node>).flags |= NodeFlags.ThisNodeOrAnySubNodesHasError; 388 } 389 390 // Also mark that we've propagated the child information to this node. This way we can 391 // always consult the bit directly on this node without needing to check its children 392 // again. 393 (node as Mutable<Node>).flags |= NodeFlags.HasAggregatedChildData; 394 } 395} 396 397/** @internal */ 398export function getSourceFileOfNode(node: Node): SourceFile; 399/** @internal */ 400export function getSourceFileOfNode(node: Node | undefined): SourceFile | undefined; 401/** @internal */ 402export function getSourceFileOfNode(node: Node): SourceFile { 403 while (node && node.kind !== SyntaxKind.SourceFile) { 404 node = node.parent; 405 } 406 return node as SourceFile; 407} 408 409/** @internal */ 410export function getSourceFileOfModule(module: Symbol) { 411 return getSourceFileOfNode(module.valueDeclaration || getNonAugmentationDeclaration(module)); 412} 413 414/** @internal */ 415export function isPlainJsFile(file: SourceFile | undefined, checkJs: boolean | undefined): boolean { 416 return !!file && (file.scriptKind === ScriptKind.JS || file.scriptKind === ScriptKind.JSX) && !file.checkJsDirective && checkJs === undefined; 417} 418 419/** @internal */ 420export function isStatementWithLocals(node: Node) { 421 switch (node.kind) { 422 case SyntaxKind.Block: 423 case SyntaxKind.CaseBlock: 424 case SyntaxKind.ForStatement: 425 case SyntaxKind.ForInStatement: 426 case SyntaxKind.ForOfStatement: 427 return true; 428 } 429 return false; 430} 431 432/** @internal */ 433export function getStartPositionOfLine(line: number, sourceFile: SourceFileLike): number { 434 Debug.assert(line >= 0); 435 return getLineStarts(sourceFile)[line]; 436} 437 438// This is a useful function for debugging purposes. 439/** @internal */ 440export function nodePosToString(node: Node): string { 441 const file = getSourceFileOfNode(node); 442 const loc = getLineAndCharacterOfPosition(file, node.pos); 443 return `${file.fileName}(${loc.line + 1},${loc.character + 1})`; 444} 445 446/** @internal */ 447export function getEndLinePosition(line: number, sourceFile: SourceFileLike): number { 448 Debug.assert(line >= 0); 449 const lineStarts = getLineStarts(sourceFile); 450 451 const lineIndex = line; 452 const sourceText = sourceFile.text; 453 if (lineIndex + 1 === lineStarts.length) { 454 // last line - return EOF 455 return sourceText.length - 1; 456 } 457 else { 458 // current line start 459 const start = lineStarts[lineIndex]; 460 // take the start position of the next line - 1 = it should be some line break 461 let pos = lineStarts[lineIndex + 1] - 1; 462 Debug.assert(isLineBreak(sourceText.charCodeAt(pos))); 463 // walk backwards skipping line breaks, stop the the beginning of current line. 464 // i.e: 465 // <some text> 466 // $ <- end of line for this position should match the start position 467 while (start <= pos && isLineBreak(sourceText.charCodeAt(pos))) { 468 pos--; 469 } 470 return pos; 471 } 472} 473 474/** 475 * Returns a value indicating whether a name is unique globally or within the current file. 476 * Note: This does not consider whether a name appears as a free identifier or not, so at the expression `x.y` this includes both `x` and `y`. 477 * 478 * @internal 479 */ 480export function isFileLevelUniqueName(sourceFile: SourceFile, name: string, hasGlobalName?: PrintHandlers["hasGlobalName"]): boolean { 481 return !(hasGlobalName && hasGlobalName(name)) && !sourceFile.identifiers.has(name); 482} 483 484// Returns true if this node is missing from the actual source code. A 'missing' node is different 485// from 'undefined/defined'. When a node is undefined (which can happen for optional nodes 486// in the tree), it is definitely missing. However, a node may be defined, but still be 487// missing. This happens whenever the parser knows it needs to parse something, but can't 488// get anything in the source code that it expects at that location. For example: 489// 490// let a: ; 491// 492// Here, the Type in the Type-Annotation is not-optional (as there is a colon in the source 493// code). So the parser will attempt to parse out a type, and will create an actual node. 494// However, this node will be 'missing' in the sense that no actual source-code/tokens are 495// contained within it. 496/** @internal */ 497export function nodeIsMissing(node: Node | undefined): boolean { 498 if (node === undefined) { 499 return true; 500 } 501 502 // if node type is virtual, do not judge position 503 if(node.virtual) { 504 return false; 505 } 506 507 return node.pos === node.end && node.pos >= 0 && node.kind !== SyntaxKind.EndOfFileToken; 508} 509 510/** @internal */ 511export function nodeIsPresent(node: Node | undefined): boolean { 512 return !nodeIsMissing(node); 513} 514 515function insertStatementsAfterPrologue<T extends Statement>(to: T[], from: readonly T[] | undefined, isPrologueDirective: (node: Node) => boolean): T[] { 516 if (from === undefined || from.length === 0) return to; 517 let statementIndex = 0; 518 // skip all prologue directives to insert at the correct position 519 for (; statementIndex < to.length; ++statementIndex) { 520 if (!isPrologueDirective(to[statementIndex])) { 521 break; 522 } 523 } 524 to.splice(statementIndex, 0, ...from); 525 return to; 526} 527 528function insertStatementAfterPrologue<T extends Statement>(to: T[], statement: T | undefined, isPrologueDirective: (node: Node) => boolean): T[] { 529 if (statement === undefined) return to; 530 let statementIndex = 0; 531 // skip all prologue directives to insert at the correct position 532 for (; statementIndex < to.length; ++statementIndex) { 533 if (!isPrologueDirective(to[statementIndex])) { 534 break; 535 } 536 } 537 to.splice(statementIndex, 0, statement); 538 return to; 539} 540 541 542function isAnyPrologueDirective(node: Node) { 543 return isPrologueDirective(node) || !!(getEmitFlags(node) & EmitFlags.CustomPrologue); 544} 545 546/** 547 * Prepends statements to an array while taking care of prologue directives. 548 * 549 * @internal 550 */ 551export function insertStatementsAfterStandardPrologue<T extends Statement>(to: T[], from: readonly T[] | undefined): T[] { 552 return insertStatementsAfterPrologue(to, from, isPrologueDirective); 553} 554 555/** @internal */ 556export function insertStatementsAfterCustomPrologue<T extends Statement>(to: T[], from: readonly T[] | undefined): T[] { 557 return insertStatementsAfterPrologue(to, from, isAnyPrologueDirective); 558} 559 560/** 561 * Prepends statements to an array while taking care of prologue directives. 562 * 563 * @internal 564 */ 565export function insertStatementAfterStandardPrologue<T extends Statement>(to: T[], statement: T | undefined): T[] { 566 return insertStatementAfterPrologue(to, statement, isPrologueDirective); 567} 568 569/** @internal */ 570export function insertStatementAfterCustomPrologue<T extends Statement>(to: T[], statement: T | undefined): T[] { 571 return insertStatementAfterPrologue(to, statement, isAnyPrologueDirective); 572} 573 574/** @internal */ 575export function getEtsLibs(program: TypeCheckerHost): string[] { 576 const etsComponentsLibNames: string[] = []; 577 const etsComponentsLib = program.getCompilerOptions().ets?.libs ?? []; 578 if (etsComponentsLib.length) { 579 forEach(etsComponentsLib, libFileName => { 580 etsComponentsLibNames.push(sys.resolvePath(libFileName)); 581 const sourceFile = getSourceFileByName(program, libFileName); 582 forEach(sourceFile?.referencedFiles, ref => { 583 const referencedFileName = sys.resolvePath(resolveTripleslashReference(ref.fileName, sourceFile!.fileName)); 584 etsComponentsLibNames.push(referencedFileName); 585 }); 586 }); 587 } 588 return etsComponentsLibNames; 589} 590 591function getSourceFileByName(program: TypeCheckerHost, libFileName: string): SourceFile | undefined { 592 if(!libFileName) { 593 return undefined; 594 } 595 596 const originFileName = sys.resolvePath(libFileName); 597 for (const file of program.getSourceFiles()) { 598 if(!file.fileName) { 599 continue; 600 } 601 const sourceFileName = sys.resolvePath(file.fileName); 602 if (sourceFileName === originFileName) { 603 return file; 604 } 605 } 606 return undefined; 607} 608 609/** 610 * Determine if the given comment is a triple-slash 611 * 612 * @return true if the comment is a triple-slash comment else false 613 * 614 * @internal 615 */ 616export function isRecognizedTripleSlashComment(text: string, commentPos: number, commentEnd: number) { 617 // Verify this is /// comment, but do the regexp match only when we first can find /// in the comment text 618 // so that we don't end up computing comment string and doing match for all // comments 619 if (text.charCodeAt(commentPos + 1) === CharacterCodes.slash && 620 commentPos + 2 < commentEnd && 621 text.charCodeAt(commentPos + 2) === CharacterCodes.slash) { 622 const textSubStr = text.substring(commentPos, commentEnd); 623 return fullTripleSlashReferencePathRegEx.test(textSubStr) || 624 fullTripleSlashAMDReferencePathRegEx.test(textSubStr) || 625 fullTripleSlashReferenceTypeReferenceDirectiveRegEx.test(textSubStr) || 626 defaultLibReferenceRegEx.test(textSubStr) ? 627 true : false; 628 } 629 return false; 630} 631 632/** @internal */ 633export function isPinnedComment(text: string, start: number) { 634 return text.charCodeAt(start + 1) === CharacterCodes.asterisk && 635 text.charCodeAt(start + 2) === CharacterCodes.exclamation; 636} 637 638/** @internal */ 639export function createCommentDirectivesMap(sourceFile: SourceFile, commentDirectives: CommentDirective[]): CommentDirectivesMap { 640 const directivesByLine = new Map( 641 commentDirectives.map(commentDirective => ([ 642 `${getLineAndCharacterOfPosition(sourceFile, commentDirective.range.end).line}`, 643 commentDirective, 644 ])) 645 ); 646 647 const usedLines = new Map<string, boolean>(); 648 649 return { getUnusedExpectations, markUsed }; 650 651 function getUnusedExpectations() { 652 return arrayFrom(directivesByLine.entries()) 653 .filter(([line, directive]) => directive.type === CommentDirectiveType.ExpectError && !usedLines.get(line)) 654 .map(([_, directive]) => directive); 655 } 656 657 function markUsed(line: number) { 658 if (!directivesByLine.has(`${line}`)) { 659 return false; 660 } 661 662 usedLines.set(`${line}`, true); 663 return true; 664 } 665} 666 667/** @internal */ 668export function getTokenPosOfNode(node: Node, sourceFile?: SourceFileLike, includeJsDoc?: boolean): number { 669 // With nodes that have no width (i.e. 'Missing' nodes), we actually *don't* 670 // want to skip trivia because this will launch us forward to the next token. 671 if (nodeIsMissing(node)) { 672 return node.pos; 673 } 674 675 if (isJSDocNode(node) || node.kind === SyntaxKind.JsxText) { 676 // JsxText cannot actually contain comments, even though the scanner will think it sees comments 677 return skipTrivia((sourceFile || getSourceFileOfNode(node)).text, node.pos, /*stopAfterLineBreak*/ false, /*stopAtComments*/ true); 678 } 679 680 if (includeJsDoc && hasJSDocNodes(node)) { 681 return getTokenPosOfNode(node.jsDoc![0], sourceFile); 682 } 683 684 // For a syntax list, it is possible that one of its children has JSDocComment nodes, while 685 // the syntax list itself considers them as normal trivia. Therefore if we simply skip 686 // trivia for the list, we may have skipped the JSDocComment as well. So we should process its 687 // first child to determine the actual position of its first token. 688 if (node.kind === SyntaxKind.SyntaxList && (node as SyntaxList)._children.length > 0) { 689 return getTokenPosOfNode((node as SyntaxList)._children[0], sourceFile, includeJsDoc); 690 } 691 692 return node.virtual ? node.pos : skipTrivia((sourceFile || getSourceFileOfNode(node)).text, node.pos, /*stopAfterLineBreak*/ false, 693 /*stopAtComments*/ false, isInJSDoc(node)); 694} 695 696/** @internal */ 697export function getNonDecoratorTokenPosOfNode(node: Node, sourceFile?: SourceFileLike): number { 698 const lastDecorator = !nodeIsMissing(node) && canHaveModifiers(node) ? findLast(node.modifiers, isDecorator) : undefined; 699 if (!lastDecorator) { 700 return getTokenPosOfNode(node, sourceFile); 701 } 702 703 return skipTrivia((sourceFile || getSourceFileOfNode(node)).text, lastDecorator.end); 704} 705 706/** @internal */ 707export function getSourceTextOfNodeFromSourceFile(sourceFile: SourceFile, node: Node, includeTrivia = false): string { 708 return getTextOfNodeFromSourceText(sourceFile.text, node, includeTrivia); 709} 710 711function isJSDocTypeExpressionOrChild(node: Node): boolean { 712 return !!findAncestor(node, isJSDocTypeExpression); 713} 714 715/** @internal */ 716export function isExportNamespaceAsDefaultDeclaration(node: Node): boolean { 717 return !!(isExportDeclaration(node) && node.exportClause && isNamespaceExport(node.exportClause) && node.exportClause.name.escapedText === "default"); 718} 719 720/** @internal */ 721export function getTextOfNodeFromSourceText(sourceText: string, node: Node, includeTrivia = false): string { 722 if (nodeIsMissing(node)) { 723 return ""; 724 } 725 726 let text = sourceText.substring(includeTrivia ? node.pos : skipTrivia(sourceText, node.pos), node.end); 727 728 if (isJSDocTypeExpressionOrChild(node)) { 729 // strip space + asterisk at line start 730 text = text.split(/\r\n|\n|\r/).map(line => trimStringStart(line.replace(/^\s*\*/, ""))).join("\n"); 731 } 732 733 return text; 734} 735 736/** @internal */ 737export function getTextOfNode(node: Node, includeTrivia = false): string { 738 return getSourceTextOfNodeFromSourceFile(getSourceFileOfNode(node), node, includeTrivia); 739} 740 741function getPos(range: Node) { 742 return range.pos; 743} 744 745/** 746 * Note: it is expected that the `nodeArray` and the `node` are within the same file. 747 * For example, searching for a `SourceFile` in a `SourceFile[]` wouldn't work. 748 * 749 * @internal 750 */ 751export function indexOfNode(nodeArray: readonly Node[], node: Node) { 752 return binarySearch(nodeArray, node, getPos, compareValues); 753} 754 755/** 756 * Gets flags that control emit behavior of a node. 757 * 758 * @internal 759 */ 760export function getEmitFlags(node: Node): EmitFlags { 761 const emitNode = node.emitNode; 762 return emitNode && emitNode.flags || 0; 763} 764 765/** @internal */ 766export interface ScriptTargetFeatures { 767 [key: string]: { [key: string]: string[] | undefined }; 768} 769 770/** @internal */ 771export function getScriptTargetFeatures(): ScriptTargetFeatures { 772 return { 773 es2015: { 774 Array: ["find", "findIndex", "fill", "copyWithin", "entries", "keys", "values"], 775 RegExp: ["flags", "sticky", "unicode"], 776 Reflect: ["apply", "construct", "defineProperty", "deleteProperty", "get"," getOwnPropertyDescriptor", "getPrototypeOf", "has", "isExtensible", "ownKeys", "preventExtensions", "set", "setPrototypeOf"], 777 ArrayConstructor: ["from", "of"], 778 ObjectConstructor: ["assign", "getOwnPropertySymbols", "keys", "is", "setPrototypeOf"], 779 NumberConstructor: ["isFinite", "isInteger", "isNaN", "isSafeInteger", "parseFloat", "parseInt"], 780 Math: ["clz32", "imul", "sign", "log10", "log2", "log1p", "expm1", "cosh", "sinh", "tanh", "acosh", "asinh", "atanh", "hypot", "trunc", "fround", "cbrt"], 781 Map: ["entries", "keys", "values"], 782 Set: ["entries", "keys", "values"], 783 Promise: emptyArray, 784 PromiseConstructor: ["all", "race", "reject", "resolve"], 785 Symbol: ["for", "keyFor"], 786 WeakMap: ["entries", "keys", "values"], 787 WeakSet: ["entries", "keys", "values"], 788 Iterator: emptyArray, 789 AsyncIterator: emptyArray, 790 String: ["codePointAt", "includes", "endsWith", "normalize", "repeat", "startsWith", "anchor", "big", "blink", "bold", "fixed", "fontcolor", "fontsize", "italics", "link", "small", "strike", "sub", "sup"], 791 StringConstructor: ["fromCodePoint", "raw"] 792 }, 793 es2016: { 794 Array: ["includes"] 795 }, 796 es2017: { 797 Atomics: emptyArray, 798 SharedArrayBuffer: emptyArray, 799 String: ["padStart", "padEnd"], 800 ObjectConstructor: ["values", "entries", "getOwnPropertyDescriptors"], 801 DateTimeFormat: ["formatToParts"] 802 }, 803 es2018: { 804 Promise: ["finally"], 805 RegExpMatchArray: ["groups"], 806 RegExpExecArray: ["groups"], 807 RegExp: ["dotAll"], 808 Intl: ["PluralRules"], 809 AsyncIterable: emptyArray, 810 AsyncIterableIterator: emptyArray, 811 AsyncGenerator: emptyArray, 812 AsyncGeneratorFunction: emptyArray, 813 NumberFormat: ["formatToParts"] 814 }, 815 es2019: { 816 Array: ["flat", "flatMap"], 817 ObjectConstructor: ["fromEntries"], 818 String: ["trimStart", "trimEnd", "trimLeft", "trimRight"], 819 Symbol: ["description"] 820 }, 821 es2020: { 822 BigInt: emptyArray, 823 BigInt64Array: emptyArray, 824 BigUint64Array: emptyArray, 825 PromiseConstructor: ["allSettled"], 826 SymbolConstructor: ["matchAll"], 827 String: ["matchAll"], 828 DataView: ["setBigInt64", "setBigUint64", "getBigInt64", "getBigUint64"], 829 RelativeTimeFormat: ["format", "formatToParts", "resolvedOptions"] 830 }, 831 es2021: { 832 PromiseConstructor: ["any"], 833 String: ["replaceAll"] 834 }, 835 es2022: { 836 Array: ["at"], 837 String: ["at"], 838 Int8Array: ["at"], 839 Uint8Array: ["at"], 840 Uint8ClampedArray: ["at"], 841 Int16Array: ["at"], 842 Uint16Array: ["at"], 843 Int32Array: ["at"], 844 Uint32Array: ["at"], 845 Float32Array: ["at"], 846 Float64Array: ["at"], 847 BigInt64Array: ["at"], 848 BigUint64Array: ["at"], 849 ObjectConstructor: ["hasOwn"], 850 Error: ["cause"] 851 } 852 }; 853} 854 855/** @internal */ 856export const enum GetLiteralTextFlags { 857 None = 0, 858 NeverAsciiEscape = 1 << 0, 859 JsxAttributeEscape = 1 << 1, 860 TerminateUnterminatedLiterals = 1 << 2, 861 AllowNumericSeparator = 1 << 3 862} 863 864/** @internal */ 865export function getLiteralText(node: LiteralLikeNode, sourceFile: SourceFile | undefined, flags: GetLiteralTextFlags) { 866 // If we don't need to downlevel and we can reach the original source text using 867 // the node's parent reference, then simply get the text as it was originally written. 868 if (sourceFile && canUseOriginalText(node, flags)) { 869 return getSourceTextOfNodeFromSourceFile(sourceFile, node); 870 } 871 872 // If we can't reach the original source text, use the canonical form if it's a number, 873 // or a (possibly escaped) quoted form of the original text if it's string-like. 874 switch (node.kind) { 875 case SyntaxKind.StringLiteral: { 876 const escapeText = flags & GetLiteralTextFlags.JsxAttributeEscape ? escapeJsxAttributeString : 877 flags & GetLiteralTextFlags.NeverAsciiEscape || (getEmitFlags(node) & EmitFlags.NoAsciiEscaping) ? escapeString : 878 escapeNonAsciiString; 879 if ((node as StringLiteral).singleQuote) { 880 return "'" + escapeText(node.text, CharacterCodes.singleQuote) + "'"; 881 } 882 else { 883 return '"' + escapeText(node.text, CharacterCodes.doubleQuote) + '"'; 884 } 885 } 886 case SyntaxKind.NoSubstitutionTemplateLiteral: 887 case SyntaxKind.TemplateHead: 888 case SyntaxKind.TemplateMiddle: 889 case SyntaxKind.TemplateTail: { 890 // If a NoSubstitutionTemplateLiteral appears to have a substitution in it, the original text 891 // had to include a backslash: `not \${a} substitution`. 892 const escapeText = flags & GetLiteralTextFlags.NeverAsciiEscape || (getEmitFlags(node) & EmitFlags.NoAsciiEscaping) ? escapeString : 893 escapeNonAsciiString; 894 895 const rawText = (node as TemplateLiteralLikeNode).rawText ?? escapeTemplateSubstitution(escapeText(node.text, CharacterCodes.backtick)); 896 switch (node.kind) { 897 case SyntaxKind.NoSubstitutionTemplateLiteral: 898 return "`" + rawText + "`"; 899 case SyntaxKind.TemplateHead: 900 return "`" + rawText + "${"; 901 case SyntaxKind.TemplateMiddle: 902 return "}" + rawText + "${"; 903 case SyntaxKind.TemplateTail: 904 return "}" + rawText + "`"; 905 } 906 } 907 case SyntaxKind.NumericLiteral: 908 case SyntaxKind.BigIntLiteral: 909 return node.text; 910 case SyntaxKind.RegularExpressionLiteral: 911 if (flags & GetLiteralTextFlags.TerminateUnterminatedLiterals && node.isUnterminated) { 912 return node.text + (node.text.charCodeAt(node.text.length - 1) === CharacterCodes.backslash ? " /" : "/"); 913 } 914 return node.text; 915 } 916 917 return Debug.fail(`Literal kind '${node.kind}' not accounted for.`); 918} 919 920function canUseOriginalText(node: LiteralLikeNode, flags: GetLiteralTextFlags): boolean { 921 if (nodeIsSynthesized(node) || !node.parent || (flags & GetLiteralTextFlags.TerminateUnterminatedLiterals && node.isUnterminated) || 922 (node.flags & NodeFlags.NoOriginalText)) { 923 return false; 924 } 925 926 if (isNumericLiteral(node) && node.numericLiteralFlags & TokenFlags.ContainsSeparator) { 927 return !!(flags & GetLiteralTextFlags.AllowNumericSeparator); 928 } 929 930 return !isBigIntLiteral(node); 931} 932 933/** @internal */ 934export function getTextOfConstantValue(value: string | number) { 935 return isString(value) ? '"' + escapeNonAsciiString(value) + '"' : "" + value; 936} 937 938// Make an identifier from an external module name by extracting the string after the last "/" and replacing 939// all non-alphanumeric characters with underscores 940/** @internal */ 941export function makeIdentifierFromModuleName(moduleName: string): string { 942 return getBaseFileName(moduleName).replace(/^(\d)/, "_$1").replace(/\W/g, "_"); 943} 944 945/** @internal */ 946export function isBlockOrCatchScoped(declaration: Declaration) { 947 return (getCombinedNodeFlags(declaration) & NodeFlags.BlockScoped) !== 0 || 948 isCatchClauseVariableDeclarationOrBindingElement(declaration); 949} 950 951/** @internal */ 952export function isCatchClauseVariableDeclarationOrBindingElement(declaration: Declaration) { 953 const node = getRootDeclaration(declaration); 954 return node.kind === SyntaxKind.VariableDeclaration && node.parent.kind === SyntaxKind.CatchClause; 955} 956 957/** @internal */ 958export function isAmbientModule(node: Node): node is AmbientModuleDeclaration { 959 return isModuleDeclaration(node) && (node.name.kind === SyntaxKind.StringLiteral || isGlobalScopeAugmentation(node)); 960} 961 962/** @internal */ 963export function isModuleWithStringLiteralName(node: Node): node is ModuleDeclaration { 964 return isModuleDeclaration(node) && node.name.kind === SyntaxKind.StringLiteral; 965} 966 967/** @internal */ 968export function isNonGlobalAmbientModule(node: Node): node is ModuleDeclaration & { name: StringLiteral } { 969 return isModuleDeclaration(node) && isStringLiteral(node.name); 970} 971 972/** 973 * An effective module (namespace) declaration is either 974 * 1. An actual declaration: namespace X { ... } 975 * 2. A Javascript declaration, which is: 976 * An identifier in a nested property access expression: Y in `X.Y.Z = { ... }` 977 * 978 * @internal 979 */ 980export function isEffectiveModuleDeclaration(node: Node) { 981 return isModuleDeclaration(node) || isIdentifier(node); 982} 983 984/** 985 * Given a symbol for a module, checks that it is a shorthand ambient module. 986 * 987 * @internal 988 */ 989export function isShorthandAmbientModuleSymbol(moduleSymbol: Symbol): boolean { 990 return isShorthandAmbientModule(moduleSymbol.valueDeclaration); 991} 992 993function isShorthandAmbientModule(node: Node | undefined): boolean { 994 // The only kind of module that can be missing a body is a shorthand ambient module. 995 return !!node && node.kind === SyntaxKind.ModuleDeclaration && (!(node as ModuleDeclaration).body); 996} 997 998/** @internal */ 999export function isBlockScopedContainerTopLevel(node: Node): boolean { 1000 return node.kind === SyntaxKind.SourceFile || 1001 node.kind === SyntaxKind.ModuleDeclaration || 1002 isFunctionLikeOrClassStaticBlockDeclaration(node); 1003} 1004 1005/** @internal */ 1006export function isGlobalScopeAugmentation(module: ModuleDeclaration): boolean { 1007 return !!(module.flags & NodeFlags.GlobalAugmentation); 1008} 1009 1010/** @internal */ 1011export function isExternalModuleAugmentation(node: Node): node is AmbientModuleDeclaration { 1012 return isAmbientModule(node) && isModuleAugmentationExternal(node); 1013} 1014 1015/** @internal */ 1016export function isModuleAugmentationExternal(node: AmbientModuleDeclaration) { 1017 // external module augmentation is a ambient module declaration that is either: 1018 // - defined in the top level scope and source file is an external module 1019 // - defined inside ambient module declaration located in the top level scope and source file not an external module 1020 switch (node.parent.kind) { 1021 case SyntaxKind.SourceFile: 1022 return isExternalModule(node.parent); 1023 case SyntaxKind.ModuleBlock: 1024 return isAmbientModule(node.parent.parent) && isSourceFile(node.parent.parent.parent) && !isExternalModule(node.parent.parent.parent); 1025 } 1026 return false; 1027} 1028 1029/** @internal */ 1030export function getNonAugmentationDeclaration(symbol: Symbol) { 1031 return symbol.declarations?.find(d => !isExternalModuleAugmentation(d) && !(isModuleDeclaration(d) && isGlobalScopeAugmentation(d))); 1032} 1033 1034function isCommonJSContainingModuleKind(kind: ModuleKind) { 1035 return kind === ModuleKind.CommonJS || kind === ModuleKind.Node16 || kind === ModuleKind.NodeNext; 1036} 1037 1038/** @internal */ 1039export function isEffectiveExternalModule(node: SourceFile, compilerOptions: CompilerOptions) { 1040 return isExternalModule(node) || compilerOptions.isolatedModules || (isCommonJSContainingModuleKind(getEmitModuleKind(compilerOptions)) && !!node.commonJsModuleIndicator); 1041} 1042 1043/** 1044 * Returns whether the source file will be treated as if it were in strict mode at runtime. 1045 * 1046 * @internal 1047 */ 1048export function isEffectiveStrictModeSourceFile(node: SourceFile, compilerOptions: CompilerOptions) { 1049 // We can only verify strict mode for JS/TS files 1050 switch (node.scriptKind) { 1051 case ScriptKind.JS: 1052 case ScriptKind.TS: 1053 case ScriptKind.JSX: 1054 case ScriptKind.TSX: 1055 case ScriptKind.ETS: 1056 break; 1057 default: 1058 return false; 1059 } 1060 // Strict mode does not matter for declaration files. 1061 if (node.isDeclarationFile) { 1062 return false; 1063 } 1064 // If `alwaysStrict` is set, then treat the file as strict. 1065 if (getStrictOptionValue(compilerOptions, "alwaysStrict")) { 1066 return true; 1067 } 1068 // Starting with a "use strict" directive indicates the file is strict. 1069 if (startsWithUseStrict(node.statements)) { 1070 return true; 1071 } 1072 if (isExternalModule(node) || compilerOptions.isolatedModules) { 1073 // ECMAScript Modules are always strict. 1074 if (getEmitModuleKind(compilerOptions) >= ModuleKind.ES2015) { 1075 return true; 1076 } 1077 // Other modules are strict unless otherwise specified. 1078 return !compilerOptions.noImplicitUseStrict; 1079 } 1080 return false; 1081} 1082 1083/** @internal */ 1084export function isAmbientPropertyDeclaration(node: PropertyDeclaration) { 1085 return !!(node.flags & NodeFlags.Ambient) || hasSyntacticModifier(node, ModifierFlags.Ambient); 1086} 1087 1088/** @internal */ 1089export function isBlockScope(node: Node, parentNode: Node | undefined): boolean { 1090 switch (node.kind) { 1091 case SyntaxKind.SourceFile: 1092 case SyntaxKind.CaseBlock: 1093 case SyntaxKind.CatchClause: 1094 case SyntaxKind.ModuleDeclaration: 1095 case SyntaxKind.ForStatement: 1096 case SyntaxKind.ForInStatement: 1097 case SyntaxKind.ForOfStatement: 1098 case SyntaxKind.Constructor: 1099 case SyntaxKind.MethodDeclaration: 1100 case SyntaxKind.GetAccessor: 1101 case SyntaxKind.SetAccessor: 1102 case SyntaxKind.FunctionDeclaration: 1103 case SyntaxKind.FunctionExpression: 1104 case SyntaxKind.ArrowFunction: 1105 case SyntaxKind.PropertyDeclaration: 1106 case SyntaxKind.AnnotationPropertyDeclaration: 1107 case SyntaxKind.ClassStaticBlockDeclaration: 1108 return true; 1109 1110 case SyntaxKind.Block: 1111 // function block is not considered block-scope container 1112 // see comment in binder.ts: bind(...), case for SyntaxKind.Block 1113 return !isFunctionLikeOrClassStaticBlockDeclaration(parentNode); 1114 } 1115 1116 return false; 1117} 1118 1119/** @internal */ 1120export function isDeclarationWithTypeParameters(node: Node): node is DeclarationWithTypeParameters; 1121/** @internal */ 1122export function isDeclarationWithTypeParameters(node: DeclarationWithTypeParameters): node is DeclarationWithTypeParameters { 1123 switch (node.kind) { 1124 case SyntaxKind.JSDocCallbackTag: 1125 case SyntaxKind.JSDocTypedefTag: 1126 case SyntaxKind.JSDocSignature: 1127 return true; 1128 default: 1129 assertType<DeclarationWithTypeParameterChildren>(node); 1130 return isDeclarationWithTypeParameterChildren(node); 1131 } 1132} 1133 1134/** @internal */ 1135export function isDeclarationWithTypeParameterChildren(node: Node): node is DeclarationWithTypeParameterChildren; 1136/** @internal */ 1137export function isDeclarationWithTypeParameterChildren(node: DeclarationWithTypeParameterChildren): node is DeclarationWithTypeParameterChildren { 1138 switch (node.kind) { 1139 case SyntaxKind.CallSignature: 1140 case SyntaxKind.ConstructSignature: 1141 case SyntaxKind.MethodSignature: 1142 case SyntaxKind.IndexSignature: 1143 case SyntaxKind.FunctionType: 1144 case SyntaxKind.ConstructorType: 1145 case SyntaxKind.JSDocFunctionType: 1146 case SyntaxKind.ClassDeclaration: 1147 case SyntaxKind.StructDeclaration: 1148 case SyntaxKind.ClassExpression: 1149 case SyntaxKind.InterfaceDeclaration: 1150 case SyntaxKind.TypeAliasDeclaration: 1151 case SyntaxKind.JSDocTemplateTag: 1152 case SyntaxKind.FunctionDeclaration: 1153 case SyntaxKind.MethodDeclaration: 1154 case SyntaxKind.Constructor: 1155 case SyntaxKind.GetAccessor: 1156 case SyntaxKind.SetAccessor: 1157 case SyntaxKind.FunctionExpression: 1158 case SyntaxKind.ArrowFunction: 1159 return true; 1160 default: 1161 assertType<never>(node); 1162 return false; 1163 } 1164} 1165 1166/** @internal */ 1167export function isAnyImportSyntax(node: Node): node is AnyImportSyntax { 1168 switch (node.kind) { 1169 case SyntaxKind.ImportDeclaration: 1170 case SyntaxKind.ImportEqualsDeclaration: 1171 return true; 1172 default: 1173 return false; 1174 } 1175} 1176 1177/** @internal */ 1178export function isAnyImportOrBareOrAccessedRequire(node: Node): node is AnyImportOrBareOrAccessedRequire { 1179 return isAnyImportSyntax(node) || isVariableDeclarationInitializedToBareOrAccessedRequire(node); 1180} 1181 1182/** @internal */ 1183export function isLateVisibilityPaintedStatement(node: Node): node is LateVisibilityPaintedStatement { 1184 switch (node.kind) { 1185 case SyntaxKind.ImportDeclaration: 1186 case SyntaxKind.ImportEqualsDeclaration: 1187 case SyntaxKind.VariableStatement: 1188 case SyntaxKind.ClassDeclaration: 1189 case SyntaxKind.StructDeclaration: 1190 case SyntaxKind.AnnotationDeclaration: 1191 case SyntaxKind.FunctionDeclaration: 1192 case SyntaxKind.ModuleDeclaration: 1193 case SyntaxKind.TypeAliasDeclaration: 1194 case SyntaxKind.InterfaceDeclaration: 1195 case SyntaxKind.EnumDeclaration: 1196 return true; 1197 default: 1198 return false; 1199 } 1200} 1201 1202/** @internal */ 1203export function hasPossibleExternalModuleReference(node: Node): node is AnyImportOrReExport | ModuleDeclaration | ImportTypeNode | ImportCall { 1204 return isAnyImportOrReExport(node) || isModuleDeclaration(node) || isImportTypeNode(node) || isImportCall(node); 1205} 1206 1207/** @internal */ 1208export function isAnyImportOrReExport(node: Node): node is AnyImportOrReExport { 1209 return isAnyImportSyntax(node) || isExportDeclaration(node); 1210} 1211 1212// Gets the nearest enclosing block scope container that has the provided node 1213// as a descendant, that is not the provided node. 1214/** @internal */ 1215export function getEnclosingBlockScopeContainer(node: Node): Node { 1216 return findAncestor(node.parent, current => isBlockScope(current, current.parent))!; 1217} 1218 1219/** @internal */ 1220export function forEachEnclosingBlockScopeContainer(node: Node, cb: (container: Node) => void): void { 1221 let container = getEnclosingBlockScopeContainer(node); 1222 while (container) { 1223 cb(container); 1224 container = getEnclosingBlockScopeContainer(container); 1225 } 1226} 1227 1228// Return display name of an identifier 1229// Computed property names will just be emitted as "[<expr>]", where <expr> is the source 1230// text of the expression in the computed property. 1231/** @internal */ 1232export function declarationNameToString(name: DeclarationName | QualifiedName | undefined) { 1233 if (name && name.virtual && name.kind === SyntaxKind.Identifier) { 1234 return name.escapedText.toString(); 1235 } 1236 else { 1237 return !name || getFullWidth(name) === 0 ? "(Missing)" : getTextOfNode(name); 1238 } 1239} 1240 1241/** @internal */ 1242export function getNameFromIndexInfo(info: IndexInfo): string | undefined { 1243 return info.declaration ? declarationNameToString(info.declaration.parameters[0].name) : undefined; 1244} 1245 1246/** @internal */ 1247export function isComputedNonLiteralName(name: PropertyName): boolean { 1248 return name.kind === SyntaxKind.ComputedPropertyName && !isStringOrNumericLiteralLike(name.expression); 1249} 1250 1251/** @internal */ 1252export function tryGetTextOfPropertyName(name: PropertyName | NoSubstitutionTemplateLiteral): __String | undefined { 1253 switch (name.kind) { 1254 case SyntaxKind.Identifier: 1255 case SyntaxKind.PrivateIdentifier: 1256 return name.autoGenerateFlags ? undefined : name.escapedText; 1257 case SyntaxKind.StringLiteral: 1258 case SyntaxKind.NumericLiteral: 1259 case SyntaxKind.NoSubstitutionTemplateLiteral: 1260 return escapeLeadingUnderscores(name.text); 1261 case SyntaxKind.ComputedPropertyName: 1262 if (isStringOrNumericLiteralLike(name.expression)) return escapeLeadingUnderscores(name.expression.text); 1263 return undefined; 1264 default: 1265 return Debug.assertNever(name); 1266 } 1267} 1268 1269/** @internal */ 1270export function getTextOfPropertyName(name: PropertyName | NoSubstitutionTemplateLiteral): __String { 1271 return Debug.checkDefined(tryGetTextOfPropertyName(name)); 1272} 1273 1274/** @internal */ 1275export function entityNameToString(name: EntityNameOrEntityNameExpression | JSDocMemberName | JsxTagNameExpression | PrivateIdentifier): string { 1276 switch (name.kind) { 1277 case SyntaxKind.ThisKeyword: 1278 return "this"; 1279 case SyntaxKind.PrivateIdentifier: 1280 case SyntaxKind.Identifier: 1281 return getFullWidth(name) === 0 ? idText(name) : getTextOfNode(name); 1282 case SyntaxKind.QualifiedName: 1283 return entityNameToString(name.left) + "." + entityNameToString(name.right); 1284 case SyntaxKind.PropertyAccessExpression: 1285 if (isIdentifier(name.name) || isPrivateIdentifier(name.name)) { 1286 return entityNameToString(name.expression) + "." + entityNameToString(name.name); 1287 } 1288 else { 1289 return Debug.assertNever(name.name); 1290 } 1291 case SyntaxKind.JSDocMemberName: 1292 return entityNameToString(name.left) + entityNameToString(name.right); 1293 default: 1294 return Debug.assertNever(name); 1295 } 1296} 1297 1298/** @internal */ 1299export function createDiagnosticForNode(node: Node, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number): DiagnosticWithLocation { 1300 const sourceFile = getSourceFileOfNode(node); 1301 return createDiagnosticForNodeInSourceFile(sourceFile, node, message, arg0, arg1, arg2, arg3); 1302} 1303 1304/** @internal */ 1305export function createDiagnosticForNodeArray(sourceFile: SourceFile, nodes: NodeArray<Node>, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number): DiagnosticWithLocation { 1306 const start = skipTrivia(sourceFile.text, nodes.pos); 1307 return createFileDiagnostic(sourceFile, start, nodes.end - start, message, arg0, arg1, arg2, arg3); 1308} 1309 1310/** @internal */ 1311export function createDiagnosticForNodeInSourceFile(sourceFile: SourceFile, node: Node, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number): DiagnosticWithLocation { 1312 const span = getErrorSpanForNode(sourceFile, node); 1313 return createFileDiagnostic(sourceFile, span.start, span.length, message, arg0, arg1, arg2, arg3); 1314} 1315 1316/** @internal */ 1317export function createDiagnosticForNodeFromMessageChain(node: Node, messageChain: DiagnosticMessageChain, relatedInformation?: DiagnosticRelatedInformation[]): DiagnosticWithLocation { 1318 const sourceFile = getSourceFileOfNode(node); 1319 const span = getErrorSpanForNode(sourceFile, node); 1320 return createFileDiagnosticFromMessageChain(sourceFile, span.start, span.length, messageChain, relatedInformation); 1321} 1322 1323function assertDiagnosticLocation(file: SourceFile | undefined, start: number, length: number) { 1324 Debug.assertGreaterThanOrEqual(start, 0); 1325 Debug.assertGreaterThanOrEqual(length, 0); 1326 1327 if (file) { 1328 Debug.assertLessThanOrEqual(start, file.text.length); 1329 Debug.assertLessThanOrEqual(start + length, file.text.length); 1330 } 1331} 1332 1333/** @internal */ 1334export function createFileDiagnosticFromMessageChain(file: SourceFile, start: number, length: number, messageChain: DiagnosticMessageChain, relatedInformation?: DiagnosticRelatedInformation[]): DiagnosticWithLocation { 1335 assertDiagnosticLocation(file, start, length); 1336 return { 1337 file, 1338 start, 1339 length, 1340 code: messageChain.code, 1341 category: messageChain.category, 1342 messageText: messageChain.next ? messageChain : messageChain.messageText, 1343 relatedInformation 1344 }; 1345} 1346 1347/** @internal */ 1348export function createDiagnosticForFileFromMessageChain(sourceFile: SourceFile, messageChain: DiagnosticMessageChain, relatedInformation?: DiagnosticRelatedInformation[]): DiagnosticWithLocation { 1349 return { 1350 file: sourceFile, 1351 start: 0, 1352 length: 0, 1353 code: messageChain.code, 1354 category: messageChain.category, 1355 messageText: messageChain.next ? messageChain : messageChain.messageText, 1356 relatedInformation 1357 }; 1358} 1359 1360/** @internal */ 1361export function createDiagnosticMessageChainFromDiagnostic(diagnostic: DiagnosticRelatedInformation): DiagnosticMessageChain { 1362 return typeof diagnostic.messageText === "string" ? { 1363 code: diagnostic.code, 1364 category: diagnostic.category, 1365 messageText: diagnostic.messageText, 1366 next: (diagnostic as DiagnosticMessageChain).next, 1367 } : diagnostic.messageText; 1368} 1369 1370/** @internal */ 1371export function createDiagnosticForRange(sourceFile: SourceFile, range: TextRange, message: DiagnosticMessage): DiagnosticWithLocation { 1372 return { 1373 file: sourceFile, 1374 start: range.pos, 1375 length: range.end - range.pos, 1376 code: message.code, 1377 category: message.category, 1378 messageText: message.message, 1379 }; 1380} 1381 1382/** @internal */ 1383export function getSpanOfTokenAtPosition(sourceFile: SourceFile, pos: number): TextSpan { 1384 const scanner = createScanner(sourceFile.languageVersion, /*skipTrivia*/ true, sourceFile.languageVariant, sourceFile.text, /*onError:*/ undefined, pos); 1385 scanner.scan(); 1386 const start = scanner.getTokenPos(); 1387 return createTextSpanFromBounds(start, scanner.getTextPos()); 1388} 1389 1390function getErrorSpanForArrowFunction(sourceFile: SourceFile, node: ArrowFunction): TextSpan { 1391 const pos = skipTrivia(sourceFile.text, node.pos); 1392 if (node.body && node.body.kind === SyntaxKind.Block) { 1393 const { line: startLine } = getLineAndCharacterOfPosition(sourceFile, node.body.pos); 1394 const { line: endLine } = getLineAndCharacterOfPosition(sourceFile, node.body.end); 1395 if (startLine < endLine) { 1396 // The arrow function spans multiple lines, 1397 // make the error span be the first line, inclusive. 1398 return createTextSpan(pos, getEndLinePosition(startLine, sourceFile) - pos + 1); 1399 } 1400 } 1401 return createTextSpanFromBounds(pos, node.end); 1402} 1403 1404/** @internal */ 1405export function getErrorSpanForNode(sourceFile: SourceFile, node: Node): TextSpan { 1406 let errorNode: Node | undefined = node; 1407 switch (node.kind) { 1408 case SyntaxKind.SourceFile: 1409 const pos = skipTrivia(sourceFile.text, 0, /*stopAfterLineBreak*/ false); 1410 if (pos === sourceFile.text.length) { 1411 // file is empty - return span for the beginning of the file 1412 return createTextSpan(0, 0); 1413 } 1414 return getSpanOfTokenAtPosition(sourceFile, pos); 1415 // This list is a work in progress. Add missing node kinds to improve their error 1416 // spans. 1417 case SyntaxKind.VariableDeclaration: 1418 case SyntaxKind.BindingElement: 1419 case SyntaxKind.ClassDeclaration: 1420 case SyntaxKind.ClassExpression: 1421 case SyntaxKind.StructDeclaration: 1422 case SyntaxKind.InterfaceDeclaration: 1423 case SyntaxKind.ModuleDeclaration: 1424 case SyntaxKind.EnumDeclaration: 1425 case SyntaxKind.EnumMember: 1426 case SyntaxKind.FunctionDeclaration: 1427 case SyntaxKind.FunctionExpression: 1428 case SyntaxKind.MethodDeclaration: 1429 case SyntaxKind.GetAccessor: 1430 case SyntaxKind.SetAccessor: 1431 case SyntaxKind.TypeAliasDeclaration: 1432 case SyntaxKind.PropertyDeclaration: 1433 case SyntaxKind.AnnotationPropertyDeclaration: 1434 case SyntaxKind.PropertySignature: 1435 case SyntaxKind.NamespaceImport: 1436 errorNode = (node as NamedDeclaration).name; 1437 break; 1438 case SyntaxKind.ArrowFunction: 1439 return getErrorSpanForArrowFunction(sourceFile, node as ArrowFunction); 1440 case SyntaxKind.CaseClause: 1441 case SyntaxKind.DefaultClause: 1442 const start = skipTrivia(sourceFile.text, (node as CaseOrDefaultClause).pos); 1443 const end = (node as CaseOrDefaultClause).statements.length > 0 ? (node as CaseOrDefaultClause).statements[0].pos : (node as CaseOrDefaultClause).end; 1444 return createTextSpanFromBounds(start, end); 1445 } 1446 1447 if (errorNode === undefined) { 1448 // If we don't have a better node, then just set the error on the first token of 1449 // construct. 1450 return getSpanOfTokenAtPosition(sourceFile, node.pos); 1451 } 1452 1453 Debug.assert(!isJSDoc(errorNode)); 1454 1455 const isMissing = nodeIsMissing(errorNode); 1456 const pos = isMissing || isJsxText(node) || (node.virtual && !(node.flags & NodeFlags.KitImportFlags)) 1457 ? errorNode.pos 1458 : skipTrivia(sourceFile.text, errorNode.pos); 1459 1460 // These asserts should all be satisfied for a properly constructed `errorNode`. 1461 if (isMissing) { 1462 Debug.assert(pos === errorNode.pos, "This failure could trigger https://github.com/Microsoft/TypeScript/issues/20809"); 1463 Debug.assert(pos === errorNode.end, "This failure could trigger https://github.com/Microsoft/TypeScript/issues/20809"); 1464 } 1465 else { 1466 Debug.assert(pos >= errorNode.pos, "This failure could trigger https://github.com/Microsoft/TypeScript/issues/20809"); 1467 Debug.assert(pos <= errorNode.end, "This failure could trigger https://github.com/Microsoft/TypeScript/issues/20809"); 1468 } 1469 1470 return createTextSpanFromBounds(pos, errorNode.end); 1471} 1472 1473/** @internal */ 1474export function isExternalOrCommonJsModule(file: SourceFile): boolean { 1475 return (file.externalModuleIndicator || file.commonJsModuleIndicator) !== undefined; 1476} 1477 1478 1479/** @internal */ 1480export function isJsonSourceFile(file: SourceFile): file is JsonSourceFile { 1481 return file.scriptKind === ScriptKind.JSON; 1482} 1483 1484/** @internal */ 1485export function isEmitNodeModulesFiles(emitNodeModulesFiles: boolean | undefined): boolean { 1486 return !!emitNodeModulesFiles; 1487} 1488 1489/** @internal */ 1490export function isEnumConst(node: EnumDeclaration): boolean { 1491 return !!(getCombinedModifierFlags(node) & ModifierFlags.Const); 1492} 1493 1494/** @internal */ 1495export function isDeclarationReadonly(declaration: Declaration): boolean { 1496 return !!(getCombinedModifierFlags(declaration) & ModifierFlags.Readonly && !isParameterPropertyDeclaration(declaration, declaration.parent)); 1497} 1498 1499/** @internal */ 1500export function isVarConst(node: VariableDeclaration | VariableDeclarationList): boolean { 1501 return !!(getCombinedNodeFlags(node) & NodeFlags.Const); 1502} 1503 1504/** @internal */ 1505export function isLet(node: Node): boolean { 1506 return !!(getCombinedNodeFlags(node) & NodeFlags.Let); 1507} 1508 1509/** @internal */ 1510export function isSuperCall(n: Node): n is SuperCall { 1511 return n.kind === SyntaxKind.CallExpression && (n as CallExpression).expression.kind === SyntaxKind.SuperKeyword; 1512} 1513 1514/** @internal */ 1515export function isImportCall(n: Node): n is ImportCall { 1516 return n.kind === SyntaxKind.CallExpression && (n as CallExpression).expression.kind === SyntaxKind.ImportKeyword; 1517} 1518 1519/** @internal */ 1520export function isImportMeta(n: Node): n is ImportMetaProperty { 1521 return isMetaProperty(n) 1522 && n.keywordToken === SyntaxKind.ImportKeyword 1523 && n.name.escapedText === "meta"; 1524} 1525 1526/** @internal */ 1527export function isLiteralImportTypeNode(n: Node): n is LiteralImportTypeNode { 1528 return isImportTypeNode(n) && isLiteralTypeNode(n.argument) && isStringLiteral(n.argument.literal); 1529} 1530 1531/** @internal */ 1532export function isPrologueDirective(node: Node): node is PrologueDirective { 1533 return node.kind === SyntaxKind.ExpressionStatement 1534 && (node as ExpressionStatement).expression.kind === SyntaxKind.StringLiteral; 1535} 1536 1537/** @internal */ 1538export function isCustomPrologue(node: Statement) { 1539 return !!(getEmitFlags(node) & EmitFlags.CustomPrologue); 1540} 1541 1542/** @internal */ 1543export function isHoistedFunction(node: Statement) { 1544 return isCustomPrologue(node) 1545 && isFunctionDeclaration(node); 1546} 1547 1548function isHoistedVariable(node: VariableDeclaration) { 1549 return isIdentifier(node.name) 1550 && !node.initializer; 1551} 1552 1553/** @internal */ 1554export function isHoistedVariableStatement(node: Statement) { 1555 return isCustomPrologue(node) 1556 && isVariableStatement(node) 1557 && every(node.declarationList.declarations, isHoistedVariable); 1558} 1559 1560/** @internal */ 1561export function getJSDocCommentRanges(node: Node, text: string) { 1562 const commentRanges = (node.kind === SyntaxKind.Parameter || 1563 node.kind === SyntaxKind.TypeParameter || 1564 node.kind === SyntaxKind.FunctionExpression || 1565 node.kind === SyntaxKind.ArrowFunction || 1566 node.kind === SyntaxKind.ParenthesizedExpression || 1567 node.kind === SyntaxKind.VariableDeclaration || 1568 node.kind === SyntaxKind.ExportSpecifier) ? 1569 concatenate(getTrailingCommentRanges(text, node.pos), getLeadingCommentRanges(text, node.pos)) : 1570 getLeadingCommentRanges(text, node.pos); 1571 // True if the comment starts with '/**' but not if it is '/**/' 1572 return filter(commentRanges, comment => 1573 text.charCodeAt(comment.pos + 1) === CharacterCodes.asterisk && 1574 text.charCodeAt(comment.pos + 2) === CharacterCodes.asterisk && 1575 text.charCodeAt(comment.pos + 3) !== CharacterCodes.slash); 1576} 1577 1578/** @internal */ 1579export const fullTripleSlashReferencePathRegEx = /^(\/\/\/\s*<reference\s+path\s*=\s*)(('[^']*')|("[^"]*")).*?\/>/; 1580const fullTripleSlashReferenceTypeReferenceDirectiveRegEx = /^(\/\/\/\s*<reference\s+types\s*=\s*)(('[^']*')|("[^"]*")).*?\/>/; 1581/** @internal */ 1582export const fullTripleSlashAMDReferencePathRegEx = /^(\/\/\/\s*<amd-dependency\s+path\s*=\s*)(('[^']*')|("[^"]*")).*?\/>/; 1583const defaultLibReferenceRegEx = /^(\/\/\/\s*<reference\s+no-default-lib\s*=\s*)(('[^']*')|("[^"]*"))\s*\/>/; 1584 1585export function isPartOfTypeNode(node: Node): boolean { 1586 if (SyntaxKind.FirstTypeNode <= node.kind && node.kind <= SyntaxKind.LastTypeNode) { 1587 return true; 1588 } 1589 1590 switch (node.kind) { 1591 case SyntaxKind.AnyKeyword: 1592 case SyntaxKind.UnknownKeyword: 1593 case SyntaxKind.NumberKeyword: 1594 case SyntaxKind.BigIntKeyword: 1595 case SyntaxKind.StringKeyword: 1596 case SyntaxKind.BooleanKeyword: 1597 case SyntaxKind.SymbolKeyword: 1598 case SyntaxKind.ObjectKeyword: 1599 case SyntaxKind.UndefinedKeyword: 1600 case SyntaxKind.NeverKeyword: 1601 return true; 1602 case SyntaxKind.VoidKeyword: 1603 return node.parent.kind !== SyntaxKind.VoidExpression; 1604 case SyntaxKind.ExpressionWithTypeArguments: 1605 return isHeritageClause(node.parent) && !isExpressionWithTypeArgumentsInClassExtendsClause(node); 1606 case SyntaxKind.TypeParameter: 1607 return node.parent.kind === SyntaxKind.MappedType || node.parent.kind === SyntaxKind.InferType; 1608 1609 // Identifiers and qualified names may be type nodes, depending on their context. Climb 1610 // above them to find the lowest container 1611 case SyntaxKind.Identifier: 1612 // If the identifier is the RHS of a qualified name, then it's a type iff its parent is. 1613 if (node.parent.kind === SyntaxKind.QualifiedName && (node.parent as QualifiedName).right === node) { 1614 node = node.parent; 1615 } 1616 else if (node.parent.kind === SyntaxKind.PropertyAccessExpression && (node.parent as PropertyAccessExpression).name === node) { 1617 node = node.parent; 1618 } 1619 // At this point, node is either a qualified name or an identifier 1620 Debug.assert(node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.QualifiedName || node.kind === SyntaxKind.PropertyAccessExpression, 1621 "'node' was expected to be a qualified name, identifier or property access in 'isPartOfTypeNode'."); 1622 // falls through 1623 1624 case SyntaxKind.QualifiedName: 1625 case SyntaxKind.PropertyAccessExpression: 1626 case SyntaxKind.ThisKeyword: { 1627 const { parent } = node; 1628 if (parent.kind === SyntaxKind.TypeQuery) { 1629 return false; 1630 } 1631 if (parent.kind === SyntaxKind.ImportType) { 1632 return !(parent as ImportTypeNode).isTypeOf; 1633 } 1634 // Do not recursively call isPartOfTypeNode on the parent. In the example: 1635 // 1636 // let a: A.B.C; 1637 // 1638 // Calling isPartOfTypeNode would consider the qualified name A.B a type node. 1639 // Only C and A.B.C are type nodes. 1640 if (SyntaxKind.FirstTypeNode <= parent.kind && parent.kind <= SyntaxKind.LastTypeNode) { 1641 return true; 1642 } 1643 switch (parent.kind) { 1644 case SyntaxKind.ExpressionWithTypeArguments: 1645 return isHeritageClause(parent.parent) && !isExpressionWithTypeArgumentsInClassExtendsClause(parent); 1646 case SyntaxKind.TypeParameter: 1647 return node === (parent as TypeParameterDeclaration).constraint; 1648 case SyntaxKind.JSDocTemplateTag: 1649 return node === (parent as JSDocTemplateTag).constraint; 1650 case SyntaxKind.PropertyDeclaration: 1651 case SyntaxKind.PropertySignature: 1652 case SyntaxKind.Parameter: 1653 case SyntaxKind.VariableDeclaration: 1654 return node === (parent as HasType).type; 1655 case SyntaxKind.FunctionDeclaration: 1656 case SyntaxKind.FunctionExpression: 1657 case SyntaxKind.ArrowFunction: 1658 case SyntaxKind.Constructor: 1659 case SyntaxKind.MethodDeclaration: 1660 case SyntaxKind.MethodSignature: 1661 case SyntaxKind.GetAccessor: 1662 case SyntaxKind.SetAccessor: 1663 return node === (parent as FunctionLikeDeclaration).type; 1664 case SyntaxKind.CallSignature: 1665 case SyntaxKind.ConstructSignature: 1666 case SyntaxKind.IndexSignature: 1667 return node === (parent as SignatureDeclaration).type; 1668 case SyntaxKind.TypeAssertionExpression: 1669 return node === (parent as TypeAssertion).type; 1670 case SyntaxKind.CallExpression: 1671 case SyntaxKind.NewExpression: 1672 return contains((parent as CallExpression).typeArguments, node); 1673 case SyntaxKind.TaggedTemplateExpression: 1674 // TODO (drosen): TaggedTemplateExpressions may eventually support type arguments. 1675 return false; 1676 } 1677 } 1678 } 1679 1680 return false; 1681} 1682 1683/** @internal */ 1684export function isChildOfNodeWithKind(node: Node, kind: SyntaxKind): boolean { 1685 while (node) { 1686 if (node.kind === kind) { 1687 return true; 1688 } 1689 node = node.parent; 1690 } 1691 return false; 1692} 1693 1694// Warning: This has the same semantics as the forEach family of functions, 1695// in that traversal terminates in the event that 'visitor' supplies a truthy value. 1696/** @internal */ 1697export function forEachReturnStatement<T>(body: Block | Statement, visitor: (stmt: ReturnStatement) => T): T | undefined { 1698 1699 return traverse(body); 1700 1701 function traverse(node: Node): T | undefined { 1702 switch (node.kind) { 1703 case SyntaxKind.ReturnStatement: 1704 return visitor(node as ReturnStatement); 1705 case SyntaxKind.CaseBlock: 1706 case SyntaxKind.Block: 1707 case SyntaxKind.IfStatement: 1708 case SyntaxKind.DoStatement: 1709 case SyntaxKind.WhileStatement: 1710 case SyntaxKind.ForStatement: 1711 case SyntaxKind.ForInStatement: 1712 case SyntaxKind.ForOfStatement: 1713 case SyntaxKind.WithStatement: 1714 case SyntaxKind.SwitchStatement: 1715 case SyntaxKind.CaseClause: 1716 case SyntaxKind.DefaultClause: 1717 case SyntaxKind.LabeledStatement: 1718 case SyntaxKind.TryStatement: 1719 case SyntaxKind.CatchClause: 1720 return forEachChild(node, traverse); 1721 } 1722 } 1723} 1724 1725/** @internal */ 1726export function forEachYieldExpression(body: Block, visitor: (expr: YieldExpression) => void): void { 1727 1728 return traverse(body); 1729 1730 function traverse(node: Node): void { 1731 switch (node.kind) { 1732 case SyntaxKind.YieldExpression: 1733 visitor(node as YieldExpression); 1734 const operand = (node as YieldExpression).expression; 1735 if (operand) { 1736 traverse(operand); 1737 } 1738 return; 1739 case SyntaxKind.EnumDeclaration: 1740 case SyntaxKind.InterfaceDeclaration: 1741 case SyntaxKind.ModuleDeclaration: 1742 case SyntaxKind.TypeAliasDeclaration: 1743 // These are not allowed inside a generator now, but eventually they may be allowed 1744 // as local types. Regardless, skip them to avoid the work. 1745 return; 1746 default: 1747 if (isFunctionLike(node)) { 1748 if (node.name && node.name.kind === SyntaxKind.ComputedPropertyName) { 1749 // Note that we will not include methods/accessors of a class because they would require 1750 // first descending into the class. This is by design. 1751 traverse(node.name.expression); 1752 return; 1753 } 1754 } 1755 else if (!isPartOfTypeNode(node)) { 1756 // This is the general case, which should include mostly expressions and statements. 1757 // Also includes NodeArrays. 1758 forEachChild(node, traverse); 1759 } 1760 } 1761 } 1762} 1763 1764/** 1765 * Gets the most likely element type for a TypeNode. This is not an exhaustive test 1766 * as it assumes a rest argument can only be an array type (either T[], or Array<T>). 1767 * 1768 * @param node The type node. 1769 * 1770 * @internal 1771 */ 1772export function getRestParameterElementType(node: TypeNode | undefined) { 1773 if (node && node.kind === SyntaxKind.ArrayType) { 1774 return (node as ArrayTypeNode).elementType; 1775 } 1776 else if (node && node.kind === SyntaxKind.TypeReference) { 1777 return singleOrUndefined((node as TypeReferenceNode).typeArguments); 1778 } 1779 else { 1780 return undefined; 1781 } 1782} 1783 1784/** @internal */ 1785export function getMembersOfDeclaration(node: Declaration): NodeArray<ClassElement | TypeElement | ObjectLiteralElement> | undefined { 1786 switch (node.kind) { 1787 case SyntaxKind.InterfaceDeclaration: 1788 case SyntaxKind.ClassDeclaration: 1789 case SyntaxKind.ClassExpression: 1790 case SyntaxKind.StructDeclaration: 1791 case SyntaxKind.TypeLiteral: 1792 return (node as ObjectTypeDeclaration).members; 1793 case SyntaxKind.ObjectLiteralExpression: 1794 return (node as ObjectLiteralExpression).properties; 1795 } 1796} 1797 1798/** @internal */ 1799export function isVariableLike(node: Node): node is VariableLikeDeclaration { 1800 if (node) { 1801 switch (node.kind) { 1802 case SyntaxKind.BindingElement: 1803 case SyntaxKind.EnumMember: 1804 case SyntaxKind.Parameter: 1805 case SyntaxKind.PropertyAssignment: 1806 case SyntaxKind.PropertyDeclaration: 1807 case SyntaxKind.PropertySignature: 1808 case SyntaxKind.ShorthandPropertyAssignment: 1809 case SyntaxKind.VariableDeclaration: 1810 return true; 1811 } 1812 } 1813 return false; 1814} 1815 1816/** @internal */ 1817export function isVariableLikeOrAccessor(node: Node): node is AccessorDeclaration | VariableLikeDeclaration { 1818 return isVariableLike(node) || isAccessor(node); 1819} 1820 1821/** @internal */ 1822export function isVariableDeclarationInVariableStatement(node: VariableDeclaration) { 1823 return node.parent.kind === SyntaxKind.VariableDeclarationList 1824 && node.parent.parent.kind === SyntaxKind.VariableStatement; 1825} 1826 1827/** @internal */ 1828export function isCommonJsExportedExpression(node: Node) { 1829 if (!isInJSFile(node)) return false; 1830 return (isObjectLiteralExpression(node.parent) && isBinaryExpression(node.parent.parent) && getAssignmentDeclarationKind(node.parent.parent) === AssignmentDeclarationKind.ModuleExports) || 1831 isCommonJsExportPropertyAssignment(node.parent); 1832} 1833 1834/** @internal */ 1835export function isCommonJsExportPropertyAssignment(node: Node) { 1836 if (!isInJSFile(node)) return false; 1837 return (isBinaryExpression(node) && getAssignmentDeclarationKind(node) === AssignmentDeclarationKind.ExportsProperty); 1838} 1839 1840/** @internal */ 1841export function isValidESSymbolDeclaration(node: Node): boolean { 1842 return (isVariableDeclaration(node) ? isVarConst(node) && isIdentifier(node.name) && isVariableDeclarationInVariableStatement(node) : 1843 isPropertyDeclaration(node) ? hasEffectiveReadonlyModifier(node) && hasStaticModifier(node) : 1844 isPropertySignature(node) && hasEffectiveReadonlyModifier(node)) || isCommonJsExportPropertyAssignment(node); 1845} 1846 1847/** @internal */ 1848export function introducesArgumentsExoticObject(node: Node) { 1849 switch (node.kind) { 1850 case SyntaxKind.MethodDeclaration: 1851 case SyntaxKind.MethodSignature: 1852 case SyntaxKind.Constructor: 1853 case SyntaxKind.GetAccessor: 1854 case SyntaxKind.SetAccessor: 1855 case SyntaxKind.FunctionDeclaration: 1856 case SyntaxKind.FunctionExpression: 1857 return true; 1858 } 1859 return false; 1860} 1861 1862/** @internal */ 1863export function unwrapInnermostStatementOfLabel(node: LabeledStatement, beforeUnwrapLabelCallback?: (node: LabeledStatement) => void): Statement { 1864 while (true) { 1865 if (beforeUnwrapLabelCallback) { 1866 beforeUnwrapLabelCallback(node); 1867 } 1868 if (node.statement.kind !== SyntaxKind.LabeledStatement) { 1869 return node.statement; 1870 } 1871 node = node.statement as LabeledStatement; 1872 } 1873} 1874 1875/** @internal */ 1876export function isFunctionBlock(node: Node): boolean { 1877 return node && node.kind === SyntaxKind.Block && isFunctionLike(node.parent); 1878} 1879 1880/** @internal */ 1881export function isObjectLiteralMethod(node: Node): node is MethodDeclaration { 1882 return node && node.kind === SyntaxKind.MethodDeclaration && node.parent.kind === SyntaxKind.ObjectLiteralExpression; 1883} 1884 1885/** @internal */ 1886export function isObjectLiteralOrClassExpressionMethodOrAccessor(node: Node): node is MethodDeclaration { 1887 return (node.kind === SyntaxKind.MethodDeclaration || node.kind === SyntaxKind.GetAccessor || node.kind === SyntaxKind.SetAccessor) && 1888 (node.parent.kind === SyntaxKind.ObjectLiteralExpression || 1889 node.parent.kind === SyntaxKind.ClassExpression); 1890} 1891 1892/** @internal */ 1893export function isIdentifierTypePredicate(predicate: TypePredicate): predicate is IdentifierTypePredicate { 1894 return predicate && predicate.kind === TypePredicateKind.Identifier; 1895} 1896 1897/** @internal */ 1898export function isThisTypePredicate(predicate: TypePredicate): predicate is ThisTypePredicate { 1899 return predicate && predicate.kind === TypePredicateKind.This; 1900} 1901 1902/** @internal */ 1903export function getPropertyAssignment(objectLiteral: ObjectLiteralExpression, key: string, key2?: string): readonly PropertyAssignment[] { 1904 return objectLiteral.properties.filter((property): property is PropertyAssignment => { 1905 if (property.kind === SyntaxKind.PropertyAssignment) { 1906 const propName = tryGetTextOfPropertyName(property.name); 1907 return key === propName || (!!key2 && key2 === propName); 1908 } 1909 return false; 1910 }); 1911} 1912 1913/** @internal */ 1914export function getPropertyArrayElementValue(objectLiteral: ObjectLiteralExpression, propKey: string, elementValue: string): StringLiteral | undefined { 1915 return firstDefined(getPropertyAssignment(objectLiteral, propKey), property => 1916 isArrayLiteralExpression(property.initializer) ? 1917 find(property.initializer.elements, (element): element is StringLiteral => isStringLiteral(element) && element.text === elementValue) : 1918 undefined); 1919} 1920 1921/** @internal */ 1922export function getTsConfigObjectLiteralExpression(tsConfigSourceFile: TsConfigSourceFile | undefined): ObjectLiteralExpression | undefined { 1923 if (tsConfigSourceFile && tsConfigSourceFile.statements.length) { 1924 const expression = tsConfigSourceFile.statements[0].expression; 1925 return tryCast(expression, isObjectLiteralExpression); 1926 } 1927} 1928 1929/** @internal */ 1930export function getTsConfigPropArrayElementValue(tsConfigSourceFile: TsConfigSourceFile | undefined, propKey: string, elementValue: string): StringLiteral | undefined { 1931 return firstDefined(getTsConfigPropArray(tsConfigSourceFile, propKey), property => 1932 isArrayLiteralExpression(property.initializer) ? 1933 find(property.initializer.elements, (element): element is StringLiteral => isStringLiteral(element) && element.text === elementValue) : 1934 undefined); 1935} 1936 1937/** @internal */ 1938export function getTsConfigPropArray(tsConfigSourceFile: TsConfigSourceFile | undefined, propKey: string): readonly PropertyAssignment[] { 1939 const jsonObjectLiteral = getTsConfigObjectLiteralExpression(tsConfigSourceFile); 1940 return jsonObjectLiteral ? getPropertyAssignment(jsonObjectLiteral, propKey) : emptyArray; 1941} 1942 1943/** @internal */ 1944export function getContainingFunction(node: Node): SignatureDeclaration | undefined { 1945 return findAncestor(node.parent, isFunctionLike); 1946} 1947 1948/** @internal */ 1949export function getContainingFunctionDeclaration(node: Node): FunctionLikeDeclaration | undefined { 1950 return findAncestor(node.parent, isFunctionLikeDeclaration); 1951} 1952 1953/** @internal */ 1954export function getContainingClass(node: Node): ClassLikeDeclaration | undefined { 1955 return findAncestor(node.parent, isClassLike); 1956} 1957 1958/** @internal */ 1959export function getContaningConstructorDeclaration(node?: Node): ConstructorDeclaration | undefined { 1960 return node ? findAncestor(node, isConstructorDeclaration): undefined; 1961} 1962/** @internal */ 1963export function getContainingClassStaticBlock(node: Node): Node | undefined { 1964 return findAncestor(node.parent, n => { 1965 if (isClassLike(n) || isFunctionLike(n)) { 1966 return "quit"; 1967 } 1968 return isClassStaticBlockDeclaration(n); 1969 }); 1970} 1971 1972/** @internal */ 1973export function getContainingFunctionOrClassStaticBlock(node: Node): SignatureDeclaration | ClassStaticBlockDeclaration | undefined { 1974 return findAncestor(node.parent, isFunctionLikeOrClassStaticBlockDeclaration); 1975} 1976 1977/** @internal */ 1978export function getContainingStruct(node: Node): StructDeclaration | undefined { 1979 return findAncestor(node.parent, isStruct); 1980} 1981 1982/** @internal */ 1983export function getThisContainer(node: Node, includeArrowFunctions: boolean): Node { 1984 Debug.assert(node.kind !== SyntaxKind.SourceFile); 1985 while (true) { 1986 node = node.parent; 1987 if (!node) { 1988 return Debug.fail(); // If we never pass in a SourceFile, this should be unreachable, since we'll stop when we reach that. 1989 } 1990 switch (node.kind) { 1991 case SyntaxKind.ComputedPropertyName: 1992 // If the grandparent node is an object literal (as opposed to a class), 1993 // then the computed property is not a 'this' container. 1994 // A computed property name in a class needs to be a this container 1995 // so that we can error on it. 1996 if (isClassLike(node.parent.parent)) { 1997 return node; 1998 } 1999 // If this is a computed property, then the parent should not 2000 // make it a this container. The parent might be a property 2001 // in an object literal, like a method or accessor. But in order for 2002 // such a parent to be a this container, the reference must be in 2003 // the *body* of the container. 2004 node = node.parent; 2005 break; 2006 case SyntaxKind.Decorator: 2007 // Decorators are always applied outside of the body of a class or method. 2008 if (node.parent.kind === SyntaxKind.Parameter && isClassElement(node.parent.parent)) { 2009 // If the decorator's parent is a Parameter, we resolve the this container from 2010 // the grandparent class declaration. 2011 node = node.parent.parent; 2012 } 2013 else if (isClassElement(node.parent)) { 2014 // If the decorator's parent is a class element, we resolve the 'this' container 2015 // from the parent class declaration. 2016 node = node.parent; 2017 } 2018 break; 2019 case SyntaxKind.ArrowFunction: 2020 if (!includeArrowFunctions) { 2021 continue; 2022 } 2023 // falls through 2024 2025 case SyntaxKind.FunctionDeclaration: 2026 case SyntaxKind.FunctionExpression: 2027 case SyntaxKind.ModuleDeclaration: 2028 case SyntaxKind.ClassStaticBlockDeclaration: 2029 case SyntaxKind.PropertyDeclaration: 2030 case SyntaxKind.PropertySignature: 2031 case SyntaxKind.MethodDeclaration: 2032 case SyntaxKind.MethodSignature: 2033 case SyntaxKind.Constructor: 2034 case SyntaxKind.GetAccessor: 2035 case SyntaxKind.SetAccessor: 2036 case SyntaxKind.CallSignature: 2037 case SyntaxKind.ConstructSignature: 2038 case SyntaxKind.IndexSignature: 2039 case SyntaxKind.EnumDeclaration: 2040 case SyntaxKind.SourceFile: 2041 return node; 2042 } 2043 } 2044} 2045 2046/** 2047 * @returns Whether the node creates a new 'this' scope for its children. 2048 * 2049 * @internal 2050 */ 2051export function isThisContainerOrFunctionBlock(node: Node): boolean { 2052 switch (node.kind) { 2053 // Arrow functions use the same scope, but may do so in a "delayed" manner 2054 // For example, `const getThis = () => this` may be before a super() call in a derived constructor 2055 case SyntaxKind.ArrowFunction: 2056 case SyntaxKind.FunctionDeclaration: 2057 case SyntaxKind.FunctionExpression: 2058 case SyntaxKind.PropertyDeclaration: 2059 return true; 2060 case SyntaxKind.Block: 2061 switch (node.parent.kind) { 2062 case SyntaxKind.Constructor: 2063 case SyntaxKind.MethodDeclaration: 2064 case SyntaxKind.GetAccessor: 2065 case SyntaxKind.SetAccessor: 2066 // Object properties can have computed names; only method-like bodies start a new scope 2067 return true; 2068 default: 2069 return false; 2070 } 2071 default: 2072 return false; 2073 } 2074} 2075 2076/** @internal */ 2077export function isInTopLevelContext(node: Node) { 2078 // The name of a class or function declaration is a BindingIdentifier in its surrounding scope. 2079 if (isIdentifier(node) && (isClassDeclaration(node.parent) || isFunctionDeclaration(node.parent)) && node.parent.name === node) { 2080 node = node.parent; 2081 } 2082 const container = getThisContainer(node, /*includeArrowFunctions*/ true); 2083 return isSourceFile(container); 2084} 2085 2086/** @internal */ 2087export function getNewTargetContainer(node: Node) { 2088 const container = getThisContainer(node, /*includeArrowFunctions*/ false); 2089 if (container) { 2090 switch (container.kind) { 2091 case SyntaxKind.Constructor: 2092 case SyntaxKind.FunctionDeclaration: 2093 case SyntaxKind.FunctionExpression: 2094 return container; 2095 } 2096 } 2097 2098 return undefined; 2099} 2100 2101/** 2102 * Given an super call/property node, returns the closest node where 2103 * - a super call/property access is legal in the node and not legal in the parent node the node. 2104 * i.e. super call is legal in constructor but not legal in the class body. 2105 * - the container is an arrow function (so caller might need to call getSuperContainer again in case it needs to climb higher) 2106 * - a super call/property is definitely illegal in the container (but might be legal in some subnode) 2107 * i.e. super property access is illegal in function declaration but can be legal in the statement list 2108 * 2109 * @internal 2110 */ 2111export function getSuperContainer(node: Node, stopOnFunctions: boolean): Node { 2112 while (true) { 2113 node = node.parent; 2114 if (!node) { 2115 return node; 2116 } 2117 switch (node.kind) { 2118 case SyntaxKind.ComputedPropertyName: 2119 node = node.parent; 2120 break; 2121 case SyntaxKind.FunctionDeclaration: 2122 case SyntaxKind.FunctionExpression: 2123 case SyntaxKind.ArrowFunction: 2124 if (!stopOnFunctions) { 2125 continue; 2126 } 2127 // falls through 2128 2129 case SyntaxKind.PropertyDeclaration: 2130 case SyntaxKind.PropertySignature: 2131 case SyntaxKind.MethodDeclaration: 2132 case SyntaxKind.MethodSignature: 2133 case SyntaxKind.Constructor: 2134 case SyntaxKind.GetAccessor: 2135 case SyntaxKind.SetAccessor: 2136 case SyntaxKind.ClassStaticBlockDeclaration: 2137 return node; 2138 case SyntaxKind.Decorator: 2139 // Decorators are always applied outside of the body of a class or method. 2140 if (node.parent.kind === SyntaxKind.Parameter && isClassElement(node.parent.parent)) { 2141 // If the decorator's parent is a Parameter, we resolve the this container from 2142 // the grandparent class declaration. 2143 node = node.parent.parent; 2144 } 2145 else if (isClassElement(node.parent)) { 2146 // If the decorator's parent is a class element, we resolve the 'this' container 2147 // from the parent class declaration. 2148 node = node.parent; 2149 } 2150 break; 2151 } 2152 } 2153} 2154 2155/** @internal */ 2156export function getImmediatelyInvokedFunctionExpression(func: Node): CallExpression | undefined { 2157 if (func.kind === SyntaxKind.FunctionExpression || func.kind === SyntaxKind.ArrowFunction) { 2158 let prev = func; 2159 let parent = func.parent; 2160 while (parent.kind === SyntaxKind.ParenthesizedExpression) { 2161 prev = parent; 2162 parent = parent.parent; 2163 } 2164 if (parent.kind === SyntaxKind.CallExpression && (parent as CallExpression).expression === prev) { 2165 return parent as CallExpression; 2166 } 2167 } 2168} 2169 2170/** @internal */ 2171export function isSuperOrSuperProperty(node: Node): node is SuperExpression | SuperProperty { 2172 return node.kind === SyntaxKind.SuperKeyword 2173 || isSuperProperty(node); 2174} 2175 2176/** 2177 * Determines whether a node is a property or element access expression for `super`. 2178 * 2179 * @internal 2180 */ 2181export function isSuperProperty(node: Node): node is SuperProperty { 2182 const kind = node.kind; 2183 return (kind === SyntaxKind.PropertyAccessExpression || kind === SyntaxKind.ElementAccessExpression) 2184 && (node as PropertyAccessExpression | ElementAccessExpression).expression.kind === SyntaxKind.SuperKeyword; 2185} 2186 2187/** 2188 * Determines whether a node is a property or element access expression for `this`. 2189 * 2190 * @internal 2191 */ 2192export function isThisProperty(node: Node): boolean { 2193 const kind = node.kind; 2194 return (kind === SyntaxKind.PropertyAccessExpression || kind === SyntaxKind.ElementAccessExpression) 2195 && (node as PropertyAccessExpression | ElementAccessExpression).expression.kind === SyntaxKind.ThisKeyword; 2196} 2197 2198/** @internal */ 2199export function isThisInitializedDeclaration(node: Node | undefined): boolean { 2200 return !!node && isVariableDeclaration(node) && node.initializer?.kind === SyntaxKind.ThisKeyword; 2201} 2202 2203/** @internal */ 2204export function isThisInitializedObjectBindingExpression(node: Node | undefined): boolean { 2205 return !!node 2206 && (isShorthandPropertyAssignment(node) || isPropertyAssignment(node)) 2207 && isBinaryExpression(node.parent.parent) 2208 && node.parent.parent.operatorToken.kind === SyntaxKind.EqualsToken 2209 && node.parent.parent.right.kind === SyntaxKind.ThisKeyword; 2210} 2211 2212/** @internal */ 2213export function getEntityNameFromTypeNode(node: TypeNode): EntityNameOrEntityNameExpression | undefined { 2214 switch (node.kind) { 2215 case SyntaxKind.TypeReference: 2216 return (node as TypeReferenceNode).typeName; 2217 2218 case SyntaxKind.ExpressionWithTypeArguments: 2219 return isEntityNameExpression((node as ExpressionWithTypeArguments).expression) 2220 ? (node as ExpressionWithTypeArguments).expression as EntityNameExpression 2221 : undefined; 2222 2223 // TODO(rbuckton): These aren't valid TypeNodes, but we treat them as such because of `isPartOfTypeNode`, which returns `true` for things that aren't `TypeNode`s. 2224 case SyntaxKind.Identifier as TypeNodeSyntaxKind: 2225 case SyntaxKind.QualifiedName as TypeNodeSyntaxKind: 2226 return (node as Node as EntityName); 2227 } 2228 2229 return undefined; 2230} 2231 2232/** @internal */ 2233export function getInvokedExpression(node: CallLikeExpression): Expression { 2234 switch (node.kind) { 2235 case SyntaxKind.TaggedTemplateExpression: 2236 return node.tag; 2237 case SyntaxKind.JsxOpeningElement: 2238 case SyntaxKind.JsxSelfClosingElement: 2239 return node.tagName; 2240 default: 2241 return node.expression; 2242 } 2243} 2244 2245/** @internal */ 2246export function nodeCanBeDecorated(node: ClassDeclaration): true; 2247/** @internal */ 2248export function nodeCanBeDecorated(node: ClassElement, parent: Node): boolean; 2249/** @internal */ 2250export function nodeCanBeDecorated(node: Node, parent: Node, grandparent: Node, compilerOptions: CompilerOptions): boolean; 2251/** @internal */ 2252export function nodeCanBeDecorated(node: Node, parent: Node, grandparent: Node, compilerOptions?: CompilerOptions): boolean; 2253/** @internal */ 2254export function nodeCanBeDecorated(node: Node, parent?: Node, grandparent?: Node, compilerOptions?: CompilerOptions): boolean { 2255 // private names cannot be used with decorators yet 2256 if (isNamedDeclaration(node) && isPrivateIdentifier(node.name)) { 2257 return false; 2258 } 2259 switch (node.kind) { 2260 case SyntaxKind.ClassDeclaration: 2261 case SyntaxKind.StructDeclaration: 2262 // classes are valid targets 2263 return true; 2264 2265 case SyntaxKind.PropertyDeclaration: 2266 // property declarations are valid if their parent is a class declaration. 2267 return parent!.kind === SyntaxKind.ClassDeclaration || parent!.kind === SyntaxKind.StructDeclaration; 2268 2269 case SyntaxKind.GetAccessor: 2270 case SyntaxKind.SetAccessor: 2271 case SyntaxKind.MethodDeclaration: 2272 // if this method has a body and its parent is a class declaration, this is a valid target. 2273 return (node as FunctionLikeDeclaration).body !== undefined 2274 && (parent!.kind === SyntaxKind.ClassDeclaration || parent!.kind === SyntaxKind.StructDeclaration); 2275 2276 case SyntaxKind.Parameter: 2277 // if the parameter's parent has a body and its grandparent is a class declaration, this is a valid target; 2278 return (parent as FunctionLikeDeclaration).body !== undefined 2279 && (parent!.kind === SyntaxKind.Constructor 2280 || parent!.kind === SyntaxKind.MethodDeclaration 2281 || parent!.kind === SyntaxKind.SetAccessor) 2282 && (grandparent!.kind === SyntaxKind.ClassDeclaration || grandparent!.kind === SyntaxKind.StructDeclaration); 2283 case SyntaxKind.FunctionDeclaration: 2284 return isArkTsDecorator(node, compilerOptions) || isSendableFunctionOrType(node); 2285 case SyntaxKind.TypeAliasDeclaration: 2286 return isSendableFunctionOrType(node); 2287 } 2288 2289 return false; 2290} 2291 2292/** @internal */ 2293export function nodeIsDecorated(node: ClassDeclaration): boolean; 2294/** @internal */ 2295export function nodeIsDecorated(node: ClassElement, parent: Node): boolean; 2296/** @internal */ 2297export function nodeIsDecorated(node: Node, parent: Node, grandparent: Node): boolean; 2298/** @internal */ 2299export function nodeIsDecorated(node: Node, parent?: Node, grandparent?: Node): boolean { 2300 return hasDecorators(node) 2301 && nodeCanBeDecorated(node, parent!, grandparent!); // TODO: GH#18217 2302} 2303 2304/** @internal */ 2305export function nodeOrChildIsDecorated(node: ClassDeclaration): boolean; 2306/** @internal */ 2307export function nodeOrChildIsDecorated(node: ClassElement, parent: Node): boolean; 2308/** @internal */ 2309export function nodeOrChildIsDecorated(node: Node, parent: Node, grandparent: Node): boolean; 2310/** @internal */ 2311export function nodeOrChildIsDecorated(node: Node, parent?: Node, grandparent?: Node): boolean { 2312 return nodeIsDecorated(node, parent!, grandparent!) || childIsDecorated(node, parent!); // TODO: GH#18217 2313} 2314 2315/** @internal */ 2316export function childIsDecorated(node: ClassDeclaration): boolean; 2317/** @internal */ 2318export function childIsDecorated(node: Node, parent: Node): boolean; 2319/** @internal */ 2320export function childIsDecorated(node: Node, parent?: Node): boolean { 2321 switch (node.kind) { 2322 case SyntaxKind.ClassDeclaration: 2323 return some((node as ClassDeclaration).members, m => nodeOrChildIsDecorated(m, node, parent!)); // TODO: GH#18217 2324 case SyntaxKind.StructDeclaration: 2325 return some((node as StructDeclaration).members, m => nodeOrChildIsDecorated(m, node, parent!)); // TODO: GH#18217 2326 case SyntaxKind.MethodDeclaration: 2327 case SyntaxKind.SetAccessor: 2328 case SyntaxKind.Constructor: 2329 return some((node as FunctionLikeDeclaration).parameters, p => nodeIsDecorated(p, node, parent!)); // TODO: GH#18217 2330 default: 2331 return false; 2332 } 2333} 2334 2335/** @internal */ 2336export function classOrConstructorParameterIsDecorated(node: ClassDeclaration): boolean { 2337 if (nodeIsDecorated(node)) return true; 2338 const constructor = getFirstConstructorWithBody(node); 2339 return !!constructor && childIsDecorated(constructor, node); 2340} 2341 2342/** @internal */ 2343export function isJSXTagName(node: Node) { 2344 const { parent } = node; 2345 if (parent.kind === SyntaxKind.JsxOpeningElement || 2346 parent.kind === SyntaxKind.JsxSelfClosingElement || 2347 parent.kind === SyntaxKind.JsxClosingElement) { 2348 return (parent as JsxOpeningLikeElement).tagName === node; 2349 } 2350 return false; 2351} 2352 2353/** @internal */ 2354export function isExpressionNode(node: Node): boolean { 2355 switch (node.kind) { 2356 case SyntaxKind.SuperKeyword: 2357 case SyntaxKind.NullKeyword: 2358 case SyntaxKind.TrueKeyword: 2359 case SyntaxKind.FalseKeyword: 2360 case SyntaxKind.RegularExpressionLiteral: 2361 case SyntaxKind.ArrayLiteralExpression: 2362 case SyntaxKind.ObjectLiteralExpression: 2363 case SyntaxKind.PropertyAccessExpression: 2364 case SyntaxKind.EtsComponentExpression: 2365 case SyntaxKind.ElementAccessExpression: 2366 case SyntaxKind.CallExpression: 2367 case SyntaxKind.NewExpression: 2368 case SyntaxKind.TaggedTemplateExpression: 2369 case SyntaxKind.AsExpression: 2370 case SyntaxKind.TypeAssertionExpression: 2371 case SyntaxKind.SatisfiesExpression: 2372 case SyntaxKind.NonNullExpression: 2373 case SyntaxKind.ParenthesizedExpression: 2374 case SyntaxKind.FunctionExpression: 2375 case SyntaxKind.ClassExpression: 2376 case SyntaxKind.ArrowFunction: 2377 case SyntaxKind.VoidExpression: 2378 case SyntaxKind.DeleteExpression: 2379 case SyntaxKind.TypeOfExpression: 2380 case SyntaxKind.PrefixUnaryExpression: 2381 case SyntaxKind.PostfixUnaryExpression: 2382 case SyntaxKind.BinaryExpression: 2383 case SyntaxKind.ConditionalExpression: 2384 case SyntaxKind.SpreadElement: 2385 case SyntaxKind.TemplateExpression: 2386 case SyntaxKind.OmittedExpression: 2387 case SyntaxKind.JsxElement: 2388 case SyntaxKind.JsxSelfClosingElement: 2389 case SyntaxKind.JsxFragment: 2390 case SyntaxKind.YieldExpression: 2391 case SyntaxKind.AwaitExpression: 2392 case SyntaxKind.MetaProperty: 2393 return true; 2394 case SyntaxKind.ExpressionWithTypeArguments: 2395 return !isHeritageClause(node.parent); 2396 case SyntaxKind.QualifiedName: 2397 while (node.parent.kind === SyntaxKind.QualifiedName) { 2398 node = node.parent; 2399 } 2400 return node.parent.kind === SyntaxKind.TypeQuery || isJSDocLinkLike(node.parent) || isJSDocNameReference(node.parent) || isJSDocMemberName(node.parent) || isJSXTagName(node); 2401 case SyntaxKind.JSDocMemberName: 2402 while (isJSDocMemberName(node.parent)) { 2403 node = node.parent; 2404 } 2405 return node.parent.kind === SyntaxKind.TypeQuery || isJSDocLinkLike(node.parent) || isJSDocNameReference(node.parent) || isJSDocMemberName(node.parent) || isJSXTagName(node); 2406 case SyntaxKind.PrivateIdentifier: 2407 return isBinaryExpression(node.parent) && node.parent.left === node && node.parent.operatorToken.kind === SyntaxKind.InKeyword; 2408 case SyntaxKind.Identifier: 2409 if (node.parent.kind === SyntaxKind.TypeQuery || isJSDocLinkLike(node.parent) || isJSDocNameReference(node.parent) || isJSDocMemberName(node.parent) || isJSXTagName(node)) { 2410 return true; 2411 } 2412 // falls through 2413 2414 case SyntaxKind.NumericLiteral: 2415 case SyntaxKind.BigIntLiteral: 2416 case SyntaxKind.StringLiteral: 2417 case SyntaxKind.NoSubstitutionTemplateLiteral: 2418 case SyntaxKind.ThisKeyword: 2419 return isInExpressionContext(node); 2420 default: 2421 return false; 2422 } 2423} 2424 2425/** @internal */ 2426export function isInExpressionContext(node: Node): boolean { 2427 const { parent } = node; 2428 switch (parent.kind) { 2429 case SyntaxKind.VariableDeclaration: 2430 case SyntaxKind.Parameter: 2431 case SyntaxKind.PropertyDeclaration: 2432 case SyntaxKind.PropertySignature: 2433 case SyntaxKind.EnumMember: 2434 case SyntaxKind.PropertyAssignment: 2435 case SyntaxKind.BindingElement: 2436 return (parent as HasInitializer).initializer === node; 2437 case SyntaxKind.ExpressionStatement: 2438 case SyntaxKind.IfStatement: 2439 case SyntaxKind.DoStatement: 2440 case SyntaxKind.WhileStatement: 2441 case SyntaxKind.ReturnStatement: 2442 case SyntaxKind.WithStatement: 2443 case SyntaxKind.SwitchStatement: 2444 case SyntaxKind.CaseClause: 2445 case SyntaxKind.ThrowStatement: 2446 return (parent as ExpressionStatement).expression === node; 2447 case SyntaxKind.ForStatement: 2448 const forStatement = parent as ForStatement; 2449 return (forStatement.initializer === node && forStatement.initializer.kind !== SyntaxKind.VariableDeclarationList) || 2450 forStatement.condition === node || 2451 forStatement.incrementor === node; 2452 case SyntaxKind.ForInStatement: 2453 case SyntaxKind.ForOfStatement: 2454 const forInStatement = parent as ForInStatement | ForOfStatement; 2455 return (forInStatement.initializer === node && forInStatement.initializer.kind !== SyntaxKind.VariableDeclarationList) || 2456 forInStatement.expression === node; 2457 case SyntaxKind.TypeAssertionExpression: 2458 case SyntaxKind.AsExpression: 2459 return node === (parent as AssertionExpression).expression; 2460 case SyntaxKind.TemplateSpan: 2461 return node === (parent as TemplateSpan).expression; 2462 case SyntaxKind.ComputedPropertyName: 2463 return node === (parent as ComputedPropertyName).expression; 2464 case SyntaxKind.Decorator: 2465 case SyntaxKind.JsxExpression: 2466 case SyntaxKind.JsxSpreadAttribute: 2467 case SyntaxKind.SpreadAssignment: 2468 return true; 2469 case SyntaxKind.ExpressionWithTypeArguments: 2470 return (parent as ExpressionWithTypeArguments).expression === node && !isPartOfTypeNode(parent); 2471 case SyntaxKind.ShorthandPropertyAssignment: 2472 return (parent as ShorthandPropertyAssignment).objectAssignmentInitializer === node; 2473 case SyntaxKind.SatisfiesExpression: 2474 return node === (parent as SatisfiesExpression).expression; 2475 default: 2476 return isExpressionNode(parent); 2477 } 2478} 2479 2480/** @internal */ 2481export function isPartOfTypeQuery(node: Node) { 2482 while (node.kind === SyntaxKind.QualifiedName || node.kind === SyntaxKind.Identifier) { 2483 node = node.parent; 2484 } 2485 return node.kind === SyntaxKind.TypeQuery; 2486} 2487 2488/** @internal */ 2489export function isNamespaceReexportDeclaration(node: Node): boolean { 2490 return isNamespaceExport(node) && !!node.parent.moduleSpecifier; 2491} 2492 2493/** @internal */ 2494export function isExternalModuleImportEqualsDeclaration(node: Node): node is ImportEqualsDeclaration & { moduleReference: ExternalModuleReference } { 2495 return node.kind === SyntaxKind.ImportEqualsDeclaration && (node as ImportEqualsDeclaration).moduleReference.kind === SyntaxKind.ExternalModuleReference; 2496} 2497 2498/** @internal */ 2499export function getExternalModuleImportEqualsDeclarationExpression(node: Node) { 2500 Debug.assert(isExternalModuleImportEqualsDeclaration(node)); 2501 return ((node as ImportEqualsDeclaration).moduleReference as ExternalModuleReference).expression; 2502} 2503 2504/** @internal */ 2505export function getExternalModuleRequireArgument(node: Node) { 2506 return isVariableDeclarationInitializedToBareOrAccessedRequire(node) && (getLeftmostAccessExpression(node.initializer) as CallExpression).arguments[0] as StringLiteral; 2507} 2508 2509/** @internal */ 2510export function isInternalModuleImportEqualsDeclaration(node: Node): node is ImportEqualsDeclaration { 2511 return node.kind === SyntaxKind.ImportEqualsDeclaration && (node as ImportEqualsDeclaration).moduleReference.kind !== SyntaxKind.ExternalModuleReference; 2512} 2513 2514/** @internal */ 2515export function isSourceFileJS(file: SourceFile): boolean { 2516 return isInJSFile(file); 2517} 2518 2519/** @internal */ 2520export function isSourceFileNotJS(file: SourceFile): boolean { 2521 return !isInJSFile(file); 2522} 2523 2524/** @internal */ 2525export function isInJSFile(node: Node | undefined): boolean { 2526 return !!node && !!(node.flags & NodeFlags.JavaScriptFile); 2527} 2528 2529/** @internal */ 2530export function isInJsonFile(node: Node | undefined): boolean { 2531 return !!node && !!(node.flags & NodeFlags.JsonFile); 2532} 2533 2534/** @internal */ 2535export function isSourceFileNotJson(file: SourceFile) { 2536 return !isJsonSourceFile(file); 2537} 2538 2539/** @internal */ 2540export function isInJSDoc(node: Node | undefined): boolean { 2541 return !!node && !!(node.flags & NodeFlags.JSDoc); 2542} 2543 2544/** @internal */ 2545export function isJSDocIndexSignature(node: TypeReferenceNode | ExpressionWithTypeArguments) { 2546 return isTypeReferenceNode(node) && 2547 isIdentifier(node.typeName) && 2548 node.typeName.escapedText === "Object" && 2549 node.typeArguments && node.typeArguments.length === 2 && 2550 (node.typeArguments[0].kind === SyntaxKind.StringKeyword || node.typeArguments[0].kind === SyntaxKind.NumberKeyword); 2551} 2552 2553/** @internal */ 2554export function isInETSFile(node: Node | undefined): boolean { 2555 return !!node && getSourceFileOfNode(node).scriptKind === ScriptKind.ETS; 2556} 2557 2558/** @internal */ 2559export function isInBuildOrPageTransitionContext(node: Node | undefined, compilerOptions: CompilerOptions): boolean { 2560 if (!node) { 2561 return false; 2562 } 2563 const methodNames = compilerOptions.ets?.render?.method; 2564 const decoratorNames = compilerOptions.ets?.render?.decorator; 2565 if (!methodNames && !decoratorNames) { 2566 return false; 2567 } 2568 2569 let container = getContainingFunctionDeclaration(node); 2570 while (container) { 2571 // check if is in build or pageTransition method 2572 if (methodNames && isMethodDeclaration(container) && isInStruct(container)) { 2573 const containerMethodName = getTextOfPropertyName(container.name).toString(); 2574 if (methodNames.some(name => name === containerMethodName)) { 2575 return true; 2576 } 2577 } 2578 2579 // check if is in function or method with the decorator "@Builder @LocalBuilder" 2580 const decorators = getAllDecorators(container); 2581 if (decoratorNames && decoratorNames.length && 2582 (isMethodDeclaration(container) || isFunctionDeclaration(container)) && 2583 decorators && decorators.some( 2584 decorator => isIdentifier(decorator.expression) && decoratorNames.includes(getTextOfPropertyName(decorator.expression).toString()))) { 2585 return true; 2586 } 2587 2588 container = getContainingFunctionDeclaration(container); 2589 } 2590 2591 return false; 2592} 2593 2594function isInStruct(node: MethodDeclaration): boolean { 2595 const container = getContainingClass(node); 2596 return !!container && isStruct(container); 2597} 2598 2599/** 2600 * Returns true if the node is a CallExpression to the identifier 'require' with 2601 * exactly one argument (of the form 'require("name")'). 2602 * This function does not test if the node is in a JavaScript file or not. 2603 * 2604 * @internal 2605 */ 2606export function isRequireCall(callExpression: Node, requireStringLiteralLikeArgument: true): callExpression is RequireOrImportCall & { expression: Identifier, arguments: [StringLiteralLike] }; 2607/** @internal */ 2608export function isRequireCall(callExpression: Node, requireStringLiteralLikeArgument: boolean): callExpression is CallExpression; 2609/** @internal */ 2610export function isRequireCall(callExpression: Node, requireStringLiteralLikeArgument: boolean): callExpression is CallExpression { 2611 if (callExpression.kind !== SyntaxKind.CallExpression) { 2612 return false; 2613 } 2614 const { expression, arguments: args } = callExpression as CallExpression; 2615 2616 if (expression.kind !== SyntaxKind.Identifier || (expression as Identifier).escapedText !== "require") { 2617 return false; 2618 } 2619 2620 if (args.length !== 1) { 2621 return false; 2622 } 2623 const arg = args[0]; 2624 return !requireStringLiteralLikeArgument || isStringLiteralLike(arg); 2625} 2626 2627/** 2628 * Returns true if the node is a VariableDeclaration initialized to a require call (see `isRequireCall`). 2629 * This function does not test if the node is in a JavaScript file or not. 2630 * 2631 * @internal 2632 */ 2633export function isVariableDeclarationInitializedToRequire(node: Node): node is VariableDeclarationInitializedTo<RequireOrImportCall> { 2634 return isVariableDeclarationInitializedWithRequireHelper(node, /*allowAccessedRequire*/ false); 2635} 2636 2637/** 2638 * Like {@link isVariableDeclarationInitializedToRequire} but allows things like `require("...").foo.bar` or `require("...")["baz"]`. 2639 * 2640 * @internal 2641 */ 2642export function isVariableDeclarationInitializedToBareOrAccessedRequire(node: Node): node is VariableDeclarationInitializedTo<RequireOrImportCall | AccessExpression> { 2643 return isVariableDeclarationInitializedWithRequireHelper(node, /*allowAccessedRequire*/ true); 2644} 2645 2646function isVariableDeclarationInitializedWithRequireHelper(node: Node, allowAccessedRequire: boolean) { 2647 return isVariableDeclaration(node) && 2648 !!node.initializer && 2649 isRequireCall(allowAccessedRequire ? getLeftmostAccessExpression(node.initializer) : node.initializer, /*requireStringLiteralLikeArgument*/ true); 2650} 2651 2652/** @internal */ 2653export function isRequireVariableStatement(node: Node): node is RequireVariableStatement { 2654 return isVariableStatement(node) 2655 && node.declarationList.declarations.length > 0 2656 && every(node.declarationList.declarations, decl => isVariableDeclarationInitializedToRequire(decl)); 2657} 2658 2659/** @internal */ 2660export function isSingleOrDoubleQuote(charCode: number) { 2661 return charCode === CharacterCodes.singleQuote || charCode === CharacterCodes.doubleQuote; 2662} 2663 2664/** @internal */ 2665export function isStringDoubleQuoted(str: StringLiteralLike, sourceFile: SourceFile): boolean { 2666 return getSourceTextOfNodeFromSourceFile(sourceFile, str).charCodeAt(0) === CharacterCodes.doubleQuote; 2667} 2668 2669/** @internal */ 2670export function isAssignmentDeclaration(decl: Declaration) { 2671 return isBinaryExpression(decl) || isAccessExpression(decl) || isIdentifier(decl) || isCallExpression(decl); 2672} 2673 2674/** 2675 * Get the initializer, taking into account defaulted Javascript initializers 2676 * 2677 * @internal 2678 */ 2679export function getEffectiveInitializer(node: HasExpressionInitializer) { 2680 if (isInJSFile(node) && node.initializer && 2681 isBinaryExpression(node.initializer) && 2682 (node.initializer.operatorToken.kind === SyntaxKind.BarBarToken || node.initializer.operatorToken.kind === SyntaxKind.QuestionQuestionToken) && 2683 node.name && isEntityNameExpression(node.name) && isSameEntityName(node.name, node.initializer.left)) { 2684 return node.initializer.right; 2685 } 2686 return node.initializer; 2687} 2688 2689/** 2690 * Get the declaration initializer when it is container-like (See getExpandoInitializer). 2691 * 2692 * @internal 2693 */ 2694export function getDeclaredExpandoInitializer(node: HasExpressionInitializer) { 2695 const init = getEffectiveInitializer(node); 2696 return init && getExpandoInitializer(init, isPrototypeAccess(node.name)); 2697} 2698 2699function hasExpandoValueProperty(node: ObjectLiteralExpression, isPrototypeAssignment: boolean) { 2700 return forEach(node.properties, p => 2701 isPropertyAssignment(p) && 2702 isIdentifier(p.name) && 2703 p.name.escapedText === "value" && 2704 p.initializer && 2705 getExpandoInitializer(p.initializer, isPrototypeAssignment)); 2706} 2707 2708/** 2709 * Get the assignment 'initializer' -- the righthand side-- when the initializer is container-like (See getExpandoInitializer). 2710 * We treat the right hand side of assignments with container-like initializers as declarations. 2711 * 2712 * @internal 2713 */ 2714export function getAssignedExpandoInitializer(node: Node | undefined): Expression | undefined { 2715 if (node && node.parent && isBinaryExpression(node.parent) && node.parent.operatorToken.kind === SyntaxKind.EqualsToken) { 2716 const isPrototypeAssignment = isPrototypeAccess(node.parent.left); 2717 return getExpandoInitializer(node.parent.right, isPrototypeAssignment) || 2718 getDefaultedExpandoInitializer(node.parent.left, node.parent.right, isPrototypeAssignment); 2719 } 2720 if (node && isCallExpression(node) && isBindableObjectDefinePropertyCall(node)) { 2721 const result = hasExpandoValueProperty(node.arguments[2], node.arguments[1].text === "prototype"); 2722 if (result) { 2723 return result; 2724 } 2725 } 2726} 2727 2728/** 2729 * Recognized expando initializers are: 2730 * 1. (function() {})() -- IIFEs 2731 * 2. function() { } -- Function expressions 2732 * 3. class { } -- Class expressions 2733 * 4. {} -- Empty object literals 2734 * 5. { ... } -- Non-empty object literals, when used to initialize a prototype, like `C.prototype = { m() { } }` 2735 * 2736 * This function returns the provided initializer, or undefined if it is not valid. 2737 * 2738 * @internal 2739 */ 2740export function getExpandoInitializer(initializer: Node, isPrototypeAssignment: boolean): Expression | undefined { 2741 if (isCallExpression(initializer)) { 2742 const e = skipParentheses(initializer.expression); 2743 return e.kind === SyntaxKind.FunctionExpression || e.kind === SyntaxKind.ArrowFunction ? initializer : undefined; 2744 } 2745 if (initializer.kind === SyntaxKind.FunctionExpression || 2746 initializer.kind === SyntaxKind.ClassExpression || 2747 initializer.kind === SyntaxKind.ArrowFunction) { 2748 return initializer as Expression; 2749 } 2750 if (isObjectLiteralExpression(initializer) && (initializer.properties.length === 0 || isPrototypeAssignment)) { 2751 return initializer; 2752 } 2753} 2754 2755/** 2756 * A defaulted expando initializer matches the pattern 2757 * `Lhs = Lhs || ExpandoInitializer` 2758 * or `var Lhs = Lhs || ExpandoInitializer` 2759 * 2760 * The second Lhs is required to be the same as the first except that it may be prefixed with 2761 * 'window.', 'global.' or 'self.' The second Lhs is otherwise ignored by the binder and checker. 2762 */ 2763function getDefaultedExpandoInitializer(name: Expression, initializer: Expression, isPrototypeAssignment: boolean) { 2764 const e = isBinaryExpression(initializer) 2765 && (initializer.operatorToken.kind === SyntaxKind.BarBarToken || initializer.operatorToken.kind === SyntaxKind.QuestionQuestionToken) 2766 && getExpandoInitializer(initializer.right, isPrototypeAssignment); 2767 if (e && isSameEntityName(name, initializer.left)) { 2768 return e; 2769 } 2770} 2771 2772/** @internal */ 2773export function isDefaultedExpandoInitializer(node: BinaryExpression) { 2774 const name = isVariableDeclaration(node.parent) ? node.parent.name : 2775 isBinaryExpression(node.parent) && node.parent.operatorToken.kind === SyntaxKind.EqualsToken ? node.parent.left : 2776 undefined; 2777 return name && getExpandoInitializer(node.right, isPrototypeAccess(name)) && isEntityNameExpression(name) && isSameEntityName(name, node.left); 2778} 2779 2780/** 2781 * Given an expando initializer, return its declaration name, or the left-hand side of the assignment if it's part of an assignment declaration. 2782 * 2783 * @internal 2784 */ 2785export function getNameOfExpando(node: Declaration): DeclarationName | undefined { 2786 if (isBinaryExpression(node.parent)) { 2787 const parent = ((node.parent.operatorToken.kind === SyntaxKind.BarBarToken || node.parent.operatorToken.kind === SyntaxKind.QuestionQuestionToken) && isBinaryExpression(node.parent.parent)) ? node.parent.parent : node.parent; 2788 if (parent.operatorToken.kind === SyntaxKind.EqualsToken && isIdentifier(parent.left)) { 2789 return parent.left; 2790 } 2791 } 2792 else if (isVariableDeclaration(node.parent)) { 2793 return node.parent.name; 2794 } 2795} 2796 2797/** 2798 * Is the 'declared' name the same as the one in the initializer? 2799 * @return true for identical entity names, as well as ones where the initializer is prefixed with 2800 * 'window', 'self' or 'global'. For example: 2801 * 2802 * var my = my || {} 2803 * var min = window.min || {} 2804 * my.app = self.my.app || class { } 2805 * 2806 * @internal 2807 */ 2808export function isSameEntityName(name: Expression, initializer: Expression): boolean { 2809 if (isPropertyNameLiteral(name) && isPropertyNameLiteral(initializer)) { 2810 return getTextOfIdentifierOrLiteral(name) === getTextOfIdentifierOrLiteral(initializer); 2811 } 2812 if (isMemberName(name) && isLiteralLikeAccess(initializer) && 2813 (initializer.expression.kind === SyntaxKind.ThisKeyword || 2814 isIdentifier(initializer.expression) && 2815 (initializer.expression.escapedText === "window" || 2816 initializer.expression.escapedText === "self" || 2817 initializer.expression.escapedText === "global"))) { 2818 return isSameEntityName(name, getNameOrArgument(initializer)); 2819 } 2820 if (isLiteralLikeAccess(name) && isLiteralLikeAccess(initializer)) { 2821 return getElementOrPropertyAccessName(name) === getElementOrPropertyAccessName(initializer) 2822 && isSameEntityName(name.expression, initializer.expression); 2823 } 2824 return false; 2825} 2826 2827/** @internal */ 2828export function getRightMostAssignedExpression(node: Expression): Expression { 2829 while (isAssignmentExpression(node, /*excludeCompoundAssignments*/ true)) { 2830 node = node.right; 2831 } 2832 return node; 2833} 2834 2835/** @internal */ 2836export function isExportsIdentifier(node: Node) { 2837 return isIdentifier(node) && node.escapedText === "exports"; 2838} 2839 2840/** @internal */ 2841export function isModuleIdentifier(node: Node) { 2842 return isIdentifier(node) && node.escapedText === "module"; 2843} 2844 2845/** @internal */ 2846export function isModuleExportsAccessExpression(node: Node): node is LiteralLikeElementAccessExpression & { expression: Identifier } { 2847 return (isPropertyAccessExpression(node) || isLiteralLikeElementAccess(node)) 2848 && isModuleIdentifier(node.expression) 2849 && getElementOrPropertyAccessName(node) === "exports"; 2850} 2851 2852/// Given a BinaryExpression, returns SpecialPropertyAssignmentKind for the various kinds of property 2853/// assignments we treat as special in the binder 2854/** @internal */ 2855export function getAssignmentDeclarationKind(expr: BinaryExpression | CallExpression): AssignmentDeclarationKind { 2856 const special = getAssignmentDeclarationKindWorker(expr); 2857 return special === AssignmentDeclarationKind.Property || isInJSFile(expr) ? special : AssignmentDeclarationKind.None; 2858} 2859 2860/** @internal */ 2861export function isBindableObjectDefinePropertyCall(expr: CallExpression): expr is BindableObjectDefinePropertyCall { 2862 return length(expr.arguments) === 3 && 2863 isPropertyAccessExpression(expr.expression) && 2864 isIdentifier(expr.expression.expression) && 2865 idText(expr.expression.expression) === "Object" && 2866 idText(expr.expression.name) === "defineProperty" && 2867 isStringOrNumericLiteralLike(expr.arguments[1]) && 2868 isBindableStaticNameExpression(expr.arguments[0], /*excludeThisKeyword*/ true); 2869} 2870 2871/** 2872 * x.y OR x[0] 2873 * 2874 * @internal 2875 */ 2876export function isLiteralLikeAccess(node: Node): node is LiteralLikeElementAccessExpression | PropertyAccessExpression { 2877 return isPropertyAccessExpression(node) || isLiteralLikeElementAccess(node); 2878} 2879 2880/** 2881 * x[0] OR x['a'] OR x[Symbol.y] 2882 * 2883 * @internal 2884 */ 2885export function isLiteralLikeElementAccess(node: Node): node is LiteralLikeElementAccessExpression { 2886 return isElementAccessExpression(node) && isStringOrNumericLiteralLike(node.argumentExpression); 2887} 2888 2889/** 2890 * Any series of property and element accesses. 2891 * 2892 * @internal 2893 */ 2894export function isBindableStaticAccessExpression(node: Node, excludeThisKeyword?: boolean): node is BindableStaticAccessExpression { 2895 return isPropertyAccessExpression(node) && (!excludeThisKeyword && node.expression.kind === SyntaxKind.ThisKeyword || isIdentifier(node.name) && isBindableStaticNameExpression(node.expression, /*excludeThisKeyword*/ true)) 2896 || isBindableStaticElementAccessExpression(node, excludeThisKeyword); 2897} 2898 2899/** 2900 * Any series of property and element accesses, ending in a literal element access 2901 * 2902 * @internal 2903 */ 2904export function isBindableStaticElementAccessExpression(node: Node, excludeThisKeyword?: boolean): node is BindableStaticElementAccessExpression { 2905 return isLiteralLikeElementAccess(node) 2906 && ((!excludeThisKeyword && node.expression.kind === SyntaxKind.ThisKeyword) || 2907 isEntityNameExpression(node.expression) || 2908 isBindableStaticAccessExpression(node.expression, /*excludeThisKeyword*/ true)); 2909} 2910 2911/** @internal */ 2912export function isBindableStaticNameExpression(node: Node, excludeThisKeyword?: boolean): node is BindableStaticNameExpression { 2913 return isEntityNameExpression(node) || isBindableStaticAccessExpression(node, excludeThisKeyword); 2914} 2915 2916/** @internal */ 2917export function getNameOrArgument(expr: PropertyAccessExpression | LiteralLikeElementAccessExpression) { 2918 if (isPropertyAccessExpression(expr)) { 2919 return expr.name; 2920 } 2921 return expr.argumentExpression; 2922} 2923 2924function getAssignmentDeclarationKindWorker(expr: BinaryExpression | CallExpression): AssignmentDeclarationKind { 2925 if (isCallExpression(expr)) { 2926 if (!isBindableObjectDefinePropertyCall(expr)) { 2927 return AssignmentDeclarationKind.None; 2928 } 2929 const entityName = expr.arguments[0]; 2930 if (isExportsIdentifier(entityName) || isModuleExportsAccessExpression(entityName)) { 2931 return AssignmentDeclarationKind.ObjectDefinePropertyExports; 2932 } 2933 if (isBindableStaticAccessExpression(entityName) && getElementOrPropertyAccessName(entityName) === "prototype") { 2934 return AssignmentDeclarationKind.ObjectDefinePrototypeProperty; 2935 } 2936 return AssignmentDeclarationKind.ObjectDefinePropertyValue; 2937 } 2938 if (expr.operatorToken.kind !== SyntaxKind.EqualsToken || !isAccessExpression(expr.left) || isVoidZero(getRightMostAssignedExpression(expr))) { 2939 return AssignmentDeclarationKind.None; 2940 } 2941 if (isBindableStaticNameExpression(expr.left.expression, /*excludeThisKeyword*/ true) && getElementOrPropertyAccessName(expr.left) === "prototype" && isObjectLiteralExpression(getInitializerOfBinaryExpression(expr))) { 2942 // F.prototype = { ... } 2943 return AssignmentDeclarationKind.Prototype; 2944 } 2945 return getAssignmentDeclarationPropertyAccessKind(expr.left); 2946} 2947 2948function isVoidZero(node: Node) { 2949 return isVoidExpression(node) && isNumericLiteral(node.expression) && node.expression.text === "0"; 2950} 2951 2952/** 2953 * Does not handle signed numeric names like `a[+0]` - handling those would require handling prefix unary expressions 2954 * throughout late binding handling as well, which is awkward (but ultimately probably doable if there is demand) 2955 * 2956 * @internal 2957 */ 2958export function getElementOrPropertyAccessArgumentExpressionOrName(node: AccessExpression): Identifier | PrivateIdentifier | StringLiteralLike | NumericLiteral | ElementAccessExpression | undefined { 2959 if (isPropertyAccessExpression(node)) { 2960 return node.name; 2961 } 2962 const arg = skipParentheses(node.argumentExpression); 2963 if (isNumericLiteral(arg) || isStringLiteralLike(arg)) { 2964 return arg; 2965 } 2966 return node; 2967} 2968 2969/** @internal */ 2970export function getElementOrPropertyAccessName(node: LiteralLikeElementAccessExpression | PropertyAccessExpression): __String; 2971/** @internal */ 2972export function getElementOrPropertyAccessName(node: AccessExpression): __String | undefined; 2973/** @internal */ 2974export function getElementOrPropertyAccessName(node: AccessExpression): __String | undefined { 2975 const name = getElementOrPropertyAccessArgumentExpressionOrName(node); 2976 if (name) { 2977 if (isIdentifier(name)) { 2978 return name.escapedText; 2979 } 2980 if (isStringLiteralLike(name) || isNumericLiteral(name)) { 2981 return escapeLeadingUnderscores(name.text); 2982 } 2983 } 2984 return undefined; 2985} 2986 2987/** @internal */ 2988export function getAssignmentDeclarationPropertyAccessKind(lhs: AccessExpression): AssignmentDeclarationKind { 2989 if (lhs.expression.kind === SyntaxKind.ThisKeyword) { 2990 return AssignmentDeclarationKind.ThisProperty; 2991 } 2992 else if (isModuleExportsAccessExpression(lhs)) { 2993 // module.exports = expr 2994 return AssignmentDeclarationKind.ModuleExports; 2995 } 2996 else if (isBindableStaticNameExpression(lhs.expression, /*excludeThisKeyword*/ true)) { 2997 if (isPrototypeAccess(lhs.expression)) { 2998 // F.G....prototype.x = expr 2999 return AssignmentDeclarationKind.PrototypeProperty; 3000 } 3001 3002 let nextToLast = lhs; 3003 while (!isIdentifier(nextToLast.expression)) { 3004 nextToLast = nextToLast.expression as Exclude<BindableStaticNameExpression, Identifier>; 3005 } 3006 const id = nextToLast.expression; 3007 if ((id.escapedText === "exports" || 3008 id.escapedText === "module" && getElementOrPropertyAccessName(nextToLast) === "exports") && 3009 // ExportsProperty does not support binding with computed names 3010 isBindableStaticAccessExpression(lhs)) { 3011 // exports.name = expr OR module.exports.name = expr OR exports["name"] = expr ... 3012 return AssignmentDeclarationKind.ExportsProperty; 3013 } 3014 if (isBindableStaticNameExpression(lhs, /*excludeThisKeyword*/ true) || (isElementAccessExpression(lhs) && isDynamicName(lhs))) { 3015 // F.G...x = expr 3016 return AssignmentDeclarationKind.Property; 3017 } 3018 } 3019 3020 return AssignmentDeclarationKind.None; 3021} 3022 3023/** @internal */ 3024export function getInitializerOfBinaryExpression(expr: BinaryExpression) { 3025 while (isBinaryExpression(expr.right)) { 3026 expr = expr.right; 3027 } 3028 return expr.right; 3029} 3030 3031/** @internal */ 3032export function isPrototypePropertyAssignment(node: Node): node is BinaryExpression { 3033 return isBinaryExpression(node) && getAssignmentDeclarationKind(node) === AssignmentDeclarationKind.PrototypeProperty; 3034} 3035 3036/** @internal */ 3037export function isSpecialPropertyDeclaration(expr: PropertyAccessExpression | ElementAccessExpression): expr is PropertyAccessExpression | LiteralLikeElementAccessExpression { 3038 return isInJSFile(expr) && 3039 expr.parent && expr.parent.kind === SyntaxKind.ExpressionStatement && 3040 (!isElementAccessExpression(expr) || isLiteralLikeElementAccess(expr)) && 3041 !!getJSDocTypeTag(expr.parent); 3042} 3043 3044/** @internal */ 3045export function setValueDeclaration(symbol: Symbol, node: Declaration): void { 3046 const { valueDeclaration } = symbol; 3047 if (!valueDeclaration || 3048 !(node.flags & NodeFlags.Ambient && !(valueDeclaration.flags & NodeFlags.Ambient)) && 3049 (isAssignmentDeclaration(valueDeclaration) && !isAssignmentDeclaration(node)) || 3050 (valueDeclaration.kind !== node.kind && isEffectiveModuleDeclaration(valueDeclaration))) { 3051 // other kinds of value declarations take precedence over modules and assignment declarations 3052 symbol.valueDeclaration = node; 3053 } 3054} 3055 3056/** @internal */ 3057export function isFunctionSymbol(symbol: Symbol | undefined) { 3058 if (!symbol || !symbol.valueDeclaration) { 3059 return false; 3060 } 3061 const decl = symbol.valueDeclaration; 3062 return decl.kind === SyntaxKind.FunctionDeclaration || isVariableDeclaration(decl) && decl.initializer && isFunctionLike(decl.initializer); 3063} 3064 3065/** @internal */ 3066export function tryGetModuleSpecifierFromDeclaration(node: AnyImportOrBareOrAccessedRequire): StringLiteralLike | undefined { 3067 switch (node.kind) { 3068 case SyntaxKind.VariableDeclaration: 3069 return findAncestor(node.initializer, (node): node is RequireOrImportCall => isRequireCall(node, /*requireStringLiteralLikeArgument*/ true))?.arguments[0]; 3070 case SyntaxKind.ImportDeclaration: 3071 return tryCast(node.moduleSpecifier, isStringLiteralLike); 3072 case SyntaxKind.ImportEqualsDeclaration: 3073 return tryCast(tryCast(node.moduleReference, isExternalModuleReference)?.expression, isStringLiteralLike); 3074 default: 3075 Debug.assertNever(node); 3076 } 3077} 3078 3079/** @internal */ 3080export function importFromModuleSpecifier(node: StringLiteralLike): AnyValidImportOrReExport { 3081 return tryGetImportFromModuleSpecifier(node) || Debug.failBadSyntaxKind(node.parent); 3082} 3083 3084/** @internal */ 3085export function tryGetImportFromModuleSpecifier(node: StringLiteralLike): AnyValidImportOrReExport | undefined { 3086 switch (node.parent.kind) { 3087 case SyntaxKind.ImportDeclaration: 3088 case SyntaxKind.ExportDeclaration: 3089 return node.parent as AnyValidImportOrReExport; 3090 case SyntaxKind.ExternalModuleReference: 3091 return (node.parent as ExternalModuleReference).parent as AnyValidImportOrReExport; 3092 case SyntaxKind.CallExpression: 3093 return isImportCall(node.parent) || isRequireCall(node.parent, /*checkArg*/ false) ? node.parent as RequireOrImportCall : undefined; 3094 case SyntaxKind.LiteralType: 3095 Debug.assert(isStringLiteral(node)); 3096 return tryCast(node.parent.parent, isImportTypeNode) as ValidImportTypeNode | undefined; 3097 default: 3098 return undefined; 3099 } 3100} 3101 3102/** @internal */ 3103export function getExternalModuleName(node: AnyImportOrReExport | ImportTypeNode | ImportCall | ModuleDeclaration): Expression | undefined { 3104 switch (node.kind) { 3105 case SyntaxKind.ImportDeclaration: 3106 case SyntaxKind.ExportDeclaration: 3107 return node.moduleSpecifier; 3108 case SyntaxKind.ImportEqualsDeclaration: 3109 return node.moduleReference.kind === SyntaxKind.ExternalModuleReference ? node.moduleReference.expression : undefined; 3110 case SyntaxKind.ImportType: 3111 return isLiteralImportTypeNode(node) ? node.argument.literal : undefined; 3112 case SyntaxKind.CallExpression: 3113 return node.arguments[0]; 3114 case SyntaxKind.ModuleDeclaration: 3115 return node.name.kind === SyntaxKind.StringLiteral ? node.name : undefined; 3116 default: 3117 return Debug.assertNever(node); 3118 } 3119} 3120 3121/** @internal */ 3122export function getNamespaceDeclarationNode(node: ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration): ImportEqualsDeclaration | NamespaceImport | NamespaceExport | undefined { 3123 switch (node.kind) { 3124 case SyntaxKind.ImportDeclaration: 3125 return node.importClause && tryCast(node.importClause.namedBindings, isNamespaceImport); 3126 case SyntaxKind.ImportEqualsDeclaration: 3127 return node; 3128 case SyntaxKind.ExportDeclaration: 3129 return node.exportClause && tryCast(node.exportClause, isNamespaceExport); 3130 default: 3131 return Debug.assertNever(node); 3132 } 3133} 3134 3135/** @internal */ 3136export function isDefaultImport(node: ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration): boolean { 3137 return node.kind === SyntaxKind.ImportDeclaration && !!node.importClause && !!node.importClause.name; 3138} 3139 3140/** @internal */ 3141export function forEachImportClauseDeclaration<T>(node: ImportClause, action: (declaration: ImportClause | NamespaceImport | ImportSpecifier) => T | undefined): T | undefined { 3142 if (node.name) { 3143 const result = action(node); 3144 if (result) return result; 3145 } 3146 if (node.namedBindings) { 3147 const result = isNamespaceImport(node.namedBindings) 3148 ? action(node.namedBindings) 3149 : forEach(node.namedBindings.elements, action); 3150 if (result) return result; 3151 } 3152} 3153 3154/** @internal */ 3155export function hasQuestionToken(node: Node) { 3156 if (node) { 3157 switch (node.kind) { 3158 case SyntaxKind.Parameter: 3159 case SyntaxKind.MethodDeclaration: 3160 case SyntaxKind.MethodSignature: 3161 case SyntaxKind.ShorthandPropertyAssignment: 3162 case SyntaxKind.PropertyAssignment: 3163 case SyntaxKind.PropertyDeclaration: 3164 case SyntaxKind.PropertySignature: 3165 return (node as ParameterDeclaration | MethodDeclaration | PropertyDeclaration).questionToken !== undefined; 3166 } 3167 } 3168 3169 return false; 3170} 3171 3172/** @internal */ 3173export function isJSDocConstructSignature(node: Node) { 3174 const param = isJSDocFunctionType(node) ? firstOrUndefined(node.parameters) : undefined; 3175 const name = tryCast(param && param.name, isIdentifier); 3176 return !!name && name.escapedText === "new"; 3177} 3178 3179/** @internal */ 3180export function isJSDocTypeAlias(node: Node): node is JSDocTypedefTag | JSDocCallbackTag | JSDocEnumTag { 3181 return node.kind === SyntaxKind.JSDocTypedefTag || node.kind === SyntaxKind.JSDocCallbackTag || node.kind === SyntaxKind.JSDocEnumTag; 3182} 3183 3184/** @internal */ 3185export function isTypeAlias(node: Node): node is JSDocTypedefTag | JSDocCallbackTag | JSDocEnumTag | TypeAliasDeclaration { 3186 return isJSDocTypeAlias(node) || isTypeAliasDeclaration(node); 3187} 3188 3189function getSourceOfAssignment(node: Node): Node | undefined { 3190 return isExpressionStatement(node) && 3191 isBinaryExpression(node.expression) && 3192 node.expression.operatorToken.kind === SyntaxKind.EqualsToken 3193 ? getRightMostAssignedExpression(node.expression) 3194 : undefined; 3195} 3196 3197function getSourceOfDefaultedAssignment(node: Node): Node | undefined { 3198 return isExpressionStatement(node) && 3199 isBinaryExpression(node.expression) && 3200 getAssignmentDeclarationKind(node.expression) !== AssignmentDeclarationKind.None && 3201 isBinaryExpression(node.expression.right) && 3202 (node.expression.right.operatorToken.kind === SyntaxKind.BarBarToken || node.expression.right.operatorToken.kind === SyntaxKind.QuestionQuestionToken) 3203 ? node.expression.right.right 3204 : undefined; 3205} 3206 3207/** @internal */ 3208export function getSingleInitializerOfVariableStatementOrPropertyDeclaration(node: Node): Expression | undefined { 3209 switch (node.kind) { 3210 case SyntaxKind.VariableStatement: 3211 const v = getSingleVariableOfVariableStatement(node); 3212 return v && v.initializer; 3213 case SyntaxKind.PropertyDeclaration: 3214 return (node as PropertyDeclaration).initializer; 3215 case SyntaxKind.AnnotationPropertyDeclaration: 3216 return (node as AnnotationPropertyDeclaration).initializer; 3217 case SyntaxKind.PropertyAssignment: 3218 return (node as PropertyAssignment).initializer; 3219 } 3220} 3221 3222/** @internal */ 3223export function getSingleVariableOfVariableStatement(node: Node): VariableDeclaration | undefined { 3224 return isVariableStatement(node) ? firstOrUndefined(node.declarationList.declarations) : undefined; 3225} 3226 3227function getNestedModuleDeclaration(node: Node): Node | undefined { 3228 return isModuleDeclaration(node) && 3229 node.body && 3230 node.body.kind === SyntaxKind.ModuleDeclaration 3231 ? node.body 3232 : undefined; 3233} 3234 3235export function getJSDocCommentsAndTags(hostNode: Node, noCache?: boolean): readonly (JSDoc | JSDocTag)[] { 3236 let result: (JSDoc | JSDocTag)[] | undefined; 3237 // Pull parameter comments from declaring function as well 3238 if (isVariableLike(hostNode) && hasInitializer(hostNode) && hasJSDocNodes(hostNode.initializer!)) { 3239 result = addRange(result, filterOwnedJSDocTags(hostNode, last((hostNode.initializer as HasJSDoc).jsDoc!))); 3240 } 3241 let node: Node | undefined = hostNode; 3242 while (node && node.parent) { 3243 if (hasJSDocNodes(node)) { 3244 result = addRange(result, filterOwnedJSDocTags(hostNode, last(node.jsDoc!))); 3245 } 3246 3247 if (node.kind === SyntaxKind.Parameter) { 3248 result = addRange(result, (noCache ? getJSDocParameterTagsNoCache : getJSDocParameterTags)(node as ParameterDeclaration)); 3249 break; 3250 } 3251 if (node.kind === SyntaxKind.TypeParameter) { 3252 result = addRange(result, (noCache ? getJSDocTypeParameterTagsNoCache : getJSDocTypeParameterTags)(node as TypeParameterDeclaration)); 3253 break; 3254 } 3255 node = getNextJSDocCommentLocation(node); 3256 } 3257 return result || emptyArray; 3258} 3259 3260function filterOwnedJSDocTags(hostNode: Node, jsDoc: JSDoc | JSDocTag) { 3261 if (isJSDoc(jsDoc)) { 3262 const ownedTags = filter(jsDoc.tags, tag => ownsJSDocTag(hostNode, tag)); 3263 return jsDoc.tags === ownedTags ? [jsDoc] : ownedTags; 3264 } 3265 return ownsJSDocTag(hostNode, jsDoc) ? [jsDoc] : undefined; 3266} 3267 3268/** 3269 * Determines whether a host node owns a jsDoc tag. A `@type` tag attached to a 3270 * a ParenthesizedExpression belongs only to the ParenthesizedExpression. 3271 */ 3272function ownsJSDocTag(hostNode: Node, tag: JSDocTag) { 3273 return !isJSDocTypeTag(tag) 3274 || !tag.parent 3275 || !isJSDoc(tag.parent) 3276 || !isParenthesizedExpression(tag.parent.parent) 3277 || tag.parent.parent === hostNode; 3278} 3279 3280/** @internal */ 3281export function getNextJSDocCommentLocation(node: Node) { 3282 const parent = node.parent; 3283 if (parent.kind === SyntaxKind.PropertyAssignment || 3284 parent.kind === SyntaxKind.ExportAssignment || 3285 parent.kind === SyntaxKind.PropertyDeclaration || 3286 parent.kind === SyntaxKind.ExpressionStatement && node.kind === SyntaxKind.PropertyAccessExpression || 3287 parent.kind === SyntaxKind.ReturnStatement || 3288 getNestedModuleDeclaration(parent) || 3289 isBinaryExpression(node) && node.operatorToken.kind === SyntaxKind.EqualsToken) { 3290 return parent; 3291 } 3292 // Try to recognize this pattern when node is initializer of variable declaration and JSDoc comments are on containing variable statement. 3293 // /** 3294 // * @param {number} name 3295 // * @returns {number} 3296 // */ 3297 // var x = function(name) { return name.length; } 3298 else if (parent.parent && 3299 (getSingleVariableOfVariableStatement(parent.parent) === node || 3300 isBinaryExpression(parent) && parent.operatorToken.kind === SyntaxKind.EqualsToken)) { 3301 return parent.parent; 3302 } 3303 else if (parent.parent && parent.parent.parent && 3304 (getSingleVariableOfVariableStatement(parent.parent.parent) || 3305 getSingleInitializerOfVariableStatementOrPropertyDeclaration(parent.parent.parent) === node || 3306 getSourceOfDefaultedAssignment(parent.parent.parent))) { 3307 return parent.parent.parent; 3308 } 3309} 3310 3311/** 3312 * Does the opposite of `getJSDocParameterTags`: given a JSDoc parameter, finds the parameter corresponding to it. 3313 * 3314 * @internal 3315 */ 3316export function getParameterSymbolFromJSDoc(node: JSDocParameterTag): Symbol | undefined { 3317 if (node.symbol) { 3318 return node.symbol; 3319 } 3320 if (!isIdentifier(node.name)) { 3321 return undefined; 3322 } 3323 const name = node.name.escapedText; 3324 const decl = getHostSignatureFromJSDoc(node); 3325 if (!decl) { 3326 return undefined; 3327 } 3328 const parameter = find(decl.parameters, p => p.name.kind === SyntaxKind.Identifier && p.name.escapedText === name); 3329 return parameter && parameter.symbol; 3330} 3331 3332/** @internal */ 3333export function getEffectiveContainerForJSDocTemplateTag(node: JSDocTemplateTag) { 3334 if (isJSDoc(node.parent) && node.parent.tags) { 3335 // A @template tag belongs to any @typedef, @callback, or @enum tags in the same comment block, if they exist. 3336 const typeAlias = find(node.parent.tags, isJSDocTypeAlias); 3337 if (typeAlias) { 3338 return typeAlias; 3339 } 3340 } 3341 // otherwise it belongs to the host it annotates 3342 return getHostSignatureFromJSDoc(node); 3343} 3344 3345/** @internal */ 3346export function getHostSignatureFromJSDoc(node: Node): SignatureDeclaration | undefined { 3347 const host = getEffectiveJSDocHost(node); 3348 if (host) { 3349 return isPropertySignature(host) && host.type && isFunctionLike(host.type) ? host.type : 3350 isFunctionLike(host) ? host : undefined; 3351 } 3352 return undefined; 3353} 3354 3355/** @internal */ 3356export function getEffectiveJSDocHost(node: Node): Node | undefined { 3357 const host = getJSDocHost(node); 3358 if (host) { 3359 return getSourceOfDefaultedAssignment(host) 3360 || getSourceOfAssignment(host) 3361 || getSingleInitializerOfVariableStatementOrPropertyDeclaration(host) 3362 || getSingleVariableOfVariableStatement(host) 3363 || getNestedModuleDeclaration(host) 3364 || host; 3365 } 3366} 3367 3368/** 3369 * Use getEffectiveJSDocHost if you additionally need to look for jsdoc on parent nodes, like assignments. 3370 * 3371 * @internal 3372 */ 3373export function getJSDocHost(node: Node): HasJSDoc | undefined { 3374 const jsDoc = getJSDocRoot(node); 3375 if (!jsDoc) { 3376 return undefined; 3377 } 3378 3379 const host = jsDoc.parent; 3380 if (host && host.jsDoc && jsDoc === lastOrUndefined(host.jsDoc)) { 3381 return host; 3382 } 3383} 3384 3385/** @internal */ 3386export function getJSDocRoot(node: Node): JSDoc | undefined { 3387 return findAncestor(node.parent, isJSDoc); 3388} 3389 3390/** @internal */ 3391export function getTypeParameterFromJsDoc(node: TypeParameterDeclaration & { parent: JSDocTemplateTag }): TypeParameterDeclaration | undefined { 3392 const name = node.name.escapedText; 3393 const { typeParameters } = (node.parent.parent.parent as SignatureDeclaration | InterfaceDeclaration | ClassDeclaration); 3394 return typeParameters && find(typeParameters, p => p.name.escapedText === name); 3395} 3396 3397/** @internal */ 3398export function hasTypeArguments(node: Node): node is HasTypeArguments { 3399 return !!(node as HasTypeArguments).typeArguments; 3400} 3401 3402/** @internal */ 3403export const enum AssignmentKind { 3404 None, Definite, Compound 3405} 3406 3407/** @internal */ 3408export function getAssignmentTargetKind(node: Node): AssignmentKind { 3409 let parent = node.parent; 3410 while (true) { 3411 switch (parent.kind) { 3412 case SyntaxKind.BinaryExpression: 3413 const binaryOperator = (parent as BinaryExpression).operatorToken.kind; 3414 return isAssignmentOperator(binaryOperator) && (parent as BinaryExpression).left === node ? 3415 binaryOperator === SyntaxKind.EqualsToken || isLogicalOrCoalescingAssignmentOperator(binaryOperator) ? AssignmentKind.Definite : AssignmentKind.Compound : 3416 AssignmentKind.None; 3417 case SyntaxKind.PrefixUnaryExpression: 3418 case SyntaxKind.PostfixUnaryExpression: 3419 const unaryOperator = (parent as PrefixUnaryExpression | PostfixUnaryExpression).operator; 3420 return unaryOperator === SyntaxKind.PlusPlusToken || unaryOperator === SyntaxKind.MinusMinusToken ? AssignmentKind.Compound : AssignmentKind.None; 3421 case SyntaxKind.ForInStatement: 3422 case SyntaxKind.ForOfStatement: 3423 return (parent as ForInOrOfStatement).initializer === node ? AssignmentKind.Definite : AssignmentKind.None; 3424 case SyntaxKind.ParenthesizedExpression: 3425 case SyntaxKind.ArrayLiteralExpression: 3426 case SyntaxKind.SpreadElement: 3427 case SyntaxKind.NonNullExpression: 3428 node = parent; 3429 break; 3430 case SyntaxKind.SpreadAssignment: 3431 node = parent.parent; 3432 break; 3433 case SyntaxKind.ShorthandPropertyAssignment: 3434 if ((parent as ShorthandPropertyAssignment).name !== node) { 3435 return AssignmentKind.None; 3436 } 3437 node = parent.parent; 3438 break; 3439 case SyntaxKind.PropertyAssignment: 3440 if ((parent as ShorthandPropertyAssignment).name === node) { 3441 return AssignmentKind.None; 3442 } 3443 node = parent.parent; 3444 break; 3445 default: 3446 return AssignmentKind.None; 3447 } 3448 parent = node.parent; 3449 } 3450} 3451 3452// A node is an assignment target if it is on the left hand side of an '=' token, if it is parented by a property 3453// assignment in an object literal that is an assignment target, or if it is parented by an array literal that is 3454// an assignment target. Examples include 'a = xxx', '{ p: a } = xxx', '[{ a }] = xxx'. 3455// (Note that `p` is not a target in the above examples, only `a`.) 3456/** @internal */ 3457export function isAssignmentTarget(node: Node): boolean { 3458 return getAssignmentTargetKind(node) !== AssignmentKind.None; 3459} 3460 3461/** @internal */ 3462export type NodeWithPossibleHoistedDeclaration = 3463 | Block 3464 | VariableStatement 3465 | WithStatement 3466 | IfStatement 3467 | SwitchStatement 3468 | CaseBlock 3469 | CaseClause 3470 | DefaultClause 3471 | LabeledStatement 3472 | ForStatement 3473 | ForInStatement 3474 | ForOfStatement 3475 | DoStatement 3476 | WhileStatement 3477 | TryStatement 3478 | CatchClause; 3479 3480/** 3481 * Indicates whether a node could contain a `var` VariableDeclarationList that contributes to 3482 * the same `var` declaration scope as the node's parent. 3483 * 3484 * @internal 3485 */ 3486export function isNodeWithPossibleHoistedDeclaration(node: Node): node is NodeWithPossibleHoistedDeclaration { 3487 switch (node.kind) { 3488 case SyntaxKind.Block: 3489 case SyntaxKind.VariableStatement: 3490 case SyntaxKind.WithStatement: 3491 case SyntaxKind.IfStatement: 3492 case SyntaxKind.SwitchStatement: 3493 case SyntaxKind.CaseBlock: 3494 case SyntaxKind.CaseClause: 3495 case SyntaxKind.DefaultClause: 3496 case SyntaxKind.LabeledStatement: 3497 case SyntaxKind.ForStatement: 3498 case SyntaxKind.ForInStatement: 3499 case SyntaxKind.ForOfStatement: 3500 case SyntaxKind.DoStatement: 3501 case SyntaxKind.WhileStatement: 3502 case SyntaxKind.TryStatement: 3503 case SyntaxKind.CatchClause: 3504 return true; 3505 } 3506 return false; 3507} 3508 3509/** @internal */ 3510export type ValueSignatureDeclaration = 3511 | FunctionDeclaration 3512 | MethodDeclaration 3513 | ConstructorDeclaration 3514 | AccessorDeclaration 3515 | FunctionExpression 3516 | ArrowFunction; 3517 3518/** @internal */ 3519export function isValueSignatureDeclaration(node: Node): node is ValueSignatureDeclaration { 3520 return isFunctionExpression(node) || isArrowFunction(node) || isMethodOrAccessor(node) || isFunctionDeclaration(node) || isConstructorDeclaration(node); 3521} 3522 3523function walkUp(node: Node, kind: SyntaxKind) { 3524 while (node && node.kind === kind) { 3525 node = node.parent; 3526 } 3527 return node; 3528} 3529 3530/** @internal */ 3531export function walkUpParenthesizedTypes(node: Node) { 3532 return walkUp(node, SyntaxKind.ParenthesizedType); 3533} 3534 3535/** @internal */ 3536export function walkUpParenthesizedExpressions(node: Node) { 3537 return walkUp(node, SyntaxKind.ParenthesizedExpression); 3538} 3539 3540/** 3541 * Walks up parenthesized types. 3542 * It returns both the outermost parenthesized type and its parent. 3543 * If given node is not a parenthesiezd type, undefined is return as the former. 3544 * 3545 * @internal 3546 */ 3547export function walkUpParenthesizedTypesAndGetParentAndChild(node: Node): [ParenthesizedTypeNode | undefined, Node] { 3548 let child: ParenthesizedTypeNode | undefined; 3549 while (node && node.kind === SyntaxKind.ParenthesizedType) { 3550 child = node as ParenthesizedTypeNode; 3551 node = node.parent; 3552 } 3553 return [child, node]; 3554} 3555 3556/** @internal */ 3557export function skipTypeParentheses(node: TypeNode): TypeNode { 3558 while (isParenthesizedTypeNode(node)) node = node.type; 3559 return node; 3560} 3561 3562/** @internal */ 3563export function skipParentheses(node: Expression, excludeJSDocTypeAssertions?: boolean): Expression; 3564/** @internal */ 3565export function skipParentheses(node: Node, excludeJSDocTypeAssertions?: boolean): Node; 3566/** @internal */ 3567export function skipParentheses(node: Node, excludeJSDocTypeAssertions?: boolean): Node { 3568 const flags = excludeJSDocTypeAssertions ? 3569 OuterExpressionKinds.Parentheses | OuterExpressionKinds.ExcludeJSDocTypeAssertion : 3570 OuterExpressionKinds.Parentheses; 3571 return skipOuterExpressions(node, flags); 3572} 3573 3574// a node is delete target iff. it is PropertyAccessExpression/ElementAccessExpression with parentheses skipped 3575/** @internal */ 3576export function isDeleteTarget(node: Node): boolean { 3577 if (node.kind !== SyntaxKind.PropertyAccessExpression && node.kind !== SyntaxKind.ElementAccessExpression) { 3578 return false; 3579 } 3580 node = walkUpParenthesizedExpressions(node.parent); 3581 return node && node.kind === SyntaxKind.DeleteExpression; 3582} 3583 3584/** @internal */ 3585export function isNodeDescendantOf(node: Node, ancestor: Node | undefined): boolean { 3586 while (node) { 3587 if (node === ancestor) return true; 3588 node = node.parent; 3589 } 3590 return false; 3591} 3592 3593// True if `name` is the name of a declaration node 3594/** @internal */ 3595export function isDeclarationName(name: Node): boolean { 3596 return !isSourceFile(name) && !isBindingPattern(name) && isDeclaration(name.parent) && name.parent.name === name; 3597} 3598 3599// See GH#16030 3600/** @internal */ 3601export function getDeclarationFromName(name: Node): Declaration | undefined { 3602 const parent = name.parent; 3603 switch (name.kind) { 3604 case SyntaxKind.StringLiteral: 3605 case SyntaxKind.NoSubstitutionTemplateLiteral: 3606 case SyntaxKind.NumericLiteral: 3607 if (isComputedPropertyName(parent)) return parent.parent; 3608 // falls through 3609 case SyntaxKind.Identifier: 3610 if (isDeclaration(parent)) { 3611 return parent.name === name ? parent : undefined; 3612 } 3613 else if (isQualifiedName(parent)) { 3614 const tag = parent.parent; 3615 return isJSDocParameterTag(tag) && tag.name === parent ? tag : undefined; 3616 } 3617 else { 3618 const binExp = parent.parent; 3619 return isBinaryExpression(binExp) && 3620 getAssignmentDeclarationKind(binExp) !== AssignmentDeclarationKind.None && 3621 (binExp.left.symbol || binExp.symbol) && 3622 getNameOfDeclaration(binExp) === name 3623 ? binExp 3624 : undefined; 3625 } 3626 case SyntaxKind.PrivateIdentifier: 3627 return isDeclaration(parent) && parent.name === name ? parent : undefined; 3628 default: 3629 return undefined; 3630 } 3631} 3632 3633/** @internal */ 3634export function isLiteralComputedPropertyDeclarationName(node: Node) { 3635 return isStringOrNumericLiteralLike(node) && 3636 node.parent.kind === SyntaxKind.ComputedPropertyName && 3637 isDeclaration(node.parent.parent); 3638} 3639 3640// Return true if the given identifier is classified as an IdentifierName 3641/** @internal */ 3642export function isIdentifierName(node: Identifier): boolean { 3643 const parent = node.parent; 3644 switch (parent.kind) { 3645 case SyntaxKind.PropertyDeclaration: 3646 case SyntaxKind.AnnotationPropertyDeclaration: 3647 case SyntaxKind.PropertySignature: 3648 case SyntaxKind.MethodDeclaration: 3649 case SyntaxKind.MethodSignature: 3650 case SyntaxKind.GetAccessor: 3651 case SyntaxKind.SetAccessor: 3652 case SyntaxKind.EnumMember: 3653 case SyntaxKind.PropertyAssignment: 3654 case SyntaxKind.PropertyAccessExpression: 3655 // Name in member declaration or property name in property access 3656 return (parent as NamedDeclaration | PropertyAccessExpression).name === node; 3657 case SyntaxKind.QualifiedName: 3658 // Name on right hand side of dot in a type query or type reference 3659 return (parent as QualifiedName).right === node; 3660 case SyntaxKind.BindingElement: 3661 case SyntaxKind.ImportSpecifier: 3662 // Property name in binding element or import specifier 3663 return (parent as BindingElement | ImportSpecifier).propertyName === node; 3664 case SyntaxKind.ExportSpecifier: 3665 case SyntaxKind.JsxAttribute: 3666 case SyntaxKind.JsxSelfClosingElement: 3667 case SyntaxKind.JsxOpeningElement: 3668 case SyntaxKind.JsxClosingElement: 3669 // Any name in an export specifier or JSX Attribute or Jsx Element 3670 return true; 3671 } 3672 return false; 3673} 3674 3675// An alias symbol is created by one of the following declarations: 3676// import <symbol> = ... 3677// import <symbol> from ... 3678// import * as <symbol> from ... 3679// import { x as <symbol> } from ... 3680// export { x as <symbol> } from ... 3681// export * as ns <symbol> from ... 3682// export = <EntityNameExpression> 3683// export default <EntityNameExpression> 3684// module.exports = <EntityNameExpression> 3685// module.exports.x = <EntityNameExpression> 3686// const x = require("...") 3687// const { x } = require("...") 3688// const x = require("...").y 3689// const { x } = require("...").y 3690/** @internal */ 3691export function isAliasSymbolDeclaration(node: Node): boolean { 3692 if (node.kind === SyntaxKind.ImportEqualsDeclaration || 3693 node.kind === SyntaxKind.NamespaceExportDeclaration || 3694 node.kind === SyntaxKind.ImportClause && !!(node as ImportClause).name || 3695 node.kind === SyntaxKind.NamespaceImport || 3696 node.kind === SyntaxKind.NamespaceExport || 3697 node.kind === SyntaxKind.ImportSpecifier || 3698 node.kind === SyntaxKind.ExportSpecifier || 3699 node.kind === SyntaxKind.ExportAssignment && exportAssignmentIsAlias(node as ExportAssignment) 3700 ) { 3701 return true; 3702 } 3703 3704 return isInJSFile(node) && ( 3705 isBinaryExpression(node) && getAssignmentDeclarationKind(node) === AssignmentDeclarationKind.ModuleExports && exportAssignmentIsAlias(node) || 3706 isPropertyAccessExpression(node) 3707 && isBinaryExpression(node.parent) 3708 && node.parent.left === node 3709 && node.parent.operatorToken.kind === SyntaxKind.EqualsToken 3710 && isAliasableExpression(node.parent.right)); 3711} 3712 3713/** @internal */ 3714export function getAliasDeclarationFromName(node: EntityName): Declaration | undefined { 3715 switch (node.parent.kind) { 3716 case SyntaxKind.ImportClause: 3717 case SyntaxKind.ImportSpecifier: 3718 case SyntaxKind.NamespaceImport: 3719 case SyntaxKind.ExportSpecifier: 3720 case SyntaxKind.ExportAssignment: 3721 case SyntaxKind.ImportEqualsDeclaration: 3722 case SyntaxKind.NamespaceExport: 3723 return node.parent as Declaration; 3724 case SyntaxKind.QualifiedName: 3725 do { 3726 node = node.parent as QualifiedName; 3727 } while (node.parent.kind === SyntaxKind.QualifiedName); 3728 return getAliasDeclarationFromName(node); 3729 } 3730} 3731 3732/** @internal */ 3733export function isAliasableExpression(e: Expression) { 3734 return isEntityNameExpression(e) || isClassExpression(e); 3735} 3736 3737/** @internal */ 3738export function exportAssignmentIsAlias(node: ExportAssignment | BinaryExpression): boolean { 3739 const e = getExportAssignmentExpression(node); 3740 return isAliasableExpression(e); 3741} 3742 3743/** @internal */ 3744export function getExportAssignmentExpression(node: ExportAssignment | BinaryExpression): Expression { 3745 return isExportAssignment(node) ? node.expression : node.right; 3746} 3747 3748/** @internal */ 3749export function getPropertyAssignmentAliasLikeExpression(node: PropertyAssignment | ShorthandPropertyAssignment | PropertyAccessExpression): Expression { 3750 return node.kind === SyntaxKind.ShorthandPropertyAssignment ? node.name : node.kind === SyntaxKind.PropertyAssignment ? node.initializer : 3751 (node.parent as BinaryExpression).right; 3752} 3753 3754/** @internal */ 3755export function getEffectiveBaseTypeNode(node: ClassLikeDeclaration | InterfaceDeclaration) { 3756 const baseType = getClassExtendsHeritageElement(node); 3757 if (baseType && isInJSFile(node)) { 3758 // Prefer an @augments tag because it may have type parameters. 3759 const tag = getJSDocAugmentsTag(node); 3760 if (tag) { 3761 return tag.class; 3762 } 3763 } 3764 return baseType; 3765} 3766 3767/** @internal */ 3768export function getClassExtendsHeritageElement(node: ClassLikeDeclaration | InterfaceDeclaration) { 3769 const heritageClause = getHeritageClause(node.heritageClauses, SyntaxKind.ExtendsKeyword); 3770 return heritageClause && heritageClause.types.length > 0 ? heritageClause.types[0] : undefined; 3771} 3772 3773/** @internal */ 3774export function getEffectiveImplementsTypeNodes(node: ClassLikeDeclaration): undefined | readonly ExpressionWithTypeArguments[]{ 3775 if (isInJSFile(node)) { 3776 return getJSDocImplementsTags(node).map(n => n.class); 3777 } 3778 else { 3779 const heritageClause = getHeritageClause(node.heritageClauses, SyntaxKind.ImplementsKeyword); 3780 return heritageClause?.types; 3781 } 3782} 3783 3784/** 3785 * Returns the node in an `extends` or `implements` clause of a class or interface. 3786 * 3787 * @internal 3788 */ 3789export function getAllSuperTypeNodes(node: Node): readonly TypeNode[] { 3790 return isInterfaceDeclaration(node) ? getInterfaceBaseTypeNodes(node) || emptyArray : 3791 isClassLike(node) ? concatenate(singleElementArray(getEffectiveBaseTypeNode(node)), getEffectiveImplementsTypeNodes(node)) || emptyArray : 3792 emptyArray; 3793} 3794 3795/** @internal */ 3796export function getInterfaceBaseTypeNodes(node: InterfaceDeclaration) { 3797 const heritageClause = getHeritageClause(node.heritageClauses, SyntaxKind.ExtendsKeyword); 3798 return heritageClause ? heritageClause.types : undefined; 3799} 3800 3801/** @internal */ 3802export function getHeritageClause(clauses: NodeArray<HeritageClause> | undefined, kind: SyntaxKind) { 3803 if (clauses) { 3804 for (const clause of clauses) { 3805 if (clause.token === kind) { 3806 return clause; 3807 } 3808 } 3809 } 3810 3811 return undefined; 3812} 3813 3814/** @internal */ 3815export function getAncestor(node: Node | undefined, kind: SyntaxKind): Node | undefined { 3816 while (node) { 3817 if (node.kind === kind) { 3818 return node; 3819 } 3820 node = node.parent; 3821 } 3822 return undefined; 3823} 3824 3825/** @internal */ 3826export function getRootEtsComponent(node: Node | undefined): EtsComponentExpression | undefined { 3827 while (node) { 3828 if (isEtsComponentExpression(node)) { 3829 return node; 3830 } 3831 node = (node as PropertyAccessExpression | CallExpression).expression; 3832 } 3833 return undefined; 3834} 3835 3836/** @internal */ 3837export function getRootComponent(node: Node | undefined, compilerOptions: CompilerOptions): 3838 [EtsComponentExpression | CallExpression | undefined, string | undefined] { 3839 while (node) { 3840 if (isEtsComponentExpression(node)) { 3841 return [node, 'etsComponentType']; 3842 } else if (isCallExpression(node) && isIdentifier(node.expression)) { 3843 if (compilerOptions.ets?.syntaxComponents?.attrUICallback?.map((item: any) => item.name).includes(node.expression.escapedText.toString())) { 3844 return [node, 'callExpressionComponentType']; 3845 } else { 3846 return [node, 'otherType']; 3847 } 3848 3849 } 3850 node = (node as PropertyAccessExpression | CallExpression).expression; 3851 } 3852 return [undefined, undefined]; 3853} 3854 3855/** @internal */ 3856export function getVirtualEtsComponent(node: Node | undefined): PropertyAccessExpression | undefined { 3857 while (node) { 3858 if (isPropertyAccessExpression(node) && node.expression && node.expression.virtual) { 3859 return node; 3860 } 3861 node = (node as PropertyAccessExpression | CallExpression).expression; 3862 } 3863 return undefined; 3864} 3865 3866/** @internal */ 3867export function isKeyword(token: SyntaxKind): token is KeywordSyntaxKind { 3868 return SyntaxKind.FirstKeyword <= token && token <= SyntaxKind.LastKeyword; 3869} 3870 3871/** @internal */ 3872export function isContextualKeyword(token: SyntaxKind): boolean { 3873 return SyntaxKind.FirstContextualKeyword <= token && token <= SyntaxKind.LastContextualKeyword; 3874} 3875 3876/** @internal */ 3877export function isNonContextualKeyword(token: SyntaxKind): boolean { 3878 return isKeyword(token) && !isContextualKeyword(token); 3879} 3880 3881/** @internal */ 3882export function isFutureReservedKeyword(token: SyntaxKind): boolean { 3883 return SyntaxKind.FirstFutureReservedWord <= token && token <= SyntaxKind.LastFutureReservedWord; 3884} 3885 3886/** @internal */ 3887export function isStringANonContextualKeyword(name: string) { 3888 const token = stringToToken(name); 3889 return token !== undefined && isNonContextualKeyword(token); 3890} 3891 3892/** @internal */ 3893export function isStringAKeyword(name: string) { 3894 const token = stringToToken(name); 3895 return token !== undefined && isKeyword(token); 3896} 3897 3898/** @internal */ 3899export function isIdentifierANonContextualKeyword({ originalKeywordKind }: Identifier): boolean { 3900 return !!originalKeywordKind && !isContextualKeyword(originalKeywordKind); 3901} 3902 3903/** @internal */ 3904export function isTrivia(token: SyntaxKind): token is TriviaSyntaxKind { 3905 return SyntaxKind.FirstTriviaToken <= token && token <= SyntaxKind.LastTriviaToken; 3906} 3907 3908/** @internal */ 3909export const enum FunctionFlags { 3910 Normal = 0, // Function is a normal function 3911 Generator = 1 << 0, // Function is a generator function or async generator function 3912 Async = 1 << 1, // Function is an async function or an async generator function 3913 Invalid = 1 << 2, // Function is a signature or overload and does not have a body. 3914 AsyncGenerator = Async | Generator, // Function is an async generator function 3915} 3916 3917/** @internal */ 3918export function getFunctionFlags(node: SignatureDeclaration | undefined) { 3919 if (!node) { 3920 return FunctionFlags.Invalid; 3921 } 3922 3923 let flags = FunctionFlags.Normal; 3924 switch (node.kind) { 3925 case SyntaxKind.FunctionDeclaration: 3926 case SyntaxKind.FunctionExpression: 3927 case SyntaxKind.MethodDeclaration: 3928 if (node.asteriskToken) { 3929 flags |= FunctionFlags.Generator; 3930 } 3931 // falls through 3932 3933 case SyntaxKind.ArrowFunction: 3934 if (hasSyntacticModifier(node, ModifierFlags.Async)) { 3935 flags |= FunctionFlags.Async; 3936 } 3937 break; 3938 } 3939 3940 if (!(node as FunctionLikeDeclaration).body) { 3941 flags |= FunctionFlags.Invalid; 3942 } 3943 3944 return flags; 3945} 3946 3947/** @internal */ 3948export function isAsyncFunction(node: Node): boolean { 3949 switch (node.kind) { 3950 case SyntaxKind.FunctionDeclaration: 3951 case SyntaxKind.FunctionExpression: 3952 case SyntaxKind.ArrowFunction: 3953 case SyntaxKind.MethodDeclaration: 3954 return (node as FunctionLikeDeclaration).body !== undefined 3955 && (node as FunctionLikeDeclaration).asteriskToken === undefined 3956 && hasSyntacticModifier(node, ModifierFlags.Async); 3957 } 3958 return false; 3959} 3960 3961/** @internal */ 3962export function isStringOrNumericLiteralLike(node: Node): node is StringLiteralLike | NumericLiteral { 3963 return isStringLiteralLike(node) || isNumericLiteral(node); 3964} 3965 3966/** @internal */ 3967export function isSignedNumericLiteral(node: Node): node is PrefixUnaryExpression & { operand: NumericLiteral } { 3968 return isPrefixUnaryExpression(node) && (node.operator === SyntaxKind.PlusToken || node.operator === SyntaxKind.MinusToken) && isNumericLiteral(node.operand); 3969} 3970 3971/** 3972 * A declaration has a dynamic name if all of the following are true: 3973 * 1. The declaration has a computed property name. 3974 * 2. The computed name is *not* expressed as a StringLiteral. 3975 * 3. The computed name is *not* expressed as a NumericLiteral. 3976 * 4. The computed name is *not* expressed as a PlusToken or MinusToken 3977 * immediately followed by a NumericLiteral. 3978 * 3979 * @internal 3980 */ 3981export function hasDynamicName(declaration: Declaration): declaration is DynamicNamedDeclaration | DynamicNamedBinaryExpression { 3982 const name = getNameOfDeclaration(declaration); 3983 return !!name && isDynamicName(name); 3984} 3985 3986/** @internal */ 3987export function isDynamicName(name: DeclarationName): boolean { 3988 if (!(name.kind === SyntaxKind.ComputedPropertyName || name.kind === SyntaxKind.ElementAccessExpression)) { 3989 return false; 3990 } 3991 const expr = isElementAccessExpression(name) ? skipParentheses(name.argumentExpression) : name.expression; 3992 return !isStringOrNumericLiteralLike(expr) && 3993 !isSignedNumericLiteral(expr); 3994} 3995 3996/** @internal */ 3997export function getPropertyNameForPropertyNameNode(name: PropertyName): __String | undefined { 3998 switch (name.kind) { 3999 case SyntaxKind.Identifier: 4000 case SyntaxKind.PrivateIdentifier: 4001 return name.escapedText; 4002 case SyntaxKind.StringLiteral: 4003 case SyntaxKind.NumericLiteral: 4004 return escapeLeadingUnderscores(name.text); 4005 case SyntaxKind.ComputedPropertyName: 4006 const nameExpression = name.expression; 4007 if (isStringOrNumericLiteralLike(nameExpression)) { 4008 return escapeLeadingUnderscores(nameExpression.text); 4009 } 4010 else if (isSignedNumericLiteral(nameExpression)) { 4011 if (nameExpression.operator === SyntaxKind.MinusToken) { 4012 return tokenToString(nameExpression.operator) + nameExpression.operand.text as __String; 4013 } 4014 return nameExpression.operand.text as __String; 4015 } 4016 return undefined; 4017 default: 4018 return Debug.assertNever(name); 4019 } 4020} 4021 4022/** @internal */ 4023export function isPropertyNameLiteral(node: Node): node is PropertyNameLiteral { 4024 switch (node.kind) { 4025 case SyntaxKind.Identifier: 4026 case SyntaxKind.StringLiteral: 4027 case SyntaxKind.NoSubstitutionTemplateLiteral: 4028 case SyntaxKind.NumericLiteral: 4029 return true; 4030 default: 4031 return false; 4032 } 4033} 4034/** @internal */ 4035export function getTextOfIdentifierOrLiteral(node: PropertyNameLiteral | PrivateIdentifier): string { 4036 return isMemberName(node) ? idText(node) : node.text; 4037} 4038 4039/** @internal */ 4040export function getEscapedTextOfIdentifierOrLiteral(node: PropertyNameLiteral): __String { 4041 return isMemberName(node) ? node.escapedText : escapeLeadingUnderscores(node.text); 4042} 4043 4044/** @internal */ 4045export function getPropertyNameForUniqueESSymbol(symbol: Symbol): __String { 4046 return `__@${getSymbolId(symbol)}@${symbol.escapedName}` as __String; 4047} 4048 4049/** @internal */ 4050export function getSymbolNameForPrivateIdentifier(containingClassSymbol: Symbol, description: __String): __String { 4051 return `__#${getSymbolId(containingClassSymbol)}@${description}` as __String; 4052} 4053 4054/** @internal */ 4055export function isKnownSymbol(symbol: Symbol): boolean { 4056 return startsWith(symbol.escapedName as string, "__@"); 4057} 4058 4059/** @internal */ 4060export function isPrivateIdentifierSymbol(symbol: Symbol): boolean { 4061 return startsWith(symbol.escapedName as string, "__#"); 4062} 4063 4064/** 4065 * Includes the word "Symbol" with unicode escapes 4066 * 4067 * @internal 4068 */ 4069export function isESSymbolIdentifier(node: Node): boolean { 4070 return node.kind === SyntaxKind.Identifier && (node as Identifier).escapedText === "Symbol"; 4071} 4072 4073/** @internal */ 4074export function isPushOrUnshiftIdentifier(node: Identifier) { 4075 return node.escapedText === "push" || node.escapedText === "unshift"; 4076} 4077 4078/** @internal */ 4079export function isParameterDeclaration(node: VariableLikeDeclaration): boolean { 4080 const root = getRootDeclaration(node); 4081 return root.kind === SyntaxKind.Parameter; 4082} 4083 4084/** @internal */ 4085export function getRootDeclaration(node: Node): Node { 4086 while (node.kind === SyntaxKind.BindingElement) { 4087 node = node.parent.parent; 4088 } 4089 return node; 4090} 4091 4092/** @internal */ 4093export function nodeStartsNewLexicalEnvironment(node: Node): boolean { 4094 const kind = node.kind; 4095 return kind === SyntaxKind.Constructor 4096 || kind === SyntaxKind.FunctionExpression 4097 || kind === SyntaxKind.FunctionDeclaration 4098 || kind === SyntaxKind.ArrowFunction 4099 || kind === SyntaxKind.MethodDeclaration 4100 || kind === SyntaxKind.GetAccessor 4101 || kind === SyntaxKind.SetAccessor 4102 || kind === SyntaxKind.ModuleDeclaration 4103 || kind === SyntaxKind.SourceFile; 4104} 4105 4106/** @internal */ 4107export function nodeIsSynthesized(range: TextRange): boolean { 4108 return positionIsSynthesized(range.pos) 4109 || positionIsSynthesized(range.end); 4110} 4111 4112/** @internal */ 4113export function getOriginalSourceFile(sourceFile: SourceFile) { 4114 return getParseTreeNode(sourceFile, isSourceFile) || sourceFile; 4115} 4116 4117/** @internal */ 4118export const enum Associativity { 4119 Left, 4120 Right 4121} 4122 4123/** @internal */ 4124export function getExpressionAssociativity(expression: Expression) { 4125 const operator = getOperator(expression); 4126 const hasArguments = expression.kind === SyntaxKind.NewExpression && (expression as NewExpression).arguments !== undefined; 4127 return getOperatorAssociativity(expression.kind, operator, hasArguments); 4128} 4129 4130/** @internal */ 4131export function getOperatorAssociativity(kind: SyntaxKind, operator: SyntaxKind, hasArguments?: boolean) { 4132 switch (kind) { 4133 case SyntaxKind.NewExpression: 4134 return hasArguments ? Associativity.Left : Associativity.Right; 4135 4136 case SyntaxKind.PrefixUnaryExpression: 4137 case SyntaxKind.TypeOfExpression: 4138 case SyntaxKind.VoidExpression: 4139 case SyntaxKind.DeleteExpression: 4140 case SyntaxKind.AwaitExpression: 4141 case SyntaxKind.ConditionalExpression: 4142 case SyntaxKind.YieldExpression: 4143 return Associativity.Right; 4144 4145 case SyntaxKind.BinaryExpression: 4146 switch (operator) { 4147 case SyntaxKind.AsteriskAsteriskToken: 4148 case SyntaxKind.EqualsToken: 4149 case SyntaxKind.PlusEqualsToken: 4150 case SyntaxKind.MinusEqualsToken: 4151 case SyntaxKind.AsteriskAsteriskEqualsToken: 4152 case SyntaxKind.AsteriskEqualsToken: 4153 case SyntaxKind.SlashEqualsToken: 4154 case SyntaxKind.PercentEqualsToken: 4155 case SyntaxKind.LessThanLessThanEqualsToken: 4156 case SyntaxKind.GreaterThanGreaterThanEqualsToken: 4157 case SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken: 4158 case SyntaxKind.AmpersandEqualsToken: 4159 case SyntaxKind.CaretEqualsToken: 4160 case SyntaxKind.BarEqualsToken: 4161 case SyntaxKind.BarBarEqualsToken: 4162 case SyntaxKind.AmpersandAmpersandEqualsToken: 4163 case SyntaxKind.QuestionQuestionEqualsToken: 4164 return Associativity.Right; 4165 } 4166 } 4167 return Associativity.Left; 4168} 4169 4170/** @internal */ 4171export function getExpressionPrecedence(expression: Expression) { 4172 const operator = getOperator(expression); 4173 const hasArguments = expression.kind === SyntaxKind.NewExpression && (expression as NewExpression).arguments !== undefined; 4174 return getOperatorPrecedence(expression.kind, operator, hasArguments); 4175} 4176 4177/** @internal */ 4178export function getOperator(expression: Expression): SyntaxKind { 4179 if (expression.kind === SyntaxKind.BinaryExpression) { 4180 return (expression as BinaryExpression).operatorToken.kind; 4181 } 4182 else if (expression.kind === SyntaxKind.PrefixUnaryExpression || expression.kind === SyntaxKind.PostfixUnaryExpression) { 4183 return (expression as PrefixUnaryExpression | PostfixUnaryExpression).operator; 4184 } 4185 else { 4186 return expression.kind; 4187 } 4188} 4189 4190/** @internal */ 4191export const enum OperatorPrecedence { 4192 // Expression: 4193 // AssignmentExpression 4194 // Expression `,` AssignmentExpression 4195 Comma, 4196 4197 // NOTE: `Spread` is higher than `Comma` due to how it is parsed in |ElementList| 4198 // SpreadElement: 4199 // `...` AssignmentExpression 4200 Spread, 4201 4202 // AssignmentExpression: 4203 // ConditionalExpression 4204 // YieldExpression 4205 // ArrowFunction 4206 // AsyncArrowFunction 4207 // LeftHandSideExpression `=` AssignmentExpression 4208 // LeftHandSideExpression AssignmentOperator AssignmentExpression 4209 // 4210 // NOTE: AssignmentExpression is broken down into several precedences due to the requirements 4211 // of the parenthesizer rules. 4212 4213 // AssignmentExpression: YieldExpression 4214 // YieldExpression: 4215 // `yield` 4216 // `yield` AssignmentExpression 4217 // `yield` `*` AssignmentExpression 4218 Yield, 4219 4220 // AssignmentExpression: LeftHandSideExpression `=` AssignmentExpression 4221 // AssignmentExpression: LeftHandSideExpression AssignmentOperator AssignmentExpression 4222 // AssignmentOperator: one of 4223 // `*=` `/=` `%=` `+=` `-=` `<<=` `>>=` `>>>=` `&=` `^=` `|=` `**=` 4224 Assignment, 4225 4226 // NOTE: `Conditional` is considered higher than `Assignment` here, but in reality they have 4227 // the same precedence. 4228 // AssignmentExpression: ConditionalExpression 4229 // ConditionalExpression: 4230 // ShortCircuitExpression 4231 // ShortCircuitExpression `?` AssignmentExpression `:` AssignmentExpression 4232 // ShortCircuitExpression: 4233 // LogicalORExpression 4234 // CoalesceExpression 4235 Conditional, 4236 4237 // CoalesceExpression: 4238 // CoalesceExpressionHead `??` BitwiseORExpression 4239 // CoalesceExpressionHead: 4240 // CoalesceExpression 4241 // BitwiseORExpression 4242 Coalesce = Conditional, // NOTE: This is wrong 4243 4244 // LogicalORExpression: 4245 // LogicalANDExpression 4246 // LogicalORExpression `||` LogicalANDExpression 4247 LogicalOR, 4248 4249 // LogicalANDExpression: 4250 // BitwiseORExpression 4251 // LogicalANDExprerssion `&&` BitwiseORExpression 4252 LogicalAND, 4253 4254 // BitwiseORExpression: 4255 // BitwiseXORExpression 4256 // BitwiseORExpression `^` BitwiseXORExpression 4257 BitwiseOR, 4258 4259 // BitwiseXORExpression: 4260 // BitwiseANDExpression 4261 // BitwiseXORExpression `^` BitwiseANDExpression 4262 BitwiseXOR, 4263 4264 // BitwiseANDExpression: 4265 // EqualityExpression 4266 // BitwiseANDExpression `^` EqualityExpression 4267 BitwiseAND, 4268 4269 // EqualityExpression: 4270 // RelationalExpression 4271 // EqualityExpression `==` RelationalExpression 4272 // EqualityExpression `!=` RelationalExpression 4273 // EqualityExpression `===` RelationalExpression 4274 // EqualityExpression `!==` RelationalExpression 4275 Equality, 4276 4277 // RelationalExpression: 4278 // ShiftExpression 4279 // RelationalExpression `<` ShiftExpression 4280 // RelationalExpression `>` ShiftExpression 4281 // RelationalExpression `<=` ShiftExpression 4282 // RelationalExpression `>=` ShiftExpression 4283 // RelationalExpression `instanceof` ShiftExpression 4284 // RelationalExpression `in` ShiftExpression 4285 // [+TypeScript] RelationalExpression `as` Type 4286 Relational, 4287 4288 // ShiftExpression: 4289 // AdditiveExpression 4290 // ShiftExpression `<<` AdditiveExpression 4291 // ShiftExpression `>>` AdditiveExpression 4292 // ShiftExpression `>>>` AdditiveExpression 4293 Shift, 4294 4295 // AdditiveExpression: 4296 // MultiplicativeExpression 4297 // AdditiveExpression `+` MultiplicativeExpression 4298 // AdditiveExpression `-` MultiplicativeExpression 4299 Additive, 4300 4301 // MultiplicativeExpression: 4302 // ExponentiationExpression 4303 // MultiplicativeExpression MultiplicativeOperator ExponentiationExpression 4304 // MultiplicativeOperator: one of `*`, `/`, `%` 4305 Multiplicative, 4306 4307 // ExponentiationExpression: 4308 // UnaryExpression 4309 // UpdateExpression `**` ExponentiationExpression 4310 Exponentiation, 4311 4312 // UnaryExpression: 4313 // UpdateExpression 4314 // `delete` UnaryExpression 4315 // `void` UnaryExpression 4316 // `typeof` UnaryExpression 4317 // `+` UnaryExpression 4318 // `-` UnaryExpression 4319 // `~` UnaryExpression 4320 // `!` UnaryExpression 4321 // AwaitExpression 4322 // UpdateExpression: // TODO: Do we need to investigate the precedence here? 4323 // `++` UnaryExpression 4324 // `--` UnaryExpression 4325 Unary, 4326 4327 4328 // UpdateExpression: 4329 // LeftHandSideExpression 4330 // LeftHandSideExpression `++` 4331 // LeftHandSideExpression `--` 4332 Update, 4333 4334 // LeftHandSideExpression: 4335 // NewExpression 4336 // CallExpression 4337 // NewExpression: 4338 // MemberExpression 4339 // `new` NewExpression 4340 LeftHandSide, 4341 4342 // CallExpression: 4343 // CoverCallExpressionAndAsyncArrowHead 4344 // SuperCall 4345 // ImportCall 4346 // CallExpression Arguments 4347 // CallExpression `[` Expression `]` 4348 // CallExpression `.` IdentifierName 4349 // CallExpression TemplateLiteral 4350 // MemberExpression: 4351 // PrimaryExpression 4352 // MemberExpression `[` Expression `]` 4353 // MemberExpression `.` IdentifierName 4354 // MemberExpression TemplateLiteral 4355 // SuperProperty 4356 // MetaProperty 4357 // `new` MemberExpression Arguments 4358 Member, 4359 4360 // TODO: JSXElement? 4361 // PrimaryExpression: 4362 // `this` 4363 // IdentifierReference 4364 // Literal 4365 // ArrayLiteral 4366 // ObjectLiteral 4367 // FunctionExpression 4368 // ClassExpression 4369 // GeneratorExpression 4370 // AsyncFunctionExpression 4371 // AsyncGeneratorExpression 4372 // RegularExpressionLiteral 4373 // TemplateLiteral 4374 // CoverParenthesizedExpressionAndArrowParameterList 4375 Primary, 4376 4377 Highest = Primary, 4378 Lowest = Comma, 4379 // -1 is lower than all other precedences. Returning it will cause binary expression 4380 // parsing to stop. 4381 Invalid = -1, 4382} 4383 4384/** @internal */ 4385export function getOperatorPrecedence(nodeKind: SyntaxKind, operatorKind: SyntaxKind, hasArguments?: boolean) { 4386 switch (nodeKind) { 4387 case SyntaxKind.CommaListExpression: 4388 return OperatorPrecedence.Comma; 4389 4390 case SyntaxKind.SpreadElement: 4391 return OperatorPrecedence.Spread; 4392 4393 case SyntaxKind.YieldExpression: 4394 return OperatorPrecedence.Yield; 4395 4396 case SyntaxKind.ConditionalExpression: 4397 return OperatorPrecedence.Conditional; 4398 4399 case SyntaxKind.BinaryExpression: 4400 switch (operatorKind) { 4401 case SyntaxKind.CommaToken: 4402 return OperatorPrecedence.Comma; 4403 4404 case SyntaxKind.EqualsToken: 4405 case SyntaxKind.PlusEqualsToken: 4406 case SyntaxKind.MinusEqualsToken: 4407 case SyntaxKind.AsteriskAsteriskEqualsToken: 4408 case SyntaxKind.AsteriskEqualsToken: 4409 case SyntaxKind.SlashEqualsToken: 4410 case SyntaxKind.PercentEqualsToken: 4411 case SyntaxKind.LessThanLessThanEqualsToken: 4412 case SyntaxKind.GreaterThanGreaterThanEqualsToken: 4413 case SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken: 4414 case SyntaxKind.AmpersandEqualsToken: 4415 case SyntaxKind.CaretEqualsToken: 4416 case SyntaxKind.BarEqualsToken: 4417 case SyntaxKind.BarBarEqualsToken: 4418 case SyntaxKind.AmpersandAmpersandEqualsToken: 4419 case SyntaxKind.QuestionQuestionEqualsToken: 4420 return OperatorPrecedence.Assignment; 4421 4422 default: 4423 return getBinaryOperatorPrecedence(operatorKind); 4424 } 4425 4426 // TODO: Should prefix `++` and `--` be moved to the `Update` precedence? 4427 case SyntaxKind.TypeAssertionExpression: 4428 case SyntaxKind.NonNullExpression: 4429 case SyntaxKind.PrefixUnaryExpression: 4430 case SyntaxKind.TypeOfExpression: 4431 case SyntaxKind.VoidExpression: 4432 case SyntaxKind.DeleteExpression: 4433 case SyntaxKind.AwaitExpression: 4434 return OperatorPrecedence.Unary; 4435 4436 case SyntaxKind.PostfixUnaryExpression: 4437 return OperatorPrecedence.Update; 4438 4439 case SyntaxKind.CallExpression: 4440 return OperatorPrecedence.LeftHandSide; 4441 4442 case SyntaxKind.NewExpression: 4443 return hasArguments ? OperatorPrecedence.Member : OperatorPrecedence.LeftHandSide; 4444 4445 case SyntaxKind.TaggedTemplateExpression: 4446 case SyntaxKind.PropertyAccessExpression: 4447 case SyntaxKind.ElementAccessExpression: 4448 case SyntaxKind.MetaProperty: 4449 return OperatorPrecedence.Member; 4450 4451 case SyntaxKind.AsExpression: 4452 case SyntaxKind.SatisfiesExpression: 4453 return OperatorPrecedence.Relational; 4454 4455 case SyntaxKind.ThisKeyword: 4456 case SyntaxKind.SuperKeyword: 4457 case SyntaxKind.Identifier: 4458 case SyntaxKind.PrivateIdentifier: 4459 case SyntaxKind.NullKeyword: 4460 case SyntaxKind.TrueKeyword: 4461 case SyntaxKind.FalseKeyword: 4462 case SyntaxKind.NumericLiteral: 4463 case SyntaxKind.BigIntLiteral: 4464 case SyntaxKind.StringLiteral: 4465 case SyntaxKind.ArrayLiteralExpression: 4466 case SyntaxKind.ObjectLiteralExpression: 4467 case SyntaxKind.FunctionExpression: 4468 case SyntaxKind.ArrowFunction: 4469 case SyntaxKind.ClassExpression: 4470 case SyntaxKind.RegularExpressionLiteral: 4471 case SyntaxKind.NoSubstitutionTemplateLiteral: 4472 case SyntaxKind.TemplateExpression: 4473 case SyntaxKind.ParenthesizedExpression: 4474 case SyntaxKind.OmittedExpression: 4475 case SyntaxKind.JsxElement: 4476 case SyntaxKind.JsxSelfClosingElement: 4477 case SyntaxKind.JsxFragment: 4478 return OperatorPrecedence.Primary; 4479 4480 default: 4481 return OperatorPrecedence.Invalid; 4482 } 4483} 4484 4485/** @internal */ 4486export function getBinaryOperatorPrecedence(kind: SyntaxKind): OperatorPrecedence { 4487 switch (kind) { 4488 case SyntaxKind.QuestionQuestionToken: 4489 return OperatorPrecedence.Coalesce; 4490 case SyntaxKind.BarBarToken: 4491 return OperatorPrecedence.LogicalOR; 4492 case SyntaxKind.AmpersandAmpersandToken: 4493 return OperatorPrecedence.LogicalAND; 4494 case SyntaxKind.BarToken: 4495 return OperatorPrecedence.BitwiseOR; 4496 case SyntaxKind.CaretToken: 4497 return OperatorPrecedence.BitwiseXOR; 4498 case SyntaxKind.AmpersandToken: 4499 return OperatorPrecedence.BitwiseAND; 4500 case SyntaxKind.EqualsEqualsToken: 4501 case SyntaxKind.ExclamationEqualsToken: 4502 case SyntaxKind.EqualsEqualsEqualsToken: 4503 case SyntaxKind.ExclamationEqualsEqualsToken: 4504 return OperatorPrecedence.Equality; 4505 case SyntaxKind.LessThanToken: 4506 case SyntaxKind.GreaterThanToken: 4507 case SyntaxKind.LessThanEqualsToken: 4508 case SyntaxKind.GreaterThanEqualsToken: 4509 case SyntaxKind.InstanceOfKeyword: 4510 case SyntaxKind.InKeyword: 4511 case SyntaxKind.AsKeyword: 4512 case SyntaxKind.SatisfiesKeyword: 4513 return OperatorPrecedence.Relational; 4514 case SyntaxKind.LessThanLessThanToken: 4515 case SyntaxKind.GreaterThanGreaterThanToken: 4516 case SyntaxKind.GreaterThanGreaterThanGreaterThanToken: 4517 return OperatorPrecedence.Shift; 4518 case SyntaxKind.PlusToken: 4519 case SyntaxKind.MinusToken: 4520 return OperatorPrecedence.Additive; 4521 case SyntaxKind.AsteriskToken: 4522 case SyntaxKind.SlashToken: 4523 case SyntaxKind.PercentToken: 4524 return OperatorPrecedence.Multiplicative; 4525 case SyntaxKind.AsteriskAsteriskToken: 4526 return OperatorPrecedence.Exponentiation; 4527 } 4528 4529 // -1 is lower than all other precedences. Returning it will cause binary expression 4530 // parsing to stop. 4531 return -1; 4532} 4533 4534/** @internal */ 4535export function getSemanticJsxChildren(children: readonly JsxChild[]) { 4536 return filter(children, i => { 4537 switch (i.kind) { 4538 case SyntaxKind.JsxExpression: 4539 return !!i.expression; 4540 case SyntaxKind.JsxText: 4541 return !i.containsOnlyTriviaWhiteSpaces; 4542 default: 4543 return true; 4544 } 4545 }); 4546} 4547 4548/** @internal */ 4549export function createDiagnosticCollection(): DiagnosticCollection { 4550 let nonFileDiagnostics = [] as Diagnostic[] as SortedArray<Diagnostic>; // See GH#19873 4551 const filesWithDiagnostics = [] as string[] as SortedArray<string>; 4552 const fileDiagnostics = new Map<string, SortedArray<DiagnosticWithLocation>>(); 4553 let hasReadNonFileDiagnostics = false; 4554 4555 return { 4556 add, 4557 lookup, 4558 getGlobalDiagnostics, 4559 getDiagnostics, 4560 }; 4561 4562 function lookup(diagnostic: Diagnostic): Diagnostic | undefined { 4563 let diagnostics: SortedArray<Diagnostic> | undefined; 4564 if (diagnostic.file) { 4565 diagnostics = fileDiagnostics.get(diagnostic.file.fileName); 4566 } 4567 else { 4568 diagnostics = nonFileDiagnostics; 4569 } 4570 if (!diagnostics) { 4571 return undefined; 4572 } 4573 const result = binarySearch(diagnostics, diagnostic, identity, compareDiagnosticsSkipRelatedInformation); 4574 if (result >= 0) { 4575 return diagnostics[result]; 4576 } 4577 return undefined; 4578 } 4579 4580 function add(diagnostic: Diagnostic): void { 4581 let diagnostics: SortedArray<Diagnostic> | undefined; 4582 if (diagnostic.file) { 4583 diagnostics = fileDiagnostics.get(diagnostic.file.fileName); 4584 if (!diagnostics) { 4585 diagnostics = [] as Diagnostic[] as SortedArray<DiagnosticWithLocation>; // See GH#19873 4586 fileDiagnostics.set(diagnostic.file.fileName, diagnostics as SortedArray<DiagnosticWithLocation>); 4587 insertSorted(filesWithDiagnostics, diagnostic.file.fileName, compareStringsCaseSensitive); 4588 } 4589 } 4590 else { 4591 // If we've already read the non-file diagnostics, do not modify the existing array. 4592 if (hasReadNonFileDiagnostics) { 4593 hasReadNonFileDiagnostics = false; 4594 nonFileDiagnostics = nonFileDiagnostics.slice() as SortedArray<Diagnostic>; 4595 } 4596 4597 diagnostics = nonFileDiagnostics; 4598 } 4599 4600 insertSorted(diagnostics, diagnostic, compareDiagnosticsSkipRelatedInformation); 4601 } 4602 4603 function getGlobalDiagnostics(): Diagnostic[] { 4604 hasReadNonFileDiagnostics = true; 4605 return nonFileDiagnostics; 4606 } 4607 4608 function getDiagnostics(fileName: string): DiagnosticWithLocation[]; 4609 function getDiagnostics(): Diagnostic[]; 4610 function getDiagnostics(fileName?: string): Diagnostic[] { 4611 if (fileName) { 4612 return fileDiagnostics.get(fileName) || []; 4613 } 4614 4615 const fileDiags: Diagnostic[] = flatMapToMutable(filesWithDiagnostics, f => fileDiagnostics.get(f)); 4616 if (!nonFileDiagnostics.length) { 4617 return fileDiags; 4618 } 4619 fileDiags.unshift(...nonFileDiagnostics); 4620 return fileDiags; 4621 } 4622} 4623 4624const templateSubstitutionRegExp = /\$\{/g; 4625function escapeTemplateSubstitution(str: string): string { 4626 return str.replace(templateSubstitutionRegExp, "\\${"); 4627} 4628 4629/** @internal */ 4630export function hasInvalidEscape(template: TemplateLiteral): boolean { 4631 return template && !!(isNoSubstitutionTemplateLiteral(template) 4632 ? template.templateFlags 4633 : (template.head.templateFlags || some(template.templateSpans, span => !!span.literal.templateFlags))); 4634} 4635 4636// This consists of the first 19 unprintable ASCII characters, canonical escapes, lineSeparator, 4637// paragraphSeparator, and nextLine. The latter three are just desirable to suppress new lines in 4638// the language service. These characters should be escaped when printing, and if any characters are added, 4639// the map below must be updated. Note that this regexp *does not* include the 'delete' character. 4640// There is no reason for this other than that JSON.stringify does not handle it either. 4641const doubleQuoteEscapedCharsRegExp = /[\\\"\u0000-\u001f\t\v\f\b\r\n\u2028\u2029\u0085]/g; 4642const singleQuoteEscapedCharsRegExp = /[\\\'\u0000-\u001f\t\v\f\b\r\n\u2028\u2029\u0085]/g; 4643// Template strings preserve simple LF newlines, still encode CRLF (or CR) 4644const backtickQuoteEscapedCharsRegExp = /\r\n|[\\\`\u0000-\u001f\t\v\f\b\r\u2028\u2029\u0085]/g; 4645const escapedCharsMap = new Map(getEntries({ 4646 "\t": "\\t", 4647 "\v": "\\v", 4648 "\f": "\\f", 4649 "\b": "\\b", 4650 "\r": "\\r", 4651 "\n": "\\n", 4652 "\\": "\\\\", 4653 "\"": "\\\"", 4654 "\'": "\\\'", 4655 "\`": "\\\`", 4656 "\u2028": "\\u2028", // lineSeparator 4657 "\u2029": "\\u2029", // paragraphSeparator 4658 "\u0085": "\\u0085", // nextLine 4659 "\r\n": "\\r\\n", // special case for CRLFs in backticks 4660})); 4661 4662function encodeUtf16EscapeSequence(charCode: number): string { 4663 const hexCharCode = charCode.toString(16).toUpperCase(); 4664 const paddedHexCode = ("0000" + hexCharCode).slice(-4); 4665 return "\\u" + paddedHexCode; 4666} 4667 4668function getReplacement(c: string, offset: number, input: string) { 4669 if (c.charCodeAt(0) === CharacterCodes.nullCharacter) { 4670 const lookAhead = input.charCodeAt(offset + c.length); 4671 if (lookAhead >= CharacterCodes._0 && lookAhead <= CharacterCodes._9) { 4672 // If the null character is followed by digits, print as a hex escape to prevent the result from parsing as an octal (which is forbidden in strict mode) 4673 return "\\x00"; 4674 } 4675 // Otherwise, keep printing a literal \0 for the null character 4676 return "\\0"; 4677 } 4678 return escapedCharsMap.get(c) || encodeUtf16EscapeSequence(c.charCodeAt(0)); 4679} 4680 4681/** 4682 * Based heavily on the abstract 'Quote'/'QuoteJSONString' operation from ECMA-262 (24.3.2.2), 4683 * but augmented for a few select characters (e.g. lineSeparator, paragraphSeparator, nextLine) 4684 * Note that this doesn't actually wrap the input in double quotes. 4685 * 4686 * @internal 4687 */ 4688export function escapeString(s: string, quoteChar?: CharacterCodes.doubleQuote | CharacterCodes.singleQuote | CharacterCodes.backtick): string { 4689 const escapedCharsRegExp = 4690 quoteChar === CharacterCodes.backtick ? backtickQuoteEscapedCharsRegExp : 4691 quoteChar === CharacterCodes.singleQuote ? singleQuoteEscapedCharsRegExp : 4692 doubleQuoteEscapedCharsRegExp; 4693 return s.replace(escapedCharsRegExp, getReplacement); 4694} 4695 4696const nonAsciiCharacters = /[^\u0000-\u007F]/g; 4697/** @internal */ 4698export function escapeNonAsciiString(s: string, quoteChar?: CharacterCodes.doubleQuote | CharacterCodes.singleQuote | CharacterCodes.backtick): string { 4699 s = escapeString(s, quoteChar); 4700 // Replace non-ASCII characters with '\uNNNN' escapes if any exist. 4701 // Otherwise just return the original string. 4702 return nonAsciiCharacters.test(s) ? 4703 s.replace(nonAsciiCharacters, c => encodeUtf16EscapeSequence(c.charCodeAt(0))) : 4704 s; 4705} 4706 4707// This consists of the first 19 unprintable ASCII characters, JSX canonical escapes, lineSeparator, 4708// paragraphSeparator, and nextLine. The latter three are just desirable to suppress new lines in 4709// the language service. These characters should be escaped when printing, and if any characters are added, 4710// the map below must be updated. 4711const jsxDoubleQuoteEscapedCharsRegExp = /[\"\u0000-\u001f\u2028\u2029\u0085]/g; 4712const jsxSingleQuoteEscapedCharsRegExp = /[\'\u0000-\u001f\u2028\u2029\u0085]/g; 4713const jsxEscapedCharsMap = new Map(getEntries({ 4714 "\"": """, 4715 "\'": "'" 4716})); 4717 4718function encodeJsxCharacterEntity(charCode: number): string { 4719 const hexCharCode = charCode.toString(16).toUpperCase(); 4720 return "&#x" + hexCharCode + ";"; 4721} 4722 4723function getJsxAttributeStringReplacement(c: string) { 4724 if (c.charCodeAt(0) === CharacterCodes.nullCharacter) { 4725 return "�"; 4726 } 4727 return jsxEscapedCharsMap.get(c) || encodeJsxCharacterEntity(c.charCodeAt(0)); 4728} 4729 4730/** @internal */ 4731export function escapeJsxAttributeString(s: string, quoteChar?: CharacterCodes.doubleQuote | CharacterCodes.singleQuote) { 4732 const escapedCharsRegExp = 4733 quoteChar === CharacterCodes.singleQuote ? jsxSingleQuoteEscapedCharsRegExp : 4734 jsxDoubleQuoteEscapedCharsRegExp; 4735 return s.replace(escapedCharsRegExp, getJsxAttributeStringReplacement); 4736} 4737 4738/** 4739 * Strip off existed surrounding single quotes, double quotes, or backticks from a given string 4740 * 4741 * @return non-quoted string 4742 * 4743 * @internal 4744 */ 4745export function stripQuotes(name: string) { 4746 const length = name.length; 4747 if (length >= 2 && name.charCodeAt(0) === name.charCodeAt(length - 1) && isQuoteOrBacktick(name.charCodeAt(0))) { 4748 return name.substring(1, length - 1); 4749 } 4750 return name; 4751} 4752 4753function isQuoteOrBacktick(charCode: number) { 4754 return charCode === CharacterCodes.singleQuote || 4755 charCode === CharacterCodes.doubleQuote || 4756 charCode === CharacterCodes.backtick; 4757} 4758 4759/** @internal */ 4760export function isIntrinsicJsxName(name: __String | string) { 4761 const ch = (name as string).charCodeAt(0); 4762 return (ch >= CharacterCodes.a && ch <= CharacterCodes.z) || stringContains((name as string), "-") || stringContains((name as string), ":"); 4763} 4764 4765const indentStrings: string[] = ["", " "]; 4766/** @internal */ 4767export function getIndentString(level: number) { 4768 // prepopulate cache 4769 const singleLevel = indentStrings[1]; 4770 for (let current = indentStrings.length; current <= level; current++) { 4771 indentStrings.push(indentStrings[current - 1] + singleLevel); 4772 } 4773 return indentStrings[level]; 4774} 4775 4776/** @internal */ 4777export function getIndentSize() { 4778 return indentStrings[1].length; 4779} 4780 4781/** @internal */ 4782export function isNightly() { 4783 return stringContains(version, "-dev") || stringContains(version, "-insiders"); 4784} 4785 4786/** @internal */ 4787export function getTrailingSemicolonDeferringWriter(writer: EmitTextWriter): EmitTextWriter { 4788 let pendingTrailingSemicolon = false; 4789 4790 function commitPendingTrailingSemicolon() { 4791 if (pendingTrailingSemicolon) { 4792 writer.writeTrailingSemicolon(";"); 4793 pendingTrailingSemicolon = false; 4794 } 4795 } 4796 4797 return { 4798 ...writer, 4799 writeTrailingSemicolon() { 4800 pendingTrailingSemicolon = true; 4801 }, 4802 writeLiteral(s) { 4803 commitPendingTrailingSemicolon(); 4804 writer.writeLiteral(s); 4805 }, 4806 writeStringLiteral(s) { 4807 commitPendingTrailingSemicolon(); 4808 writer.writeStringLiteral(s); 4809 }, 4810 writeSymbol(s, sym) { 4811 commitPendingTrailingSemicolon(); 4812 writer.writeSymbol(s, sym); 4813 }, 4814 writePunctuation(s) { 4815 commitPendingTrailingSemicolon(); 4816 writer.writePunctuation(s); 4817 }, 4818 writeKeyword(s) { 4819 commitPendingTrailingSemicolon(); 4820 writer.writeKeyword(s); 4821 }, 4822 writeOperator(s) { 4823 commitPendingTrailingSemicolon(); 4824 writer.writeOperator(s); 4825 }, 4826 writeParameter(s) { 4827 commitPendingTrailingSemicolon(); 4828 writer.writeParameter(s); 4829 }, 4830 writeSpace(s) { 4831 commitPendingTrailingSemicolon(); 4832 writer.writeSpace(s); 4833 }, 4834 writeProperty(s) { 4835 commitPendingTrailingSemicolon(); 4836 writer.writeProperty(s); 4837 }, 4838 writeComment(s) { 4839 commitPendingTrailingSemicolon(); 4840 writer.writeComment(s); 4841 }, 4842 writeLine() { 4843 commitPendingTrailingSemicolon(); 4844 writer.writeLine(); 4845 }, 4846 increaseIndent() { 4847 commitPendingTrailingSemicolon(); 4848 writer.increaseIndent(); 4849 }, 4850 decreaseIndent() { 4851 commitPendingTrailingSemicolon(); 4852 writer.decreaseIndent(); 4853 }, 4854 }; 4855} 4856 4857/** @internal */ 4858export function hostUsesCaseSensitiveFileNames(host: { useCaseSensitiveFileNames?(): boolean; }): boolean { 4859 return host.useCaseSensitiveFileNames ? host.useCaseSensitiveFileNames() : false; 4860} 4861 4862/** @internal */ 4863export function hostGetCanonicalFileName(host: { useCaseSensitiveFileNames?(): boolean; }): GetCanonicalFileName { 4864 return createGetCanonicalFileName(hostUsesCaseSensitiveFileNames(host)); 4865} 4866 4867/** @internal */ 4868export interface ResolveModuleNameResolutionHost { 4869 getCanonicalFileName(p: string): string; 4870 getCommonSourceDirectory(): string; 4871 getCurrentDirectory(): string; 4872} 4873 4874/** @internal */ 4875export function getResolvedExternalModuleName(host: ResolveModuleNameResolutionHost, file: SourceFile, referenceFile?: SourceFile): string { 4876 return file.moduleName || getExternalModuleNameFromPath(host, file.fileName, referenceFile && referenceFile.fileName); 4877} 4878 4879function getCanonicalAbsolutePath(host: ResolveModuleNameResolutionHost, path: string) { 4880 return host.getCanonicalFileName(getNormalizedAbsolutePath(path, host.getCurrentDirectory())); 4881} 4882 4883/** @internal */ 4884export function getExternalModuleNameFromDeclaration(host: ResolveModuleNameResolutionHost, resolver: EmitResolver, declaration: ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration | ModuleDeclaration | ImportTypeNode): string | undefined { 4885 const file = resolver.getExternalModuleFileFromDeclaration(declaration); 4886 if (!file || file.isDeclarationFile) { 4887 return undefined; 4888 } 4889 // If the declaration already uses a non-relative name, and is outside the common source directory, continue to use it 4890 const specifier = getExternalModuleName(declaration); 4891 if (specifier && isStringLiteralLike(specifier) && !pathIsRelative(specifier.text) && 4892 getCanonicalAbsolutePath(host, file.path).indexOf(getCanonicalAbsolutePath(host, ensureTrailingDirectorySeparator(host.getCommonSourceDirectory()))) === -1) { 4893 return undefined; 4894 } 4895 return getResolvedExternalModuleName(host, file); 4896} 4897 4898/** 4899 * Resolves a local path to a path which is absolute to the base of the emit 4900 * 4901 * @internal 4902 */ 4903export function getExternalModuleNameFromPath(host: ResolveModuleNameResolutionHost, fileName: string, referencePath?: string): string { 4904 const getCanonicalFileName = (f: string) => host.getCanonicalFileName(f); 4905 const dir = toPath(referencePath ? getDirectoryPath(referencePath) : host.getCommonSourceDirectory(), host.getCurrentDirectory(), getCanonicalFileName); 4906 const filePath = getNormalizedAbsolutePath(fileName, host.getCurrentDirectory()); 4907 const relativePath = getRelativePathToDirectoryOrUrl(dir, filePath, dir, getCanonicalFileName, /*isAbsolutePathAnUrl*/ false); 4908 const extensionless = removeFileExtension(relativePath); 4909 return referencePath ? ensurePathIsNonModuleName(extensionless) : extensionless; 4910} 4911 4912/** @internal */ 4913export function getOwnEmitOutputFilePath(fileName: string, host: EmitHost, extension: string) { 4914 const compilerOptions = host.getCompilerOptions(); 4915 let emitOutputFilePathWithoutExtension: string; 4916 if (compilerOptions.outDir) { 4917 emitOutputFilePathWithoutExtension = removeFileExtension(getSourceFilePathInNewDir(fileName, host, compilerOptions.outDir)); 4918 } 4919 else { 4920 emitOutputFilePathWithoutExtension = removeFileExtension(fileName); 4921 } 4922 4923 return emitOutputFilePathWithoutExtension + extension; 4924} 4925 4926/** @internal */ 4927export function getDeclarationEmitOutputFilePath(fileName: string, host: EmitHost) { 4928 return getDeclarationEmitOutputFilePathWorker(fileName, host.getCompilerOptions(), host.getCurrentDirectory(), host.getCommonSourceDirectory(), f => host.getCanonicalFileName(f)); 4929} 4930 4931/** @internal */ 4932export function getDeclarationEmitOutputFilePathWorker(fileName: string, options: CompilerOptions, currentDirectory: string, commonSourceDirectory: string, getCanonicalFileName: GetCanonicalFileName): string { 4933 const outputDir = options.declarationDir || options.outDir; // Prefer declaration folder if specified 4934 4935 const path = outputDir 4936 ? getSourceFilePathInNewDirWorker(fileName, outputDir, currentDirectory, commonSourceDirectory, getCanonicalFileName) 4937 : fileName; 4938 const declarationExtension = getDeclarationEmitExtensionForPath(path); 4939 return removeFileExtension(path) + declarationExtension; 4940} 4941 4942/** @internal */ 4943export function getDeclarationEmitExtensionForPath(path: string) { 4944 return fileExtensionIsOneOf(path, [Extension.Mjs, Extension.Mts]) ? Extension.Dmts : 4945 fileExtensionIsOneOf(path, [Extension.Cjs, Extension.Cts]) ? Extension.Dcts : 4946 fileExtensionIsOneOf(path, [Extension.Json]) ? `.json.d.ts` : // Drive-by redefinition of json declaration file output name so if it's ever enabled, it behaves well 4947 fileExtensionIsOneOf(path, [Extension.Ets]) ? Extension.Dets : Extension.Dts; 4948} 4949 4950/** 4951 * This function is an inverse of `getDeclarationEmitExtensionForPath`. 4952 * 4953 * @internal 4954 */ 4955export function getPossibleOriginalInputExtensionForExtension(path: string) { 4956 return fileExtensionIsOneOf(path, [Extension.Dmts, Extension.Mjs, Extension.Mts]) ? [Extension.Mts, Extension.Mjs] : 4957 fileExtensionIsOneOf(path, [Extension.Dcts, Extension.Cjs, Extension.Cts]) ? [Extension.Cts, Extension.Cjs]: 4958 fileExtensionIsOneOf(path, [`.json.d.ts`]) ? [Extension.Json] : 4959 [Extension.Tsx, Extension.Ts, Extension.Jsx, Extension.Js]; 4960} 4961 4962/** @internal */ 4963export function outFile(options: CompilerOptions) { 4964 return options.outFile || options.out; 4965} 4966 4967/** 4968 * Returns 'undefined' if and only if 'options.paths' is undefined. 4969 * 4970 * @internal 4971 */ 4972export function getPathsBasePath(options: CompilerOptions, host: { getCurrentDirectory?(): string }) { 4973 if (!options.paths) return undefined; 4974 return options.baseUrl ?? Debug.checkDefined(options.pathsBasePath || host.getCurrentDirectory?.(), "Encountered 'paths' without a 'baseUrl', config file, or host 'getCurrentDirectory'."); 4975} 4976 4977/** @internal */ 4978export interface EmitFileNames { 4979 jsFilePath?: string | undefined; 4980 sourceMapFilePath?: string | undefined; 4981 declarationFilePath?: string | undefined; 4982 declarationMapPath?: string | undefined; 4983 buildInfoPath?: string | undefined; 4984} 4985 4986/** 4987 * Gets the source files that are expected to have an emit output. 4988 * 4989 * Originally part of `forEachExpectedEmitFile`, this functionality was extracted to support 4990 * transformations. 4991 * 4992 * @param host An EmitHost. 4993 * @param targetSourceFile An optional target source file to emit. 4994 * 4995 * @internal 4996 */ 4997export function getSourceFilesToEmit(host: EmitHost, targetSourceFile?: SourceFile, forceDtsEmit?: boolean): readonly SourceFile[] { 4998 const options = host.getCompilerOptions(); 4999 if (outFile(options)) { 5000 const moduleKind = getEmitModuleKind(options); 5001 const moduleEmitEnabled = options.emitDeclarationOnly || moduleKind === ModuleKind.AMD || moduleKind === ModuleKind.System; 5002 // Can emit only sources that are not declaration file and are either non module code or module with --module or --target es6 specified 5003 return filter( 5004 host.getSourceFiles(), 5005 sourceFile => 5006 (moduleEmitEnabled || !isExternalModule(sourceFile)) && 5007 sourceFileMayBeEmitted(sourceFile, host, forceDtsEmit) 5008 ); 5009 } 5010 else { 5011 const sourceFiles = targetSourceFile === undefined ? host.getSourceFiles() : [targetSourceFile]; 5012 return filter( 5013 sourceFiles, 5014 sourceFile => sourceFileMayBeEmitted(sourceFile, host, forceDtsEmit) 5015 ); 5016 } 5017} 5018 5019/** 5020 * Don't call this for `--outFile`, just for `--outDir` or plain emit. `--outFile` needs additional checks. 5021 * 5022 * @internal 5023 */ 5024export function sourceFileMayBeEmitted(sourceFile: SourceFile, host: SourceFileMayBeEmittedHost, forceDtsEmit?: boolean) { 5025 const options = host.getCompilerOptions(); 5026 return !(options.noEmitForJsFiles && isSourceFileJS(sourceFile)) && 5027 !sourceFile.isDeclarationFile && 5028 (!host.isSourceFileFromExternalLibrary(sourceFile) || isEmitNodeModulesFiles(host.getCompilerOptions().emitNodeModulesFiles)) && 5029 (forceDtsEmit || ( 5030 !(isJsonSourceFile(sourceFile) && host.getResolvedProjectReferenceToRedirect(sourceFile.fileName)) && 5031 !host.isSourceOfProjectReferenceRedirect(sourceFile.fileName) 5032 )); 5033} 5034 5035/** @internal */ 5036export function getSourceFilePathInNewDir(fileName: string, host: EmitHost, newDirPath: string): string { 5037 return getSourceFilePathInNewDirWorker(fileName, newDirPath, host.getCurrentDirectory(), host.getCommonSourceDirectory(), f => host.getCanonicalFileName(f)); 5038} 5039 5040/** @internal */ 5041export function getSourceFilePathInNewDirWorker(fileName: string, newDirPath: string, currentDirectory: string, commonSourceDirectory: string, getCanonicalFileName: GetCanonicalFileName): string { 5042 let sourceFilePath = getNormalizedAbsolutePath(fileName, currentDirectory); 5043 const isSourceFileInCommonSourceDirectory = getCanonicalFileName(sourceFilePath).indexOf(getCanonicalFileName(commonSourceDirectory)) === 0; 5044 sourceFilePath = isSourceFileInCommonSourceDirectory ? sourceFilePath.substring(commonSourceDirectory.length) : sourceFilePath; 5045 return combinePaths(newDirPath, sourceFilePath); 5046} 5047 5048/** @internal */ 5049export function writeFile(host: { writeFile: WriteFileCallback; }, diagnostics: DiagnosticCollection, fileName: string, text: string, writeByteOrderMark: boolean, sourceFiles?: readonly SourceFile[], data?: WriteFileCallbackData) { 5050 host.writeFile(fileName, text, writeByteOrderMark, hostErrorMessage => { 5051 diagnostics.add(createCompilerDiagnostic(Diagnostics.Could_not_write_file_0_Colon_1, fileName, hostErrorMessage)); 5052 }, sourceFiles, data); 5053} 5054 5055function ensureDirectoriesExist( 5056 directoryPath: string, 5057 createDirectory: (path: string) => void, 5058 directoryExists: (path: string) => boolean): void { 5059 if (directoryPath.length > getRootLength(directoryPath) && !directoryExists(directoryPath)) { 5060 const parentDirectory = getDirectoryPath(directoryPath); 5061 ensureDirectoriesExist(parentDirectory, createDirectory, directoryExists); 5062 createDirectory(directoryPath); 5063 } 5064} 5065 5066/** @internal */ 5067export function writeFileEnsuringDirectories( 5068 path: string, 5069 data: string, 5070 writeByteOrderMark: boolean, 5071 writeFile: (path: string, data: string, writeByteOrderMark: boolean) => void, 5072 createDirectory: (path: string) => void, 5073 directoryExists: (path: string) => boolean): void { 5074 5075 // PERF: Checking for directory existence is expensive. Instead, assume the directory exists 5076 // and fall back to creating it if the file write fails. 5077 try { 5078 writeFile(path, data, writeByteOrderMark); 5079 } 5080 catch { 5081 ensureDirectoriesExist(getDirectoryPath(normalizePath(path)), createDirectory, directoryExists); 5082 writeFile(path, data, writeByteOrderMark); 5083 } 5084} 5085 5086/** @internal */ 5087export function getLineOfLocalPosition(sourceFile: SourceFile, pos: number) { 5088 const lineStarts = getLineStarts(sourceFile); 5089 return computeLineOfPosition(lineStarts, pos); 5090} 5091 5092/** @internal */ 5093export function getLineOfLocalPositionFromLineMap(lineMap: readonly number[], pos: number) { 5094 return computeLineOfPosition(lineMap, pos); 5095} 5096 5097/** @internal */ 5098export function getFirstConstructorWithBody(node: ClassLikeDeclaration): ConstructorDeclaration & { body: FunctionBody } | undefined { 5099 return find(node.members, (member): member is ConstructorDeclaration & { body: FunctionBody } => isConstructorDeclaration(member) && nodeIsPresent(member.body)); 5100} 5101 5102/** @internal */ 5103export function getSetAccessorValueParameter(accessor: SetAccessorDeclaration): ParameterDeclaration | undefined { 5104 if (accessor && accessor.parameters.length > 0) { 5105 const hasThis = accessor.parameters.length === 2 && parameterIsThisKeyword(accessor.parameters[0]); 5106 return accessor.parameters[hasThis ? 1 : 0]; 5107 } 5108} 5109 5110/** 5111 * Get the type annotation for the value parameter. 5112 * 5113 * @internal 5114 */ 5115export function getSetAccessorTypeAnnotationNode(accessor: SetAccessorDeclaration): TypeNode | undefined { 5116 const parameter = getSetAccessorValueParameter(accessor); 5117 return parameter && parameter.type; 5118} 5119 5120/** @internal */ 5121export function getThisParameter(signature: SignatureDeclaration | JSDocSignature): ParameterDeclaration | undefined { 5122 // callback tags do not currently support this parameters 5123 if (signature.parameters.length && !isJSDocSignature(signature)) { 5124 const thisParameter = signature.parameters[0]; 5125 if (parameterIsThisKeyword(thisParameter)) { 5126 return thisParameter; 5127 } 5128 } 5129} 5130 5131/** @internal */ 5132export function parameterIsThisKeyword(parameter: ParameterDeclaration): boolean { 5133 return isThisIdentifier(parameter.name); 5134} 5135 5136/** @internal */ 5137export function isThisIdentifier(node: Node | undefined): boolean { 5138 return !!node && node.kind === SyntaxKind.Identifier && identifierIsThisKeyword(node as Identifier); 5139} 5140 5141/** @internal */ 5142export function isThisInTypeQuery(node: Node): boolean { 5143 if (!isThisIdentifier(node)) { 5144 return false; 5145 } 5146 5147 while (isQualifiedName(node.parent) && node.parent.left === node) { 5148 node = node.parent; 5149 } 5150 5151 return node.parent.kind === SyntaxKind.TypeQuery; 5152} 5153 5154/** @internal */ 5155export function identifierIsThisKeyword(id: Identifier): boolean { 5156 return id.originalKeywordKind === SyntaxKind.ThisKeyword; 5157} 5158 5159/** @internal */ 5160export function getAllAccessorDeclarations(declarations: readonly Declaration[] | undefined, accessor: AccessorDeclaration): AllAccessorDeclarations { 5161 // TODO: GH#18217 5162 let firstAccessor!: AccessorDeclaration; 5163 let secondAccessor!: AccessorDeclaration; 5164 let getAccessor!: GetAccessorDeclaration; 5165 let setAccessor!: SetAccessorDeclaration; 5166 if (hasDynamicName(accessor)) { 5167 firstAccessor = accessor; 5168 if (accessor.kind === SyntaxKind.GetAccessor) { 5169 getAccessor = accessor; 5170 } 5171 else if (accessor.kind === SyntaxKind.SetAccessor) { 5172 setAccessor = accessor; 5173 } 5174 else { 5175 Debug.fail("Accessor has wrong kind"); 5176 } 5177 } 5178 else { 5179 forEach(declarations, member => { 5180 if (isAccessor(member) 5181 && isStatic(member) === isStatic(accessor)) { 5182 const memberName = getPropertyNameForPropertyNameNode(member.name); 5183 const accessorName = getPropertyNameForPropertyNameNode(accessor.name); 5184 if (memberName === accessorName) { 5185 if (!firstAccessor) { 5186 firstAccessor = member; 5187 } 5188 else if (!secondAccessor) { 5189 secondAccessor = member; 5190 } 5191 5192 if (member.kind === SyntaxKind.GetAccessor && !getAccessor) { 5193 getAccessor = member; 5194 } 5195 5196 if (member.kind === SyntaxKind.SetAccessor && !setAccessor) { 5197 setAccessor = member; 5198 } 5199 } 5200 } 5201 }); 5202 } 5203 return { 5204 firstAccessor, 5205 secondAccessor, 5206 getAccessor, 5207 setAccessor 5208 }; 5209} 5210 5211/** 5212 * Gets the effective type annotation of a variable, parameter, or property. If the node was 5213 * parsed in a JavaScript file, gets the type annotation from JSDoc. Also gets the type of 5214 * functions only the JSDoc case. 5215 * 5216 * @internal 5217 */ 5218export function getEffectiveTypeAnnotationNode(node: Node): TypeNode | undefined { 5219 if (!isInJSFile(node) && isFunctionDeclaration(node)) return undefined; 5220 const type = (node as HasType).type; 5221 if (type || !isInJSFile(node)) return type; 5222 return isJSDocPropertyLikeTag(node) ? node.typeExpression && node.typeExpression.type : getJSDocType(node); 5223} 5224 5225/** @internal */ 5226export function getTypeAnnotationNode(node: Node): TypeNode | undefined { 5227 return (node as HasType).type; 5228} 5229 5230/** 5231 * Gets the effective return type annotation of a signature. If the node was parsed in a 5232 * JavaScript file, gets the return type annotation from JSDoc. 5233 * 5234 * @internal 5235 */ 5236export function getEffectiveReturnTypeNode(node: SignatureDeclaration | JSDocSignature): TypeNode | undefined { 5237 return isJSDocSignature(node) ? 5238 node.type && node.type.typeExpression && node.type.typeExpression.type : 5239 node.type || (isInJSFile(node) ? getJSDocReturnType(node) : undefined); 5240} 5241 5242/** @internal */ 5243export function getJSDocTypeParameterDeclarations(node: DeclarationWithTypeParameters): readonly TypeParameterDeclaration[] { 5244 return flatMap(getJSDocTags(node), tag => isNonTypeAliasTemplate(tag) ? tag.typeParameters : undefined); 5245} 5246 5247/** template tags are only available when a typedef isn't already using them */ 5248function isNonTypeAliasTemplate(tag: JSDocTag): tag is JSDocTemplateTag { 5249 return isJSDocTemplateTag(tag) && !(tag.parent.kind === SyntaxKind.JSDoc && tag.parent.tags!.some(isJSDocTypeAlias)); 5250} 5251 5252/** 5253 * Gets the effective type annotation of the value parameter of a set accessor. If the node 5254 * was parsed in a JavaScript file, gets the type annotation from JSDoc. 5255 * 5256 * @internal 5257 */ 5258export function getEffectiveSetAccessorTypeAnnotationNode(node: SetAccessorDeclaration): TypeNode | undefined { 5259 const parameter = getSetAccessorValueParameter(node); 5260 return parameter && getEffectiveTypeAnnotationNode(parameter); 5261} 5262 5263/** @internal */ 5264export function emitNewLineBeforeLeadingComments(lineMap: readonly number[], writer: EmitTextWriter, node: TextRange, leadingComments: readonly CommentRange[] | undefined) { 5265 emitNewLineBeforeLeadingCommentsOfPosition(lineMap, writer, node.pos, leadingComments); 5266} 5267 5268/** @internal */ 5269export function emitNewLineBeforeLeadingCommentsOfPosition(lineMap: readonly number[], writer: EmitTextWriter, pos: number, leadingComments: readonly CommentRange[] | undefined) { 5270 // If the leading comments start on different line than the start of node, write new line 5271 if (leadingComments && leadingComments.length && pos !== leadingComments[0].pos && 5272 getLineOfLocalPositionFromLineMap(lineMap, pos) !== getLineOfLocalPositionFromLineMap(lineMap, leadingComments[0].pos)) { 5273 writer.writeLine(); 5274 } 5275} 5276 5277/** @internal */ 5278export function emitNewLineBeforeLeadingCommentOfPosition(lineMap: readonly number[], writer: EmitTextWriter, pos: number, commentPos: number) { 5279 // If the leading comments start on different line than the start of node, write new line 5280 if (pos !== commentPos && 5281 getLineOfLocalPositionFromLineMap(lineMap, pos) !== getLineOfLocalPositionFromLineMap(lineMap, commentPos)) { 5282 writer.writeLine(); 5283 } 5284} 5285 5286/** @internal */ 5287export function emitComments( 5288 text: string, 5289 lineMap: readonly number[], 5290 writer: EmitTextWriter, 5291 comments: readonly CommentRange[] | undefined, 5292 leadingSeparator: boolean, 5293 trailingSeparator: boolean, 5294 newLine: string, 5295 writeComment: (text: string, lineMap: readonly number[], writer: EmitTextWriter, commentPos: number, commentEnd: number, newLine: string) => void) { 5296 if (comments && comments.length > 0) { 5297 if (leadingSeparator) { 5298 writer.writeSpace(" "); 5299 } 5300 5301 let emitInterveningSeparator = false; 5302 for (const comment of comments) { 5303 if (emitInterveningSeparator) { 5304 writer.writeSpace(" "); 5305 emitInterveningSeparator = false; 5306 } 5307 5308 writeComment(text, lineMap, writer, comment.pos, comment.end, newLine); 5309 if (comment.hasTrailingNewLine) { 5310 writer.writeLine(); 5311 } 5312 else { 5313 emitInterveningSeparator = true; 5314 } 5315 } 5316 5317 if (emitInterveningSeparator && trailingSeparator) { 5318 writer.writeSpace(" "); 5319 } 5320 } 5321} 5322 5323/** 5324 * Detached comment is a comment at the top of file or function body that is separated from 5325 * the next statement by space. 5326 * 5327 * @internal 5328 */ 5329export function emitDetachedComments(text: string, lineMap: readonly number[], writer: EmitTextWriter, 5330 writeComment: (text: string, lineMap: readonly number[], writer: EmitTextWriter, commentPos: number, commentEnd: number, newLine: string) => void, 5331 node: TextRange, newLine: string, removeComments: boolean) { 5332 let leadingComments: CommentRange[] | undefined; 5333 let currentDetachedCommentInfo: { nodePos: number, detachedCommentEndPos: number } | undefined; 5334 if (removeComments) { 5335 // removeComments is true, only reserve pinned comment at the top of file 5336 // For example: 5337 // /*! Pinned Comment */ 5338 // 5339 // var x = 10; 5340 if (node.pos === 0) { 5341 leadingComments = filter(getLeadingCommentRanges(text, node.pos), isPinnedCommentLocal); 5342 } 5343 } 5344 else { 5345 // removeComments is false, just get detached as normal and bypass the process to filter comment 5346 leadingComments = getLeadingCommentRanges(text, node.pos); 5347 } 5348 5349 if (leadingComments) { 5350 const detachedComments: CommentRange[] = []; 5351 let lastComment: CommentRange | undefined; 5352 5353 for (const comment of leadingComments) { 5354 if (lastComment) { 5355 const lastCommentLine = getLineOfLocalPositionFromLineMap(lineMap, lastComment.end); 5356 const commentLine = getLineOfLocalPositionFromLineMap(lineMap, comment.pos); 5357 5358 if (commentLine >= lastCommentLine + 2) { 5359 // There was a blank line between the last comment and this comment. This 5360 // comment is not part of the copyright comments. Return what we have so 5361 // far. 5362 break; 5363 } 5364 } 5365 5366 detachedComments.push(comment); 5367 lastComment = comment; 5368 } 5369 5370 if (detachedComments.length) { 5371 // All comments look like they could have been part of the copyright header. Make 5372 // sure there is at least one blank line between it and the node. If not, it's not 5373 // a copyright header. 5374 const lastCommentLine = getLineOfLocalPositionFromLineMap(lineMap, last(detachedComments).end); 5375 const nodeLine = getLineOfLocalPositionFromLineMap(lineMap, skipTrivia(text, node.pos)); 5376 if (nodeLine >= lastCommentLine + 2) { 5377 // Valid detachedComments 5378 emitNewLineBeforeLeadingComments(lineMap, writer, node, leadingComments); 5379 emitComments(text, lineMap, writer, detachedComments, /*leadingSeparator*/ false, /*trailingSeparator*/ true, newLine, writeComment); 5380 currentDetachedCommentInfo = { nodePos: node.pos, detachedCommentEndPos: last(detachedComments).end }; 5381 } 5382 } 5383 } 5384 5385 return currentDetachedCommentInfo; 5386 5387 function isPinnedCommentLocal(comment: CommentRange) { 5388 return isPinnedComment(text, comment.pos); 5389 } 5390 5391} 5392 5393/** @internal */ 5394export function writeCommentRange(text: string, lineMap: readonly number[], writer: EmitTextWriter, commentPos: number, commentEnd: number, newLine: string) { 5395 if (text.charCodeAt(commentPos + 1) === CharacterCodes.asterisk) { 5396 const firstCommentLineAndCharacter = computeLineAndCharacterOfPosition(lineMap, commentPos); 5397 const lineCount = lineMap.length; 5398 let firstCommentLineIndent: number | undefined; 5399 for (let pos = commentPos, currentLine = firstCommentLineAndCharacter.line; pos < commentEnd; currentLine++) { 5400 const nextLineStart = (currentLine + 1) === lineCount 5401 ? text.length + 1 5402 : lineMap[currentLine + 1]; 5403 5404 if (pos !== commentPos) { 5405 // If we are not emitting first line, we need to write the spaces to adjust the alignment 5406 if (firstCommentLineIndent === undefined) { 5407 firstCommentLineIndent = calculateIndent(text, lineMap[firstCommentLineAndCharacter.line], commentPos); 5408 } 5409 5410 // These are number of spaces writer is going to write at current indent 5411 const currentWriterIndentSpacing = writer.getIndent() * getIndentSize(); 5412 5413 // Number of spaces we want to be writing 5414 // eg: Assume writer indent 5415 // module m { 5416 // /* starts at character 9 this is line 1 5417 // * starts at character pos 4 line --1 = 8 - 8 + 3 5418 // More left indented comment */ --2 = 8 - 8 + 2 5419 // class c { } 5420 // } 5421 // module m { 5422 // /* this is line 1 -- Assume current writer indent 8 5423 // * line --3 = 8 - 4 + 5 5424 // More right indented comment */ --4 = 8 - 4 + 11 5425 // class c { } 5426 // } 5427 const spacesToEmit = currentWriterIndentSpacing - firstCommentLineIndent + calculateIndent(text, pos, nextLineStart); 5428 if (spacesToEmit > 0) { 5429 let numberOfSingleSpacesToEmit = spacesToEmit % getIndentSize(); 5430 const indentSizeSpaceString = getIndentString((spacesToEmit - numberOfSingleSpacesToEmit) / getIndentSize()); 5431 5432 // Write indent size string ( in eg 1: = "", 2: "" , 3: string with 8 spaces 4: string with 12 spaces 5433 writer.rawWrite(indentSizeSpaceString); 5434 5435 // Emit the single spaces (in eg: 1: 3 spaces, 2: 2 spaces, 3: 1 space, 4: 3 spaces) 5436 while (numberOfSingleSpacesToEmit) { 5437 writer.rawWrite(" "); 5438 numberOfSingleSpacesToEmit--; 5439 } 5440 } 5441 else { 5442 // No spaces to emit write empty string 5443 writer.rawWrite(""); 5444 } 5445 } 5446 5447 // Write the comment line text 5448 writeTrimmedCurrentLine(text, commentEnd, writer, newLine, pos, nextLineStart); 5449 5450 pos = nextLineStart; 5451 } 5452 } 5453 else { 5454 // Single line comment of style //.... 5455 writer.writeComment(text.substring(commentPos, commentEnd)); 5456 } 5457} 5458 5459function writeTrimmedCurrentLine(text: string, commentEnd: number, writer: EmitTextWriter, newLine: string, pos: number, nextLineStart: number) { 5460 const end = Math.min(commentEnd, nextLineStart - 1); 5461 const currentLineText = trimString(text.substring(pos, end)); 5462 if (currentLineText) { 5463 // trimmed forward and ending spaces text 5464 writer.writeComment(currentLineText); 5465 if (end !== commentEnd) { 5466 writer.writeLine(); 5467 } 5468 } 5469 else { 5470 // Empty string - make sure we write empty line 5471 writer.rawWrite(newLine); 5472 } 5473} 5474 5475function calculateIndent(text: string, pos: number, end: number) { 5476 let currentLineIndent = 0; 5477 for (; pos < end && isWhiteSpaceSingleLine(text.charCodeAt(pos)); pos++) { 5478 if (text.charCodeAt(pos) === CharacterCodes.tab) { 5479 // Tabs = TabSize = indent size and go to next tabStop 5480 currentLineIndent += getIndentSize() - (currentLineIndent % getIndentSize()); 5481 } 5482 else { 5483 // Single space 5484 currentLineIndent++; 5485 } 5486 } 5487 5488 return currentLineIndent; 5489} 5490 5491/** @internal */ 5492export function hasEffectiveModifiers(node: Node) { 5493 return getEffectiveModifierFlags(node) !== ModifierFlags.None; 5494} 5495 5496/** @internal */ 5497export function hasSyntacticModifiers(node: Node) { 5498 return getSyntacticModifierFlags(node) !== ModifierFlags.None; 5499} 5500 5501/** @internal */ 5502export function hasEffectiveModifier(node: Node, flags: ModifierFlags): boolean { 5503 return !!getSelectedEffectiveModifierFlags(node, flags); 5504} 5505 5506/** @internal */ 5507export function hasSyntacticModifier(node: Node, flags: ModifierFlags): boolean { 5508 return !!getSelectedSyntacticModifierFlags(node, flags); 5509} 5510 5511/** @internal */ 5512export function isStatic(node: Node) { 5513 // https://tc39.es/ecma262/#sec-static-semantics-isstatic 5514 return isClassElement(node) && hasStaticModifier(node) || isClassStaticBlockDeclaration(node); 5515} 5516 5517/** @internal */ 5518export function hasIllegalDecorators(node: Node): boolean { 5519 return canHaveIllegalDecorators(node); 5520} 5521 5522/** @internal */ 5523export function hasStaticModifier(node: Node): boolean { 5524 return hasSyntacticModifier(node, ModifierFlags.Static); 5525} 5526 5527/** @internal */ 5528export function hasOverrideModifier(node: Node): boolean { 5529 return hasEffectiveModifier(node, ModifierFlags.Override); 5530} 5531 5532/** @internal */ 5533export function hasAbstractModifier(node: Node): boolean { 5534 return hasSyntacticModifier(node, ModifierFlags.Abstract); 5535} 5536 5537/** @internal */ 5538export function hasAmbientModifier(node: Node): boolean { 5539 return hasSyntacticModifier(node, ModifierFlags.Ambient); 5540} 5541 5542/** @internal */ 5543export function hasAccessorModifier(node: Node): boolean { 5544 return hasSyntacticModifier(node, ModifierFlags.Accessor); 5545} 5546 5547/** @internal */ 5548export function hasEffectiveReadonlyModifier(node: Node): boolean { 5549 return hasEffectiveModifier(node, ModifierFlags.Readonly); 5550} 5551 5552/** @internal */ 5553export function hasDecorators(node: Node): boolean { 5554 return hasSyntacticModifier(node, ModifierFlags.Decorator) && some((node as HasModifiers).modifiers, isDecorator); 5555} 5556 5557/** @internal */ 5558export function hasAnnotations(node: Node): boolean { 5559 return hasSyntacticModifier(node, ModifierFlags.Decorator) && some((node as HasModifiers).modifiers, isAnnotation); 5560} 5561 5562/** @internal */ 5563export function getSelectedEffectiveModifierFlags(node: ts.Node, flags: ts.ModifierFlags): ts.ModifierFlags { 5564 return getEffectiveModifierFlags(node) & flags; 5565} 5566 5567/** @internal */ 5568export function getSelectedSyntacticModifierFlags(node: Node, flags: ModifierFlags): ModifierFlags { 5569 return getSyntacticModifierFlags(node) & flags; 5570} 5571 5572function getModifierFlagsWorker(node: Node, includeJSDoc: boolean, alwaysIncludeJSDoc?: boolean): ModifierFlags { 5573 if (node.kind >= SyntaxKind.FirstToken && node.kind <= SyntaxKind.LastToken) { 5574 return ModifierFlags.None; 5575 } 5576 5577 if (!(node.modifierFlagsCache & ModifierFlags.HasComputedFlags)) { 5578 node.modifierFlagsCache = getSyntacticModifierFlagsNoCache(node) | ModifierFlags.HasComputedFlags; 5579 } 5580 5581 if (includeJSDoc && !(node.modifierFlagsCache & ModifierFlags.HasComputedJSDocModifiers) && (alwaysIncludeJSDoc || isInJSFile(node)) && node.parent) { 5582 node.modifierFlagsCache |= getJSDocModifierFlagsNoCache(node) | ModifierFlags.HasComputedJSDocModifiers; 5583 } 5584 5585 return node.modifierFlagsCache & ~(ModifierFlags.HasComputedFlags | ModifierFlags.HasComputedJSDocModifiers); 5586} 5587 5588/** 5589 * Gets the effective ModifierFlags for the provided node, including JSDoc modifiers. The modifiers will be cached on the node to improve performance. 5590 * 5591 * NOTE: This function may use `parent` pointers. 5592 * 5593 * @internal 5594 */ 5595export function getEffectiveModifierFlags(node: Node): ModifierFlags { 5596 return getModifierFlagsWorker(node, /*includeJSDoc*/ true); 5597} 5598 5599/** @internal */ 5600export function getEffectiveModifierFlagsAlwaysIncludeJSDoc(node: Node): ModifierFlags { 5601 return getModifierFlagsWorker(node, /*includeJSDOc*/ true, /*alwaysIncludeJSDOc*/ true); 5602} 5603 5604/** 5605 * Gets the ModifierFlags for syntactic modifiers on the provided node. The modifiers will be cached on the node to improve performance. 5606 * 5607 * NOTE: This function does not use `parent` pointers and will not include modifiers from JSDoc. 5608 * 5609 * @internal 5610 */ 5611export function getSyntacticModifierFlags(node: Node): ModifierFlags { 5612 return getModifierFlagsWorker(node, /*includeJSDoc*/ false); 5613} 5614 5615function getJSDocModifierFlagsNoCache(node: Node): ModifierFlags { 5616 let flags = ModifierFlags.None; 5617 if (!!node.parent && !isParameter(node)) { 5618 if (isInJSFile(node)) { 5619 if (getJSDocPublicTagNoCache(node)) flags |= ModifierFlags.Public; 5620 if (getJSDocPrivateTagNoCache(node)) flags |= ModifierFlags.Private; 5621 if (getJSDocProtectedTagNoCache(node)) flags |= ModifierFlags.Protected; 5622 if (getJSDocReadonlyTagNoCache(node)) flags |= ModifierFlags.Readonly; 5623 if (getJSDocOverrideTagNoCache(node)) flags |= ModifierFlags.Override; 5624 } 5625 if (getJSDocDeprecatedTagNoCache(node)) flags |= ModifierFlags.Deprecated; 5626 } 5627 5628 return flags; 5629} 5630 5631/** 5632 * Gets the effective ModifierFlags for the provided node, including JSDoc modifiers. The modifier flags cache on the node is ignored. 5633 * 5634 * NOTE: This function may use `parent` pointers. 5635 * 5636 * @internal 5637 */ 5638export function getEffectiveModifierFlagsNoCache(node: Node): ModifierFlags { 5639 return getSyntacticModifierFlagsNoCache(node) | getJSDocModifierFlagsNoCache(node); 5640} 5641 5642/** 5643 * Gets the ModifierFlags for syntactic modifiers on the provided node. The modifier flags cache on the node is ignored. 5644 * 5645 * NOTE: This function does not use `parent` pointers and will not include modifiers from JSDoc. 5646 * 5647 * @internal 5648 */ 5649export function getSyntacticModifierFlagsNoCache(node: Node): ModifierFlags { 5650 let flags = canHaveModifiers(node) ? modifiersToFlags(node.modifiers) : ModifierFlags.None; 5651 if (node.flags & NodeFlags.NestedNamespace || (node.kind === SyntaxKind.Identifier && (node as Identifier).isInJSDocNamespace)) { 5652 flags |= ModifierFlags.Export; 5653 } 5654 return flags; 5655} 5656 5657/** @internal */ 5658export function modifiersToFlags(modifiers: readonly ModifierLike[] | undefined) { 5659 let flags = ModifierFlags.None; 5660 if (modifiers) { 5661 for (const modifier of modifiers) { 5662 flags |= modifierToFlag(modifier.kind); 5663 } 5664 } 5665 return flags; 5666} 5667 5668/** @internal */ 5669export function modifierToFlag(token: SyntaxKind): ModifierFlags { 5670 switch (token) { 5671 case SyntaxKind.StaticKeyword: return ModifierFlags.Static; 5672 case SyntaxKind.PublicKeyword: return ModifierFlags.Public; 5673 case SyntaxKind.ProtectedKeyword: return ModifierFlags.Protected; 5674 case SyntaxKind.PrivateKeyword: return ModifierFlags.Private; 5675 case SyntaxKind.AbstractKeyword: return ModifierFlags.Abstract; 5676 case SyntaxKind.AccessorKeyword: return ModifierFlags.Accessor; 5677 case SyntaxKind.ExportKeyword: return ModifierFlags.Export; 5678 case SyntaxKind.DeclareKeyword: return ModifierFlags.Ambient; 5679 case SyntaxKind.ConstKeyword: return ModifierFlags.Const; 5680 case SyntaxKind.DefaultKeyword: return ModifierFlags.Default; 5681 case SyntaxKind.AsyncKeyword: return ModifierFlags.Async; 5682 case SyntaxKind.ReadonlyKeyword: return ModifierFlags.Readonly; 5683 case SyntaxKind.OverrideKeyword: return ModifierFlags.Override; 5684 case SyntaxKind.InKeyword: return ModifierFlags.In; 5685 case SyntaxKind.OutKeyword: return ModifierFlags.Out; 5686 case SyntaxKind.Decorator: return ModifierFlags.Decorator; 5687 } 5688 return ModifierFlags.None; 5689} 5690 5691/** @internal */ 5692export function isLogicalOperator(token: SyntaxKind): boolean { 5693 return token === SyntaxKind.BarBarToken 5694 || token === SyntaxKind.AmpersandAmpersandToken 5695 || token === SyntaxKind.ExclamationToken; 5696} 5697 5698/** @internal */ 5699export function isLogicalOrCoalescingAssignmentOperator(token: SyntaxKind): token is LogicalOrCoalescingAssignmentOperator { 5700 return token === SyntaxKind.BarBarEqualsToken 5701 || token === SyntaxKind.AmpersandAmpersandEqualsToken 5702 || token === SyntaxKind.QuestionQuestionEqualsToken; 5703} 5704 5705/** @internal */ 5706export function isLogicalOrCoalescingAssignmentExpression(expr: BinaryExpression): expr is AssignmentExpression<Token<LogicalOrCoalescingAssignmentOperator>> { 5707 return isLogicalOrCoalescingAssignmentOperator(expr.operatorToken.kind); 5708} 5709 5710/** @internal */ 5711export function isAssignmentOperator(token: SyntaxKind): boolean { 5712 return token >= SyntaxKind.FirstAssignment && token <= SyntaxKind.LastAssignment; 5713} 5714 5715/** 5716 * Get `C` given `N` if `N` is in the position `class C extends N` where `N` is an ExpressionWithTypeArguments.、 5717 * 5718 * @internal 5719 */ 5720export function tryGetClassExtendingExpressionWithTypeArguments(node: Node): ClassLikeDeclaration | undefined { 5721 const cls = tryGetClassImplementingOrExtendingExpressionWithTypeArguments(node); 5722 return cls && !cls.isImplements ? cls.class : undefined; 5723} 5724 5725/** @internal */ 5726export interface ClassImplementingOrExtendingExpressionWithTypeArguments { 5727 readonly class: ClassLikeDeclaration; 5728 readonly isImplements: boolean; 5729} 5730/** @internal */ 5731export function tryGetClassImplementingOrExtendingExpressionWithTypeArguments(node: Node): ClassImplementingOrExtendingExpressionWithTypeArguments | undefined { 5732 return isExpressionWithTypeArguments(node) 5733 && isHeritageClause(node.parent) 5734 && isClassLike(node.parent.parent) 5735 ? { class: node.parent.parent, isImplements: node.parent.token === SyntaxKind.ImplementsKeyword } 5736 : undefined; 5737} 5738 5739/** @internal */ 5740export function isAssignmentExpression(node: Node, excludeCompoundAssignment: true): node is AssignmentExpression<EqualsToken>; 5741/** @internal */ 5742export function isAssignmentExpression(node: Node, excludeCompoundAssignment?: false): node is AssignmentExpression<AssignmentOperatorToken>; 5743/** @internal */ 5744export function isAssignmentExpression(node: Node, excludeCompoundAssignment?: boolean): node is AssignmentExpression<AssignmentOperatorToken> { 5745 return isBinaryExpression(node) 5746 && (excludeCompoundAssignment 5747 ? node.operatorToken.kind === SyntaxKind.EqualsToken 5748 : isAssignmentOperator(node.operatorToken.kind)) 5749 && isLeftHandSideExpression(node.left); 5750} 5751 5752/** @internal */ 5753export function isLeftHandSideOfAssignment(node: Node) { 5754 return isAssignmentExpression(node.parent) && node.parent.left === node; 5755} 5756/** @internal */ 5757export function isDestructuringAssignment(node: Node): node is DestructuringAssignment { 5758 if (isAssignmentExpression(node, /*excludeCompoundAssignment*/ true)) { 5759 const kind = node.left.kind; 5760 return kind === SyntaxKind.ObjectLiteralExpression 5761 || kind === SyntaxKind.ArrayLiteralExpression; 5762 } 5763 5764 return false; 5765} 5766 5767/** @internal */ 5768export function isExpressionWithTypeArgumentsInClassExtendsClause(node: Node): node is ExpressionWithTypeArguments { 5769 return tryGetClassExtendingExpressionWithTypeArguments(node) !== undefined; 5770} 5771 5772/** @internal */ 5773export function isEntityNameExpression(node: Node): node is EntityNameExpression { 5774 return node.kind === SyntaxKind.Identifier || isPropertyAccessEntityNameExpression(node); 5775} 5776 5777/** @internal */ 5778export function getFirstIdentifier(node: EntityNameOrEntityNameExpression): Identifier { 5779 switch (node.kind) { 5780 case SyntaxKind.Identifier: 5781 return node; 5782 case SyntaxKind.QualifiedName: 5783 do { 5784 node = node.left; 5785 } while (node.kind !== SyntaxKind.Identifier); 5786 return node; 5787 case SyntaxKind.PropertyAccessExpression: 5788 do { 5789 node = node.expression; 5790 } while (node.kind !== SyntaxKind.Identifier); 5791 return node; 5792 } 5793} 5794 5795/** @internal */ 5796export function isDottedName(node: Expression): boolean { 5797 return node.kind === SyntaxKind.Identifier 5798 || node.kind === SyntaxKind.ThisKeyword 5799 || node.kind === SyntaxKind.SuperKeyword 5800 || node.kind === SyntaxKind.MetaProperty 5801 || node.kind === SyntaxKind.PropertyAccessExpression && isDottedName((node as PropertyAccessExpression).expression) 5802 || node.kind === SyntaxKind.ParenthesizedExpression && isDottedName((node as ParenthesizedExpression).expression); 5803} 5804 5805/** @internal */ 5806export function isPropertyAccessEntityNameExpression(node: Node): node is PropertyAccessEntityNameExpression { 5807 return isPropertyAccessExpression(node) && isIdentifier(node.name) && isEntityNameExpression(node.expression); 5808} 5809 5810/** @internal */ 5811export function tryGetPropertyAccessOrIdentifierToString(expr: Expression): string | undefined { 5812 if (isPropertyAccessExpression(expr)) { 5813 const baseStr = tryGetPropertyAccessOrIdentifierToString(expr.expression); 5814 if (baseStr !== undefined) { 5815 return baseStr + "." + entityNameToString(expr.name); 5816 } 5817 } 5818 else if (isElementAccessExpression(expr)) { 5819 const baseStr = tryGetPropertyAccessOrIdentifierToString(expr.expression); 5820 if (baseStr !== undefined && isPropertyName(expr.argumentExpression)) { 5821 return baseStr + "." + getPropertyNameForPropertyNameNode(expr.argumentExpression); 5822 } 5823 } 5824 else if (isIdentifier(expr)) { 5825 return unescapeLeadingUnderscores(expr.escapedText); 5826 } 5827 return undefined; 5828} 5829 5830/** @internal */ 5831export function isPrototypeAccess(node: Node): node is BindableStaticAccessExpression { 5832 return isBindableStaticAccessExpression(node) && getElementOrPropertyAccessName(node) === "prototype"; 5833} 5834 5835/** @internal */ 5836export function isRightSideOfQualifiedNameOrPropertyAccess(node: Node) { 5837 return (node.parent.kind === SyntaxKind.QualifiedName && (node.parent as QualifiedName).right === node) || 5838 (node.parent.kind === SyntaxKind.PropertyAccessExpression && (node.parent as PropertyAccessExpression).name === node); 5839} 5840 5841/** @internal */ 5842export function isRightSideOfAccessExpression(node: Node) { 5843 return isPropertyAccessExpression(node.parent) && node.parent.name === node 5844 || isElementAccessExpression(node.parent) && node.parent.argumentExpression === node; 5845} 5846 5847/** @internal */ 5848export function isRightSideOfQualifiedNameOrPropertyAccessOrJSDocMemberName(node: Node) { 5849 return isQualifiedName(node.parent) && node.parent.right === node 5850 || isPropertyAccessExpression(node.parent) && node.parent.name === node 5851 || isJSDocMemberName(node.parent) && node.parent.right === node; 5852} 5853 5854/** @internal */ 5855export function isEmptyObjectLiteral(expression: Node): boolean { 5856 return expression.kind === SyntaxKind.ObjectLiteralExpression && 5857 (expression as ObjectLiteralExpression).properties.length === 0; 5858} 5859 5860/** @internal */ 5861export function isEmptyArrayLiteral(expression: Node): boolean { 5862 return expression.kind === SyntaxKind.ArrayLiteralExpression && 5863 (expression as ArrayLiteralExpression).elements.length === 0; 5864} 5865 5866/** @internal */ 5867export function getLocalSymbolForExportDefault(symbol: Symbol) { 5868 if (!isExportDefaultSymbol(symbol) || !symbol.declarations) return undefined; 5869 for (const decl of symbol.declarations) { 5870 if (decl.localSymbol) return decl.localSymbol; 5871 } 5872 return undefined; 5873} 5874 5875function isExportDefaultSymbol(symbol: Symbol): boolean { 5876 return symbol && length(symbol.declarations) > 0 && hasSyntacticModifier(symbol.declarations![0], ModifierFlags.Default); 5877} 5878 5879/** 5880 * Return ".ts", ".d.ts", or ".tsx", if that is the extension. 5881 * 5882 * @internal 5883 */ 5884export function tryExtractTSExtension(fileName: string): string | undefined { 5885 return find(supportedTSExtensionsForExtractExtension, extension => fileExtensionIs(fileName, extension)); 5886} 5887/** 5888 * Replace each instance of non-ascii characters by one, two, three, or four escape sequences 5889 * representing the UTF-8 encoding of the character, and return the expanded char code list. 5890 */ 5891function getExpandedCharCodes(input: string): number[] { 5892 const output: number[] = []; 5893 const length = input.length; 5894 5895 for (let i = 0; i < length; i++) { 5896 const charCode = input.charCodeAt(i); 5897 5898 // handle utf8 5899 if (charCode < 0x80) { 5900 output.push(charCode); 5901 } 5902 else if (charCode < 0x800) { 5903 output.push((charCode >> 6) | 0B11000000); 5904 output.push((charCode & 0B00111111) | 0B10000000); 5905 } 5906 else if (charCode < 0x10000) { 5907 output.push((charCode >> 12) | 0B11100000); 5908 output.push(((charCode >> 6) & 0B00111111) | 0B10000000); 5909 output.push((charCode & 0B00111111) | 0B10000000); 5910 } 5911 else if (charCode < 0x20000) { 5912 output.push((charCode >> 18) | 0B11110000); 5913 output.push(((charCode >> 12) & 0B00111111) | 0B10000000); 5914 output.push(((charCode >> 6) & 0B00111111) | 0B10000000); 5915 output.push((charCode & 0B00111111) | 0B10000000); 5916 } 5917 else { 5918 Debug.assert(false, "Unexpected code point"); 5919 } 5920 } 5921 5922 return output; 5923} 5924 5925const base64Digits = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; 5926 5927/** 5928 * Converts a string to a base-64 encoded ASCII string. 5929 * 5930 * @internal 5931 */ 5932export function convertToBase64(input: string): string { 5933 let result = ""; 5934 const charCodes = getExpandedCharCodes(input); 5935 let i = 0; 5936 const length = charCodes.length; 5937 let byte1: number, byte2: number, byte3: number, byte4: number; 5938 5939 while (i < length) { 5940 // Convert every 6-bits in the input 3 character points 5941 // into a base64 digit 5942 byte1 = charCodes[i] >> 2; 5943 byte2 = (charCodes[i] & 0B00000011) << 4 | charCodes[i + 1] >> 4; 5944 byte3 = (charCodes[i + 1] & 0B00001111) << 2 | charCodes[i + 2] >> 6; 5945 byte4 = charCodes[i + 2] & 0B00111111; 5946 5947 // We are out of characters in the input, set the extra 5948 // digits to 64 (padding character). 5949 if (i + 1 >= length) { 5950 byte3 = byte4 = 64; 5951 } 5952 else if (i + 2 >= length) { 5953 byte4 = 64; 5954 } 5955 5956 // Write to the output 5957 result += base64Digits.charAt(byte1) + base64Digits.charAt(byte2) + base64Digits.charAt(byte3) + base64Digits.charAt(byte4); 5958 5959 i += 3; 5960 } 5961 5962 return result; 5963} 5964 5965function getStringFromExpandedCharCodes(codes: number[]): string { 5966 let output = ""; 5967 let i = 0; 5968 const length = codes.length; 5969 while (i < length) { 5970 const charCode = codes[i]; 5971 5972 if (charCode < 0x80) { 5973 output += String.fromCharCode(charCode); 5974 i++; 5975 } 5976 else if ((charCode & 0B11000000) === 0B11000000) { 5977 let value = charCode & 0B00111111; 5978 i++; 5979 let nextCode: number = codes[i]; 5980 while ((nextCode & 0B11000000) === 0B10000000) { 5981 value = (value << 6) | (nextCode & 0B00111111); 5982 i++; 5983 nextCode = codes[i]; 5984 } 5985 // `value` may be greater than 10FFFF (the maximum unicode codepoint) - JS will just make this into an invalid character for us 5986 output += String.fromCharCode(value); 5987 } 5988 else { 5989 // We don't want to kill the process when decoding fails (due to a following char byte not 5990 // following a leading char), so we just print the (bad) value 5991 output += String.fromCharCode(charCode); 5992 i++; 5993 } 5994 } 5995 return output; 5996} 5997 5998/** @internal */ 5999export function base64encode(host: { base64encode?(input: string): string } | undefined, input: string): string { 6000 if (host && host.base64encode) { 6001 return host.base64encode(input); 6002 } 6003 return convertToBase64(input); 6004} 6005 6006/** @internal */ 6007export function base64decode(host: { base64decode?(input: string): string } | undefined, input: string): string { 6008 if (host && host.base64decode) { 6009 return host.base64decode(input); 6010 } 6011 const length = input.length; 6012 const expandedCharCodes: number[] = []; 6013 let i = 0; 6014 while (i < length) { 6015 // Stop decoding once padding characters are present 6016 if (input.charCodeAt(i) === base64Digits.charCodeAt(64)) { 6017 break; 6018 } 6019 // convert 4 input digits into three characters, ignoring padding characters at the end 6020 const ch1 = base64Digits.indexOf(input[i]); 6021 const ch2 = base64Digits.indexOf(input[i + 1]); 6022 const ch3 = base64Digits.indexOf(input[i + 2]); 6023 const ch4 = base64Digits.indexOf(input[i + 3]); 6024 6025 const code1 = ((ch1 & 0B00111111) << 2) | ((ch2 >> 4) & 0B00000011); 6026 const code2 = ((ch2 & 0B00001111) << 4) | ((ch3 >> 2) & 0B00001111); 6027 const code3 = ((ch3 & 0B00000011) << 6) | (ch4 & 0B00111111); 6028 6029 if (code2 === 0 && ch3 !== 0) { // code2 decoded to zero, but ch3 was padding - elide code2 and code3 6030 expandedCharCodes.push(code1); 6031 } 6032 else if (code3 === 0 && ch4 !== 0) { // code3 decoded to zero, but ch4 was padding, elide code3 6033 expandedCharCodes.push(code1, code2); 6034 } 6035 else { 6036 expandedCharCodes.push(code1, code2, code3); 6037 } 6038 i += 4; 6039 } 6040 return getStringFromExpandedCharCodes(expandedCharCodes); 6041} 6042 6043/** @internal */ 6044export function readJsonOrUndefined(path: string, hostOrText: { readFile(fileName: string): string | undefined } | string): object | undefined { 6045 const jsonText = isString(hostOrText) ? hostOrText : hostOrText.readFile(path); 6046 if (!jsonText) return undefined; 6047 // gracefully handle if readFile fails or returns not JSON 6048 const result = parseConfigFileTextToJson(path, jsonText); 6049 return !result.error ? result.config : undefined; 6050} 6051 6052/** @internal */ 6053export function readJson(path: string, host: { readFile(fileName: string): string | undefined }): object { 6054 return readJsonOrUndefined(path, host) || {}; 6055} 6056 6057/** @internal */ 6058export function directoryProbablyExists(directoryName: string, host: { directoryExists?: (directoryName: string) => boolean }): boolean { 6059 // if host does not support 'directoryExists' assume that directory will exist 6060 return !host.directoryExists || host.directoryExists(directoryName); 6061} 6062 6063const carriageReturnLineFeed = "\r\n"; 6064const lineFeed = "\n"; 6065/** @internal */ 6066export function getNewLineCharacter(options: CompilerOptions | PrinterOptions, getNewLine?: () => string): string { 6067 switch (options.newLine) { 6068 case NewLineKind.CarriageReturnLineFeed: 6069 return carriageReturnLineFeed; 6070 case NewLineKind.LineFeed: 6071 return lineFeed; 6072 } 6073 return getNewLine ? getNewLine() : sys ? sys.newLine : carriageReturnLineFeed; 6074} 6075 6076/** 6077 * Creates a new TextRange from the provided pos and end. 6078 * 6079 * @param pos The start position. 6080 * @param end The end position. 6081 * 6082 * @internal 6083 */ 6084export function createRange(pos: number, end: number = pos): TextRange { 6085 Debug.assert(end >= pos || end === -1); 6086 return { pos, end }; 6087} 6088 6089/** 6090 * Creates a new TextRange from a provided range with a new end position. 6091 * 6092 * @param range A TextRange. 6093 * @param end The new end position. 6094 * 6095 * @internal 6096 */ 6097export function moveRangeEnd(range: TextRange, end: number): TextRange { 6098 return createRange(range.pos, end); 6099} 6100 6101/** 6102 * Creates a new TextRange from a provided range with a new start position. 6103 * 6104 * @param range A TextRange. 6105 * @param pos The new Start position. 6106 * 6107 * @internal 6108 */ 6109export function moveRangePos(range: TextRange, pos: number): TextRange { 6110 return createRange(pos, range.end); 6111} 6112 6113/** 6114 * Moves the start position of a range past any decorators. 6115 * 6116 * @internal 6117 */ 6118export function moveRangePastDecorators(node: Node): TextRange { 6119 const lastDecorator = canHaveModifiers(node) ? findLast(node.modifiers, isDecorator) : undefined; 6120 return lastDecorator && !positionIsSynthesized(lastDecorator.end) 6121 ? moveRangePos(node, lastDecorator.end) 6122 : node; 6123} 6124 6125/** 6126 * Moves the start position of a range past any decorators or modifiers. 6127 * 6128 * @internal 6129 */ 6130export function moveRangePastModifiers(node: Node): TextRange { 6131 const lastModifier = canHaveModifiers(node) ? lastOrUndefined(node.modifiers) : undefined; 6132 return lastModifier && !positionIsSynthesized(lastModifier.end) 6133 ? moveRangePos(node, lastModifier.end) 6134 : moveRangePastDecorators(node); 6135} 6136 6137/** 6138 * Determines whether a TextRange has the same start and end positions. 6139 * 6140 * @param range A TextRange. 6141 * 6142 * @internal 6143 */ 6144export function isCollapsedRange(range: TextRange) { 6145 return range.pos === range.end; 6146} 6147 6148/** 6149 * Creates a new TextRange for a token at the provides start position. 6150 * 6151 * @param pos The start position. 6152 * @param token The token. 6153 * 6154 * @internal 6155 */ 6156export function createTokenRange(pos: number, token: SyntaxKind): TextRange { 6157 return createRange(pos, pos + tokenToString(token)!.length); 6158} 6159 6160/** @internal */ 6161export function rangeIsOnSingleLine(range: TextRange, sourceFile: SourceFile) { 6162 return rangeStartIsOnSameLineAsRangeEnd(range, range, sourceFile); 6163} 6164 6165/** @internal */ 6166export function rangeStartPositionsAreOnSameLine(range1: TextRange, range2: TextRange, sourceFile: SourceFile) { 6167 return positionsAreOnSameLine( 6168 getStartPositionOfRange(range1, sourceFile, /*includeComments*/ false), 6169 getStartPositionOfRange(range2, sourceFile, /*includeComments*/ false), 6170 sourceFile); 6171} 6172 6173/** @internal */ 6174export function rangeEndPositionsAreOnSameLine(range1: TextRange, range2: TextRange, sourceFile: SourceFile) { 6175 return positionsAreOnSameLine(range1.end, range2.end, sourceFile); 6176} 6177 6178/** @internal */ 6179export function rangeStartIsOnSameLineAsRangeEnd(range1: TextRange, range2: TextRange, sourceFile: SourceFile) { 6180 return positionsAreOnSameLine(getStartPositionOfRange(range1, sourceFile, /*includeComments*/ false), range2.end, sourceFile); 6181} 6182 6183/** @internal */ 6184export function rangeEndIsOnSameLineAsRangeStart(range1: TextRange, range2: TextRange, sourceFile: SourceFile) { 6185 return positionsAreOnSameLine(range1.end, getStartPositionOfRange(range2, sourceFile, /*includeComments*/ false), sourceFile); 6186} 6187 6188/** @internal */ 6189export function getLinesBetweenRangeEndAndRangeStart(range1: TextRange, range2: TextRange, sourceFile: SourceFile, includeSecondRangeComments: boolean) { 6190 const range2Start = getStartPositionOfRange(range2, sourceFile, includeSecondRangeComments); 6191 return getLinesBetweenPositions(sourceFile, range1.end, range2Start); 6192} 6193 6194/** @internal */ 6195export function getLinesBetweenRangeEndPositions(range1: TextRange, range2: TextRange, sourceFile: SourceFile) { 6196 return getLinesBetweenPositions(sourceFile, range1.end, range2.end); 6197} 6198 6199/** @internal */ 6200export function isNodeArrayMultiLine(list: NodeArray<Node>, sourceFile: SourceFile): boolean { 6201 return !positionsAreOnSameLine(list.pos, list.end, sourceFile); 6202} 6203 6204/** @internal */ 6205export function positionsAreOnSameLine(pos1: number, pos2: number, sourceFile: SourceFile) { 6206 return getLinesBetweenPositions(sourceFile, pos1, pos2) === 0; 6207} 6208 6209/** @internal */ 6210export function getStartPositionOfRange(range: TextRange, sourceFile: SourceFile, includeComments: boolean) { 6211 return positionIsSynthesized(range.pos) ? -1 : skipTrivia(sourceFile.text, range.pos, /*stopAfterLineBreak*/ false, includeComments); 6212} 6213 6214/** @internal */ 6215export function getLinesBetweenPositionAndPrecedingNonWhitespaceCharacter(pos: number, stopPos: number, sourceFile: SourceFile, includeComments?: boolean) { 6216 const startPos = skipTrivia(sourceFile.text, pos, /*stopAfterLineBreak*/ false, includeComments); 6217 const prevPos = getPreviousNonWhitespacePosition(startPos, stopPos, sourceFile); 6218 return getLinesBetweenPositions(sourceFile, prevPos ?? stopPos, startPos); 6219} 6220 6221/** @internal */ 6222export function getLinesBetweenPositionAndNextNonWhitespaceCharacter(pos: number, stopPos: number, sourceFile: SourceFile, includeComments?: boolean) { 6223 const nextPos = skipTrivia(sourceFile.text, pos, /*stopAfterLineBreak*/ false, includeComments); 6224 return getLinesBetweenPositions(sourceFile, pos, Math.min(stopPos, nextPos)); 6225} 6226 6227function getPreviousNonWhitespacePosition(pos: number, stopPos = 0, sourceFile: SourceFile) { 6228 while (pos-- > stopPos) { 6229 if (!isWhiteSpaceLike(sourceFile.text.charCodeAt(pos))) { 6230 return pos; 6231 } 6232 } 6233} 6234 6235/** 6236 * Determines whether a name was originally the declaration name of an enum or namespace 6237 * declaration. 6238 * 6239 * @internal 6240 */ 6241export function isDeclarationNameOfEnumOrNamespace(node: Identifier) { 6242 const parseNode = getParseTreeNode(node); 6243 if (parseNode) { 6244 switch (parseNode.parent.kind) { 6245 case SyntaxKind.EnumDeclaration: 6246 case SyntaxKind.ModuleDeclaration: 6247 return parseNode === (parseNode.parent as EnumDeclaration | ModuleDeclaration).name; 6248 } 6249 } 6250 return false; 6251} 6252 6253/** @internal */ 6254export function getInitializedVariables(node: VariableDeclarationList) { 6255 return filter(node.declarations, isInitializedVariable); 6256} 6257 6258function isInitializedVariable(node: VariableDeclaration): node is InitializedVariableDeclaration { 6259 return node.initializer !== undefined; 6260} 6261 6262/** @internal */ 6263export function isWatchSet(options: CompilerOptions) { 6264 // Firefox has Object.prototype.watch 6265 return options.watch && hasProperty(options, "watch"); 6266} 6267 6268/** @internal */ 6269export function closeFileWatcher(watcher: FileWatcher) { 6270 watcher.close(); 6271} 6272 6273/** @internal */ 6274export function getCheckFlags(symbol: Symbol): CheckFlags { 6275 return symbol.flags & SymbolFlags.Transient ? (symbol as TransientSymbol).checkFlags : 0; 6276} 6277 6278/** @internal */ 6279export function getDeclarationModifierFlagsFromSymbol(s: Symbol, isWrite = false): ModifierFlags { 6280 if (s.valueDeclaration) { 6281 const declaration = (isWrite && s.declarations && find(s.declarations, isSetAccessorDeclaration)) 6282 || (s.flags & SymbolFlags.GetAccessor && find(s.declarations, isGetAccessorDeclaration)) || s.valueDeclaration; 6283 const flags = getCombinedModifierFlags(declaration); 6284 return s.parent && s.parent.flags & SymbolFlags.Class ? flags : flags & ~ModifierFlags.AccessibilityModifier; 6285 } 6286 if (getCheckFlags(s) & CheckFlags.Synthetic) { 6287 const checkFlags = (s as TransientSymbol).checkFlags; 6288 const accessModifier = checkFlags & CheckFlags.ContainsPrivate ? ModifierFlags.Private : 6289 checkFlags & CheckFlags.ContainsPublic ? ModifierFlags.Public : 6290 ModifierFlags.Protected; 6291 const staticModifier = checkFlags & CheckFlags.ContainsStatic ? ModifierFlags.Static : 0; 6292 return accessModifier | staticModifier; 6293 } 6294 if (s.flags & SymbolFlags.Prototype) { 6295 return ModifierFlags.Public | ModifierFlags.Static; 6296 } 6297 return 0; 6298} 6299 6300/** @internal */ 6301export function skipAlias(symbol: Symbol, checker: TypeChecker) { 6302 return symbol.flags & SymbolFlags.Alias ? checker.getAliasedSymbol(symbol) : symbol; 6303} 6304 6305/** 6306 * See comment on `declareModuleMember` in `binder.ts`. 6307 * 6308 * @internal 6309 */ 6310export function getCombinedLocalAndExportSymbolFlags(symbol: Symbol): SymbolFlags { 6311 return symbol.exportSymbol ? symbol.exportSymbol.flags | symbol.flags : symbol.flags; 6312} 6313 6314/** @internal */ 6315export function isWriteOnlyAccess(node: Node) { 6316 return accessKind(node) === AccessKind.Write; 6317} 6318 6319/** @internal */ 6320export function isWriteAccess(node: Node) { 6321 return accessKind(node) !== AccessKind.Read; 6322} 6323 6324const enum AccessKind { 6325 /** Only reads from a variable. */ 6326 Read, 6327 /** Only writes to a variable without using the result. E.g.: `x++;`. */ 6328 Write, 6329 /** Writes to a variable and uses the result as an expression. E.g.: `f(x++);`. */ 6330 ReadWrite 6331} 6332function accessKind(node: Node): AccessKind { 6333 const { parent } = node; 6334 if (!parent) return AccessKind.Read; 6335 6336 switch (parent.kind) { 6337 case SyntaxKind.ParenthesizedExpression: 6338 return accessKind(parent); 6339 case SyntaxKind.PostfixUnaryExpression: 6340 case SyntaxKind.PrefixUnaryExpression: 6341 const { operator } = parent as PrefixUnaryExpression | PostfixUnaryExpression; 6342 return operator === SyntaxKind.PlusPlusToken || operator === SyntaxKind.MinusMinusToken ? writeOrReadWrite() : AccessKind.Read; 6343 case SyntaxKind.BinaryExpression: 6344 const { left, operatorToken } = parent as BinaryExpression; 6345 return left === node && isAssignmentOperator(operatorToken.kind) ? 6346 operatorToken.kind === SyntaxKind.EqualsToken ? AccessKind.Write : writeOrReadWrite() 6347 : AccessKind.Read; 6348 case SyntaxKind.PropertyAccessExpression: 6349 return (parent as PropertyAccessExpression).name !== node ? AccessKind.Read : accessKind(parent); 6350 case SyntaxKind.PropertyAssignment: { 6351 const parentAccess = accessKind(parent.parent); 6352 // In `({ x: varname }) = { x: 1 }`, the left `x` is a read, the right `x` is a write. 6353 return node === (parent as PropertyAssignment).name ? reverseAccessKind(parentAccess) : parentAccess; 6354 } 6355 case SyntaxKind.ShorthandPropertyAssignment: 6356 // Assume it's the local variable being accessed, since we don't check public properties for --noUnusedLocals. 6357 return node === (parent as ShorthandPropertyAssignment).objectAssignmentInitializer ? AccessKind.Read : accessKind(parent.parent); 6358 case SyntaxKind.ArrayLiteralExpression: 6359 return accessKind(parent); 6360 default: 6361 return AccessKind.Read; 6362 } 6363 6364 function writeOrReadWrite(): AccessKind { 6365 // If grandparent is not an ExpressionStatement, this is used as an expression in addition to having a side effect. 6366 return parent.parent && walkUpParenthesizedExpressions(parent.parent).kind === SyntaxKind.ExpressionStatement ? AccessKind.Write : AccessKind.ReadWrite; 6367 } 6368} 6369function reverseAccessKind(a: AccessKind): AccessKind { 6370 switch (a) { 6371 case AccessKind.Read: 6372 return AccessKind.Write; 6373 case AccessKind.Write: 6374 return AccessKind.Read; 6375 case AccessKind.ReadWrite: 6376 return AccessKind.ReadWrite; 6377 default: 6378 return Debug.assertNever(a); 6379 } 6380} 6381 6382/** @internal */ 6383export function compareDataObjects(dst: any, src: any): boolean { 6384 if (!dst || !src || Object.keys(dst).length !== Object.keys(src).length) { 6385 return false; 6386 } 6387 6388 for (const e in dst) { 6389 if (typeof dst[e] === "object") { 6390 if (!compareDataObjects(dst[e], src[e])) { 6391 return false; 6392 } 6393 } 6394 else if (typeof dst[e] !== "function") { 6395 if (dst[e] !== src[e]) { 6396 return false; 6397 } 6398 } 6399 } 6400 return true; 6401} 6402 6403/** 6404 * clears already present map by calling onDeleteExistingValue callback before deleting that key/value 6405 * 6406 * @internal 6407 */ 6408export function clearMap<K, T>(map: { forEach: ESMap<K, T>["forEach"]; clear: ESMap<K, T>["clear"]; }, onDeleteValue: (valueInMap: T, key: K) => void) { 6409 // Remove all 6410 map.forEach(onDeleteValue); 6411 map.clear(); 6412} 6413 6414/** @internal */ 6415export interface MutateMapSkippingNewValuesOptions<K, T, U> { 6416 onDeleteValue(existingValue: T, key: K): void; 6417 6418 /** 6419 * If present this is called with the key when there is value for that key both in new map as well as existing map provided 6420 * Caller can then decide to update or remove this key. 6421 * If the key is removed, caller will get callback of createNewValue for that key. 6422 * If this callback is not provided, the value of such keys is not updated. 6423 */ 6424 onExistingValue?(existingValue: T, valueInNewMap: U, key: K): void; 6425} 6426 6427/** 6428 * Mutates the map with newMap such that keys in map will be same as newMap. 6429 * 6430 * @internal 6431 */ 6432export function mutateMapSkippingNewValues<K, T, U>( 6433 map: ESMap<K, T>, 6434 newMap: ReadonlyESMap<K, U>, 6435 options: MutateMapSkippingNewValuesOptions<K, T, U> 6436) { 6437 const { onDeleteValue, onExistingValue } = options; 6438 // Needs update 6439 map.forEach((existingValue, key) => { 6440 const valueInNewMap = newMap.get(key); 6441 // Not present any more in new map, remove it 6442 if (valueInNewMap === undefined) { 6443 map.delete(key); 6444 onDeleteValue(existingValue, key); 6445 } 6446 // If present notify about existing values 6447 else if (onExistingValue) { 6448 onExistingValue(existingValue, valueInNewMap, key); 6449 } 6450 }); 6451} 6452 6453/** @internal */ 6454export interface MutateMapOptions<K, T, U> extends MutateMapSkippingNewValuesOptions<K, T, U> { 6455 createNewValue(key: K, valueInNewMap: U): T; 6456} 6457 6458/** 6459 * Mutates the map with newMap such that keys in map will be same as newMap. 6460 * 6461 * @internal 6462 */ 6463export function mutateMap<K, T, U>(map: ESMap<K, T>, newMap: ReadonlyESMap<K, U>, options: MutateMapOptions<K, T, U>) { 6464 // Needs update 6465 mutateMapSkippingNewValues(map, newMap, options); 6466 6467 const { createNewValue } = options; 6468 // Add new values that are not already present 6469 newMap.forEach((valueInNewMap, key) => { 6470 if (!map.has(key)) { 6471 // New values 6472 map.set(key, createNewValue(key, valueInNewMap)); 6473 } 6474 }); 6475} 6476 6477/** @internal */ 6478export function isAbstractConstructorSymbol(symbol: Symbol): boolean { 6479 if (symbol.flags & SymbolFlags.Class) { 6480 const declaration = getClassLikeDeclarationOfSymbol(symbol); 6481 return !!declaration && hasSyntacticModifier(declaration, ModifierFlags.Abstract); 6482 } 6483 return false; 6484} 6485 6486/** @internal */ 6487export function getClassLikeDeclarationOfSymbol(symbol: Symbol): ClassLikeDeclaration | undefined { 6488 return symbol.declarations?.find(isClassLike); 6489} 6490 6491/** @internal */ 6492export function getObjectFlags(type: Type): ObjectFlags { 6493 return type.flags & TypeFlags.ObjectFlagsType ? (type as ObjectFlagsType).objectFlags : 0; 6494} 6495 6496/** @internal */ 6497export function typeHasCallOrConstructSignatures(type: Type, checker: TypeChecker) { 6498 return checker.getSignaturesOfType(type, SignatureKind.Call).length !== 0 || checker.getSignaturesOfType(type, SignatureKind.Construct).length !== 0; 6499} 6500 6501/** @internal */ 6502export function forSomeAncestorDirectory(directory: string, callback: (directory: string) => boolean): boolean { 6503 return !!forEachAncestorDirectory(directory, d => callback(d) ? true : undefined); 6504} 6505 6506/** @internal */ 6507export function isUMDExportSymbol(symbol: Symbol | undefined): boolean { 6508 return !!symbol && !!symbol.declarations && !!symbol.declarations[0] && isNamespaceExportDeclaration(symbol.declarations[0]); 6509} 6510 6511/** @internal */ 6512export function showModuleSpecifier({ moduleSpecifier }: ImportDeclaration): string { 6513 return isStringLiteral(moduleSpecifier) ? moduleSpecifier.text : getTextOfNode(moduleSpecifier); 6514} 6515 6516/** @internal */ 6517export function getLastChild(node: Node): Node | undefined { 6518 let lastChild: Node | undefined; 6519 forEachChild(node, 6520 child => { 6521 if (nodeIsPresent(child)) lastChild = child; 6522 }, 6523 children => { 6524 // As an optimization, jump straight to the end of the list. 6525 for (let i = children.length - 1; i >= 0; i--) { 6526 if (nodeIsPresent(children[i])) { 6527 lastChild = children[i]; 6528 break; 6529 } 6530 } 6531 }); 6532 return lastChild; 6533} 6534 6535/** 6536 * Add a value to a set, and return true if it wasn't already present. 6537 * 6538 * @internal 6539 */ 6540export function addToSeen<K>(seen: ESMap<K, true>, key: K): boolean; 6541/** @internal */ 6542export function addToSeen<K, T>(seen: ESMap<K, T>, key: K, value: T): boolean; 6543/** @internal */ 6544export function addToSeen<K, T>(seen: ESMap<K, T>, key: K, value: T = true as any): boolean { 6545 if (seen.has(key)) { 6546 return false; 6547 } 6548 seen.set(key, value); 6549 return true; 6550} 6551 6552/** @internal */ 6553export function isObjectTypeDeclaration(node: Node): node is ObjectTypeDeclaration { 6554 return isClassLike(node) || isInterfaceDeclaration(node) || isTypeLiteralNode(node); 6555} 6556 6557/** @internal */ 6558export function isTypeNodeKind(kind: SyntaxKind): kind is TypeNodeSyntaxKind { 6559 return (kind >= SyntaxKind.FirstTypeNode && kind <= SyntaxKind.LastTypeNode) 6560 || kind === SyntaxKind.AnyKeyword 6561 || kind === SyntaxKind.UnknownKeyword 6562 || kind === SyntaxKind.NumberKeyword 6563 || kind === SyntaxKind.BigIntKeyword 6564 || kind === SyntaxKind.ObjectKeyword 6565 || kind === SyntaxKind.BooleanKeyword 6566 || kind === SyntaxKind.StringKeyword 6567 || kind === SyntaxKind.SymbolKeyword 6568 || kind === SyntaxKind.VoidKeyword 6569 || kind === SyntaxKind.UndefinedKeyword 6570 || kind === SyntaxKind.NeverKeyword 6571 || kind === SyntaxKind.ExpressionWithTypeArguments 6572 || kind === SyntaxKind.JSDocAllType 6573 || kind === SyntaxKind.JSDocUnknownType 6574 || kind === SyntaxKind.JSDocNullableType 6575 || kind === SyntaxKind.JSDocNonNullableType 6576 || kind === SyntaxKind.JSDocOptionalType 6577 || kind === SyntaxKind.JSDocFunctionType 6578 || kind === SyntaxKind.JSDocVariadicType; 6579} 6580 6581/** @internal */ 6582export function isAccessExpression(node: Node): node is AccessExpression { 6583 return node.kind === SyntaxKind.PropertyAccessExpression || node.kind === SyntaxKind.ElementAccessExpression; 6584} 6585 6586/** @internal */ 6587export function getNameOfAccessExpression(node: AccessExpression) { 6588 if (node.kind === SyntaxKind.PropertyAccessExpression) { 6589 return node.name; 6590 } 6591 Debug.assert(node.kind === SyntaxKind.ElementAccessExpression); 6592 return node.argumentExpression; 6593} 6594 6595/** @internal */ 6596export function isBundleFileTextLike(section: BundleFileSection): section is BundleFileTextLike { 6597 switch (section.kind) { 6598 case BundleFileSectionKind.Text: 6599 case BundleFileSectionKind.Internal: 6600 return true; 6601 default: 6602 return false; 6603 } 6604} 6605 6606/** @internal */ 6607export function isNamedImportsOrExports(node: Node): node is NamedImportsOrExports { 6608 return node.kind === SyntaxKind.NamedImports || node.kind === SyntaxKind.NamedExports; 6609} 6610 6611/** @internal */ 6612export function getLeftmostAccessExpression(expr: Expression): Expression { 6613 while (isAccessExpression(expr)) { 6614 expr = expr.expression; 6615 } 6616 return expr; 6617} 6618 6619/** @internal */ 6620export function forEachNameInAccessChainWalkingLeft<T>(name: MemberName | StringLiteralLike, action: (name: MemberName | StringLiteralLike) => T | undefined): T | undefined { 6621 if (isAccessExpression(name.parent) && isRightSideOfAccessExpression(name)) { 6622 return walkAccessExpression(name.parent); 6623 } 6624 6625 function walkAccessExpression(access: AccessExpression): T | undefined { 6626 if (access.kind === SyntaxKind.PropertyAccessExpression) { 6627 const res = action(access.name); 6628 if (res !== undefined) { 6629 return res; 6630 } 6631 } 6632 else if (access.kind === SyntaxKind.ElementAccessExpression) { 6633 if (isIdentifier(access.argumentExpression) || isStringLiteralLike(access.argumentExpression)) { 6634 const res = action(access.argumentExpression); 6635 if (res !== undefined) { 6636 return res; 6637 } 6638 } 6639 else { 6640 // Chain interrupted by non-static-name access 'x[expr()].y.z' 6641 return undefined; 6642 } 6643 } 6644 6645 if (isAccessExpression(access.expression)) { 6646 return walkAccessExpression(access.expression); 6647 } 6648 if (isIdentifier(access.expression)) { 6649 // End of chain at Identifier 'x.y.z' 6650 return action(access.expression); 6651 } 6652 // End of chain at non-Identifier 'x().y.z' 6653 return undefined; 6654 } 6655} 6656 6657 6658 6659/** @internal */ 6660export function getLeftmostExpression(node: Expression, stopAtCallExpressions: boolean) { 6661 while (true) { 6662 switch (node.kind) { 6663 case SyntaxKind.PostfixUnaryExpression: 6664 node = (node as PostfixUnaryExpression).operand; 6665 continue; 6666 6667 case SyntaxKind.BinaryExpression: 6668 node = (node as BinaryExpression).left; 6669 continue; 6670 6671 case SyntaxKind.ConditionalExpression: 6672 node = (node as ConditionalExpression).condition; 6673 continue; 6674 6675 case SyntaxKind.TaggedTemplateExpression: 6676 node = (node as TaggedTemplateExpression).tag; 6677 continue; 6678 6679 case SyntaxKind.CallExpression: 6680 if (stopAtCallExpressions) { 6681 return node; 6682 } 6683 // falls through 6684 case SyntaxKind.AsExpression: 6685 case SyntaxKind.ElementAccessExpression: 6686 case SyntaxKind.PropertyAccessExpression: 6687 case SyntaxKind.NonNullExpression: 6688 case SyntaxKind.PartiallyEmittedExpression: 6689 case SyntaxKind.SatisfiesExpression: 6690 node = (node as CallExpression | PropertyAccessExpression | ElementAccessExpression | AsExpression | NonNullExpression | PartiallyEmittedExpression | SatisfiesExpression).expression; 6691 continue; 6692 } 6693 6694 return node; 6695 } 6696} 6697 6698/** @internal */ 6699export interface ObjectAllocator { 6700 getNodeConstructor(): new (kind: SyntaxKind, pos?: number, end?: number) => Node; 6701 getTokenConstructor(): new <TKind extends SyntaxKind>(kind: TKind, pos?: number, end?: number) => Token<TKind>; 6702 getIdentifierConstructor(): new (kind: SyntaxKind.Identifier, pos?: number, end?: number) => Identifier; 6703 getPrivateIdentifierConstructor(): new (kind: SyntaxKind.PrivateIdentifier, pos?: number, end?: number) => PrivateIdentifier; 6704 getSourceFileConstructor(): new (kind: SyntaxKind.SourceFile, pos?: number, end?: number) => SourceFile; 6705 getSymbolConstructor(): new (flags: SymbolFlags, name: __String) => Symbol; 6706 getTypeConstructor(): new (checker: TypeChecker, flags: TypeFlags) => Type; 6707 getSignatureConstructor(): new (checker: TypeChecker, flags: SignatureFlags) => Signature; 6708 getSourceMapSourceConstructor(): new (fileName: string, text: string, skipTrivia?: (pos: number) => number) => SourceMapSource; 6709} 6710 6711function Symbol(this: Symbol, flags: SymbolFlags, name: __String) { 6712 this.flags = flags; 6713 this.escapedName = name; 6714 this.declarations = undefined; 6715 this.valueDeclaration = undefined; 6716 this.id = undefined; 6717 this.mergeId = undefined; 6718 this.parent = undefined; 6719} 6720 6721function Type(this: Type, checker: TypeChecker, flags: TypeFlags) { 6722 this.flags = flags; 6723 if (Debug.isDebugging || tracing) { 6724 this.checker = checker; 6725 } 6726} 6727 6728function Signature(this: Signature, checker: TypeChecker, flags: SignatureFlags) { 6729 this.flags = flags; 6730 if (Debug.isDebugging) { 6731 this.checker = checker; 6732 } 6733} 6734 6735function Node(this: Mutable<Node>, kind: SyntaxKind, pos: number, end: number) { 6736 this.pos = pos; 6737 this.end = end; 6738 this.kind = kind; 6739 this.id = 0; 6740 this.flags = NodeFlags.None; 6741 this.modifierFlagsCache = ModifierFlags.None; 6742 this.transformFlags = TransformFlags.None; 6743 this.parent = undefined!; 6744 this.original = undefined; 6745} 6746 6747function Token(this: Mutable<Node>, kind: SyntaxKind, pos: number, end: number) { 6748 this.pos = pos; 6749 this.end = end; 6750 this.kind = kind; 6751 this.id = 0; 6752 this.flags = NodeFlags.None; 6753 this.transformFlags = TransformFlags.None; 6754 this.parent = undefined!; 6755} 6756 6757function Identifier(this: Mutable<Node>, kind: SyntaxKind, pos: number, end: number) { 6758 this.pos = pos; 6759 this.end = end; 6760 this.kind = kind; 6761 this.id = 0; 6762 this.flags = NodeFlags.None; 6763 this.transformFlags = TransformFlags.None; 6764 this.parent = undefined!; 6765 this.original = undefined; 6766 this.flowNode = undefined; 6767} 6768 6769function SourceMapSource(this: SourceMapSource, fileName: string, text: string, skipTrivia?: (pos: number) => number) { 6770 this.fileName = fileName; 6771 this.text = text; 6772 this.skipTrivia = skipTrivia || (pos => pos); 6773} 6774 6775// eslint-disable-next-line prefer-const 6776/** @internal */ 6777export const objectAllocator: ObjectAllocator = { 6778 getNodeConstructor: () => Node as any, 6779 getTokenConstructor: () => Token as any, 6780 getIdentifierConstructor: () => Identifier as any, 6781 getPrivateIdentifierConstructor: () => Node as any, 6782 getSourceFileConstructor: () => Node as any, 6783 getSymbolConstructor: () => Symbol as any, 6784 getTypeConstructor: () => Type as any, 6785 getSignatureConstructor: () => Signature as any, 6786 getSourceMapSourceConstructor: () => SourceMapSource as any, 6787}; 6788 6789/** @internal */ 6790export function setObjectAllocator(alloc: ObjectAllocator) { 6791 Object.assign(objectAllocator, alloc); 6792} 6793 6794/** @internal */ 6795export function formatStringFromArgs(text: string, args: ArrayLike<string | number>, baseIndex = 0): string { 6796 return text.replace(/{(\d+)}/g, (_match, index: string) => "" + Debug.checkDefined(args[+index + baseIndex])); 6797} 6798 6799let localizedDiagnosticMessages: MapLike<string> | undefined; 6800 6801/** @internal */ 6802export function setLocalizedDiagnosticMessages(messages: MapLike<string> | undefined) { 6803 localizedDiagnosticMessages = messages; 6804} 6805 6806/** @internal */ 6807// If the localized messages json is unset, and if given function use it to set the json 6808 6809export function maybeSetLocalizedDiagnosticMessages(getMessages: undefined | (() => MapLike<string> | undefined)) { 6810 if (!localizedDiagnosticMessages && getMessages) { 6811 localizedDiagnosticMessages = getMessages(); 6812 } 6813} 6814 6815/** @internal */ 6816export function getLocaleSpecificMessage(message: DiagnosticMessage) { 6817 return localizedDiagnosticMessages && localizedDiagnosticMessages[message.key] || message.message; 6818} 6819 6820/** @internal */ 6821export function createDetachedDiagnostic(fileName: string, start: number, length: number, message: DiagnosticMessage, ...args: (string | number | undefined)[]): DiagnosticWithDetachedLocation; 6822/** @internal */ 6823export function createDetachedDiagnostic(fileName: string, start: number, length: number, message: DiagnosticMessage): DiagnosticWithDetachedLocation { 6824 assertDiagnosticLocation(/*file*/ undefined, start, length); 6825 let text = getLocaleSpecificMessage(message); 6826 6827 if (arguments.length > 4) { 6828 text = formatStringFromArgs(text, arguments, 4); 6829 } 6830 6831 return { 6832 file: undefined, 6833 start, 6834 length, 6835 6836 messageText: text, 6837 category: message.category, 6838 code: message.code, 6839 reportsUnnecessary: message.reportsUnnecessary, 6840 fileName, 6841 }; 6842} 6843 6844function isDiagnosticWithDetachedLocation(diagnostic: DiagnosticRelatedInformation | DiagnosticWithDetachedLocation): diagnostic is DiagnosticWithDetachedLocation { 6845 return diagnostic.file === undefined 6846 && diagnostic.start !== undefined 6847 && diagnostic.length !== undefined 6848 && typeof (diagnostic as DiagnosticWithDetachedLocation).fileName === "string"; 6849} 6850 6851function attachFileToDiagnostic(diagnostic: DiagnosticWithDetachedLocation, file: SourceFile): DiagnosticWithLocation { 6852 const fileName = file.fileName || ""; 6853 const length = file.text.length; 6854 Debug.assertEqual(diagnostic.fileName, fileName); 6855 Debug.assertLessThanOrEqual(diagnostic.start, length); 6856 Debug.assertLessThanOrEqual(diagnostic.start + diagnostic.length, length); 6857 const diagnosticWithLocation: DiagnosticWithLocation = { 6858 file, 6859 start: diagnostic.start, 6860 length: diagnostic.length, 6861 messageText: diagnostic.messageText, 6862 category: diagnostic.category, 6863 code: diagnostic.code, 6864 reportsUnnecessary: diagnostic.reportsUnnecessary 6865 }; 6866 if (diagnostic.relatedInformation) { 6867 diagnosticWithLocation.relatedInformation = []; 6868 for (const related of diagnostic.relatedInformation) { 6869 if (isDiagnosticWithDetachedLocation(related) && related.fileName === fileName) { 6870 Debug.assertLessThanOrEqual(related.start, length); 6871 Debug.assertLessThanOrEqual(related.start + related.length, length); 6872 diagnosticWithLocation.relatedInformation.push(attachFileToDiagnostic(related, file)); 6873 } 6874 else { 6875 diagnosticWithLocation.relatedInformation.push(related); 6876 } 6877 } 6878 } 6879 return diagnosticWithLocation; 6880} 6881 6882/** @internal */ 6883export function attachFileToDiagnostics(diagnostics: DiagnosticWithDetachedLocation[], file: SourceFile): DiagnosticWithLocation[] { 6884 const diagnosticsWithLocation: DiagnosticWithLocation[] = []; 6885 for (const diagnostic of diagnostics) { 6886 diagnosticsWithLocation.push(attachFileToDiagnostic(diagnostic, file)); 6887 } 6888 return diagnosticsWithLocation; 6889} 6890 6891/** @internal */ 6892export function createFileDiagnostic(file: SourceFile, start: number, length: number, message: DiagnosticMessage, ...args: (string | number | undefined)[]): DiagnosticWithLocation; 6893/** @internal */ 6894export function createFileDiagnostic(file: SourceFile, start: number, length: number, message: DiagnosticMessage): DiagnosticWithLocation { 6895 assertDiagnosticLocation(file, start, length); 6896 6897 let text = getLocaleSpecificMessage(message); 6898 6899 if (arguments.length > 4) { 6900 text = formatStringFromArgs(text, arguments, 4); 6901 } 6902 6903 return { 6904 file, 6905 start, 6906 length, 6907 6908 messageText: text, 6909 category: message.category, 6910 code: message.code, 6911 reportsUnnecessary: message.reportsUnnecessary, 6912 reportsDeprecated: message.reportsDeprecated 6913 }; 6914} 6915 6916/** @internal */ 6917export function formatMessage(_dummy: any, message: DiagnosticMessage, ...args: (string | number | undefined)[]): string; 6918/** @internal */ 6919export function formatMessage(_dummy: any, message: DiagnosticMessage): string { 6920 let text = getLocaleSpecificMessage(message); 6921 6922 if (arguments.length > 2) { 6923 text = formatStringFromArgs(text, arguments, 2); 6924 } 6925 6926 return text; 6927} 6928 6929/** @internal */ 6930export function createCompilerDiagnostic(message: DiagnosticMessage, ...args: (string | number | undefined)[]): Diagnostic; 6931/** @internal */ 6932export function createCompilerDiagnostic(message: DiagnosticMessage): Diagnostic { 6933 let text = getLocaleSpecificMessage(message); 6934 6935 if (arguments.length > 1) { 6936 text = formatStringFromArgs(text, arguments, 1); 6937 } 6938 6939 return { 6940 file: undefined, 6941 start: undefined, 6942 length: undefined, 6943 6944 messageText: text, 6945 category: message.category, 6946 code: message.code, 6947 reportsUnnecessary: message.reportsUnnecessary, 6948 reportsDeprecated: message.reportsDeprecated 6949 }; 6950} 6951 6952/** @internal */ 6953export function createCompilerDiagnosticFromMessageChain(chain: DiagnosticMessageChain, relatedInformation?: DiagnosticRelatedInformation[]): Diagnostic { 6954 return { 6955 file: undefined, 6956 start: undefined, 6957 length: undefined, 6958 6959 code: chain.code, 6960 category: chain.category, 6961 messageText: chain.next ? chain : chain.messageText, 6962 relatedInformation 6963 }; 6964} 6965 6966/** @internal */ 6967export function chainDiagnosticMessages(details: DiagnosticMessageChain | DiagnosticMessageChain[] | undefined, message: DiagnosticMessage, ...args: (string | number | undefined)[]): DiagnosticMessageChain; 6968/** @internal */ 6969export function chainDiagnosticMessages(details: DiagnosticMessageChain | DiagnosticMessageChain[] | undefined, message: DiagnosticMessage): DiagnosticMessageChain { 6970 let text = getLocaleSpecificMessage(message); 6971 6972 if (arguments.length > 2) { 6973 text = formatStringFromArgs(text, arguments, 2); 6974 } 6975 return { 6976 messageText: text, 6977 category: message.category, 6978 code: message.code, 6979 6980 next: details === undefined || Array.isArray(details) ? details : [details] 6981 }; 6982} 6983 6984/** @internal */ 6985export function concatenateDiagnosticMessageChains(headChain: DiagnosticMessageChain, tailChain: DiagnosticMessageChain): void { 6986 let lastChain = headChain; 6987 while (lastChain.next) { 6988 lastChain = lastChain.next[0]; 6989 } 6990 6991 lastChain.next = [tailChain]; 6992} 6993 6994function getDiagnosticFilePath(diagnostic: Diagnostic): string | undefined { 6995 return diagnostic.file ? diagnostic.file.path : undefined; 6996} 6997 6998/** @internal */ 6999export function compareDiagnostics(d1: Diagnostic, d2: Diagnostic): Comparison { 7000 return compareDiagnosticsSkipRelatedInformation(d1, d2) || 7001 compareRelatedInformation(d1, d2) || 7002 Comparison.EqualTo; 7003} 7004 7005/** @internal */ 7006export function compareDiagnosticsSkipRelatedInformation(d1: Diagnostic, d2: Diagnostic): Comparison { 7007 return compareStringsCaseSensitive(getDiagnosticFilePath(d1), getDiagnosticFilePath(d2)) || 7008 compareValues(d1.start, d2.start) || 7009 compareValues(d1.length, d2.length) || 7010 compareValues(d1.code, d2.code) || 7011 compareMessageText(d1.messageText, d2.messageText) || 7012 Comparison.EqualTo; 7013} 7014 7015function compareRelatedInformation(d1: Diagnostic, d2: Diagnostic): Comparison { 7016 if (!d1.relatedInformation && !d2.relatedInformation) { 7017 return Comparison.EqualTo; 7018 } 7019 if (d1.relatedInformation && d2.relatedInformation) { 7020 return compareValues(d1.relatedInformation.length, d2.relatedInformation.length) || forEach(d1.relatedInformation, (d1i, index) => { 7021 const d2i = d2.relatedInformation![index]; 7022 return compareDiagnostics(d1i, d2i); // EqualTo is 0, so falsy, and will cause the next item to be compared 7023 }) || Comparison.EqualTo; 7024 } 7025 return d1.relatedInformation ? Comparison.LessThan : Comparison.GreaterThan; 7026} 7027 7028function compareMessageText(t1: string | DiagnosticMessageChain, t2: string | DiagnosticMessageChain): Comparison { 7029 if (typeof t1 === "string" && typeof t2 === "string") { 7030 return compareStringsCaseSensitive(t1, t2); 7031 } 7032 else if (typeof t1 === "string") { 7033 return Comparison.LessThan; 7034 } 7035 else if (typeof t2 === "string") { 7036 return Comparison.GreaterThan; 7037 } 7038 let res = compareStringsCaseSensitive(t1.messageText, t2.messageText); 7039 if (res) { 7040 return res; 7041 } 7042 if (!t1.next && !t2.next) { 7043 return Comparison.EqualTo; 7044 } 7045 if (!t1.next) { 7046 return Comparison.LessThan; 7047 } 7048 if (!t2.next) { 7049 return Comparison.GreaterThan; 7050 } 7051 const len = Math.min(t1.next.length, t2.next.length); 7052 for (let i = 0; i < len; i++) { 7053 res = compareMessageText(t1.next[i], t2.next[i]); 7054 if (res) { 7055 return res; 7056 } 7057 } 7058 if (t1.next.length < t2.next.length) { 7059 return Comparison.LessThan; 7060 } 7061 else if (t1.next.length > t2.next.length) { 7062 return Comparison.GreaterThan; 7063 } 7064 return Comparison.EqualTo; 7065} 7066 7067/** @internal */ 7068export function getLanguageVariant(scriptKind: ScriptKind) { 7069 // .tsx and .jsx files are treated as jsx language variant. 7070 return scriptKind === ScriptKind.TSX || scriptKind === ScriptKind.JSX || scriptKind === ScriptKind.JS || scriptKind === ScriptKind.JSON ? LanguageVariant.JSX : LanguageVariant.Standard; 7071} 7072 7073/** 7074 * This is a somewhat unavoidable full tree walk to locate a JSX tag - `import.meta` requires the same, 7075 * but we avoid that walk (or parts of it) if at all possible using the `PossiblyContainsImportMeta` node flag. 7076 * Unfortunately, there's no `NodeFlag` space to do the same for JSX. 7077 */ 7078function walkTreeForJSXTags(node: Node): Node | undefined { 7079 if (!(node.transformFlags & TransformFlags.ContainsJsx)) return undefined; 7080 return isJsxOpeningLikeElement(node) || isJsxFragment(node) ? node : forEachChild(node, walkTreeForJSXTags); 7081} 7082 7083function isFileModuleFromUsingJSXTag(file: SourceFile): Node | undefined { 7084 // Excludes declaration files - they still require an explicit `export {}` or the like 7085 // for back compat purposes. (not that declaration files should contain JSX tags!) 7086 return !file.isDeclarationFile ? walkTreeForJSXTags(file) : undefined; 7087} 7088 7089/** 7090 * Note that this requires file.impliedNodeFormat be set already; meaning it must be set very early on 7091 * in SourceFile construction. 7092 */ 7093function isFileForcedToBeModuleByFormat(file: SourceFile): true | undefined { 7094 // Excludes declaration files - they still require an explicit `export {}` or the like 7095 // for back compat purposes. The only non-declaration files _not_ forced to be a module are `.js` files 7096 // that aren't esm-mode (meaning not in a `type: module` scope). 7097 return (file.impliedNodeFormat === ModuleKind.ESNext || (fileExtensionIsOneOf(file.fileName, [Extension.Cjs, Extension.Cts, Extension.Mjs, Extension.Mts]))) && !file.isDeclarationFile ? true : undefined; 7098} 7099 7100/** @internal */ 7101export function getSetExternalModuleIndicator(options: CompilerOptions): (file: SourceFile) => void { 7102 // TODO: Should this callback be cached? 7103 switch (getEmitModuleDetectionKind(options)) { 7104 case ModuleDetectionKind.Force: 7105 // All non-declaration files are modules, declaration files still do the usual isFileProbablyExternalModule 7106 return (file: SourceFile) => { 7107 file.externalModuleIndicator = isFileProbablyExternalModule(file) || !file.isDeclarationFile || undefined; 7108 }; 7109 case ModuleDetectionKind.Legacy: 7110 // Files are modules if they have imports, exports, or import.meta 7111 return (file: SourceFile) => { 7112 file.externalModuleIndicator = isFileProbablyExternalModule(file); 7113 }; 7114 case ModuleDetectionKind.Auto: 7115 // If module is nodenext or node16, all esm format files are modules 7116 // If jsx is react-jsx or react-jsxdev then jsx tags force module-ness 7117 // otherwise, the presence of import or export statments (or import.meta) implies module-ness 7118 const checks: ((file: SourceFile) => Node | true | undefined)[] = [isFileProbablyExternalModule]; 7119 if (options.jsx === JsxEmit.ReactJSX || options.jsx === JsxEmit.ReactJSXDev) { 7120 checks.push(isFileModuleFromUsingJSXTag); 7121 } 7122 checks.push(isFileForcedToBeModuleByFormat); 7123 const combined = or(...checks); 7124 const callback = (file: SourceFile) => void (file.externalModuleIndicator = combined(file)); 7125 return callback; 7126 } 7127} 7128 7129/** @internal */ 7130export function getEmitScriptTarget(compilerOptions: {module?: CompilerOptions["module"], target?: CompilerOptions["target"]}) { 7131 return compilerOptions.target || 7132 (compilerOptions.module === ModuleKind.Node16 && ScriptTarget.ES2022) || 7133 (compilerOptions.module === ModuleKind.NodeNext && ScriptTarget.ESNext) || 7134 ScriptTarget.ES3; 7135} 7136 7137/** @internal */ 7138export function getEmitModuleKind(compilerOptions: {module?: CompilerOptions["module"], target?: CompilerOptions["target"]}) { 7139 return typeof compilerOptions.module === "number" ? 7140 compilerOptions.module : 7141 getEmitScriptTarget(compilerOptions) >= ScriptTarget.ES2015 ? ModuleKind.ES2015 : ModuleKind.CommonJS; 7142} 7143 7144/** @internal */ 7145export function getEmitModuleResolutionKind(compilerOptions: CompilerOptions) { 7146 let moduleResolution = compilerOptions.moduleResolution; 7147 if (moduleResolution === undefined) { 7148 switch (getEmitModuleKind(compilerOptions)) { 7149 case ModuleKind.CommonJS: 7150 moduleResolution = ModuleResolutionKind.NodeJs; 7151 break; 7152 case ModuleKind.Node16: 7153 moduleResolution = ModuleResolutionKind.Node16; 7154 break; 7155 case ModuleKind.NodeNext: 7156 moduleResolution = ModuleResolutionKind.NodeNext; 7157 break; 7158 default: 7159 moduleResolution = ModuleResolutionKind.Classic; 7160 break; 7161 } 7162 } 7163 return moduleResolution; 7164} 7165 7166/** @internal */ 7167export function getEmitModuleDetectionKind(options: CompilerOptions) { 7168 return options.moduleDetection || 7169 (getEmitModuleKind(options) === ModuleKind.Node16 || getEmitModuleKind(options) === ModuleKind.NodeNext ? ModuleDetectionKind.Force : ModuleDetectionKind.Auto); 7170} 7171 7172/** @internal */ 7173export function hasJsonModuleEmitEnabled(options: CompilerOptions) { 7174 switch (getEmitModuleKind(options)) { 7175 case ModuleKind.CommonJS: 7176 case ModuleKind.AMD: 7177 case ModuleKind.ES2015: 7178 case ModuleKind.ES2020: 7179 case ModuleKind.ES2022: 7180 case ModuleKind.ESNext: 7181 case ModuleKind.Node16: 7182 case ModuleKind.NodeNext: 7183 return true; 7184 default: 7185 return false; 7186 } 7187} 7188 7189/** @internal */ 7190export function unreachableCodeIsError(options: CompilerOptions): boolean { 7191 return options.allowUnreachableCode === false; 7192} 7193 7194/** @internal */ 7195export function unusedLabelIsError(options: CompilerOptions): boolean { 7196 return options.allowUnusedLabels === false; 7197} 7198 7199/** @internal */ 7200export function getAreDeclarationMapsEnabled(options: CompilerOptions) { 7201 return !!(getEmitDeclarations(options) && options.declarationMap); 7202} 7203 7204/** @internal */ 7205export function getESModuleInterop(compilerOptions: CompilerOptions) { 7206 if (compilerOptions.esModuleInterop !== undefined) { 7207 return compilerOptions.esModuleInterop; 7208 } 7209 switch (getEmitModuleKind(compilerOptions)) { 7210 case ModuleKind.Node16: 7211 case ModuleKind.NodeNext: 7212 return true; 7213 } 7214 return undefined; 7215} 7216 7217/** @internal */ 7218export function getAllowSyntheticDefaultImports(compilerOptions: CompilerOptions) { 7219 const moduleKind = getEmitModuleKind(compilerOptions); 7220 return compilerOptions.allowSyntheticDefaultImports !== undefined 7221 ? compilerOptions.allowSyntheticDefaultImports 7222 : getESModuleInterop(compilerOptions) || 7223 moduleKind === ModuleKind.System; 7224} 7225 7226/** @internal */ 7227export function getEmitDeclarations(compilerOptions: CompilerOptions): boolean { 7228 return !!(compilerOptions.declaration || compilerOptions.composite); 7229} 7230 7231/** @internal */ 7232export function shouldPreserveConstEnums(compilerOptions: CompilerOptions): boolean { 7233 return !!(compilerOptions.preserveConstEnums || compilerOptions.isolatedModules); 7234} 7235 7236/** @internal */ 7237export function isIncrementalCompilation(options: CompilerOptions) { 7238 return !!(options.incremental || options.composite); 7239} 7240 7241/** @internal */ 7242export type StrictOptionName = 7243 | "noImplicitAny" 7244 | "noImplicitThis" 7245 | "strictNullChecks" 7246 | "strictFunctionTypes" 7247 | "strictBindCallApply" 7248 | "strictPropertyInitialization" 7249 | "alwaysStrict" 7250 | "useUnknownInCatchVariables" 7251 ; 7252 7253/** @internal */ 7254export function getStrictOptionValue(compilerOptions: CompilerOptions, flag: StrictOptionName): boolean { 7255 return compilerOptions[flag] === undefined ? !!compilerOptions.strict : !!compilerOptions[flag]; 7256} 7257 7258/** @internal */ 7259export function getAllowJSCompilerOption(compilerOptions: CompilerOptions): boolean { 7260 return compilerOptions.allowJs === undefined ? !!compilerOptions.checkJs : compilerOptions.allowJs; 7261} 7262 7263/** @internal */ 7264export function getUseDefineForClassFields(compilerOptions: CompilerOptions): boolean { 7265 return compilerOptions.useDefineForClassFields === undefined ? getEmitScriptTarget(compilerOptions) >= ScriptTarget.ES2022 : compilerOptions.useDefineForClassFields; 7266} 7267 7268/** @internal */ 7269export function compilerOptionsAffectSemanticDiagnostics(newOptions: CompilerOptions, oldOptions: CompilerOptions): boolean { 7270 return optionsHaveChanges(oldOptions, newOptions, semanticDiagnosticsOptionDeclarations); 7271} 7272 7273/** @internal */ 7274export function compilerOptionsAffectEmit(newOptions: CompilerOptions, oldOptions: CompilerOptions): boolean { 7275 return optionsHaveChanges(oldOptions, newOptions, affectsEmitOptionDeclarations); 7276} 7277 7278/** @internal */ 7279export function compilerOptionsAffectDeclarationPath(newOptions: CompilerOptions, oldOptions: CompilerOptions): boolean { 7280 return optionsHaveChanges(oldOptions, newOptions, affectsDeclarationPathOptionDeclarations); 7281} 7282 7283/** @internal */ 7284export function getCompilerOptionValue(options: CompilerOptions, option: CommandLineOption): unknown { 7285 return option.strictFlag ? getStrictOptionValue(options, option.name as StrictOptionName) : options[option.name]; 7286} 7287 7288/** @internal */ 7289export function getJSXTransformEnabled(options: CompilerOptions): boolean { 7290 const jsx = options.jsx; 7291 return jsx === JsxEmit.React || jsx === JsxEmit.ReactJSX || jsx === JsxEmit.ReactJSXDev; 7292} 7293 7294/** @internal */ 7295export function getJSXImplicitImportBase(compilerOptions: CompilerOptions, file?: SourceFile): string | undefined { 7296 const jsxImportSourcePragmas = file?.pragmas.get("jsximportsource"); 7297 const jsxImportSourcePragma = isArray(jsxImportSourcePragmas) ? jsxImportSourcePragmas[jsxImportSourcePragmas.length - 1] : jsxImportSourcePragmas; 7298 return compilerOptions.jsx === JsxEmit.ReactJSX || 7299 compilerOptions.jsx === JsxEmit.ReactJSXDev || 7300 compilerOptions.jsxImportSource || 7301 jsxImportSourcePragma ? 7302 jsxImportSourcePragma?.arguments.factory || compilerOptions.jsxImportSource || "react" : 7303 undefined; 7304} 7305 7306/** @internal */ 7307export function getJSXRuntimeImport(base: string | undefined, options: CompilerOptions) { 7308 return base ? `${base}/${options.jsx === JsxEmit.ReactJSXDev ? "jsx-dev-runtime" : "jsx-runtime"}` : undefined; 7309} 7310 7311/** @internal */ 7312export function hasZeroOrOneAsteriskCharacter(str: string): boolean { 7313 let seenAsterisk = false; 7314 for (let i = 0; i < str.length; i++) { 7315 if (str.charCodeAt(i) === CharacterCodes.asterisk) { 7316 if (!seenAsterisk) { 7317 seenAsterisk = true; 7318 } 7319 else { 7320 // have already seen asterisk 7321 return false; 7322 } 7323 } 7324 } 7325 return true; 7326} 7327 7328/** @internal */ 7329export interface SymlinkedDirectory { 7330 /** Matches the casing returned by `realpath`. Used to compute the `realpath` of children. */ 7331 real: string; 7332 /** toPath(real). Stored to avoid repeated recomputation. */ 7333 realPath: Path; 7334} 7335 7336/** @internal */ 7337export interface SymlinkCache { 7338 /** Gets a map from symlink to realpath. Keys have trailing directory separators. */ 7339 getSymlinkedDirectories(): ReadonlyESMap<Path, SymlinkedDirectory | false> | undefined; 7340 /** Gets a map from realpath to symlinks. Keys have trailing directory separators. */ 7341 getSymlinkedDirectoriesByRealpath(): MultiMap<Path, string> | undefined; 7342 /** Gets a map from symlink to realpath */ 7343 getSymlinkedFiles(): ReadonlyESMap<Path, string> | undefined; 7344 setSymlinkedDirectory(symlink: string, real: SymlinkedDirectory | false): void; 7345 setSymlinkedFile(symlinkPath: Path, real: string): void; 7346 /** 7347 * @internal 7348 * Uses resolvedTypeReferenceDirectives from program instead of from files, since files 7349 * don't include automatic type reference directives. Must be called only when 7350 * `hasProcessedResolutions` returns false (once per cache instance). 7351 */ 7352 setSymlinksFromResolutions(files: readonly SourceFile[], typeReferenceDirectives: ModeAwareCache<ResolvedTypeReferenceDirective | undefined> | undefined): void; 7353 /** 7354 * @internal 7355 * Whether `setSymlinksFromResolutions` has already been called. 7356 */ 7357 hasProcessedResolutions(): boolean; 7358} 7359 7360/** @internal */ 7361export function createSymlinkCache(cwd: string, getCanonicalFileName: GetCanonicalFileName, isOHModules?: boolean): SymlinkCache { 7362 let symlinkedDirectories: ESMap<Path, SymlinkedDirectory | false> | undefined; 7363 let symlinkedDirectoriesByRealpath: MultiMap<Path, string> | undefined; 7364 let symlinkedFiles: ESMap<Path, string> | undefined; 7365 let hasProcessedResolutions = false; 7366 return { 7367 getSymlinkedFiles: () => symlinkedFiles, 7368 getSymlinkedDirectories: () => symlinkedDirectories, 7369 getSymlinkedDirectoriesByRealpath: () => symlinkedDirectoriesByRealpath, 7370 setSymlinkedFile: (path, real) => (symlinkedFiles || (symlinkedFiles = new Map())).set(path, real), 7371 setSymlinkedDirectory: (symlink, real) => { 7372 // Large, interconnected dependency graphs in pnpm will have a huge number of symlinks 7373 // where both the realpath and the symlink path are inside node_modules/.pnpm. Since 7374 // this path is never a candidate for a module specifier, we can ignore it entirely. 7375 let symlinkPath = toPath(symlink, cwd, getCanonicalFileName); 7376 if (!containsIgnoredPath(symlinkPath)) { 7377 symlinkPath = ensureTrailingDirectorySeparator(symlinkPath); 7378 if (real !== false && !symlinkedDirectories?.has(symlinkPath)) { 7379 (symlinkedDirectoriesByRealpath ||= createMultiMap()).add(ensureTrailingDirectorySeparator(real.realPath), symlink); 7380 } 7381 (symlinkedDirectories || (symlinkedDirectories = new Map())).set(symlinkPath, real); 7382 } 7383 }, 7384 setSymlinksFromResolutions(files, typeReferenceDirectives) { 7385 Debug.assert(!hasProcessedResolutions); 7386 hasProcessedResolutions = true; 7387 for (const file of files) { 7388 file.resolvedModules?.forEach(resolution => processResolution(this, resolution, isOHModules)); 7389 } 7390 typeReferenceDirectives?.forEach(resolution => processResolution(this, resolution, isOHModules)); 7391 }, 7392 hasProcessedResolutions: () => hasProcessedResolutions, 7393 }; 7394 7395 function processResolution(cache: SymlinkCache, resolution: ResolvedModuleFull | ResolvedTypeReferenceDirective | undefined, isOHModules?: boolean) { 7396 if (!resolution || !resolution.originalPath || !resolution.resolvedFileName) return; 7397 const { resolvedFileName, originalPath } = resolution; 7398 cache.setSymlinkedFile(toPath(originalPath, cwd, getCanonicalFileName), resolvedFileName); 7399 const [commonResolved, commonOriginal] = guessDirectorySymlink(resolvedFileName, originalPath, cwd, getCanonicalFileName, isOHModules) || emptyArray; 7400 if (commonResolved && commonOriginal) { 7401 cache.setSymlinkedDirectory( 7402 commonOriginal, 7403 { real: commonResolved, realPath: toPath(commonResolved, cwd, getCanonicalFileName) }); 7404 } 7405 } 7406} 7407 7408function guessDirectorySymlink(a: string, b: string, cwd: string, getCanonicalFileName: GetCanonicalFileName, isOHModules?: boolean): [string, string] | undefined { 7409 const aParts = getPathComponents(getNormalizedAbsolutePath(a, cwd)); 7410 const bParts = getPathComponents(getNormalizedAbsolutePath(b, cwd)); 7411 let isDirectory = false; 7412 while ( 7413 aParts.length >= 2 && bParts.length >= 2 && 7414 !isNodeModulesOrScopedPackageDirectory(aParts[aParts.length - 2], getCanonicalFileName, isOHModules) && 7415 !isNodeModulesOrScopedPackageDirectory(bParts[bParts.length - 2], getCanonicalFileName, isOHModules) && 7416 getCanonicalFileName(aParts[aParts.length - 1]) === getCanonicalFileName(bParts[bParts.length - 1]) 7417 ) { 7418 aParts.pop(); 7419 bParts.pop(); 7420 isDirectory = true; 7421 } 7422 return isDirectory ? [getPathFromPathComponents(aParts), getPathFromPathComponents(bParts)] : undefined; 7423} 7424 7425// KLUDGE: Don't assume one 'node_modules' links to another. More likely a single directory inside the node_modules is the symlink. 7426// ALso, don't assume that an `@foo` directory is linked. More likely the contents of that are linked. 7427function isNodeModulesOrScopedPackageDirectory(s: string | undefined, getCanonicalFileName: GetCanonicalFileName, isOHModules?: boolean): boolean { 7428 return s !== undefined && (getCanonicalFileName(s) === "node_modules" || (isOHModules && getCanonicalFileName(s) === "oh_modules") || startsWith(s, "@")); 7429} 7430 7431function stripLeadingDirectorySeparator(s: string): string | undefined { 7432 return isAnyDirectorySeparator(s.charCodeAt(0)) ? s.slice(1) : undefined; 7433} 7434 7435/** @internal */ 7436export function tryRemoveDirectoryPrefix(path: string, dirPath: string, getCanonicalFileName: GetCanonicalFileName): string | undefined { 7437 const withoutPrefix = tryRemovePrefix(path, dirPath, getCanonicalFileName); 7438 return withoutPrefix === undefined ? undefined : stripLeadingDirectorySeparator(withoutPrefix); 7439} 7440 7441// Reserved characters, forces escaping of any non-word (or digit), non-whitespace character. 7442// It may be inefficient (we could just match (/[-[\]{}()*+?.,\\^$|#\s]/g), but this is future 7443// proof. 7444const reservedCharacterPattern = /[^\w\s\/]/g; 7445 7446/** @internal */ 7447export function regExpEscape(text: string) { 7448 return text.replace(reservedCharacterPattern, escapeRegExpCharacter); 7449} 7450 7451function escapeRegExpCharacter(match: string) { 7452 return "\\" + match; 7453} 7454 7455const wildcardCharCodes = [CharacterCodes.asterisk, CharacterCodes.question]; 7456 7457/** @internal */ 7458export const commonPackageFolders: readonly string[] = ["node_modules", "oh_modules", "bower_components", "jspm_packages"]; 7459 7460const implicitExcludePathRegexPattern = `(?!(${commonPackageFolders.join("|")})(/|$))`; 7461 7462interface WildcardMatcher { 7463 singleAsteriskRegexFragment: string; 7464 doubleAsteriskRegexFragment: string; 7465 replaceWildcardCharacter: (match: string) => string; 7466} 7467 7468const filesMatcher: WildcardMatcher = { 7469 /** 7470 * Matches any single directory segment unless it is the last segment and a .min.js file 7471 * Breakdown: 7472 * [^./] # matches everything up to the first . character (excluding directory separators) 7473 * (\\.(?!min\\.js$))? # matches . characters but not if they are part of the .min.js file extension 7474 */ 7475 singleAsteriskRegexFragment: "([^./]|(\\.(?!min\\.js$))?)*", 7476 /** 7477 * Regex for the ** wildcard. Matches any number of subdirectories. When used for including 7478 * files or directories, does not match subdirectories that start with a . character 7479 */ 7480 doubleAsteriskRegexFragment: `(/${implicitExcludePathRegexPattern}[^/.][^/]*)*?`, 7481 replaceWildcardCharacter: match => replaceWildcardCharacter(match, filesMatcher.singleAsteriskRegexFragment) 7482}; 7483 7484const directoriesMatcher: WildcardMatcher = { 7485 singleAsteriskRegexFragment: "[^/]*", 7486 /** 7487 * Regex for the ** wildcard. Matches any number of subdirectories. When used for including 7488 * files or directories, does not match subdirectories that start with a . character 7489 */ 7490 doubleAsteriskRegexFragment: `(/${implicitExcludePathRegexPattern}[^/.][^/]*)*?`, 7491 replaceWildcardCharacter: match => replaceWildcardCharacter(match, directoriesMatcher.singleAsteriskRegexFragment) 7492}; 7493 7494const excludeMatcher: WildcardMatcher = { 7495 singleAsteriskRegexFragment: "[^/]*", 7496 doubleAsteriskRegexFragment: "(/.+?)?", 7497 replaceWildcardCharacter: match => replaceWildcardCharacter(match, excludeMatcher.singleAsteriskRegexFragment) 7498}; 7499 7500const wildcardMatchers = { 7501 files: filesMatcher, 7502 directories: directoriesMatcher, 7503 exclude: excludeMatcher 7504}; 7505 7506/** @internal */ 7507export function getRegularExpressionForWildcard(specs: readonly string[] | undefined, basePath: string, usage: "files" | "directories" | "exclude"): string | undefined { 7508 const patterns = getRegularExpressionsForWildcards(specs, basePath, usage); 7509 if (!patterns || !patterns.length) { 7510 return undefined; 7511 } 7512 7513 const pattern = patterns.map(pattern => `(${pattern})`).join("|"); 7514 // If excluding, match "foo/bar/baz...", but if including, only allow "foo". 7515 const terminator = usage === "exclude" ? "($|/)" : "$"; 7516 return `^(${pattern})${terminator}`; 7517} 7518 7519/** @internal */ 7520export function getRegularExpressionsForWildcards(specs: readonly string[] | undefined, basePath: string, usage: "files" | "directories" | "exclude"): readonly string[] | undefined { 7521 if (specs === undefined || specs.length === 0) { 7522 return undefined; 7523 } 7524 7525 return flatMap(specs, spec => 7526 spec && getSubPatternFromSpec(spec, basePath, usage, wildcardMatchers[usage])); 7527} 7528 7529/** 7530 * An "includes" path "foo" is implicitly a glob "foo/** /*" (without the space) if its last component has no extension, 7531 * and does not contain any glob characters itself. 7532 * 7533 * @internal 7534 */ 7535export function isImplicitGlob(lastPathComponent: string): boolean { 7536 return !/[.*?]/.test(lastPathComponent); 7537} 7538 7539/** @internal */ 7540export function getPatternFromSpec(spec: string, basePath: string, usage: "files" | "directories" | "exclude") { 7541 const pattern = spec && getSubPatternFromSpec(spec, basePath, usage, wildcardMatchers[usage]); 7542 return pattern && `^(${pattern})${usage === "exclude" ? "($|/)" : "$"}`; 7543} 7544 7545function getSubPatternFromSpec(spec: string, basePath: string, usage: "files" | "directories" | "exclude", { singleAsteriskRegexFragment, doubleAsteriskRegexFragment, replaceWildcardCharacter }: WildcardMatcher): string | undefined { 7546 let subpattern = ""; 7547 let hasWrittenComponent = false; 7548 const components = getNormalizedPathComponents(spec, basePath); 7549 const lastComponent = last(components); 7550 if (usage !== "exclude" && lastComponent === "**") { 7551 return undefined; 7552 } 7553 7554 // getNormalizedPathComponents includes the separator for the root component. 7555 // We need to remove to create our regex correctly. 7556 components[0] = removeTrailingDirectorySeparator(components[0]); 7557 7558 if (isImplicitGlob(lastComponent)) { 7559 components.push("**", "*"); 7560 } 7561 7562 let optionalCount = 0; 7563 for (let component of components) { 7564 if (component === "**") { 7565 subpattern += doubleAsteriskRegexFragment; 7566 } 7567 else { 7568 if (usage === "directories") { 7569 subpattern += "("; 7570 optionalCount++; 7571 } 7572 7573 if (hasWrittenComponent) { 7574 subpattern += directorySeparator; 7575 } 7576 7577 if (usage !== "exclude") { 7578 let componentPattern = ""; 7579 // The * and ? wildcards should not match directories or files that start with . if they 7580 // appear first in a component. Dotted directories and files can be included explicitly 7581 // like so: **/.*/.* 7582 if (component.charCodeAt(0) === CharacterCodes.asterisk) { 7583 componentPattern += "([^./]" + singleAsteriskRegexFragment + ")?"; 7584 component = component.substr(1); 7585 } 7586 else if (component.charCodeAt(0) === CharacterCodes.question) { 7587 componentPattern += "[^./]"; 7588 component = component.substr(1); 7589 } 7590 7591 componentPattern += component.replace(reservedCharacterPattern, replaceWildcardCharacter); 7592 7593 // Patterns should not include subfolders like node_modules unless they are 7594 // explicitly included as part of the path. 7595 // 7596 // As an optimization, if the component pattern is the same as the component, 7597 // then there definitely were no wildcard characters and we do not need to 7598 // add the exclusion pattern. 7599 if (componentPattern !== component) { 7600 subpattern += implicitExcludePathRegexPattern; 7601 } 7602 7603 subpattern += componentPattern; 7604 } 7605 else { 7606 subpattern += component.replace(reservedCharacterPattern, replaceWildcardCharacter); 7607 } 7608 } 7609 7610 hasWrittenComponent = true; 7611 } 7612 7613 while (optionalCount > 0) { 7614 subpattern += ")?"; 7615 optionalCount--; 7616 } 7617 7618 return subpattern; 7619} 7620 7621function replaceWildcardCharacter(match: string, singleAsteriskRegexFragment: string) { 7622 return match === "*" ? singleAsteriskRegexFragment : match === "?" ? "[^/]" : "\\" + match; 7623} 7624 7625/** @internal */ 7626export interface FileSystemEntries { 7627 readonly files: readonly string[]; 7628 readonly directories: readonly string[]; 7629} 7630 7631/** @internal */ 7632export interface FileMatcherPatterns { 7633 /** One pattern for each "include" spec. */ 7634 includeFilePatterns: readonly string[] | undefined; 7635 /** One pattern matching one of any of the "include" specs. */ 7636 includeFilePattern: string | undefined; 7637 includeDirectoryPattern: string | undefined; 7638 excludePattern: string | undefined; 7639 basePaths: readonly string[]; 7640} 7641 7642/** 7643 * @param path directory of the tsconfig.json 7644 * 7645 * @internal 7646 */ 7647export function getFileMatcherPatterns(path: string, excludes: readonly string[] | undefined, includes: readonly string[] | undefined, useCaseSensitiveFileNames: boolean, currentDirectory: string): FileMatcherPatterns { 7648 path = normalizePath(path); 7649 currentDirectory = normalizePath(currentDirectory); 7650 const absolutePath = combinePaths(currentDirectory, path); 7651 7652 return { 7653 includeFilePatterns: map(getRegularExpressionsForWildcards(includes, absolutePath, "files"), pattern => `^${pattern}$`), 7654 includeFilePattern: getRegularExpressionForWildcard(includes, absolutePath, "files"), 7655 includeDirectoryPattern: getRegularExpressionForWildcard(includes, absolutePath, "directories"), 7656 excludePattern: getRegularExpressionForWildcard(excludes, absolutePath, "exclude"), 7657 basePaths: getBasePaths(path, includes, useCaseSensitiveFileNames) 7658 }; 7659} 7660 7661/** @internal */ 7662export function getRegexFromPattern(pattern: string, useCaseSensitiveFileNames: boolean): RegExp { 7663 return new RegExp(pattern, useCaseSensitiveFileNames ? "" : "i"); 7664} 7665 7666/** 7667 * @param path directory of the tsconfig.json 7668 * 7669 * @internal 7670 */ 7671export function matchFiles(path: string, extensions: readonly string[] | undefined, excludes: readonly string[] | undefined, includes: readonly string[] | undefined, useCaseSensitiveFileNames: boolean, currentDirectory: string, depth: number | undefined, getFileSystemEntries: (path: string) => FileSystemEntries, realpath: (path: string) => string): string[] { 7672 path = normalizePath(path); 7673 currentDirectory = normalizePath(currentDirectory); 7674 7675 const patterns = getFileMatcherPatterns(path, excludes, includes, useCaseSensitiveFileNames, currentDirectory); 7676 7677 const includeFileRegexes = patterns.includeFilePatterns && patterns.includeFilePatterns.map(pattern => getRegexFromPattern(pattern, useCaseSensitiveFileNames)); 7678 const includeDirectoryRegex = patterns.includeDirectoryPattern && getRegexFromPattern(patterns.includeDirectoryPattern, useCaseSensitiveFileNames); 7679 const excludeRegex = patterns.excludePattern && getRegexFromPattern(patterns.excludePattern, useCaseSensitiveFileNames); 7680 7681 // Associate an array of results with each include regex. This keeps results in order of the "include" order. 7682 // If there are no "includes", then just put everything in results[0]. 7683 const results: string[][] = includeFileRegexes ? includeFileRegexes.map(() => []) : [[]]; 7684 const visited = new Map<string, true>(); 7685 const toCanonical = createGetCanonicalFileName(useCaseSensitiveFileNames); 7686 for (const basePath of patterns.basePaths) { 7687 visitDirectory(basePath, combinePaths(currentDirectory, basePath), depth); 7688 } 7689 7690 return flatten(results); 7691 7692 function visitDirectory(path: string, absolutePath: string, depth: number | undefined) { 7693 const canonicalPath = toCanonical(realpath(absolutePath)); 7694 if (visited.has(canonicalPath)) return; 7695 visited.set(canonicalPath, true); 7696 const { files, directories } = getFileSystemEntries(path); 7697 7698 for (const current of sort<string>(files, compareStringsCaseSensitive)) { 7699 const name = combinePaths(path, current); 7700 const absoluteName = combinePaths(absolutePath, current); 7701 if (extensions && !fileExtensionIsOneOf(name, extensions)) continue; 7702 if (excludeRegex && excludeRegex.test(absoluteName)) continue; 7703 if (!includeFileRegexes) { 7704 results[0].push(name); 7705 } 7706 else { 7707 const includeIndex = findIndex(includeFileRegexes, re => re.test(absoluteName)); 7708 if (includeIndex !== -1) { 7709 results[includeIndex].push(name); 7710 } 7711 } 7712 } 7713 7714 if (depth !== undefined) { 7715 depth--; 7716 if (depth === 0) { 7717 return; 7718 } 7719 } 7720 7721 for (const current of sort<string>(directories, compareStringsCaseSensitive)) { 7722 const name = combinePaths(path, current); 7723 const absoluteName = combinePaths(absolutePath, current); 7724 if ((!includeDirectoryRegex || includeDirectoryRegex.test(absoluteName)) && 7725 (!excludeRegex || !excludeRegex.test(absoluteName))) { 7726 visitDirectory(name, absoluteName, depth); 7727 } 7728 } 7729 } 7730} 7731 7732/** 7733 * Computes the unique non-wildcard base paths amongst the provided include patterns. 7734 */ 7735function getBasePaths(path: string, includes: readonly string[] | undefined, useCaseSensitiveFileNames: boolean): string[] { 7736 // Storage for our results in the form of literal paths (e.g. the paths as written by the user). 7737 const basePaths: string[] = [path]; 7738 7739 if (includes) { 7740 // Storage for literal base paths amongst the include patterns. 7741 const includeBasePaths: string[] = []; 7742 for (const include of includes) { 7743 // We also need to check the relative paths by converting them to absolute and normalizing 7744 // in case they escape the base path (e.g "..\somedirectory") 7745 const absolute: string = isRootedDiskPath(include) ? include : normalizePath(combinePaths(path, include)); 7746 // Append the literal and canonical candidate base paths. 7747 includeBasePaths.push(getIncludeBasePath(absolute)); 7748 } 7749 7750 // Sort the offsets array using either the literal or canonical path representations. 7751 includeBasePaths.sort(getStringComparer(!useCaseSensitiveFileNames)); 7752 7753 // Iterate over each include base path and include unique base paths that are not a 7754 // subpath of an existing base path 7755 for (const includeBasePath of includeBasePaths) { 7756 if (every(basePaths, basePath => !containsPath(basePath, includeBasePath, path, !useCaseSensitiveFileNames))) { 7757 basePaths.push(includeBasePath); 7758 } 7759 } 7760 } 7761 7762 return basePaths; 7763} 7764 7765function getIncludeBasePath(absolute: string): string { 7766 const wildcardOffset = indexOfAnyCharCode(absolute, wildcardCharCodes); 7767 if (wildcardOffset < 0) { 7768 // No "*" or "?" in the path 7769 return !hasExtension(absolute) 7770 ? absolute 7771 : removeTrailingDirectorySeparator(getDirectoryPath(absolute)); 7772 } 7773 return absolute.substring(0, absolute.lastIndexOf(directorySeparator, wildcardOffset)); 7774} 7775 7776/** @internal */ 7777export function ensureScriptKind(fileName: string, scriptKind: ScriptKind | undefined): ScriptKind { 7778 // Using scriptKind as a condition handles both: 7779 // - 'scriptKind' is unspecified and thus it is `undefined` 7780 // - 'scriptKind' is set and it is `Unknown` (0) 7781 // If the 'scriptKind' is 'undefined' or 'Unknown' then we attempt 7782 // to get the ScriptKind from the file name. If it cannot be resolved 7783 // from the file name then the default 'TS' script kind is returned. 7784 return scriptKind || getScriptKindFromFileName(fileName) || ScriptKind.TS; 7785} 7786 7787/** @internal */ 7788export function getScriptKindFromFileName(fileName: string): ScriptKind { 7789 const ext = fileName.substr(fileName.lastIndexOf(".")); 7790 switch (ext.toLowerCase()) { 7791 case Extension.Js: 7792 case Extension.Cjs: 7793 case Extension.Mjs: 7794 return ScriptKind.JS; 7795 case Extension.Jsx: 7796 return ScriptKind.JSX; 7797 case Extension.Ts: 7798 case Extension.Cts: 7799 case Extension.Mts: 7800 return ScriptKind.TS; 7801 case Extension.Tsx: 7802 return ScriptKind.TSX; 7803 case Extension.Json: 7804 return ScriptKind.JSON; 7805 case Extension.Ets: 7806 return ScriptKind.ETS; 7807 default: 7808 return ScriptKind.Unknown; 7809 } 7810} 7811 7812/** 7813 * Groups of supported extensions in order of file resolution precedence. (eg, TS > TSX > DTS and seperately, CTS > DCTS) 7814 * 7815 * @internal 7816 */ 7817export const supportedTSExtensions: readonly Extension[][] = [[Extension.Ts, Extension.Tsx, Extension.Dts, Extension.Ets, Extension.Dets], [Extension.Cts, Extension.Dcts], [Extension.Mts, Extension.Dmts]]; 7818/** @internal */ 7819export const supportedTSExtensionsFlat: readonly Extension[] = flatten(supportedTSExtensions); 7820const supportedTSExtensionsWithJson: readonly Extension[][] = [...supportedTSExtensions, [Extension.Json]]; 7821/** Must have ".d.ts" first because if ".ts" goes first, that will be detected as the extension instead of ".d.ts". */ 7822const supportedTSExtensionsForExtractExtension: readonly Extension[] = [Extension.Dts, Extension.Dcts, Extension.Dmts, Extension.Dets, Extension.Cts, Extension.Mts, Extension.Ts, Extension.Tsx, Extension.Cts, Extension.Mts, Extension.Ets]; 7823/** @internal */ 7824export const supportedJSExtensions: readonly Extension[][] = [[Extension.Js, Extension.Jsx], [Extension.Mjs], [Extension.Cjs]]; 7825/** @internal */ 7826export const supportedJSExtensionsFlat: readonly Extension[] = flatten(supportedJSExtensions); 7827const allSupportedExtensions: readonly Extension[][] = [[Extension.Ts, Extension.Tsx, Extension.Dts, Extension.Ets, Extension.Dets, Extension.Js, Extension.Jsx], [Extension.Cts, Extension.Dcts, Extension.Cjs], [Extension.Mts, Extension.Dmts, Extension.Mjs]]; 7828const allSupportedExtensionsWithJson: readonly Extension[][] = [...allSupportedExtensions, [Extension.Json]]; 7829/** @internal */ 7830export const supportedDeclarationExtensions: readonly Extension[] = [Extension.Dts, Extension.Dcts, Extension.Dmts, Extension.Dets]; 7831 7832/** @internal */ 7833export function getSupportedExtensions(options?: CompilerOptions): readonly Extension[][]; 7834/** @internal */ 7835export function getSupportedExtensions(options?: CompilerOptions, extraFileExtensions?: readonly FileExtensionInfo[]): readonly string[][]; 7836/** @internal */ 7837export function getSupportedExtensions(options?: CompilerOptions, extraFileExtensions?: readonly FileExtensionInfo[]): readonly string[][] { 7838 const needJsExtensions = options && getAllowJSCompilerOption(options); 7839 7840 if (!extraFileExtensions || extraFileExtensions.length === 0) { 7841 return needJsExtensions ? allSupportedExtensions : supportedTSExtensions; 7842 } 7843 7844 const builtins = needJsExtensions ? allSupportedExtensions : supportedTSExtensions; 7845 const flatBuiltins = flatten(builtins); 7846 const extensions = [ 7847 ...builtins, 7848 ...mapDefined(extraFileExtensions, x => x.scriptKind === ScriptKind.Deferred || needJsExtensions && isJSLike(x.scriptKind) && flatBuiltins.indexOf(x.extension as Extension) === -1 ? [x.extension] : undefined) 7849 ]; 7850 7851 return extensions; 7852} 7853 7854/** @internal */ 7855export function getSupportedExtensionsWithJsonIfResolveJsonModule(options: CompilerOptions | undefined, supportedExtensions: readonly Extension[][]): readonly Extension[][]; 7856/** @internal */ 7857export function getSupportedExtensionsWithJsonIfResolveJsonModule(options: CompilerOptions | undefined, supportedExtensions: readonly string[][]): readonly string[][]; 7858/** @internal */ 7859export function getSupportedExtensionsWithJsonIfResolveJsonModule(options: CompilerOptions | undefined, supportedExtensions: readonly string[][]): readonly string[][] { 7860 if (!options || !options.resolveJsonModule) return supportedExtensions; 7861 if (supportedExtensions === allSupportedExtensions) return allSupportedExtensionsWithJson; 7862 if (supportedExtensions === supportedTSExtensions) return supportedTSExtensionsWithJson; 7863 return [...supportedExtensions, [Extension.Json]]; 7864} 7865 7866function isJSLike(scriptKind: ScriptKind | undefined): boolean { 7867 return scriptKind === ScriptKind.JS || scriptKind === ScriptKind.JSX; 7868} 7869 7870/** @internal */ 7871export function hasJSFileExtension(fileName: string): boolean { 7872 return some(supportedJSExtensionsFlat, extension => fileExtensionIs(fileName, extension)); 7873} 7874 7875/** @internal */ 7876export function hasTSFileExtension(fileName: string): boolean { 7877 return some(supportedTSExtensionsFlat, extension => fileExtensionIs(fileName, extension)); 7878} 7879 7880/** @internal */ 7881export function isSupportedSourceFileName(fileName: string, compilerOptions?: CompilerOptions, extraFileExtensions?: readonly FileExtensionInfo[]) { 7882 if (!fileName) return false; 7883 7884 const supportedExtensions = getSupportedExtensions(compilerOptions, extraFileExtensions); 7885 for (const extension of flatten(getSupportedExtensionsWithJsonIfResolveJsonModule(compilerOptions, supportedExtensions))) { 7886 if (fileExtensionIs(fileName, extension)) { 7887 return true; 7888 } 7889 } 7890 return false; 7891} 7892 7893function numberOfDirectorySeparators(str: string) { 7894 const match = str.match(/\//g); 7895 return match ? match.length : 0; 7896} 7897 7898/** @internal */ 7899export function compareNumberOfDirectorySeparators(path1: string, path2: string) { 7900 return compareValues( 7901 numberOfDirectorySeparators(path1), 7902 numberOfDirectorySeparators(path2) 7903 ); 7904} 7905 7906const extensionsToRemove = [Extension.Dts, Extension.Dets, Extension.Dmts, Extension.Dcts, Extension.Mjs, Extension.Mts, Extension.Cjs, Extension.Cts, Extension.Ts, Extension.Js, Extension.Tsx, Extension.Jsx, Extension.Json, Extension.Ets]; 7907/** @internal */ 7908export function removeFileExtension(path: string): string { 7909 for (const ext of extensionsToRemove) { 7910 const extensionless = tryRemoveExtension(path, ext); 7911 if (extensionless !== undefined) { 7912 return extensionless; 7913 } 7914 } 7915 return path; 7916} 7917 7918/** @internal */ 7919export function tryRemoveExtension(path: string, extension: string): string | undefined { 7920 return fileExtensionIs(path, extension) ? removeExtension(path, extension) : undefined; 7921} 7922 7923/** @internal */ 7924export function removeExtension(path: string, extension: string): string { 7925 return path.substring(0, path.length - extension.length); 7926} 7927 7928/** @internal */ 7929export function changeExtension<T extends string | Path>(path: T, newExtension: string): T { 7930 return changeAnyExtension(path, newExtension, extensionsToRemove, /*ignoreCase*/ false) as T; 7931} 7932 7933/** @internal */ 7934/** 7935 * Returns the input if there are no stars, a pattern if there is exactly one, 7936 * and undefined if there are more. 7937 */ 7938export function tryParsePattern(pattern: string): string | Pattern | undefined { 7939 const indexOfStar = pattern.indexOf("*"); 7940 if (indexOfStar === -1) { 7941 return pattern; 7942 } 7943 return pattern.indexOf("*", indexOfStar + 1) !== -1 7944 ? undefined 7945 : { 7946 prefix: pattern.substr(0, indexOfStar), 7947 suffix: pattern.substr(indexOfStar + 1) 7948 }; 7949} 7950 7951/** @internal */ 7952export function tryParsePatterns(paths: MapLike<string[]>): (string | Pattern)[] { 7953 return mapDefined(getOwnKeys(paths), path => tryParsePattern(path)); 7954} 7955 7956/** @internal */ 7957export function positionIsSynthesized(pos: number): boolean { 7958 // This is a fast way of testing the following conditions: 7959 // pos === undefined || pos === null || isNaN(pos) || pos < 0; 7960 return !(pos >= 0); 7961} 7962 7963/** 7964 * True if an extension is one of the supported TypeScript extensions. 7965 * 7966 * @internal 7967 */ 7968export function extensionIsTS(ext: Extension): boolean { 7969 return ext === Extension.Ts || ext === Extension.Tsx || ext === Extension.Dts || ext === Extension.Cts || ext === Extension.Mts || ext === Extension.Dmts || ext === Extension.Dcts || ext === Extension.Ets || ext === Extension.Dets; 7970} 7971 7972/** @internal */ 7973export function resolutionExtensionIsTSOrJson(ext: Extension) { 7974 return extensionIsTS(ext) || ext === Extension.Json; 7975} 7976 7977/** 7978 * Gets the extension from a path. 7979 * Path must have a valid extension. 7980 * 7981 * @internal 7982 */ 7983export function extensionFromPath(path: string): Extension { 7984 const ext = tryGetExtensionFromPath(path); 7985 return ext !== undefined ? ext : Debug.fail(`File ${path} has unknown extension.`); 7986} 7987 7988/** @internal */ 7989export function isAnySupportedFileExtension(path: string): boolean { 7990 return tryGetExtensionFromPath(path) !== undefined; 7991} 7992 7993/** @internal */ 7994export function tryGetExtensionFromPath(path: string): Extension | undefined { 7995 if (fileExtensionIs(path, Extension.Ets)) { 7996 return Extension.Ets; 7997 } 7998 return find<Extension>(extensionsToRemove, e => fileExtensionIs(path, e)); 7999} 8000 8001/** @internal */ 8002export function isCheckJsEnabledForFile(sourceFile: SourceFile, compilerOptions: CompilerOptions) { 8003 return sourceFile.checkJsDirective ? sourceFile.checkJsDirective.enabled : compilerOptions.checkJs; 8004} 8005 8006/** @internal */ 8007export const emptyFileSystemEntries: FileSystemEntries = { 8008 files: emptyArray, 8009 directories: emptyArray 8010}; 8011 8012 8013/** 8014 * patternOrStrings contains both patterns (containing "*") and regular strings. 8015 * Return an exact match if possible, or a pattern match, or undefined. 8016 * (These are verified by verifyCompilerOptions to have 0 or 1 "*" characters.) 8017 * 8018 * @internal 8019 */ 8020export function matchPatternOrExact(patternOrStrings: readonly (string | Pattern)[], candidate: string): string | Pattern | undefined { 8021 const patterns: Pattern[] = []; 8022 for (const patternOrString of patternOrStrings) { 8023 if (patternOrString === candidate) { 8024 return candidate; 8025 } 8026 8027 if (!isString(patternOrString)) { 8028 patterns.push(patternOrString); 8029 } 8030 } 8031 8032 return findBestPatternMatch(patterns, _ => _, candidate); 8033} 8034 8035/** @internal */ 8036export type Mutable<T extends object> = { -readonly [K in keyof T]: T[K] }; 8037 8038/** @internal */ 8039export function sliceAfter<T>(arr: readonly T[], value: T): readonly T[] { 8040 const index = arr.indexOf(value); 8041 Debug.assert(index !== -1); 8042 return arr.slice(index); 8043} 8044 8045/** @internal */ 8046export function addRelatedInfo<T extends Diagnostic>(diagnostic: T, ...relatedInformation: DiagnosticRelatedInformation[]): T { 8047 if (!relatedInformation.length) { 8048 return diagnostic; 8049 } 8050 if (!diagnostic.relatedInformation) { 8051 diagnostic.relatedInformation = []; 8052 } 8053 Debug.assert(diagnostic.relatedInformation !== emptyArray, "Diagnostic had empty array singleton for related info, but is still being constructed!"); 8054 diagnostic.relatedInformation.push(...relatedInformation); 8055 return diagnostic; 8056} 8057 8058/** @internal */ 8059export function minAndMax<T>(arr: readonly T[], getValue: (value: T) => number): { readonly min: number, readonly max: number } { 8060 Debug.assert(arr.length !== 0); 8061 let min = getValue(arr[0]); 8062 let max = min; 8063 for (let i = 1; i < arr.length; i++) { 8064 const value = getValue(arr[i]); 8065 if (value < min) { 8066 min = value; 8067 } 8068 else if (value > max) { 8069 max = value; 8070 } 8071 } 8072 return { min, max }; 8073} 8074 8075/** @internal */ 8076export function rangeOfNode(node: Node): TextRange { 8077 return { pos: getTokenPosOfNode(node), end: node.end }; 8078} 8079 8080/** @internal */ 8081export function rangeOfTypeParameters(sourceFile: SourceFile, typeParameters: NodeArray<TypeParameterDeclaration>): TextRange { 8082 // Include the `<>` 8083 const pos = typeParameters.pos - 1; 8084 const end = skipTrivia(sourceFile.text, typeParameters.end) + 1; 8085 return { pos, end }; 8086} 8087 8088/** @internal */ 8089export interface HostWithIsSourceOfProjectReferenceRedirect { 8090 isSourceOfProjectReferenceRedirect(fileName: string): boolean; 8091} 8092/** @internal */ 8093export function skipTypeChecking(sourceFile: SourceFile, options: CompilerOptions, host: HostWithIsSourceOfProjectReferenceRedirect) { 8094 // If skipLibCheck is enabled, skip reporting errors if file is a declaration file. 8095 // If skipDefaultLibCheck is enabled, skip reporting errors if file contains a 8096 // '/// <reference no-default-lib="true"/>' directive. 8097 return (options.skipLibCheck && sourceFile.isDeclarationFile || 8098 options.skipDefaultLibCheck && sourceFile.hasNoDefaultLib) || 8099 host.isSourceOfProjectReferenceRedirect(sourceFile.fileName); 8100} 8101 8102/** @internal */ 8103export function isJsonEqual(a: unknown, b: unknown): boolean { 8104 // eslint-disable-next-line no-null/no-null 8105 return a === b || typeof a === "object" && a !== null && typeof b === "object" && b !== null && equalOwnProperties(a as MapLike<unknown>, b as MapLike<unknown>, isJsonEqual); 8106} 8107 8108/** 8109 * Converts a bigint literal string, e.g. `0x1234n`, 8110 * to its decimal string representation, e.g. `4660`. 8111 * 8112 * @internal 8113 */ 8114export function parsePseudoBigInt(stringValue: string): string { 8115 let log2Base: number; 8116 switch (stringValue.charCodeAt(1)) { // "x" in "0x123" 8117 case CharacterCodes.b: 8118 case CharacterCodes.B: // 0b or 0B 8119 log2Base = 1; 8120 break; 8121 case CharacterCodes.o: 8122 case CharacterCodes.O: // 0o or 0O 8123 log2Base = 3; 8124 break; 8125 case CharacterCodes.x: 8126 case CharacterCodes.X: // 0x or 0X 8127 log2Base = 4; 8128 break; 8129 default: // already in decimal; omit trailing "n" 8130 const nIndex = stringValue.length - 1; 8131 // Skip leading 0s 8132 let nonZeroStart = 0; 8133 while (stringValue.charCodeAt(nonZeroStart) === CharacterCodes._0) { 8134 nonZeroStart++; 8135 } 8136 return stringValue.slice(nonZeroStart, nIndex) || "0"; 8137 } 8138 8139 // Omit leading "0b", "0o", or "0x", and trailing "n" 8140 const startIndex = 2, endIndex = stringValue.length - 1; 8141 const bitsNeeded = (endIndex - startIndex) * log2Base; 8142 // Stores the value specified by the string as a LE array of 16-bit integers 8143 // using Uint16 instead of Uint32 so combining steps can use bitwise operators 8144 const segments = new Uint16Array((bitsNeeded >>> 4) + (bitsNeeded & 15 ? 1 : 0)); 8145 // Add the digits, one at a time 8146 for (let i = endIndex - 1, bitOffset = 0; i >= startIndex; i--, bitOffset += log2Base) { 8147 const segment = bitOffset >>> 4; 8148 const digitChar = stringValue.charCodeAt(i); 8149 // Find character range: 0-9 < A-F < a-f 8150 const digit = digitChar <= CharacterCodes._9 8151 ? digitChar - CharacterCodes._0 8152 : 10 + digitChar - 8153 (digitChar <= CharacterCodes.F ? CharacterCodes.A : CharacterCodes.a); 8154 const shiftedDigit = digit << (bitOffset & 15); 8155 segments[segment] |= shiftedDigit; 8156 const residual = shiftedDigit >>> 16; 8157 if (residual) segments[segment + 1] |= residual; // overflows segment 8158 } 8159 // Repeatedly divide segments by 10 and add remainder to base10Value 8160 let base10Value = ""; 8161 let firstNonzeroSegment = segments.length - 1; 8162 let segmentsRemaining = true; 8163 while (segmentsRemaining) { 8164 let mod10 = 0; 8165 segmentsRemaining = false; 8166 for (let segment = firstNonzeroSegment; segment >= 0; segment--) { 8167 const newSegment = mod10 << 16 | segments[segment]; 8168 const segmentValue = (newSegment / 10) | 0; 8169 segments[segment] = segmentValue; 8170 mod10 = newSegment - segmentValue * 10; 8171 if (segmentValue && !segmentsRemaining) { 8172 firstNonzeroSegment = segment; 8173 segmentsRemaining = true; 8174 } 8175 } 8176 base10Value = mod10 + base10Value; 8177 } 8178 return base10Value; 8179} 8180 8181/** @internal */ 8182export function pseudoBigIntToString({negative, base10Value}: PseudoBigInt): string { 8183 return (negative && base10Value !== "0" ? "-" : "") + base10Value; 8184} 8185 8186/** @internal */ 8187export function isValidTypeOnlyAliasUseSite(useSite: Node): boolean { 8188 return !!(useSite.flags & NodeFlags.Ambient) 8189 || isPartOfTypeQuery(useSite) 8190 || isIdentifierInNonEmittingHeritageClause(useSite) 8191 || isPartOfPossiblyValidTypeOrAbstractComputedPropertyName(useSite) 8192 || !(isExpressionNode(useSite) || isShorthandPropertyNameUseSite(useSite)); 8193} 8194 8195function isShorthandPropertyNameUseSite(useSite: Node) { 8196 return isIdentifier(useSite) && isShorthandPropertyAssignment(useSite.parent) && useSite.parent.name === useSite; 8197} 8198 8199function isPartOfPossiblyValidTypeOrAbstractComputedPropertyName(node: Node) { 8200 while (node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.PropertyAccessExpression) { 8201 node = node.parent; 8202 } 8203 if (node.kind !== SyntaxKind.ComputedPropertyName) { 8204 return false; 8205 } 8206 if (hasSyntacticModifier(node.parent, ModifierFlags.Abstract)) { 8207 return true; 8208 } 8209 const containerKind = node.parent.parent.kind; 8210 return containerKind === SyntaxKind.InterfaceDeclaration || containerKind === SyntaxKind.TypeLiteral; 8211} 8212 8213/** Returns true for an identifier in 1) an `implements` clause, and 2) an `extends` clause of an interface. */ 8214function isIdentifierInNonEmittingHeritageClause(node: Node): boolean { 8215 if (node.kind !== SyntaxKind.Identifier) return false; 8216 const heritageClause = findAncestor(node.parent, parent => { 8217 switch (parent.kind) { 8218 case SyntaxKind.HeritageClause: 8219 return true; 8220 case SyntaxKind.PropertyAccessExpression: 8221 case SyntaxKind.ExpressionWithTypeArguments: 8222 return false; 8223 default: 8224 return "quit"; 8225 } 8226 }) as HeritageClause | undefined; 8227 return heritageClause?.token === SyntaxKind.ImplementsKeyword || heritageClause?.parent.kind === SyntaxKind.InterfaceDeclaration; 8228} 8229 8230/** @internal */ 8231export function isIdentifierTypeReference(node: Node): node is TypeReferenceNode & { typeName: Identifier } { 8232 return isTypeReferenceNode(node) && isIdentifier(node.typeName); 8233} 8234 8235/** @internal */ 8236export function arrayIsHomogeneous<T>(array: readonly T[], comparer: EqualityComparer<T> = equateValues) { 8237 if (array.length < 2) return true; 8238 const first = array[0]; 8239 for (let i = 1, length = array.length; i < length; i++) { 8240 const target = array[i]; 8241 if (!comparer(first, target)) return false; 8242 } 8243 return true; 8244} 8245 8246/** 8247 * Bypasses immutability and directly sets the `pos` property of a `TextRange` or `Node`. 8248 * 8249 * @internal 8250 */ 8251export function setTextRangePos<T extends ReadonlyTextRange>(range: T, pos: number) { 8252 (range as TextRange).pos = pos; 8253 return range; 8254} 8255 8256/** 8257 * Bypasses immutability and directly sets the `end` property of a `TextRange` or `Node`. 8258 * 8259 * @internal 8260 */ 8261export function setTextRangeEnd<T extends ReadonlyTextRange>(range: T, end: number) { 8262 (range as TextRange).end = end; 8263 return range; 8264} 8265 8266/** 8267 * Bypasses immutability and directly sets the `pos` and `end` properties of a `TextRange` or `Node`. 8268 * 8269 * @internal 8270 */ 8271export function setTextRangePosEnd<T extends ReadonlyTextRange>(range: T, pos: number, end: number) { 8272 return setTextRangeEnd(setTextRangePos(range, pos), end); 8273} 8274 8275/** 8276 * Bypasses immutability and directly sets the `pos` and `end` properties of a `TextRange` or `Node` from the 8277 * provided position and width. 8278 * 8279 * @internal 8280 */ 8281export function setTextRangePosWidth<T extends ReadonlyTextRange>(range: T, pos: number, width: number) { 8282 return setTextRangePosEnd(range, pos, pos + width); 8283} 8284 8285/** 8286 * Bypasses immutability and directly sets the `flags` property of a `Node`. 8287 * 8288 * @internal 8289 */ 8290export function setNodeFlags<T extends Node>(node: T, newFlags: NodeFlags): T; 8291/** @internal */ 8292export function setNodeFlags<T extends Node>(node: T | undefined, newFlags: NodeFlags): T | undefined; 8293/** @internal */ 8294export function setNodeFlags<T extends Node>(node: T | undefined, newFlags: NodeFlags): T | undefined { 8295 if (node) { 8296 (node as Mutable<T>).flags = newFlags; 8297 } 8298 return node; 8299} 8300 8301/** 8302 * Bypasses immutability and directly sets the `parent` property of a `Node`. 8303 * 8304 * @internal 8305 */ 8306export function setParent<T extends Node>(child: T, parent: T["parent"] | undefined): T; 8307/** @internal */ 8308export function setParent<T extends Node>(child: T | undefined, parent: T["parent"] | undefined): T | undefined; 8309/** @internal */ 8310export function setParent<T extends Node>(child: T | undefined, parent: T["parent"] | undefined): T | undefined { 8311 if (child && parent) { 8312 (child as Mutable<T>).parent = parent; 8313 } 8314 return child; 8315} 8316 8317/** 8318 * Bypasses immutability and directly sets the `parent` property of each `Node` in an array of nodes, if is not already set. 8319 * 8320 * @internal 8321 */ 8322export function setEachParent<T extends readonly Node[]>(children: T, parent: T[number]["parent"]): T; 8323/** @internal */ 8324export function setEachParent<T extends readonly Node[]>(children: T | undefined, parent: T[number]["parent"]): T | undefined; 8325/** @internal */ 8326export function setEachParent<T extends readonly Node[]>(children: T | undefined, parent: T[number]["parent"]): T | undefined { 8327 if (children) { 8328 for (const child of children) { 8329 setParent(child, parent); 8330 } 8331 } 8332 return children; 8333} 8334 8335function isPackedElement(node: Expression) { 8336 return !isOmittedExpression(node); 8337} 8338 8339/** 8340 * Determines whether the provided node is an ArrayLiteralExpression that contains no missing elements. 8341 * 8342 * @internal 8343 */ 8344export function isPackedArrayLiteral(node: Expression) { 8345 return isArrayLiteralExpression(node) && every(node.elements, isPackedElement); 8346} 8347 8348/** 8349 * Indicates whether the result of an `Expression` will be unused. 8350 * 8351 * NOTE: This requires a node with a valid `parent` pointer. 8352 * 8353 * @internal 8354 */ 8355export function expressionResultIsUnused(node: Expression): boolean { 8356 Debug.assertIsDefined(node.parent); 8357 while (true) { 8358 const parent: Node = node.parent; 8359 // walk up parenthesized expressions, but keep a pointer to the top-most parenthesized expression 8360 if (isParenthesizedExpression(parent)) { 8361 node = parent; 8362 continue; 8363 } 8364 // result is unused in an expression statement, `void` expression, or the initializer or incrementer of a `for` loop 8365 if (isExpressionStatement(parent) || 8366 isVoidExpression(parent) || 8367 isForStatement(parent) && (parent.initializer === node || parent.incrementor === node)) { 8368 return true; 8369 } 8370 if (isCommaListExpression(parent)) { 8371 // left side of comma is always unused 8372 if (node !== last(parent.elements)) return true; 8373 // right side of comma is unused if parent is unused 8374 node = parent; 8375 continue; 8376 } 8377 if (isBinaryExpression(parent) && parent.operatorToken.kind === SyntaxKind.CommaToken) { 8378 // left side of comma is always unused 8379 if (node === parent.left) return true; 8380 // right side of comma is unused if parent is unused 8381 node = parent; 8382 continue; 8383 } 8384 return false; 8385 } 8386} 8387 8388/** @internal */ 8389export function containsIgnoredPath(path: string) { 8390 return some(ignoredPaths, p => stringContains(path, p)); 8391} 8392 8393/** @internal */ 8394export function getContainingNodeArray(node: Node): NodeArray<Node> | undefined { 8395 if (!node.parent) return undefined; 8396 switch (node.kind) { 8397 case SyntaxKind.TypeParameter: 8398 const { parent } = node as TypeParameterDeclaration; 8399 return parent.kind === SyntaxKind.InferType ? undefined : parent.typeParameters; 8400 case SyntaxKind.Parameter: 8401 return (node as ParameterDeclaration).parent.parameters; 8402 case SyntaxKind.TemplateLiteralTypeSpan: 8403 return (node as TemplateLiteralTypeSpan).parent.templateSpans; 8404 case SyntaxKind.TemplateSpan: 8405 return (node as TemplateSpan).parent.templateSpans; 8406 case SyntaxKind.Decorator: { 8407 const { parent } = node as Decorator; 8408 return canHaveDecorators(parent) ? parent.modifiers : 8409 canHaveIllegalDecorators(parent) ? parent.illegalDecorators : 8410 undefined; 8411 } 8412 case SyntaxKind.HeritageClause: 8413 return (node as HeritageClause).parent.heritageClauses; 8414 } 8415 8416 const { parent } = node; 8417 if (isJSDocTag(node)) { 8418 return isJSDocTypeLiteral(node.parent) ? undefined : node.parent.tags; 8419 } 8420 8421 switch (parent.kind) { 8422 case SyntaxKind.TypeLiteral: 8423 case SyntaxKind.InterfaceDeclaration: 8424 return isTypeElement(node) ? (parent as TypeLiteralNode | InterfaceDeclaration).members : undefined; 8425 case SyntaxKind.UnionType: 8426 case SyntaxKind.IntersectionType: 8427 return (parent as UnionOrIntersectionTypeNode).types; 8428 case SyntaxKind.TupleType: 8429 case SyntaxKind.ArrayLiteralExpression: 8430 case SyntaxKind.CommaListExpression: 8431 case SyntaxKind.NamedImports: 8432 case SyntaxKind.NamedExports: 8433 return (parent as TupleTypeNode | ArrayLiteralExpression | CommaListExpression | NamedImports | NamedExports).elements; 8434 case SyntaxKind.ObjectLiteralExpression: 8435 case SyntaxKind.JsxAttributes: 8436 return (parent as ObjectLiteralExpressionBase<ObjectLiteralElement>).properties; 8437 case SyntaxKind.CallExpression: 8438 case SyntaxKind.NewExpression: 8439 return isTypeNode(node) ? (parent as CallExpression | NewExpression).typeArguments : 8440 (parent as CallExpression | NewExpression).expression === node ? undefined : 8441 (parent as CallExpression | NewExpression).arguments; 8442 case SyntaxKind.JsxElement: 8443 case SyntaxKind.JsxFragment: 8444 return isJsxChild(node) ? (parent as JsxElement | JsxFragment).children : undefined; 8445 case SyntaxKind.JsxOpeningElement: 8446 case SyntaxKind.JsxSelfClosingElement: 8447 return isTypeNode(node) ? (parent as JsxOpeningElement | JsxSelfClosingElement).typeArguments : undefined; 8448 case SyntaxKind.Block: 8449 case SyntaxKind.CaseClause: 8450 case SyntaxKind.DefaultClause: 8451 case SyntaxKind.ModuleBlock: 8452 return (parent as Block | CaseOrDefaultClause | ModuleBlock).statements; 8453 case SyntaxKind.CaseBlock: 8454 return (parent as CaseBlock).clauses; 8455 case SyntaxKind.ClassDeclaration: 8456 case SyntaxKind.ClassExpression: 8457 return isClassElement(node) ? (parent as ClassLikeDeclaration).members : undefined; 8458 case SyntaxKind.EnumDeclaration: 8459 return isEnumMember(node) ? (parent as EnumDeclaration).members : undefined; 8460 case SyntaxKind.SourceFile: 8461 return (parent as SourceFile).statements; 8462 } 8463} 8464 8465/** @internal */ 8466export function hasContextSensitiveParameters(node: FunctionLikeDeclaration) { 8467 // Functions with type parameters are not context sensitive. 8468 if (!node.typeParameters) { 8469 // Functions with any parameters that lack type annotations are context sensitive. 8470 if (some(node.parameters, p => !getEffectiveTypeAnnotationNode(p))) { 8471 return true; 8472 } 8473 if (node.kind !== SyntaxKind.ArrowFunction) { 8474 // If the first parameter is not an explicit 'this' parameter, then the function has 8475 // an implicit 'this' parameter which is subject to contextual typing. 8476 const parameter = firstOrUndefined(node.parameters); 8477 if (!(parameter && parameterIsThisKeyword(parameter))) { 8478 return true; 8479 } 8480 } 8481 } 8482 return false; 8483} 8484 8485/** @internal */ 8486export function isInfinityOrNaNString(name: string | __String): boolean { 8487 return name === "Infinity" || name === "-Infinity" || name === "NaN"; 8488} 8489 8490/** @internal */ 8491export function isCatchClauseVariableDeclaration(node: Node) { 8492 return node.kind === SyntaxKind.VariableDeclaration && node.parent.kind === SyntaxKind.CatchClause; 8493} 8494 8495/** @internal */ 8496export function isParameterOrCatchClauseVariable(symbol: Symbol) { 8497 const declaration = symbol.valueDeclaration && getRootDeclaration(symbol.valueDeclaration); 8498 return !!declaration && (isParameter(declaration) || isCatchClauseVariableDeclaration(declaration)); 8499} 8500 8501/** @internal */ 8502export function isFunctionExpressionOrArrowFunction(node: Node): node is FunctionExpression | ArrowFunction { 8503 return node.kind === SyntaxKind.FunctionExpression || node.kind === SyntaxKind.ArrowFunction; 8504} 8505 8506/** @internal */ 8507export function escapeSnippetText(text: string): string { 8508 return text.replace(/\$/gm, () => "\\$"); 8509} 8510 8511/** @internal */ 8512export function isNumericLiteralName(name: string | __String) { 8513 // The intent of numeric names is that 8514 // - they are names with text in a numeric form, and that 8515 // - setting properties/indexing with them is always equivalent to doing so with the numeric literal 'numLit', 8516 // acquired by applying the abstract 'ToNumber' operation on the name's text. 8517 // 8518 // The subtlety is in the latter portion, as we cannot reliably say that anything that looks like a numeric literal is a numeric name. 8519 // In fact, it is the case that the text of the name must be equal to 'ToString(numLit)' for this to hold. 8520 // 8521 // Consider the property name '"0xF00D"'. When one indexes with '0xF00D', they are actually indexing with the value of 'ToString(0xF00D)' 8522 // according to the ECMAScript specification, so it is actually as if the user indexed with the string '"61453"'. 8523 // Thus, the text of all numeric literals equivalent to '61543' such as '0xF00D', '0xf00D', '0170015', etc. are not valid numeric names 8524 // because their 'ToString' representation is not equal to their original text. 8525 // This is motivated by ECMA-262 sections 9.3.1, 9.8.1, 11.1.5, and 11.2.1. 8526 // 8527 // Here, we test whether 'ToString(ToNumber(name))' is exactly equal to 'name'. 8528 // The '+' prefix operator is equivalent here to applying the abstract ToNumber operation. 8529 // Applying the 'toString()' method on a number gives us the abstract ToString operation on a number. 8530 // 8531 // Note that this accepts the values 'Infinity', '-Infinity', and 'NaN', and that this is intentional. 8532 // This is desired behavior, because when indexing with them as numeric entities, you are indexing 8533 // with the strings '"Infinity"', '"-Infinity"', and '"NaN"' respectively. 8534 return (+name).toString() === name; 8535} 8536 8537/** @internal */ 8538export function createPropertyNameNodeForIdentifierOrLiteral(name: string, target: ScriptTarget, singleQuote?: boolean, stringNamed?: boolean) { 8539 return isIdentifierText(name, target) ? factory.createIdentifier(name) : 8540 !stringNamed && isNumericLiteralName(name) && +name >= 0 ? factory.createNumericLiteral(+name) : 8541 factory.createStringLiteral(name, !!singleQuote); 8542} 8543 8544/** @internal */ 8545export function isThisTypeParameter(type: Type): boolean { 8546 return !!(type.flags & TypeFlags.TypeParameter && (type as TypeParameter).isThisType); 8547} 8548 8549/** @internal */ 8550export interface NodeModulePathParts { 8551 readonly topLevelNodeModulesIndex: number; 8552 readonly topLevelPackageNameIndex: number; 8553 readonly packageRootIndex: number; 8554 readonly fileNameIndex: number; 8555} 8556/** @internal */ 8557export function getNodeModulePathParts(fullPath: string, modulePathPart: string): NodeModulePathParts | undefined { 8558 // If fullPath can't be valid module file within node_modules, returns undefined. 8559 // Example of expected pattern: /base/path/node_modules/[@scope/otherpackage/@otherscope/node_modules/]package/[subdirectory/]file.js 8560 // Returns indices: ^ ^ ^ ^ 8561 8562 let topLevelNodeModulesIndex = 0; 8563 let topLevelPackageNameIndex = 0; 8564 let packageRootIndex = 0; 8565 let fileNameIndex = 0; 8566 8567 const enum States { 8568 BeforeNodeModules, 8569 NodeModules, 8570 Scope, 8571 PackageContent 8572 } 8573 8574 let partStart = 0; 8575 let partEnd = 0; 8576 let state = States.BeforeNodeModules; 8577 8578 while (partEnd >= 0) { 8579 partStart = partEnd; 8580 partEnd = fullPath.indexOf("/", partStart + 1); 8581 switch (state) { 8582 case States.BeforeNodeModules: 8583 if (fullPath.indexOf(modulePathPart, partStart) === partStart) { 8584 topLevelNodeModulesIndex = partStart; 8585 topLevelPackageNameIndex = partEnd; 8586 state = States.NodeModules; 8587 } 8588 break; 8589 case States.NodeModules: 8590 case States.Scope: 8591 if (state === States.NodeModules && fullPath.charAt(partStart + 1) === "@") { 8592 state = States.Scope; 8593 } 8594 else { 8595 packageRootIndex = partEnd; 8596 state = States.PackageContent; 8597 } 8598 break; 8599 case States.PackageContent: 8600 if (fullPath.indexOf(modulePathPart, partStart) === partStart) { 8601 state = States.NodeModules; 8602 } 8603 else { 8604 state = States.PackageContent; 8605 } 8606 break; 8607 } 8608 } 8609 8610 fileNameIndex = partStart; 8611 8612 return state > States.NodeModules ? { topLevelNodeModulesIndex, topLevelPackageNameIndex, packageRootIndex, fileNameIndex } : undefined; 8613} 8614 8615/** @internal */ 8616export function getParameterTypeNode(parameter: ParameterDeclaration | JSDocParameterTag) { 8617 return parameter.kind === SyntaxKind.JSDocParameterTag ? parameter.typeExpression?.type : parameter.type; 8618} 8619 8620/** @internal */ 8621export function isTypeDeclaration(node: Node): node is TypeParameterDeclaration | ClassDeclaration | InterfaceDeclaration | TypeAliasDeclaration | JSDocTypedefTag | JSDocCallbackTag | JSDocEnumTag | EnumDeclaration | ImportClause | ImportSpecifier | ExportSpecifier { 8622 switch (node.kind) { 8623 case SyntaxKind.TypeParameter: 8624 case SyntaxKind.ClassDeclaration: 8625 case SyntaxKind.InterfaceDeclaration: 8626 case SyntaxKind.TypeAliasDeclaration: 8627 case SyntaxKind.EnumDeclaration: 8628 case SyntaxKind.JSDocTypedefTag: 8629 case SyntaxKind.JSDocCallbackTag: 8630 case SyntaxKind.JSDocEnumTag: 8631 return true; 8632 case SyntaxKind.ImportClause: 8633 return (node as ImportClause).isTypeOnly; 8634 case SyntaxKind.ImportSpecifier: 8635 case SyntaxKind.ExportSpecifier: 8636 return (node as ImportSpecifier | ExportSpecifier).parent.parent.isTypeOnly; 8637 default: 8638 return false; 8639 } 8640} 8641 8642/** @internal */ 8643export function canHaveExportModifier(node: Node): node is Extract<HasModifiers, Statement> { 8644 return isEnumDeclaration(node) || isVariableStatement(node) || isFunctionDeclaration(node) || isClassDeclaration(node) 8645 || isInterfaceDeclaration(node) || isTypeDeclaration(node) || (isModuleDeclaration(node) && !isExternalModuleAugmentation(node) && !isGlobalScopeAugmentation(node)); 8646} 8647 8648/** @internal */ 8649export function isCalledStructDeclaration(declarations: Declaration[] | undefined): boolean { 8650 if (!declarations) { 8651 return false; 8652 } 8653 8654 return declarations.some(declaration => declaration.kind === SyntaxKind.StructDeclaration); 8655} 8656 8657/** @internal */ 8658export function getNameOfDecorator(node: Decorator): string | undefined { 8659 const expression = node.expression; 8660 8661 if (isIdentifier(expression)) { 8662 return expression.escapedText.toString(); 8663 } 8664 8665 if (isCallExpression(expression) && isIdentifier(expression.expression)) { 8666 return expression.expression.escapedText.toString(); 8667 } 8668 8669 return undefined; 8670} 8671 8672export function getLeadingCommentRangesOfNode(node: Node, sourceFileOfNode: SourceFile) { 8673 return node.kind !== SyntaxKind.JsxText ? getLeadingCommentRanges(sourceFileOfNode.text, node.pos) : undefined; 8674} 8675 8676export function createTextWriter(newLine: string): EmitTextWriter { 8677 /* eslint-disable no-var */ 8678 var output: string; 8679 var indent: number; 8680 var lineStart: boolean; 8681 var lineCount: number; 8682 var linePos: number; 8683 var hasTrailingComment = false; 8684 /* eslint-enable no-var */ 8685 8686 function updateLineCountAndPosFor(s: string) { 8687 const lineStartsOfS = computeLineStarts(s); 8688 if (lineStartsOfS.length > 1) { 8689 lineCount = lineCount + lineStartsOfS.length - 1; 8690 linePos = output.length - s.length + last(lineStartsOfS); 8691 lineStart = (linePos - output.length) === 0; 8692 } 8693 else { 8694 lineStart = false; 8695 } 8696 } 8697 8698 function writeText(s: string) { 8699 if (s && s.length) { 8700 if (lineStart) { 8701 s = getIndentString(indent) + s; 8702 lineStart = false; 8703 } 8704 output += s; 8705 updateLineCountAndPosFor(s); 8706 } 8707 } 8708 8709 function write(s: string) { 8710 if (s) hasTrailingComment = false; 8711 writeText(s); 8712 } 8713 8714 function writeComment(s: string) { 8715 if (s) hasTrailingComment = true; 8716 writeText(s); 8717 } 8718 8719 function reset(): void { 8720 output = ""; 8721 indent = 0; 8722 lineStart = true; 8723 lineCount = 0; 8724 linePos = 0; 8725 hasTrailingComment = false; 8726 } 8727 8728 function rawWrite(s: string) { 8729 if (s !== undefined) { 8730 output += s; 8731 updateLineCountAndPosFor(s); 8732 hasTrailingComment = false; 8733 } 8734 } 8735 8736 function writeLiteral(s: string) { 8737 if (s && s.length) { 8738 write(s); 8739 } 8740 } 8741 8742 function writeLine(force?: boolean) { 8743 if (!lineStart || force) { 8744 output += newLine; 8745 lineCount++; 8746 linePos = output.length; 8747 lineStart = true; 8748 hasTrailingComment = false; 8749 } 8750 } 8751 8752 function getTextPosWithWriteLine() { 8753 return lineStart ? output.length : (output.length + newLine.length); 8754 } 8755 8756 reset(); 8757 8758 return { 8759 write, 8760 rawWrite, 8761 writeLiteral, 8762 writeLine, 8763 increaseIndent: () => { indent++; }, 8764 decreaseIndent: () => { indent--; }, 8765 getIndent: () => indent, 8766 getTextPos: () => output.length, 8767 getLine: () => lineCount, 8768 getColumn: () => lineStart ? indent * getIndentSize() : output.length - linePos, 8769 getText: () => output, 8770 isAtStartOfLine: () => lineStart, 8771 hasTrailingComment: () => hasTrailingComment, 8772 hasTrailingWhitespace: () => !!output.length && isWhiteSpaceLike(output.charCodeAt(output.length - 1)), 8773 clear: reset, 8774 reportInaccessibleThisError: noop, 8775 reportPrivateInBaseOfClassExpression: noop, 8776 reportInaccessibleUniqueSymbolError: noop, 8777 trackSymbol: () => false, 8778 writeKeyword: write, 8779 writeOperator: write, 8780 writeParameter: write, 8781 writeProperty: write, 8782 writePunctuation: write, 8783 writeSpace: write, 8784 writeStringLiteral: write, 8785 writeSymbol: (s, _) => write(s), 8786 writeTrailingSemicolon: write, 8787 writeComment, 8788 getTextPosWithWriteLine 8789 }; 8790} 8791 8792/** 8793 * Bypasses immutability and directly sets the `parent` property of each `Node` recursively. 8794 * @param rootNode The root node from which to start the recursion. 8795 * @param incremental When `true`, only recursively descends through nodes whose `parent` pointers are incorrect. 8796 * This allows us to quickly bail out of setting `parent` for subtrees during incremental parsing. 8797 */ 8798export function setParentRecursive<T extends Node>(rootNode: T, incremental: boolean): T; 8799export function setParentRecursive<T extends Node>(rootNode: T | undefined, incremental: boolean): T | undefined; 8800export function setParentRecursive<T extends Node>(rootNode: T | undefined, incremental: boolean): T | undefined { 8801 if (!rootNode) return rootNode; 8802 forEachChildRecursively(rootNode, isJSDocNode(rootNode) ? bindParentToChildIgnoringJSDoc : bindParentToChild); 8803 return rootNode; 8804 8805 function bindParentToChildIgnoringJSDoc(child: Node, parent: Node): void | "skip" { 8806 if (incremental && child.parent === parent) { 8807 return "skip"; 8808 } 8809 setParent(child, parent); 8810 } 8811 8812 function bindJSDoc(child: Node) { 8813 if (hasJSDocNodes(child)) { 8814 for (const doc of child.jsDoc!) { 8815 bindParentToChildIgnoringJSDoc(doc, child); 8816 forEachChildRecursively(doc, bindParentToChildIgnoringJSDoc); 8817 } 8818 } 8819 } 8820 8821 function bindParentToChild(child: Node, parent: Node) { 8822 return bindParentToChildIgnoringJSDoc(child, parent) || bindJSDoc(child); 8823 } 8824}