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