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