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