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