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