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