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