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