• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1namespace ts {
2    const brackets = createBracketsMap();
3
4    /*@internal*/
5    export function isBuildInfoFile(file: string) {
6        return fileExtensionIs(file, Extension.TsBuildInfo);
7    }
8
9    /*@internal*/
10    /**
11     * Iterates over the source files that are expected to have an emit output.
12     *
13     * @param host An EmitHost.
14     * @param action The action to execute.
15     * @param sourceFilesOrTargetSourceFile
16     *   If an array, the full list of source files to emit.
17     *   Else, calls `getSourceFilesToEmit` with the (optional) target source file to determine the list of source files to emit.
18     */
19    export function forEachEmittedFile<T>(
20        host: EmitHost, action: (emitFileNames: EmitFileNames, sourceFileOrBundle: SourceFile | Bundle | undefined) => T,
21        sourceFilesOrTargetSourceFile?: readonly SourceFile[] | SourceFile,
22        forceDtsEmit = false,
23        onlyBuildInfo?: boolean,
24        includeBuildInfo?: boolean) {
25        const sourceFiles = isArray(sourceFilesOrTargetSourceFile) ? sourceFilesOrTargetSourceFile : getSourceFilesToEmit(host, sourceFilesOrTargetSourceFile, forceDtsEmit);
26        const options = host.getCompilerOptions();
27        if (outFile(options)) {
28            const prepends = host.getPrependNodes();
29            if (sourceFiles.length || prepends.length) {
30                const bundle = factory.createBundle(sourceFiles, prepends);
31                const result = action(getOutputPathsFor(bundle, host, forceDtsEmit), bundle);
32                if (result) {
33                    return result;
34                }
35            }
36        }
37        else {
38            if (!onlyBuildInfo) {
39                for (const sourceFile of sourceFiles) {
40                    const result = action(getOutputPathsFor(sourceFile, host, forceDtsEmit), sourceFile);
41                    if (result) {
42                        return result;
43                    }
44                }
45            }
46            if (includeBuildInfo) {
47                const buildInfoPath = getTsBuildInfoEmitOutputFilePath(options);
48                if (buildInfoPath) return action({ buildInfoPath }, /*sourceFileOrBundle*/ undefined);
49            }
50        }
51    }
52
53    export function getTsBuildInfoEmitOutputFilePath(options: CompilerOptions) {
54        const configFile = options.configFilePath;
55        if (!isIncrementalCompilation(options)) return undefined;
56        if (options.tsBuildInfoFile) return options.tsBuildInfoFile;
57        const outPath = outFile(options);
58        let buildInfoExtensionLess: string;
59        if (outPath) {
60            buildInfoExtensionLess = removeFileExtension(outPath);
61        }
62        else {
63            if (!configFile) return undefined;
64            const configFileExtensionLess = removeFileExtension(configFile);
65            buildInfoExtensionLess = options.outDir ?
66                options.rootDir ?
67                    resolvePath(options.outDir, getRelativePathFromDirectory(options.rootDir, configFileExtensionLess, /*ignoreCase*/ true)) :
68                    combinePaths(options.outDir, getBaseFileName(configFileExtensionLess)) :
69                configFileExtensionLess;
70        }
71        return buildInfoExtensionLess + Extension.TsBuildInfo;
72    }
73
74    export function getTsBuildInfoEmitOutputFilePathForLinter(tsBuildInfoPath: string): string {
75        return tsBuildInfoPath + '.linter';
76    }
77
78    /*@internal*/
79    export function getOutputPathsForBundle(options: CompilerOptions, forceDtsPaths: boolean): EmitFileNames {
80        const outPath = outFile(options)!;
81        const jsFilePath = options.emitDeclarationOnly ? undefined : outPath;
82        const sourceMapFilePath = jsFilePath && getSourceMapFilePath(jsFilePath, options);
83        const declarationFilePath = (forceDtsPaths || getEmitDeclarations(options)) ? removeFileExtension(outPath) + Extension.Dts : undefined;
84        const declarationMapPath = declarationFilePath && getAreDeclarationMapsEnabled(options) ? declarationFilePath + ".map" : undefined;
85        const buildInfoPath = getTsBuildInfoEmitOutputFilePath(options);
86        return { jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath, buildInfoPath };
87    }
88
89    /*@internal*/
90    export function getOutputPathsFor(sourceFile: SourceFile | Bundle, host: EmitHost, forceDtsPaths: boolean): EmitFileNames {
91        const options = host.getCompilerOptions();
92        if (sourceFile.kind === SyntaxKind.Bundle) {
93            return getOutputPathsForBundle(options, forceDtsPaths);
94        }
95        else {
96            const ownOutputFilePath = getOwnEmitOutputFilePath(sourceFile.fileName, host, getOutputExtension(sourceFile.fileName, options));
97            const isJsonFile = isJsonSourceFile(sourceFile);
98            // If json file emits to the same location skip writing it, if emitDeclarationOnly skip writing it
99            const isJsonEmittedToSameLocation = isJsonFile &&
100                comparePaths(sourceFile.fileName, ownOutputFilePath, host.getCurrentDirectory(), !host.useCaseSensitiveFileNames()) === Comparison.EqualTo;
101            const jsFilePath = options.emitDeclarationOnly || isJsonEmittedToSameLocation ? undefined : ownOutputFilePath;
102            const sourceMapFilePath = !jsFilePath || isJsonSourceFile(sourceFile) ? undefined : getSourceMapFilePath(jsFilePath, options);
103            const declarationFilePath = (forceDtsPaths || (getEmitDeclarations(options) && !isJsonFile)) ? getDeclarationEmitOutputFilePath(sourceFile.fileName, host) : undefined;
104            const declarationMapPath = declarationFilePath && getAreDeclarationMapsEnabled(options) ? declarationFilePath + ".map" : undefined;
105            return { jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath, buildInfoPath: undefined };
106        }
107    }
108
109    function getSourceMapFilePath(jsFilePath: string, options: CompilerOptions) {
110        return (options.sourceMap && !options.inlineSourceMap) ? jsFilePath + ".map" : undefined;
111    }
112
113    /* @internal */
114    export function getOutputExtension(fileName: string, options: CompilerOptions): Extension {
115        return fileExtensionIs(fileName, Extension.Json) ? Extension.Json :
116        options.jsx === JsxEmit.Preserve && fileExtensionIsOneOf(fileName, [Extension.Jsx, Extension.Tsx]) ? Extension.Jsx :
117        fileExtensionIsOneOf(fileName, [Extension.Mts, Extension.Mjs]) ? Extension.Mjs :
118        fileExtensionIsOneOf(fileName, [Extension.Cts, Extension.Cjs]) ? Extension.Cjs :
119        Extension.Js;
120    }
121
122    function getOutputPathWithoutChangingExt(inputFileName: string, configFile: ParsedCommandLine, ignoreCase: boolean, outputDir: string | undefined, getCommonSourceDirectory?: () => string) {
123        return outputDir ?
124            resolvePath(
125                outputDir,
126                getRelativePathFromDirectory(getCommonSourceDirectory ? getCommonSourceDirectory() : getCommonSourceDirectoryOfConfig(configFile, ignoreCase), inputFileName, ignoreCase)
127            ) :
128            inputFileName;
129    }
130
131    /* @internal */
132    export function getOutputDeclarationFileName(inputFileName: string, configFile: ParsedCommandLine, ignoreCase: boolean, getCommonSourceDirectory?: () => string) {
133        return changeExtension(
134            getOutputPathWithoutChangingExt(inputFileName, configFile, ignoreCase, configFile.options.declarationDir || configFile.options.outDir, getCommonSourceDirectory),
135            getDeclarationEmitExtensionForPath(inputFileName)
136        );
137    }
138
139    function getOutputJSFileName(inputFileName: string, configFile: ParsedCommandLine, ignoreCase: boolean, getCommonSourceDirectory?: () => string) {
140        if (configFile.options.emitDeclarationOnly) return undefined;
141        const isJsonFile = fileExtensionIs(inputFileName, Extension.Json);
142        const outputFileName = changeExtension(
143            getOutputPathWithoutChangingExt(inputFileName, configFile, ignoreCase, configFile.options.outDir, getCommonSourceDirectory),
144            getOutputExtension(inputFileName, configFile.options)
145        );
146        return !isJsonFile || comparePaths(inputFileName, outputFileName, Debug.checkDefined(configFile.options.configFilePath), ignoreCase) !== Comparison.EqualTo ?
147            outputFileName :
148            undefined;
149    }
150
151    function createAddOutput() {
152        let outputs: string[] | undefined;
153        return { addOutput, getOutputs };
154        function addOutput(path: string | undefined) {
155            if (path) {
156                (outputs || (outputs = [])).push(path);
157            }
158        }
159        function getOutputs(): readonly string[] {
160            return outputs || emptyArray;
161        }
162    }
163
164    function getSingleOutputFileNames(configFile: ParsedCommandLine, addOutput: ReturnType<typeof createAddOutput>["addOutput"]) {
165        const { jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath, buildInfoPath } = getOutputPathsForBundle(configFile.options, /*forceDtsPaths*/ false);
166        addOutput(jsFilePath);
167        addOutput(sourceMapFilePath);
168        addOutput(declarationFilePath);
169        addOutput(declarationMapPath);
170        addOutput(buildInfoPath);
171    }
172
173    function getOwnOutputFileNames(configFile: ParsedCommandLine, inputFileName: string, ignoreCase: boolean, addOutput: ReturnType<typeof createAddOutput>["addOutput"], getCommonSourceDirectory?: () => string) {
174        if (isDeclarationFileName(inputFileName)) return;
175        const js = getOutputJSFileName(inputFileName, configFile, ignoreCase, getCommonSourceDirectory);
176        addOutput(js);
177        if (fileExtensionIs(inputFileName, Extension.Json)) return;
178        if (js && configFile.options.sourceMap) {
179            addOutput(`${js}.map`);
180        }
181        if (getEmitDeclarations(configFile.options)) {
182            const dts = getOutputDeclarationFileName(inputFileName, configFile, ignoreCase, getCommonSourceDirectory);
183            addOutput(dts);
184            if (configFile.options.declarationMap) {
185                addOutput(`${dts}.map`);
186            }
187        }
188    }
189
190    /*@internal*/
191    export function getCommonSourceDirectory(
192        options: CompilerOptions,
193        emittedFiles: () => readonly string[],
194        currentDirectory: string,
195        getCanonicalFileName: GetCanonicalFileName,
196        checkSourceFilesBelongToPath?: (commonSourceDirectory: string) => void
197    ): string {
198        let commonSourceDirectory;
199        if (options.rootDir) {
200            // If a rootDir is specified use it as the commonSourceDirectory
201            commonSourceDirectory = getNormalizedAbsolutePath(options.rootDir, currentDirectory);
202            checkSourceFilesBelongToPath?.(options.rootDir);
203        }
204        else if (options.composite && options.configFilePath) {
205            // Project compilations never infer their root from the input source paths
206            commonSourceDirectory = getDirectoryPath(normalizeSlashes(options.configFilePath));
207            checkSourceFilesBelongToPath?.(commonSourceDirectory);
208        }
209        else {
210            commonSourceDirectory = computeCommonSourceDirectoryOfFilenames(emittedFiles(), currentDirectory, getCanonicalFileName);
211        }
212
213        if (commonSourceDirectory && commonSourceDirectory[commonSourceDirectory.length - 1] !== directorySeparator) {
214            // Make sure directory path ends with directory separator so this string can directly
215            // used to replace with "" to get the relative path of the source file and the relative path doesn't
216            // start with / making it rooted path
217            commonSourceDirectory += directorySeparator;
218        }
219        return commonSourceDirectory;
220    }
221
222    /*@internal*/
223    export function getCommonSourceDirectoryOfConfig({ options, fileNames }: ParsedCommandLine, ignoreCase: boolean): string {
224        return getCommonSourceDirectory(
225            options,
226            () => filter(fileNames, file => !(options.noEmitForJsFiles && fileExtensionIsOneOf(file, supportedJSExtensionsFlat)) && !isDeclarationFileName(file)),
227            getDirectoryPath(normalizeSlashes(Debug.checkDefined(options.configFilePath))),
228            createGetCanonicalFileName(!ignoreCase)
229        );
230    }
231
232    /*@internal*/
233    export function getAllProjectOutputs(configFile: ParsedCommandLine, ignoreCase: boolean): readonly string[] {
234        const { addOutput, getOutputs } = createAddOutput();
235        if (outFile(configFile.options)) {
236            getSingleOutputFileNames(configFile, addOutput);
237        }
238        else {
239            const getCommonSourceDirectory = memoize(() => getCommonSourceDirectoryOfConfig(configFile, ignoreCase));
240            for (const inputFileName of configFile.fileNames) {
241                getOwnOutputFileNames(configFile, inputFileName, ignoreCase, addOutput, getCommonSourceDirectory);
242            }
243            addOutput(getTsBuildInfoEmitOutputFilePath(configFile.options));
244        }
245        return getOutputs();
246    }
247
248    export function getOutputFileNames(commandLine: ParsedCommandLine, inputFileName: string, ignoreCase: boolean): readonly string[] {
249        inputFileName = normalizePath(inputFileName);
250        Debug.assert(contains(commandLine.fileNames, inputFileName), `Expected fileName to be present in command line`);
251        const { addOutput, getOutputs } = createAddOutput();
252        if (outFile(commandLine.options)) {
253            getSingleOutputFileNames(commandLine, addOutput);
254        }
255        else {
256            getOwnOutputFileNames(commandLine, inputFileName, ignoreCase, addOutput);
257        }
258        return getOutputs();
259    }
260
261    /*@internal*/
262    export function getFirstProjectOutput(configFile: ParsedCommandLine, ignoreCase: boolean): string {
263        if (outFile(configFile.options)) {
264            const { jsFilePath } = getOutputPathsForBundle(configFile.options, /*forceDtsPaths*/ false);
265            return Debug.checkDefined(jsFilePath, `project ${configFile.options.configFilePath} expected to have at least one output`);
266        }
267
268        const getCommonSourceDirectory = memoize(() => getCommonSourceDirectoryOfConfig(configFile, ignoreCase));
269        for (const inputFileName of configFile.fileNames) {
270            if (isDeclarationFileName(inputFileName)) continue;
271            const jsFilePath = getOutputJSFileName(inputFileName, configFile, ignoreCase, getCommonSourceDirectory);
272            if (jsFilePath) return jsFilePath;
273            if (fileExtensionIs(inputFileName, Extension.Json)) continue;
274            if (getEmitDeclarations(configFile.options)) {
275                return getOutputDeclarationFileName(inputFileName, configFile, ignoreCase, getCommonSourceDirectory);
276            }
277        }
278        const buildInfoPath = getTsBuildInfoEmitOutputFilePath(configFile.options);
279        if (buildInfoPath) return buildInfoPath;
280        return Debug.fail(`project ${configFile.options.configFilePath} expected to have at least one output`);
281    }
282
283    /*@internal*/
284    // targetSourceFile is when users only want one file in entire project to be emitted. This is used in compileOnSave feature
285    export function emitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFile: SourceFile | undefined, { scriptTransformers, declarationTransformers }: EmitTransformers, emitOnlyDtsFiles?: boolean, onlyBuildInfo?: boolean, forceDtsEmit?: boolean): EmitResult {
286        const compilerOptions = host.getCompilerOptions();
287        const sourceMapDataList: SourceMapEmitResult[] | undefined = (compilerOptions.sourceMap || compilerOptions.inlineSourceMap || getAreDeclarationMapsEnabled(compilerOptions)) ? [] : undefined;
288        const emittedFilesList: string[] | undefined = compilerOptions.listEmittedFiles ? [] : undefined;
289        const emitterDiagnostics = createDiagnosticCollection();
290        const newLine = getNewLineCharacter(compilerOptions, () => host.getNewLine());
291        const writer = createTextWriter(newLine);
292        const { enter, exit } = performance.createTimer("printTime", "beforePrint", "afterPrint");
293        let bundleBuildInfo: BundleBuildInfo | undefined;
294        let emitSkipped = false;
295        const recordInfo = MemoryDotting.recordStage(MemoryDotting.EMIT_FILES);
296
297        // Emit each output file
298        enter();
299        forEachEmittedFile(
300            host,
301            emitSourceFileOrBundle,
302            getSourceFilesToEmit(host, targetSourceFile, forceDtsEmit),
303            forceDtsEmit,
304            onlyBuildInfo,
305            !targetSourceFile
306        );
307        exit();
308        MemoryDotting.stopRecordStage(recordInfo);
309
310
311        return {
312            emitSkipped,
313            diagnostics: emitterDiagnostics.getDiagnostics(),
314            emittedFiles: emittedFilesList,
315            sourceMaps: sourceMapDataList,
316        };
317
318        function emitSourceFileOrBundle({ jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath, buildInfoPath }: EmitFileNames, sourceFileOrBundle: SourceFile | Bundle | undefined) {
319            let buildInfoDirectory: string | undefined;
320            if (buildInfoPath && sourceFileOrBundle && isBundle(sourceFileOrBundle)) {
321                buildInfoDirectory = getDirectoryPath(getNormalizedAbsolutePath(buildInfoPath, host.getCurrentDirectory()));
322                bundleBuildInfo = {
323                    commonSourceDirectory: relativeToBuildInfo(host.getCommonSourceDirectory()),
324                    sourceFiles: sourceFileOrBundle.sourceFiles.map(file => relativeToBuildInfo(getNormalizedAbsolutePath(file.fileName, host.getCurrentDirectory())))
325                };
326            }
327            tracing?.push(tracing.Phase.Emit, "emitJsFileOrBundle", { jsFilePath });
328            emitJsFileOrBundle(sourceFileOrBundle, jsFilePath, sourceMapFilePath, relativeToBuildInfo);
329            tracing?.pop();
330
331            tracing?.push(tracing.Phase.Emit, "emitDeclarationFileOrBundle", { declarationFilePath });
332            emitDeclarationFileOrBundle(sourceFileOrBundle, declarationFilePath, declarationMapPath, relativeToBuildInfo);
333            tracing?.pop();
334
335            tracing?.push(tracing.Phase.Emit, "emitBuildInfo", { buildInfoPath });
336            emitBuildInfo(bundleBuildInfo, buildInfoPath);
337            tracing?.pop();
338
339            if (!emitSkipped && emittedFilesList) {
340                if (!emitOnlyDtsFiles) {
341                    if (jsFilePath) {
342                        emittedFilesList.push(jsFilePath);
343                    }
344                    if (sourceMapFilePath) {
345                        emittedFilesList.push(sourceMapFilePath);
346                    }
347                    if (buildInfoPath) {
348                        emittedFilesList.push(buildInfoPath);
349                    }
350                }
351                if (declarationFilePath) {
352                    emittedFilesList.push(declarationFilePath);
353                }
354                if (declarationMapPath) {
355                    emittedFilesList.push(declarationMapPath);
356                }
357            }
358
359            function relativeToBuildInfo(path: string) {
360                return ensurePathIsNonModuleName(getRelativePathFromDirectory(buildInfoDirectory!, path, host.getCanonicalFileName));
361            }
362        }
363
364        function emitBuildInfo(bundle: BundleBuildInfo | undefined, buildInfoPath: string | undefined) {
365            // Write build information if applicable
366            if (!buildInfoPath || targetSourceFile || emitSkipped) return;
367            const program = host.getProgramBuildInfo();
368            if (host.isEmitBlocked(buildInfoPath)) {
369                emitSkipped = true;
370                return;
371            }
372            const version = ts.version; // Extracted into a const so the form is stable between namespace and module
373            const buildInfo: BuildInfo = { bundle, program, version };
374            // Pass buildinfo as additional data to avoid having to reparse
375            writeFile(host, emitterDiagnostics, buildInfoPath, getBuildInfoText(buildInfo), /*writeByteOrderMark*/ false, /*sourceFiles*/ undefined, { buildInfo });
376
377            const programForLinter = host.getProgramBuildInfoForLinter && host.getProgramBuildInfoForLinter();
378            if (programForLinter) {
379                const versionForLinter = ts.version;
380                const buildInfoLinter: BuildInfo = { bundle: bundle, program: programForLinter, version: versionForLinter };
381                writeFile(host, emitterDiagnostics, getTsBuildInfoEmitOutputFilePathForLinter(buildInfoPath), getBuildInfoText(buildInfoLinter),
382                    /*writeByteOrderMark*/ false, /*sourceFiles*/ undefined, { buildInfo: buildInfoLinter });
383            }
384        }
385
386        function emitJsFileOrBundle(
387            sourceFileOrBundle: SourceFile | Bundle | undefined,
388            jsFilePath: string | undefined,
389            sourceMapFilePath: string | undefined,
390            relativeToBuildInfo: (path: string) => string) {
391            if (!sourceFileOrBundle || emitOnlyDtsFiles || !jsFilePath) {
392                return;
393            }
394
395            // Make sure not to write js file and source map file if any of them cannot be written
396            if (host.isEmitBlocked(jsFilePath) || compilerOptions.noEmit) {
397                emitSkipped = true;
398                return;
399            }
400            // Transform the source files
401            const transform = transformNodes(resolver, host, factory, compilerOptions, [sourceFileOrBundle], scriptTransformers, /*allowDtsFiles*/ false);
402
403            const printerOptions: PrinterOptions = {
404                removeComments: compilerOptions.removeComments,
405                newLine: compilerOptions.newLine,
406                noEmitHelpers: compilerOptions.noEmitHelpers,
407                module: compilerOptions.module,
408                target: compilerOptions.target,
409                sourceMap: compilerOptions.sourceMap,
410                inlineSourceMap: compilerOptions.inlineSourceMap,
411                inlineSources: compilerOptions.inlineSources,
412                extendedDiagnostics: compilerOptions.extendedDiagnostics,
413                writeBundleFileInfo: !!bundleBuildInfo,
414                relativeToBuildInfo
415            };
416
417            // Create a printer to print the nodes
418            const printer = createPrinter(printerOptions, {
419                // resolver hooks
420                hasGlobalName: resolver.hasGlobalName,
421
422                // transform hooks
423                onEmitNode: transform.emitNodeWithNotification,
424                isEmitNotificationEnabled: transform.isEmitNotificationEnabled,
425                substituteNode: transform.substituteNode,
426            });
427
428            Debug.assert(transform.transformed.length === 1, "Should only see one output from the transform");
429            printSourceFileOrBundle(jsFilePath, sourceMapFilePath, transform, printer, compilerOptions);
430
431            // Clean up emit nodes on parse tree
432            transform.dispose();
433            if (bundleBuildInfo) bundleBuildInfo.js = printer.bundleFileInfo;
434        }
435
436        function emitDeclarationFileOrBundle(
437            sourceFileOrBundle: SourceFile | Bundle | undefined,
438            declarationFilePath: string | undefined,
439            declarationMapPath: string | undefined,
440            relativeToBuildInfo: (path: string) => string) {
441            if (!sourceFileOrBundle) return;
442            if (!declarationFilePath) {
443                if (emitOnlyDtsFiles || compilerOptions.emitDeclarationOnly) emitSkipped = true;
444                return;
445            }
446            const sourceFiles = isSourceFile(sourceFileOrBundle) ? [sourceFileOrBundle] : sourceFileOrBundle.sourceFiles;
447            const filesForEmit = forceDtsEmit ? sourceFiles : filter(sourceFiles, isSourceFileNotJson);
448            // Setup and perform the transformation to retrieve declarations from the input files
449            const inputListOrBundle = outFile(compilerOptions) ? [factory.createBundle(filesForEmit, !isSourceFile(sourceFileOrBundle) ? sourceFileOrBundle.prepends : undefined)] : filesForEmit;
450            if (emitOnlyDtsFiles && !getEmitDeclarations(compilerOptions)) {
451                // Checker wont collect the linked aliases since thats only done when declaration is enabled.
452                // Do that here when emitting only dts files
453                filesForEmit.forEach(collectLinkedAliases);
454            }
455            const declarationTransform = transformNodes(resolver, host, factory, compilerOptions, inputListOrBundle, declarationTransformers, /*allowDtsFiles*/ false);
456            if (length(declarationTransform.diagnostics)) {
457                for (const diagnostic of declarationTransform.diagnostics!) {
458                    emitterDiagnostics.add(diagnostic);
459                }
460            }
461
462            const printerOptions: PrinterOptions = {
463                removeComments: compilerOptions.removeComments,
464                newLine: compilerOptions.newLine,
465                noEmitHelpers: true,
466                module: compilerOptions.module,
467                target: compilerOptions.target,
468                sourceMap: !forceDtsEmit && compilerOptions.declarationMap,
469                inlineSourceMap: compilerOptions.inlineSourceMap,
470                extendedDiagnostics: compilerOptions.extendedDiagnostics,
471                onlyPrintJsDocStyle: true,
472                writeBundleFileInfo: !!bundleBuildInfo,
473                recordInternalSection: !!bundleBuildInfo,
474                relativeToBuildInfo
475            };
476
477            const declarationPrinter = createPrinter(printerOptions, {
478                // resolver hooks
479                hasGlobalName: resolver.hasGlobalName,
480
481                // transform hooks
482                onEmitNode: declarationTransform.emitNodeWithNotification,
483                isEmitNotificationEnabled: declarationTransform.isEmitNotificationEnabled,
484                substituteNode: declarationTransform.substituteNode,
485            });
486            const declBlocked = (!!declarationTransform.diagnostics && !!declarationTransform.diagnostics.length) || !!host.isEmitBlocked(declarationFilePath) || !!compilerOptions.noEmit;
487            emitSkipped = emitSkipped || declBlocked;
488            if (!declBlocked || forceDtsEmit) {
489                Debug.assert(declarationTransform.transformed.length === 1, "Should only see one output from the decl transform");
490                printSourceFileOrBundle(
491                    declarationFilePath,
492                    declarationMapPath,
493                    declarationTransform,
494                    declarationPrinter,
495                    {
496                        sourceMap: printerOptions.sourceMap,
497                        sourceRoot: compilerOptions.sourceRoot,
498                        mapRoot: compilerOptions.mapRoot,
499                        extendedDiagnostics: compilerOptions.extendedDiagnostics,
500                        // Explicitly do not passthru either `inline` option
501                    }
502                );
503            }
504            declarationTransform.dispose();
505            if (bundleBuildInfo) bundleBuildInfo.dts = declarationPrinter.bundleFileInfo;
506        }
507
508        function collectLinkedAliases(node: Node) {
509            if (isExportAssignment(node)) {
510                if (node.expression.kind === SyntaxKind.Identifier) {
511                    resolver.collectLinkedAliases(node.expression as Identifier, /*setVisibility*/ true);
512                }
513                return;
514            }
515            else if (isExportSpecifier(node)) {
516                resolver.collectLinkedAliases(node.propertyName || node.name, /*setVisibility*/ true);
517                return;
518            }
519            forEachChild(node, collectLinkedAliases);
520        }
521
522        function printSourceFileOrBundle(jsFilePath: string, sourceMapFilePath: string | undefined, transform: TransformationResult<SourceFile | Bundle>, printer: Printer, mapOptions: SourceMapOptions) {
523            const sourceFileOrBundle = transform.transformed[0];
524            const bundle = sourceFileOrBundle.kind === SyntaxKind.Bundle ? sourceFileOrBundle : undefined;
525            const sourceFile = sourceFileOrBundle.kind === SyntaxKind.SourceFile ? sourceFileOrBundle : undefined;
526            const sourceFiles = bundle ? bundle.sourceFiles : [sourceFile!];
527
528            let sourceMapGenerator: SourceMapGenerator | undefined;
529            if (shouldEmitSourceMaps(mapOptions, sourceFileOrBundle)) {
530                sourceMapGenerator = createSourceMapGenerator(
531                    host,
532                    getBaseFileName(normalizeSlashes(jsFilePath)),
533                    getSourceRoot(mapOptions),
534                    getSourceMapDirectory(mapOptions, jsFilePath, sourceFile),
535                    mapOptions);
536            }
537
538            if (bundle) {
539                printer.writeBundle(bundle, writer, sourceMapGenerator);
540            }
541            else {
542                printer.writeFile(sourceFile!, writer, sourceMapGenerator);
543            }
544
545            let sourceMapUrlPos;
546            if (sourceMapGenerator) {
547                if (sourceMapDataList) {
548                    sourceMapDataList.push({
549                        inputSourceFileNames: sourceMapGenerator.getSources(),
550                        sourceMap: sourceMapGenerator.toJSON()
551                    });
552                }
553
554                const sourceMappingURL = getSourceMappingURL(
555                    mapOptions,
556                    sourceMapGenerator,
557                    jsFilePath,
558                    sourceMapFilePath,
559                    sourceFile);
560
561                if (sourceMappingURL) {
562                    if (!writer.isAtStartOfLine()) writer.rawWrite(newLine);
563                    sourceMapUrlPos = writer.getTextPos();
564                    writer.writeComment(`//# ${"sourceMappingURL"}=${sourceMappingURL}`); // Tools can sometimes see this line as a source mapping url comment
565                }
566
567                // Write the source map
568                if (sourceMapFilePath) {
569                    const sourceMap = sourceMapGenerator.toString();
570                    writeFile(host, emitterDiagnostics, sourceMapFilePath, sourceMap, /*writeByteOrderMark*/ false, sourceFiles);
571                    if (printer.bundleFileInfo) printer.bundleFileInfo.mapHash = computeSignature(sourceMap, maybeBind(host, host.createHash));
572                }
573            }
574            else {
575                writer.writeLine();
576            }
577
578            // Write the output file
579            const text = writer.getText();
580            writeFile(host, emitterDiagnostics, jsFilePath, text, !!compilerOptions.emitBOM, sourceFiles, { sourceMapUrlPos, diagnostics: transform.diagnostics });
581            // We store the hash of the text written in the buildinfo to ensure that text of the referenced d.ts file is same as whats in the buildinfo
582            // This is needed because incremental can be toggled between two runs and we might use stale file text to do text manipulation in prepend mode
583            if (printer.bundleFileInfo) printer.bundleFileInfo.hash = computeSignature(text, maybeBind(host, host.createHash));
584
585            // Reset state
586            writer.clear();
587        }
588
589        interface SourceMapOptions {
590            sourceMap?: boolean;
591            inlineSourceMap?: boolean;
592            inlineSources?: boolean;
593            sourceRoot?: string;
594            mapRoot?: string;
595            extendedDiagnostics?: boolean;
596        }
597
598        function shouldEmitSourceMaps(mapOptions: SourceMapOptions, sourceFileOrBundle: SourceFile | Bundle) {
599            return (mapOptions.sourceMap || mapOptions.inlineSourceMap)
600                && (sourceFileOrBundle.kind !== SyntaxKind.SourceFile || !fileExtensionIs(sourceFileOrBundle.fileName, Extension.Json));
601        }
602
603        function getSourceRoot(mapOptions: SourceMapOptions) {
604            // Normalize source root and make sure it has trailing "/" so that it can be used to combine paths with the
605            // relative paths of the sources list in the sourcemap
606            const sourceRoot = normalizeSlashes(mapOptions.sourceRoot || "");
607            return sourceRoot ? ensureTrailingDirectorySeparator(sourceRoot) : sourceRoot;
608        }
609
610        function getSourceMapDirectory(mapOptions: SourceMapOptions, filePath: string, sourceFile: SourceFile | undefined) {
611            if (mapOptions.sourceRoot) return host.getCommonSourceDirectory();
612            if (mapOptions.mapRoot) {
613                let sourceMapDir = normalizeSlashes(mapOptions.mapRoot);
614                if (sourceFile) {
615                    // For modules or multiple emit files the mapRoot will have directory structure like the sources
616                    // So if src\a.ts and src\lib\b.ts are compiled together user would be moving the maps into mapRoot\a.js.map and mapRoot\lib\b.js.map
617                    sourceMapDir = getDirectoryPath(getSourceFilePathInNewDir(sourceFile.fileName, host, sourceMapDir));
618                }
619                if (getRootLength(sourceMapDir) === 0) {
620                    // The relative paths are relative to the common directory
621                    sourceMapDir = combinePaths(host.getCommonSourceDirectory(), sourceMapDir);
622                }
623                return sourceMapDir;
624            }
625            return getDirectoryPath(normalizePath(filePath));
626        }
627
628        function getSourceMappingURL(mapOptions: SourceMapOptions, sourceMapGenerator: SourceMapGenerator, filePath: string, sourceMapFilePath: string | undefined, sourceFile: SourceFile | undefined) {
629            if (mapOptions.inlineSourceMap) {
630                // Encode the sourceMap into the sourceMap url
631                const sourceMapText = sourceMapGenerator.toString();
632                const base64SourceMapText = base64encode(sys, sourceMapText);
633                return `data:application/json;base64,${base64SourceMapText}`;
634            }
635
636            const sourceMapFile = getBaseFileName(normalizeSlashes(Debug.checkDefined(sourceMapFilePath)));
637            if (mapOptions.mapRoot) {
638                let sourceMapDir = normalizeSlashes(mapOptions.mapRoot);
639                if (sourceFile) {
640                    // For modules or multiple emit files the mapRoot will have directory structure like the sources
641                    // So if src\a.ts and src\lib\b.ts are compiled together user would be moving the maps into mapRoot\a.js.map and mapRoot\lib\b.js.map
642                    sourceMapDir = getDirectoryPath(getSourceFilePathInNewDir(sourceFile.fileName, host, sourceMapDir));
643                }
644                if (getRootLength(sourceMapDir) === 0) {
645                    // The relative paths are relative to the common directory
646                    sourceMapDir = combinePaths(host.getCommonSourceDirectory(), sourceMapDir);
647                    return encodeURI(
648                        getRelativePathToDirectoryOrUrl(
649                            getDirectoryPath(normalizePath(filePath)), // get the relative sourceMapDir path based on jsFilePath
650                            combinePaths(sourceMapDir, sourceMapFile), // this is where user expects to see sourceMap
651                            host.getCurrentDirectory(),
652                            host.getCanonicalFileName,
653                            /*isAbsolutePathAnUrl*/ true));
654                }
655                else {
656                    return encodeURI(combinePaths(sourceMapDir, sourceMapFile));
657                }
658            }
659            return encodeURI(sourceMapFile);
660        }
661    }
662
663    /*@internal*/
664    export function getBuildInfoText(buildInfo: BuildInfo) {
665        return JSON.stringify(buildInfo);
666    }
667
668    /*@internal*/
669    export function getBuildInfo(buildInfoFile: string, buildInfoText: string) {
670        return readJsonOrUndefined(buildInfoFile, buildInfoText) as BuildInfo | undefined;
671    }
672
673    /*@internal*/
674    export const notImplementedResolver: EmitResolver = {
675        hasGlobalName: notImplemented,
676        getReferencedExportContainer: notImplemented,
677        getReferencedImportDeclaration: notImplemented,
678        getReferencedDeclarationWithCollidingName: notImplemented,
679        isDeclarationWithCollidingName: notImplemented,
680        isValueAliasDeclaration: notImplemented,
681        isReferencedAliasDeclaration: notImplemented,
682        isReferenced: notImplemented,
683        isTopLevelValueImportEqualsWithEntityName: notImplemented,
684        getNodeCheckFlags: notImplemented,
685        isDeclarationVisible: notImplemented,
686        isLateBound: (_node): _node is LateBoundDeclaration => false,
687        collectLinkedAliases: notImplemented,
688        isImplementationOfOverload: notImplemented,
689        isRequiredInitializedParameter: notImplemented,
690        isOptionalUninitializedParameterProperty: notImplemented,
691        isExpandoFunctionDeclaration: notImplemented,
692        getPropertiesOfContainerFunction: notImplemented,
693        createTypeOfDeclaration: notImplemented,
694        createReturnTypeOfSignatureDeclaration: notImplemented,
695        createTypeOfExpression: notImplemented,
696        createLiteralConstValue: notImplemented,
697        isSymbolAccessible: notImplemented,
698        isEntityNameVisible: notImplemented,
699        // Returns the constant value this property access resolves to: notImplemented, or 'undefined' for a non-constant
700        getConstantValue: notImplemented,
701        getReferencedValueDeclaration: notImplemented,
702        getTypeReferenceSerializationKind: notImplemented,
703        isOptionalParameter: notImplemented,
704        moduleExportsSomeValue: notImplemented,
705        isArgumentsLocalBinding: notImplemented,
706        getExternalModuleFileFromDeclaration: notImplemented,
707        getTypeReferenceDirectivesForEntityName: notImplemented,
708        getTypeReferenceDirectivesForSymbol: notImplemented,
709        isLiteralConstDeclaration: notImplemented,
710        getJsxFactoryEntity: notImplemented,
711        getJsxFragmentFactoryEntity: notImplemented,
712        getAllAccessorDeclarations: notImplemented,
713        getSymbolOfExternalModuleSpecifier: notImplemented,
714        isBindingCapturedByNode: notImplemented,
715        getDeclarationStatementsForSourceFile: notImplemented,
716        isImportRequiredByAugmentation: notImplemented,
717        getAnnotationObjectLiteralEvaluatedProps: notImplemented,
718        getAnnotationPropertyEvaluatedInitializer: notImplemented,
719        getAnnotationPropertyInferredType: notImplemented,
720        isReferredToAnnotation: notImplemented,
721    };
722
723    /*@internal*/
724    /** File that isnt present resulting in error or output files */
725    export type EmitUsingBuildInfoResult = string | readonly OutputFile[];
726
727    /*@internal*/
728    export interface EmitUsingBuildInfoHost extends ModuleResolutionHost {
729        getCurrentDirectory(): string;
730        getCanonicalFileName(fileName: string): string;
731        useCaseSensitiveFileNames(): boolean;
732        getNewLine(): string;
733        createHash?(data: string): string;
734        getBuildInfo?(fileName: string, configFilePath: string | undefined): BuildInfo | undefined;
735    }
736
737    function createSourceFilesFromBundleBuildInfo(bundle: BundleBuildInfo, buildInfoDirectory: string, host: EmitUsingBuildInfoHost): readonly SourceFile[] {
738        const jsBundle = Debug.checkDefined(bundle.js);
739        const prologueMap = jsBundle.sources?.prologues && arrayToMap(jsBundle.sources.prologues, prologueInfo => prologueInfo.file);
740        return bundle.sourceFiles.map((fileName, index) => {
741            const prologueInfo = prologueMap?.get(index);
742            const statements = prologueInfo?.directives.map(directive => {
743                const literal = setTextRange(factory.createStringLiteral(directive.expression.text), directive.expression);
744                const statement = setTextRange(factory.createExpressionStatement(literal), directive);
745                setParent(literal, statement);
746                return statement;
747            });
748            const eofToken = factory.createToken(SyntaxKind.EndOfFileToken);
749            const sourceFile = factory.createSourceFile(statements ?? [], eofToken, NodeFlags.None);
750            sourceFile.fileName = getRelativePathFromDirectory(
751                host.getCurrentDirectory(),
752                getNormalizedAbsolutePath(fileName, buildInfoDirectory),
753                !host.useCaseSensitiveFileNames()
754            );
755            sourceFile.text = prologueInfo?.text ?? "";
756            setTextRangePosWidth(sourceFile, 0, prologueInfo?.text.length ?? 0);
757            setEachParent(sourceFile.statements, sourceFile);
758            setTextRangePosWidth(eofToken, sourceFile.end, 0);
759            setParent(eofToken, sourceFile);
760            return sourceFile;
761        });
762    }
763
764    /*@internal*/
765    export function emitUsingBuildInfo(
766        config: ParsedCommandLine,
767        host: EmitUsingBuildInfoHost,
768        getCommandLine: (ref: ProjectReference) => ParsedCommandLine | undefined,
769        customTransformers?: CustomTransformers
770    ): EmitUsingBuildInfoResult {
771        const createHash = maybeBind(host, host.createHash);
772        const { buildInfoPath, jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath } = getOutputPathsForBundle(config.options, /*forceDtsPaths*/ false);
773        let buildInfo: BuildInfo | undefined;
774        if (host.getBuildInfo) {
775            // If host directly provides buildinfo we can get it directly. This allows host to cache the buildinfo
776            buildInfo = host.getBuildInfo(buildInfoPath!, config.options.configFilePath);
777        }
778        else {
779            const buildInfoText = host.readFile(buildInfoPath!);
780            if (!buildInfoText) return buildInfoPath!;
781            buildInfo = getBuildInfo(buildInfoPath!, buildInfoText);
782        }
783        if (!buildInfo) return buildInfoPath!;
784        if (!buildInfo.bundle || !buildInfo.bundle.js || (declarationFilePath && !buildInfo.bundle.dts)) return buildInfoPath!;
785
786        const jsFileText = host.readFile(Debug.checkDefined(jsFilePath));
787        if (!jsFileText) return jsFilePath!;
788        // If the jsFileText is not same has what it was created with, tsbuildinfo is stale so dont use it
789        if (computeSignature(jsFileText, createHash) !== buildInfo.bundle.js.hash) return jsFilePath!;
790        const sourceMapText = sourceMapFilePath && host.readFile(sourceMapFilePath);
791        // error if no source map or for now if inline sourcemap
792        if ((sourceMapFilePath && !sourceMapText) || config.options.inlineSourceMap) return sourceMapFilePath || "inline sourcemap decoding";
793        if (sourceMapFilePath && computeSignature(sourceMapText!, createHash) !== buildInfo.bundle.js.mapHash) return sourceMapFilePath;
794
795        // read declaration text
796        const declarationText = declarationFilePath && host.readFile(declarationFilePath);
797        if (declarationFilePath && !declarationText) return declarationFilePath;
798        if (declarationFilePath && computeSignature(declarationText!, createHash) !== buildInfo.bundle.dts!.hash) return declarationFilePath;
799        const declarationMapText = declarationMapPath && host.readFile(declarationMapPath);
800        // error if no source map or for now if inline sourcemap
801        if ((declarationMapPath && !declarationMapText) || config.options.inlineSourceMap) return declarationMapPath || "inline sourcemap decoding";
802        if (declarationMapPath && computeSignature(declarationMapText!, createHash) !== buildInfo.bundle.dts!.mapHash) return declarationMapPath;
803
804        const buildInfoDirectory = getDirectoryPath(getNormalizedAbsolutePath(buildInfoPath!, host.getCurrentDirectory()));
805        const ownPrependInput = createInputFiles(
806            jsFileText,
807            declarationText!,
808            sourceMapFilePath,
809            sourceMapText,
810            declarationMapPath,
811            declarationMapText,
812            jsFilePath,
813            declarationFilePath,
814            buildInfoPath,
815            buildInfo,
816            /*onlyOwnText*/ true
817        );
818        const outputFiles: OutputFile[] = [];
819        const prependNodes = createPrependNodes(config.projectReferences, getCommandLine, f => host.readFile(f));
820        const sourceFilesForJsEmit = createSourceFilesFromBundleBuildInfo(buildInfo.bundle, buildInfoDirectory, host);
821        let changedDtsText: string | undefined;
822        let changedDtsData: WriteFileCallbackData | undefined;
823        const emitHost: EmitHost = {
824            getPrependNodes: memoize(() => [...prependNodes, ownPrependInput]),
825            getCanonicalFileName: host.getCanonicalFileName,
826            getCommonSourceDirectory: () => getNormalizedAbsolutePath(buildInfo!.bundle!.commonSourceDirectory, buildInfoDirectory),
827            getCompilerOptions: () => config.options,
828            getCurrentDirectory: () => host.getCurrentDirectory(),
829            getNewLine: () => host.getNewLine(),
830            getSourceFile: returnUndefined,
831            getSourceFileByPath: returnUndefined,
832            getSourceFiles: () => sourceFilesForJsEmit,
833            getLibFileFromReference: notImplemented,
834            isSourceFileFromExternalLibrary: returnFalse,
835            getResolvedProjectReferenceToRedirect: returnUndefined,
836            getProjectReferenceRedirect: returnUndefined,
837            isSourceOfProjectReferenceRedirect: returnFalse,
838            writeFile: (name, text, writeByteOrderMark, _onError, _sourceFiles, data) => {
839                switch (name) {
840                    case jsFilePath:
841                        if (jsFileText === text) return;
842                        break;
843                    case sourceMapFilePath:
844                        if (sourceMapText === text) return;
845                        break;
846                    case buildInfoPath:
847                        const newBuildInfo = data!.buildInfo!;
848                        newBuildInfo.program = buildInfo!.program;
849                        if (newBuildInfo.program && changedDtsText !== undefined && config.options.composite) {
850                            // Update the output signature
851                            (newBuildInfo.program as ProgramBundleEmitBuildInfo).outSignature = computeSignature(changedDtsText, createHash, changedDtsData);
852                        }
853                        // Update sourceFileInfo
854                        const { js, dts, sourceFiles } = buildInfo!.bundle!;
855                        newBuildInfo.bundle!.js!.sources = js!.sources;
856                        if (dts) {
857                            newBuildInfo.bundle!.dts!.sources = dts.sources;
858                        }
859                        newBuildInfo.bundle!.sourceFiles = sourceFiles;
860                        outputFiles.push({ name, text: getBuildInfoText(newBuildInfo), writeByteOrderMark, buildInfo: newBuildInfo });
861                        return;
862                    case declarationFilePath:
863                        if (declarationText === text) return;
864                        changedDtsText = text;
865                        changedDtsData = data;
866                        break;
867                    case declarationMapPath:
868                        if (declarationMapText === text) return;
869                        break;
870                    default:
871                        Debug.fail(`Unexpected path: ${name}`);
872                }
873                outputFiles.push({ name, text, writeByteOrderMark });
874            },
875            isEmitBlocked: returnFalse,
876            readFile: f => host.readFile(f),
877            fileExists: f => host.fileExists(f),
878            useCaseSensitiveFileNames: () => host.useCaseSensitiveFileNames(),
879            getProgramBuildInfo: returnUndefined,
880            getSourceFileFromReference: returnUndefined,
881            redirectTargetsMap: createMultiMap(),
882            getFileIncludeReasons: notImplemented,
883            createHash,
884        };
885        emitFiles(
886            notImplementedResolver,
887            emitHost,
888            /*targetSourceFile*/ undefined,
889            getTransformers(config.options, customTransformers)
890        );
891        return outputFiles;
892    }
893
894    const enum PipelinePhase {
895        Notification,
896        Substitution,
897        Comments,
898        SourceMaps,
899        Emit,
900    }
901
902    export function createPrinter(printerOptions: PrinterOptions = {}, handlers: PrintHandlers = {}): Printer {
903        const {
904            hasGlobalName,
905            onEmitNode = noEmitNotification,
906            isEmitNotificationEnabled,
907            substituteNode = noEmitSubstitution,
908            onBeforeEmitNode,
909            onAfterEmitNode,
910            onBeforeEmitNodeArray,
911            onAfterEmitNodeArray,
912            onBeforeEmitToken,
913            onAfterEmitToken
914        } = handlers;
915
916        const extendedDiagnostics = !!printerOptions.extendedDiagnostics;
917        const newLine = getNewLineCharacter(printerOptions);
918        const moduleKind = getEmitModuleKind(printerOptions);
919        const bundledHelpers = new Map<string, boolean>();
920        let removeCommentsCollection: string[] | undefined;
921        let universalRemoveCommentsCollection: RegExp[] | undefined;
922
923        let currentSourceFile: SourceFile | undefined;
924        let nodeIdToGeneratedName: string[]; // Map of generated names for specific nodes.
925        let autoGeneratedIdToGeneratedName: string[]; // Map of generated names for temp and loop variables.
926        let generatedNames: Set<string>; // Set of names generated by the NameGenerator.
927        let formattedNameTempFlagsStack: (ESMap<string, TempFlags> | undefined)[];
928        let formattedNameTempFlags: ESMap<string, TempFlags> | undefined;
929        let privateNameTempFlagsStack: TempFlags[]; // Stack of enclosing name generation scopes.
930        let privateNameTempFlags: TempFlags; // TempFlags for the current name generation scope.
931        let tempFlagsStack: TempFlags[]; // Stack of enclosing name generation scopes.
932        let tempFlags: TempFlags; // TempFlags for the current name generation scope.
933        let reservedNamesStack: Set<string>[]; // Stack of TempFlags reserved in enclosing name generation scopes.
934        let reservedNames: Set<string>; // TempFlags to reserve in nested name generation scopes.
935        let preserveSourceNewlines = printerOptions.preserveSourceNewlines; // Can be overridden inside nodes with the `IgnoreSourceNewlines` emit flag.
936        let nextListElementPos: number | undefined; // See comment in `getLeadingLineTerminatorCount`.
937
938        let writer: EmitTextWriter;
939        let ownWriter: EmitTextWriter; // Reusable `EmitTextWriter` for basic printing.
940        let write = writeBase;
941        let isOwnFileEmit: boolean;
942        const bundleFileInfo = printerOptions.writeBundleFileInfo ? { sections: [] } as BundleFileInfo : undefined;
943        const relativeToBuildInfo = bundleFileInfo ? Debug.checkDefined(printerOptions.relativeToBuildInfo) : undefined;
944        const recordInternalSection = printerOptions.recordInternalSection;
945        let sourceFileTextPos = 0;
946        let sourceFileTextKind: BundleFileTextLikeKind = BundleFileSectionKind.Text;
947
948        // Source Maps
949        let sourceMapsDisabled = true;
950        let sourceMapGenerator: SourceMapGenerator | undefined;
951        let sourceMapSource: SourceMapSource;
952        let sourceMapSourceIndex = -1;
953        let mostRecentlyAddedSourceMapSource: SourceMapSource;
954        let mostRecentlyAddedSourceMapSourceIndex = -1;
955
956        // Comments
957        let containerPos = -1;
958        let containerEnd = -1;
959        let declarationListContainerEnd = -1;
960        let currentLineMap: readonly number[] | undefined;
961        let detachedCommentsInfo: { nodePos: number, detachedCommentEndPos: number }[] | undefined;
962        let hasWrittenComment = false;
963        let commentsDisabled = !!printerOptions.removeComments;
964        let lastSubstitution: Node | undefined;
965        let currentParenthesizerRule: ((node: Node) => Node) | undefined;
966        const { enter: enterComment, exit: exitComment } = performance.createTimerIf(extendedDiagnostics, "commentTime", "beforeComment", "afterComment");
967        const parenthesizer = factory.parenthesizer;
968        const typeArgumentParenthesizerRuleSelector: OrdinalParentheizerRuleSelector<Node> = {
969            select: index => index === 0 ? parenthesizer.parenthesizeLeadingTypeArgument : undefined
970        };
971        const emitBinaryExpression = createEmitBinaryExpression();
972
973        reset();
974        return {
975            // public API
976            printNode,
977            printList,
978            printFile,
979            printBundle,
980
981            // internal API
982            writeNode,
983            writeList,
984            writeFile,
985            writeBundle,
986            bundleFileInfo
987        };
988
989        function printNode(hint: EmitHint, node: Node, sourceFile: SourceFile): string {
990            switch (hint) {
991                case EmitHint.SourceFile:
992                    Debug.assert(isSourceFile(node), "Expected a SourceFile node.");
993                    break;
994                case EmitHint.IdentifierName:
995                    Debug.assert(isIdentifier(node), "Expected an Identifier node.");
996                    break;
997                case EmitHint.Expression:
998                    Debug.assert(isExpression(node), "Expected an Expression node.");
999                    break;
1000            }
1001            switch (node.kind) {
1002                case SyntaxKind.SourceFile: return printFile(node as SourceFile);
1003                case SyntaxKind.Bundle: return printBundle(node as Bundle);
1004                case SyntaxKind.UnparsedSource: return printUnparsedSource(node as UnparsedSource);
1005            }
1006            writeNode(hint, node, sourceFile, beginPrint());
1007            return endPrint();
1008        }
1009
1010        function printList<T extends Node>(format: ListFormat, nodes: NodeArray<T>, sourceFile: SourceFile) {
1011            writeList(format, nodes, sourceFile, beginPrint());
1012            return endPrint();
1013        }
1014
1015        function printBundle(bundle: Bundle): string {
1016            writeBundle(bundle, beginPrint(), /*sourceMapEmitter*/ undefined);
1017            return endPrint();
1018        }
1019
1020        function printFile(sourceFile: SourceFile): string {
1021            writeFile(sourceFile, beginPrint(), /*sourceMapEmitter*/ undefined);
1022            return endPrint();
1023        }
1024
1025        function printUnparsedSource(unparsed: UnparsedSource): string {
1026            writeUnparsedSource(unparsed, beginPrint());
1027            return endPrint();
1028        }
1029
1030        /**
1031         * If `sourceFile` is `undefined`, `node` must be a synthesized `TypeNode`.
1032         */
1033        function writeNode(hint: EmitHint, node: TypeNode, sourceFile: undefined, output: EmitTextWriter): void;
1034        function writeNode(hint: EmitHint, node: Node, sourceFile: SourceFile, output: EmitTextWriter): void;
1035        function writeNode(hint: EmitHint, node: Node, sourceFile: SourceFile | undefined, output: EmitTextWriter) {
1036            const previousWriter = writer;
1037            setWriter(output, /*_sourceMapGenerator*/ undefined);
1038            print(hint, node, sourceFile);
1039            reset();
1040            writer = previousWriter;
1041        }
1042
1043        function writeList<T extends Node>(format: ListFormat, nodes: NodeArray<T>, sourceFile: SourceFile | undefined, output: EmitTextWriter) {
1044            const previousWriter = writer;
1045            setWriter(output, /*_sourceMapGenerator*/ undefined);
1046            if (sourceFile) {
1047                setSourceFile(sourceFile);
1048            }
1049            emitList(/*parentNode*/ undefined, nodes, format);
1050            reset();
1051            writer = previousWriter;
1052        }
1053
1054        function getTextPosWithWriteLine() {
1055            return writer.getTextPosWithWriteLine ? writer.getTextPosWithWriteLine() : writer.getTextPos();
1056        }
1057
1058        function updateOrPushBundleFileTextLike(pos: number, end: number, kind: BundleFileTextLikeKind) {
1059            const last = lastOrUndefined(bundleFileInfo!.sections);
1060            if (last && last.kind === kind) {
1061                last.end = end;
1062            }
1063            else {
1064                bundleFileInfo!.sections.push({ pos, end, kind });
1065            }
1066        }
1067
1068        function recordBundleFileInternalSectionStart(node: Node) {
1069            if (recordInternalSection &&
1070                bundleFileInfo &&
1071                currentSourceFile &&
1072                (isDeclaration(node) || isVariableStatement(node)) &&
1073                isInternalDeclaration(node, currentSourceFile) &&
1074                sourceFileTextKind !== BundleFileSectionKind.Internal) {
1075                const prevSourceFileTextKind = sourceFileTextKind;
1076                recordBundleFileTextLikeSection(writer.getTextPos());
1077                sourceFileTextPos = getTextPosWithWriteLine();
1078                sourceFileTextKind = BundleFileSectionKind.Internal;
1079                return prevSourceFileTextKind;
1080            }
1081            return undefined;
1082        }
1083
1084        function recordBundleFileInternalSectionEnd(prevSourceFileTextKind: ReturnType<typeof recordBundleFileInternalSectionStart>) {
1085            if (prevSourceFileTextKind) {
1086                recordBundleFileTextLikeSection(writer.getTextPos());
1087                sourceFileTextPos = getTextPosWithWriteLine();
1088                sourceFileTextKind = prevSourceFileTextKind;
1089            }
1090        }
1091
1092        function recordBundleFileTextLikeSection(end: number) {
1093            if (sourceFileTextPos < end) {
1094                updateOrPushBundleFileTextLike(sourceFileTextPos, end, sourceFileTextKind);
1095                return true;
1096            }
1097            return false;
1098        }
1099
1100        function writeBundle(bundle: Bundle, output: EmitTextWriter, sourceMapGenerator: SourceMapGenerator | undefined) {
1101            isOwnFileEmit = false;
1102            const previousWriter = writer;
1103            setWriter(output, sourceMapGenerator);
1104            emitShebangIfNeeded(bundle);
1105            emitPrologueDirectivesIfNeeded(bundle);
1106            emitHelpers(bundle);
1107            emitSyntheticTripleSlashReferencesIfNeeded(bundle);
1108
1109            for (const prepend of bundle.prepends) {
1110                writeLine();
1111                const pos = writer.getTextPos();
1112                const savedSections = bundleFileInfo && bundleFileInfo.sections;
1113                if (savedSections) bundleFileInfo.sections = [];
1114                print(EmitHint.Unspecified, prepend, /*sourceFile*/ undefined);
1115                if (bundleFileInfo) {
1116                    const newSections = bundleFileInfo.sections;
1117                    bundleFileInfo.sections = savedSections!;
1118                    if (prepend.oldFileOfCurrentEmit) bundleFileInfo.sections.push(...newSections);
1119                    else {
1120                        newSections.forEach(section => Debug.assert(isBundleFileTextLike(section)));
1121                        bundleFileInfo.sections.push({
1122                            pos,
1123                            end: writer.getTextPos(),
1124                            kind: BundleFileSectionKind.Prepend,
1125                            data: relativeToBuildInfo!((prepend as UnparsedSource).fileName),
1126                            texts: newSections as BundleFileTextLike[]
1127                        });
1128                    }
1129                }
1130            }
1131
1132            sourceFileTextPos = getTextPosWithWriteLine();
1133            for (const sourceFile of bundle.sourceFiles) {
1134                print(EmitHint.SourceFile, sourceFile, sourceFile);
1135            }
1136            if (bundleFileInfo && bundle.sourceFiles.length) {
1137                const end = writer.getTextPos();
1138                if (recordBundleFileTextLikeSection(end)) {
1139                    // Store prologues
1140                    const prologues = getPrologueDirectivesFromBundledSourceFiles(bundle);
1141                    if (prologues) {
1142                        if (!bundleFileInfo.sources) bundleFileInfo.sources = {};
1143                        bundleFileInfo.sources.prologues = prologues;
1144                    }
1145
1146                    // Store helpes
1147                    const helpers = getHelpersFromBundledSourceFiles(bundle);
1148                    if (helpers) {
1149                        if (!bundleFileInfo.sources) bundleFileInfo.sources = {};
1150                        bundleFileInfo.sources.helpers = helpers;
1151                    }
1152                }
1153            }
1154
1155            reset();
1156            writer = previousWriter;
1157        }
1158
1159        function writeUnparsedSource(unparsed: UnparsedSource, output: EmitTextWriter) {
1160            const previousWriter = writer;
1161            setWriter(output, /*_sourceMapGenerator*/ undefined);
1162            print(EmitHint.Unspecified, unparsed, /*sourceFile*/ undefined);
1163            reset();
1164            writer = previousWriter;
1165        }
1166
1167        function writeFile(sourceFile: SourceFile, output: EmitTextWriter, sourceMapGenerator: SourceMapGenerator | undefined) {
1168            //@ts-ignore
1169            removeCommentsCollection = sourceFile?.reservedComments;
1170            //@ts-ignore
1171            universalRemoveCommentsCollection = sourceFile?.universalReservedComments;
1172            isOwnFileEmit = true;
1173            const previousWriter = writer;
1174            setWriter(output, sourceMapGenerator);
1175            emitShebangIfNeeded(sourceFile);
1176            emitPrologueDirectivesIfNeeded(sourceFile);
1177            print(EmitHint.SourceFile, sourceFile, sourceFile);
1178            reset();
1179            writer = previousWriter;
1180        }
1181
1182        function beginPrint() {
1183            return ownWriter || (ownWriter = createTextWriter(newLine));
1184        }
1185
1186        function endPrint() {
1187            const text = ownWriter.getText();
1188            ownWriter.clear();
1189            return text;
1190        }
1191
1192        function print(hint: EmitHint, node: Node, sourceFile: SourceFile | undefined) {
1193            if (sourceFile) {
1194                setSourceFile(sourceFile);
1195            }
1196
1197            pipelineEmit(hint, node, /*parenthesizerRule*/ undefined);
1198        }
1199
1200        function setSourceFile(sourceFile: SourceFile | undefined) {
1201            currentSourceFile = sourceFile;
1202            currentLineMap = undefined;
1203            detachedCommentsInfo = undefined;
1204            if (sourceFile) {
1205                setSourceMapSource(sourceFile);
1206            }
1207        }
1208
1209        function setWriter(_writer: EmitTextWriter | undefined, _sourceMapGenerator: SourceMapGenerator | undefined) {
1210            if (_writer && printerOptions.omitTrailingSemicolon) {
1211                _writer = getTrailingSemicolonDeferringWriter(_writer);
1212            }
1213
1214            writer = _writer!; // TODO: GH#18217
1215            sourceMapGenerator = _sourceMapGenerator;
1216            sourceMapsDisabled = !writer || !sourceMapGenerator;
1217        }
1218
1219        function reset() {
1220            nodeIdToGeneratedName = [];
1221            autoGeneratedIdToGeneratedName = [];
1222            generatedNames = new Set();
1223            formattedNameTempFlagsStack = [];
1224            formattedNameTempFlags = new Map();
1225            privateNameTempFlagsStack = [];
1226            privateNameTempFlags = TempFlags.Auto;
1227            tempFlagsStack = [];
1228            tempFlags = TempFlags.Auto;
1229            reservedNamesStack = [];
1230            currentSourceFile = undefined;
1231            currentLineMap = undefined;
1232            detachedCommentsInfo = undefined;
1233            setWriter(/*output*/ undefined, /*_sourceMapGenerator*/ undefined);
1234        }
1235
1236        function getCurrentLineMap() {
1237            return currentLineMap || (currentLineMap = getLineStarts(Debug.checkDefined(currentSourceFile)));
1238        }
1239
1240        function emit(node: Node, parenthesizerRule?: (node: Node) => Node): void;
1241        function emit(node: Node | undefined, parenthesizerRule?: (node: Node) => Node): void;
1242        function emit(node: Node | undefined, parenthesizerRule?: (node: Node) => Node) {
1243            if (node === undefined) return;
1244            const prevSourceFileTextKind = recordBundleFileInternalSectionStart(node);
1245            pipelineEmit(EmitHint.Unspecified, node, parenthesizerRule);
1246            recordBundleFileInternalSectionEnd(prevSourceFileTextKind);
1247        }
1248
1249        function emitIdentifierName(node: Identifier): void;
1250        function emitIdentifierName(node: Identifier | undefined): void;
1251        function emitIdentifierName(node: Identifier | undefined) {
1252            if (node === undefined) return;
1253            pipelineEmit(EmitHint.IdentifierName, node, /*parenthesizerRule*/ undefined);
1254        }
1255
1256        function emitExpression(node: Expression, parenthesizerRule?: (node: Expression) => Expression): void;
1257        function emitExpression(node: Expression | undefined, parenthesizerRule?: (node: Expression) => Expression): void;
1258        function emitExpression(node: Expression | undefined, parenthesizerRule?: (node: Expression) => Expression) {
1259            if (node === undefined) return;
1260            pipelineEmit(EmitHint.Expression, node, parenthesizerRule);
1261        }
1262
1263        function emitJsxAttributeValue(node: StringLiteral | JsxExpression): void {
1264            pipelineEmit(isStringLiteral(node) ? EmitHint.JsxAttributeValue : EmitHint.Unspecified, node);
1265        }
1266
1267        function beforeEmitNode(node: Node) {
1268            if (preserveSourceNewlines && (getEmitFlags(node) & EmitFlags.IgnoreSourceNewlines)) {
1269                preserveSourceNewlines = false;
1270            }
1271        }
1272
1273        function afterEmitNode(savedPreserveSourceNewlines: boolean | undefined) {
1274            preserveSourceNewlines = savedPreserveSourceNewlines;
1275        }
1276
1277        function pipelineEmit(emitHint: EmitHint, node: Node, parenthesizerRule?: (node: Node) => Node) {
1278            currentParenthesizerRule = parenthesizerRule;
1279            const pipelinePhase = getPipelinePhase(PipelinePhase.Notification, emitHint, node);
1280            pipelinePhase(emitHint, node);
1281            currentParenthesizerRule = undefined;
1282        }
1283
1284        function shouldEmitComments(node: Node) {
1285            return !commentsDisabled && !isSourceFile(node);
1286        }
1287
1288        function shouldEmitSourceMaps(node: Node) {
1289            return !sourceMapsDisabled &&
1290                !isSourceFile(node) &&
1291                !isInJsonFile(node) &&
1292                !isUnparsedSource(node) &&
1293                !isUnparsedPrepend(node);
1294        }
1295
1296        function getPipelinePhase(phase: PipelinePhase, emitHint: EmitHint, node: Node) {
1297            switch (phase) {
1298                case PipelinePhase.Notification:
1299                    if (onEmitNode !== noEmitNotification && (!isEmitNotificationEnabled || isEmitNotificationEnabled(node))) {
1300                        return pipelineEmitWithNotification;
1301                    }
1302                    // falls through
1303                case PipelinePhase.Substitution:
1304                    if (substituteNode !== noEmitSubstitution && (lastSubstitution = substituteNode(emitHint, node) || node) !== node) {
1305                        if (currentParenthesizerRule) {
1306                            lastSubstitution = currentParenthesizerRule(lastSubstitution);
1307                        }
1308                        return pipelineEmitWithSubstitution;
1309                    }
1310                    // falls through
1311                case PipelinePhase.Comments:
1312                    if (shouldEmitComments(node)) {
1313                        return pipelineEmitWithComments;
1314                    }
1315                    // falls through
1316                case PipelinePhase.SourceMaps:
1317                    if (shouldEmitSourceMaps(node)) {
1318                        return pipelineEmitWithSourceMaps;
1319                    }
1320                    // falls through
1321                case PipelinePhase.Emit:
1322                    return pipelineEmitWithHint;
1323                default:
1324                    return Debug.assertNever(phase);
1325            }
1326        }
1327
1328        function getNextPipelinePhase(currentPhase: PipelinePhase, emitHint: EmitHint, node: Node) {
1329            return getPipelinePhase(currentPhase + 1, emitHint, node);
1330        }
1331
1332        function pipelineEmitWithNotification(hint: EmitHint, node: Node) {
1333            const pipelinePhase = getNextPipelinePhase(PipelinePhase.Notification, hint, node);
1334            onEmitNode(hint, node, pipelinePhase);
1335        }
1336
1337        function pipelineEmitWithHint(hint: EmitHint, node: Node): void {
1338            onBeforeEmitNode?.(node);
1339            if (preserveSourceNewlines) {
1340                const savedPreserveSourceNewlines = preserveSourceNewlines;
1341                beforeEmitNode(node);
1342                pipelineEmitWithHintWorker(hint, node);
1343                afterEmitNode(savedPreserveSourceNewlines);
1344            }
1345            else {
1346                pipelineEmitWithHintWorker(hint, node);
1347            }
1348            onAfterEmitNode?.(node);
1349            // clear the parenthesizer rule as we ascend
1350            currentParenthesizerRule = undefined;
1351        }
1352
1353        function pipelineEmitWithHintWorker(hint: EmitHint, node: Node, allowSnippets = true): void {
1354            if (allowSnippets) {
1355                const snippet = getSnippetElement(node);
1356                if (snippet) {
1357                    return emitSnippetNode(hint, node, snippet);
1358                }
1359            }
1360            if (hint === EmitHint.SourceFile) return emitSourceFile(cast(node, isSourceFile));
1361            if (hint === EmitHint.IdentifierName) return emitIdentifier(cast(node, isIdentifier));
1362            if (hint === EmitHint.JsxAttributeValue) return emitLiteral(cast(node, isStringLiteral), /*jsxAttributeEscape*/ true);
1363            if (hint === EmitHint.MappedTypeParameter) return emitMappedTypeParameter(cast(node, isTypeParameterDeclaration));
1364            if (hint === EmitHint.EmbeddedStatement) {
1365                Debug.assertNode(node, isEmptyStatement);
1366                return emitEmptyStatement(/*isEmbeddedStatement*/ true);
1367            }
1368            if (hint === EmitHint.Unspecified) {
1369                switch (node.kind) {
1370                    // Pseudo-literals
1371                    case SyntaxKind.TemplateHead:
1372                    case SyntaxKind.TemplateMiddle:
1373                    case SyntaxKind.TemplateTail:
1374                        return emitLiteral(node as LiteralExpression, /*jsxAttributeEscape*/ false);
1375
1376                    // Identifiers
1377                    case SyntaxKind.Identifier:
1378                        return emitIdentifier(node as Identifier);
1379
1380                    // PrivateIdentifiers
1381                    case SyntaxKind.PrivateIdentifier:
1382                        return emitPrivateIdentifier(node as PrivateIdentifier);
1383
1384                    // Parse tree nodes
1385                    // Names
1386                    case SyntaxKind.QualifiedName:
1387                        return emitQualifiedName(node as QualifiedName);
1388                    case SyntaxKind.ComputedPropertyName:
1389                        return emitComputedPropertyName(node as ComputedPropertyName);
1390
1391                    // Signature elements
1392                    case SyntaxKind.TypeParameter:
1393                        return emitTypeParameter(node as TypeParameterDeclaration);
1394                    case SyntaxKind.Parameter:
1395                        return emitParameter(node as ParameterDeclaration);
1396                    case SyntaxKind.Decorator:
1397                        return emitDecorator(node as Decorator);
1398
1399                    // Type members
1400                    case SyntaxKind.PropertySignature:
1401                        return emitPropertySignature(node as PropertySignature);
1402                    case SyntaxKind.PropertyDeclaration:
1403                        return emitPropertyDeclaration(node as PropertyDeclaration);
1404                    case SyntaxKind.AnnotationPropertyDeclaration:
1405                        return emitAnnotationPropertyDeclaration(node as AnnotationPropertyDeclaration);
1406                    case SyntaxKind.MethodSignature:
1407                        return emitMethodSignature(node as MethodSignature);
1408                    case SyntaxKind.MethodDeclaration:
1409                        return emitMethodDeclaration(node as MethodDeclaration);
1410                    case SyntaxKind.ClassStaticBlockDeclaration:
1411                        return emitClassStaticBlockDeclaration(node as ClassStaticBlockDeclaration);
1412                    case SyntaxKind.Constructor:
1413                        return emitConstructor(node as ConstructorDeclaration);
1414                    case SyntaxKind.GetAccessor:
1415                    case SyntaxKind.SetAccessor:
1416                        return emitAccessorDeclaration(node as AccessorDeclaration);
1417                    case SyntaxKind.CallSignature:
1418                        return emitCallSignature(node as CallSignatureDeclaration);
1419                    case SyntaxKind.ConstructSignature:
1420                        return emitConstructSignature(node as ConstructSignatureDeclaration);
1421                    case SyntaxKind.IndexSignature:
1422                        return emitIndexSignature(node as IndexSignatureDeclaration);
1423
1424                    // Types
1425                    case SyntaxKind.TypePredicate:
1426                        return emitTypePredicate(node as TypePredicateNode);
1427                    case SyntaxKind.TypeReference:
1428                        return emitTypeReference(node as TypeReferenceNode);
1429                    case SyntaxKind.FunctionType:
1430                        return emitFunctionType(node as FunctionTypeNode);
1431                    case SyntaxKind.ConstructorType:
1432                        return emitConstructorType(node as ConstructorTypeNode);
1433                    case SyntaxKind.TypeQuery:
1434                        return emitTypeQuery(node as TypeQueryNode);
1435                    case SyntaxKind.TypeLiteral:
1436                        return emitTypeLiteral(node as TypeLiteralNode);
1437                    case SyntaxKind.ArrayType:
1438                        return emitArrayType(node as ArrayTypeNode);
1439                    case SyntaxKind.TupleType:
1440                        return emitTupleType(node as TupleTypeNode);
1441                    case SyntaxKind.OptionalType:
1442                        return emitOptionalType(node as OptionalTypeNode);
1443                    // SyntaxKind.RestType is handled below
1444                    case SyntaxKind.UnionType:
1445                        return emitUnionType(node as UnionTypeNode);
1446                    case SyntaxKind.IntersectionType:
1447                        return emitIntersectionType(node as IntersectionTypeNode);
1448                    case SyntaxKind.ConditionalType:
1449                        return emitConditionalType(node as ConditionalTypeNode);
1450                    case SyntaxKind.InferType:
1451                        return emitInferType(node as InferTypeNode);
1452                    case SyntaxKind.ParenthesizedType:
1453                        return emitParenthesizedType(node as ParenthesizedTypeNode);
1454                    case SyntaxKind.ExpressionWithTypeArguments:
1455                        return emitExpressionWithTypeArguments(node as ExpressionWithTypeArguments);
1456                    case SyntaxKind.ThisType:
1457                        return emitThisType();
1458                    case SyntaxKind.TypeOperator:
1459                        return emitTypeOperator(node as TypeOperatorNode);
1460                    case SyntaxKind.IndexedAccessType:
1461                        return emitIndexedAccessType(node as IndexedAccessTypeNode);
1462                    case SyntaxKind.MappedType:
1463                        return emitMappedType(node as MappedTypeNode);
1464                    case SyntaxKind.LiteralType:
1465                        return emitLiteralType(node as LiteralTypeNode);
1466                    case SyntaxKind.NamedTupleMember:
1467                        return emitNamedTupleMember(node as NamedTupleMember);
1468                    case SyntaxKind.TemplateLiteralType:
1469                        return emitTemplateType(node as TemplateLiteralTypeNode);
1470                    case SyntaxKind.TemplateLiteralTypeSpan:
1471                        return emitTemplateTypeSpan(node as TemplateLiteralTypeSpan);
1472                    case SyntaxKind.ImportType:
1473                        return emitImportTypeNode(node as ImportTypeNode);
1474
1475                    // Binding patterns
1476                    case SyntaxKind.ObjectBindingPattern:
1477                        return emitObjectBindingPattern(node as ObjectBindingPattern);
1478                    case SyntaxKind.ArrayBindingPattern:
1479                        return emitArrayBindingPattern(node as ArrayBindingPattern);
1480                    case SyntaxKind.BindingElement:
1481                        return emitBindingElement(node as BindingElement);
1482
1483                    // Misc
1484                    case SyntaxKind.TemplateSpan:
1485                        return emitTemplateSpan(node as TemplateSpan);
1486                    case SyntaxKind.SemicolonClassElement:
1487                        return emitSemicolonClassElement();
1488
1489                    // Statements
1490                    case SyntaxKind.Block:
1491                        return emitBlock(node as Block);
1492                    case SyntaxKind.VariableStatement:
1493                        return emitVariableStatement(node as VariableStatement);
1494                    case SyntaxKind.EmptyStatement:
1495                        return emitEmptyStatement(/*isEmbeddedStatement*/ false);
1496                    case SyntaxKind.ExpressionStatement:
1497                        return emitExpressionStatement(node as ExpressionStatement);
1498                    case SyntaxKind.IfStatement:
1499                        return emitIfStatement(node as IfStatement);
1500                    case SyntaxKind.DoStatement:
1501                        return emitDoStatement(node as DoStatement);
1502                    case SyntaxKind.WhileStatement:
1503                        return emitWhileStatement(node as WhileStatement);
1504                    case SyntaxKind.ForStatement:
1505                        return emitForStatement(node as ForStatement);
1506                    case SyntaxKind.ForInStatement:
1507                        return emitForInStatement(node as ForInStatement);
1508                    case SyntaxKind.ForOfStatement:
1509                        return emitForOfStatement(node as ForOfStatement);
1510                    case SyntaxKind.ContinueStatement:
1511                        return emitContinueStatement(node as ContinueStatement);
1512                    case SyntaxKind.BreakStatement:
1513                        return emitBreakStatement(node as BreakStatement);
1514                    case SyntaxKind.ReturnStatement:
1515                        return emitReturnStatement(node as ReturnStatement);
1516                    case SyntaxKind.WithStatement:
1517                        return emitWithStatement(node as WithStatement);
1518                    case SyntaxKind.SwitchStatement:
1519                        return emitSwitchStatement(node as SwitchStatement);
1520                    case SyntaxKind.LabeledStatement:
1521                        return emitLabeledStatement(node as LabeledStatement);
1522                    case SyntaxKind.ThrowStatement:
1523                        return emitThrowStatement(node as ThrowStatement);
1524                    case SyntaxKind.TryStatement:
1525                        return emitTryStatement(node as TryStatement);
1526                    case SyntaxKind.DebuggerStatement:
1527                        return emitDebuggerStatement(node as DebuggerStatement);
1528
1529                    // Declarations
1530                    case SyntaxKind.VariableDeclaration:
1531                        return emitVariableDeclaration(node as VariableDeclaration);
1532                    case SyntaxKind.VariableDeclarationList:
1533                        return emitVariableDeclarationList(node as VariableDeclarationList);
1534                    case SyntaxKind.FunctionDeclaration:
1535                        return emitFunctionDeclaration(node as FunctionDeclaration);
1536                    case SyntaxKind.ClassDeclaration:
1537                        return emitClassOrStructDeclaration(node as ClassDeclaration);
1538                    case SyntaxKind.StructDeclaration:
1539                        return emitClassOrStructDeclaration(<StructDeclaration>node);
1540                    case SyntaxKind.AnnotationDeclaration:
1541                        return emitAnnotationDeclaration(node as AnnotationDeclaration);
1542                    case SyntaxKind.InterfaceDeclaration:
1543                        return emitInterfaceDeclaration(node as InterfaceDeclaration);
1544                    case SyntaxKind.TypeAliasDeclaration:
1545                        return emitTypeAliasDeclaration(node as TypeAliasDeclaration);
1546                    case SyntaxKind.EnumDeclaration:
1547                        return emitEnumDeclaration(node as EnumDeclaration);
1548                    case SyntaxKind.ModuleDeclaration:
1549                        return emitModuleDeclaration(node as ModuleDeclaration);
1550                    case SyntaxKind.ModuleBlock:
1551                        return emitModuleBlock(node as ModuleBlock);
1552                    case SyntaxKind.CaseBlock:
1553                        return emitCaseBlock(node as CaseBlock);
1554                    case SyntaxKind.NamespaceExportDeclaration:
1555                        return emitNamespaceExportDeclaration(node as NamespaceExportDeclaration);
1556                    case SyntaxKind.ImportEqualsDeclaration:
1557                        return emitImportEqualsDeclaration(node as ImportEqualsDeclaration);
1558                    case SyntaxKind.ImportDeclaration:
1559                        return emitImportDeclaration(node as ImportDeclaration);
1560                    case SyntaxKind.ImportClause:
1561                        return emitImportClause(node as ImportClause);
1562                    case SyntaxKind.NamespaceImport:
1563                        return emitNamespaceImport(node as NamespaceImport);
1564                    case SyntaxKind.NamespaceExport:
1565                        return emitNamespaceExport(node as NamespaceExport);
1566                    case SyntaxKind.NamedImports:
1567                        return emitNamedImports(node as NamedImports);
1568                    case SyntaxKind.ImportSpecifier:
1569                        return emitImportSpecifier(node as ImportSpecifier);
1570                    case SyntaxKind.ExportAssignment:
1571                        return emitExportAssignment(node as ExportAssignment);
1572                    case SyntaxKind.ExportDeclaration:
1573                        return emitExportDeclaration(node as ExportDeclaration);
1574                    case SyntaxKind.NamedExports:
1575                        return emitNamedExports(node as NamedExports);
1576                    case SyntaxKind.ExportSpecifier:
1577                        return emitExportSpecifier(node as ExportSpecifier);
1578                    case SyntaxKind.AssertClause:
1579                        return emitAssertClause(node as AssertClause);
1580                    case SyntaxKind.AssertEntry:
1581                        return emitAssertEntry(node as AssertEntry);
1582                    case SyntaxKind.MissingDeclaration:
1583                        return;
1584
1585                    // Module references
1586                    case SyntaxKind.ExternalModuleReference:
1587                        return emitExternalModuleReference(node as ExternalModuleReference);
1588
1589                    // JSX (non-expression)
1590                    case SyntaxKind.JsxText:
1591                        return emitJsxText(node as JsxText);
1592                    case SyntaxKind.JsxOpeningElement:
1593                    case SyntaxKind.JsxOpeningFragment:
1594                        return emitJsxOpeningElementOrFragment(node as JsxOpeningElement);
1595                    case SyntaxKind.JsxClosingElement:
1596                    case SyntaxKind.JsxClosingFragment:
1597                        return emitJsxClosingElementOrFragment(node as JsxClosingElement);
1598                    case SyntaxKind.JsxAttribute:
1599                        return emitJsxAttribute(node as JsxAttribute);
1600                    case SyntaxKind.JsxAttributes:
1601                        return emitJsxAttributes(node as JsxAttributes);
1602                    case SyntaxKind.JsxSpreadAttribute:
1603                        return emitJsxSpreadAttribute(node as JsxSpreadAttribute);
1604                    case SyntaxKind.JsxExpression:
1605                        return emitJsxExpression(node as JsxExpression);
1606
1607                    // Clauses
1608                    case SyntaxKind.CaseClause:
1609                        return emitCaseClause(node as CaseClause);
1610                    case SyntaxKind.DefaultClause:
1611                        return emitDefaultClause(node as DefaultClause);
1612                    case SyntaxKind.HeritageClause:
1613                        return emitHeritageClause(node as HeritageClause);
1614                    case SyntaxKind.CatchClause:
1615                        return emitCatchClause(node as CatchClause);
1616
1617                    // Property assignments
1618                    case SyntaxKind.PropertyAssignment:
1619                        return emitPropertyAssignment(node as PropertyAssignment);
1620                    case SyntaxKind.ShorthandPropertyAssignment:
1621                        return emitShorthandPropertyAssignment(node as ShorthandPropertyAssignment);
1622                    case SyntaxKind.SpreadAssignment:
1623                        return emitSpreadAssignment(node as SpreadAssignment);
1624
1625                    // Enum
1626                    case SyntaxKind.EnumMember:
1627                        return emitEnumMember(node as EnumMember);
1628
1629                    // Unparsed
1630                    case SyntaxKind.UnparsedPrologue:
1631                        return writeUnparsedNode(node as UnparsedNode);
1632                    case SyntaxKind.UnparsedSource:
1633                    case SyntaxKind.UnparsedPrepend:
1634                        return emitUnparsedSourceOrPrepend(node as UnparsedSource);
1635                    case SyntaxKind.UnparsedText:
1636                    case SyntaxKind.UnparsedInternalText:
1637                        return emitUnparsedTextLike(node as UnparsedTextLike);
1638                    case SyntaxKind.UnparsedSyntheticReference:
1639                        return emitUnparsedSyntheticReference(node as UnparsedSyntheticReference);
1640
1641                    // Top-level nodes
1642                    case SyntaxKind.SourceFile:
1643                        return emitSourceFile(node as SourceFile);
1644                    case SyntaxKind.Bundle:
1645                        return Debug.fail("Bundles should be printed using printBundle");
1646                    // SyntaxKind.UnparsedSource (handled above)
1647                    case SyntaxKind.InputFiles:
1648                        return Debug.fail("InputFiles should not be printed");
1649
1650                    // JSDoc nodes (only used in codefixes currently)
1651                    case SyntaxKind.JSDocTypeExpression:
1652                        return emitJSDocTypeExpression(node as JSDocTypeExpression);
1653                    case SyntaxKind.JSDocNameReference:
1654                        return emitJSDocNameReference(node as JSDocNameReference);
1655                    case SyntaxKind.JSDocAllType:
1656                        return writePunctuation("*");
1657                    case SyntaxKind.JSDocUnknownType:
1658                        return writePunctuation("?");
1659                    case SyntaxKind.JSDocNullableType:
1660                        return emitJSDocNullableType(node as JSDocNullableType);
1661                    case SyntaxKind.JSDocNonNullableType:
1662                        return emitJSDocNonNullableType(node as JSDocNonNullableType);
1663                    case SyntaxKind.JSDocOptionalType:
1664                        return emitJSDocOptionalType(node as JSDocOptionalType);
1665                    case SyntaxKind.JSDocFunctionType:
1666                        return emitJSDocFunctionType(node as JSDocFunctionType);
1667                    case SyntaxKind.RestType:
1668                    case SyntaxKind.JSDocVariadicType:
1669                        return emitRestOrJSDocVariadicType(node as RestTypeNode | JSDocVariadicType);
1670                    case SyntaxKind.JSDocNamepathType:
1671                        return;
1672                    case SyntaxKind.JSDoc:
1673                        return emitJSDoc(node as JSDoc);
1674                    case SyntaxKind.JSDocTypeLiteral:
1675                        return emitJSDocTypeLiteral(node as JSDocTypeLiteral);
1676                    case SyntaxKind.JSDocSignature:
1677                        return emitJSDocSignature(node as JSDocSignature);
1678                    case SyntaxKind.JSDocTag:
1679                    case SyntaxKind.JSDocClassTag:
1680                    case SyntaxKind.JSDocOverrideTag:
1681                        return emitJSDocSimpleTag(node as JSDocTag);
1682                    case SyntaxKind.JSDocAugmentsTag:
1683                    case SyntaxKind.JSDocImplementsTag:
1684                        return emitJSDocHeritageTag(node as JSDocImplementsTag | JSDocAugmentsTag);
1685                    case SyntaxKind.JSDocAuthorTag:
1686                    case SyntaxKind.JSDocDeprecatedTag:
1687                        return;
1688                    // SyntaxKind.JSDocClassTag (see JSDocTag, above)
1689                    case SyntaxKind.JSDocPublicTag:
1690                    case SyntaxKind.JSDocPrivateTag:
1691                    case SyntaxKind.JSDocProtectedTag:
1692                    case SyntaxKind.JSDocReadonlyTag:
1693                        return;
1694                    case SyntaxKind.JSDocCallbackTag:
1695                        return emitJSDocCallbackTag(node as JSDocCallbackTag);
1696                    // SyntaxKind.JSDocEnumTag (see below)
1697                    case SyntaxKind.JSDocParameterTag:
1698                    case SyntaxKind.JSDocPropertyTag:
1699                        return emitJSDocPropertyLikeTag(node as JSDocPropertyLikeTag);
1700                    case SyntaxKind.JSDocEnumTag:
1701                    case SyntaxKind.JSDocReturnTag:
1702                    case SyntaxKind.JSDocThisTag:
1703                    case SyntaxKind.JSDocTypeTag:
1704                        return emitJSDocSimpleTypedTag(node as JSDocTypeTag);
1705                    case SyntaxKind.JSDocTemplateTag:
1706                        return emitJSDocTemplateTag(node as JSDocTemplateTag);
1707                    case SyntaxKind.JSDocTypedefTag:
1708                        return emitJSDocTypedefTag(node as JSDocTypedefTag);
1709                    case SyntaxKind.JSDocSeeTag:
1710                        return emitJSDocSeeTag(node as JSDocSeeTag);
1711                    // SyntaxKind.JSDocPropertyTag (see JSDocParameterTag, above)
1712
1713                    // Transformation nodes
1714                    case SyntaxKind.NotEmittedStatement:
1715                    case SyntaxKind.EndOfDeclarationMarker:
1716                    case SyntaxKind.MergeDeclarationMarker:
1717                        return;
1718                }
1719                if (isExpression(node)) {
1720                    hint = EmitHint.Expression;
1721                    if (substituteNode !== noEmitSubstitution) {
1722                        const substitute = substituteNode(hint, node) || node;
1723                        if (substitute !== node) {
1724                            node = substitute;
1725                            if (currentParenthesizerRule) {
1726                                node = currentParenthesizerRule(node);
1727                            }
1728                        }
1729                    }
1730                }
1731            }
1732            if (hint === EmitHint.Expression) {
1733                switch (node.kind) {
1734                    // Literals
1735                    case SyntaxKind.NumericLiteral:
1736                    case SyntaxKind.BigIntLiteral:
1737                        return emitNumericOrBigIntLiteral(node as NumericLiteral | BigIntLiteral);
1738
1739                    case SyntaxKind.StringLiteral:
1740                    case SyntaxKind.RegularExpressionLiteral:
1741                    case SyntaxKind.NoSubstitutionTemplateLiteral:
1742                        return emitLiteral(node as LiteralExpression, /*jsxAttributeEscape*/ false);
1743
1744                    // Identifiers
1745                    case SyntaxKind.Identifier:
1746                        return emitIdentifier(node as Identifier);
1747                    case SyntaxKind.PrivateIdentifier:
1748                        return emitPrivateIdentifier(node as PrivateIdentifier);
1749
1750                    // Expressions
1751                    case SyntaxKind.ArrayLiteralExpression:
1752                        return emitArrayLiteralExpression(node as ArrayLiteralExpression);
1753                    case SyntaxKind.ObjectLiteralExpression:
1754                        return emitObjectLiteralExpression(node as ObjectLiteralExpression);
1755                    case SyntaxKind.PropertyAccessExpression:
1756                        return emitPropertyAccessExpression(node as PropertyAccessExpression);
1757                    case SyntaxKind.ElementAccessExpression:
1758                        return emitElementAccessExpression(node as ElementAccessExpression);
1759                    case SyntaxKind.CallExpression:
1760                        return emitCallExpression(node as CallExpression);
1761                    case SyntaxKind.NewExpression:
1762                        return emitNewExpression(node as NewExpression);
1763                    case SyntaxKind.TaggedTemplateExpression:
1764                        return emitTaggedTemplateExpression(node as TaggedTemplateExpression);
1765                    case SyntaxKind.TypeAssertionExpression:
1766                        return emitTypeAssertionExpression(node as TypeAssertion);
1767                    case SyntaxKind.ParenthesizedExpression:
1768                        return emitParenthesizedExpression(node as ParenthesizedExpression);
1769                    case SyntaxKind.FunctionExpression:
1770                        return emitFunctionExpression(node as FunctionExpression);
1771                    case SyntaxKind.ArrowFunction:
1772                        return emitArrowFunction(node as ArrowFunction);
1773                    case SyntaxKind.DeleteExpression:
1774                        return emitDeleteExpression(node as DeleteExpression);
1775                    case SyntaxKind.TypeOfExpression:
1776                        return emitTypeOfExpression(node as TypeOfExpression);
1777                    case SyntaxKind.VoidExpression:
1778                        return emitVoidExpression(node as VoidExpression);
1779                    case SyntaxKind.AwaitExpression:
1780                        return emitAwaitExpression(node as AwaitExpression);
1781                    case SyntaxKind.PrefixUnaryExpression:
1782                        return emitPrefixUnaryExpression(node as PrefixUnaryExpression);
1783                    case SyntaxKind.PostfixUnaryExpression:
1784                        return emitPostfixUnaryExpression(node as PostfixUnaryExpression);
1785                    case SyntaxKind.BinaryExpression:
1786                        return emitBinaryExpression(node as BinaryExpression);
1787                    case SyntaxKind.ConditionalExpression:
1788                        return emitConditionalExpression(node as ConditionalExpression);
1789                    case SyntaxKind.TemplateExpression:
1790                        return emitTemplateExpression(node as TemplateExpression);
1791                    case SyntaxKind.YieldExpression:
1792                        return emitYieldExpression(node as YieldExpression);
1793                    case SyntaxKind.SpreadElement:
1794                        return emitSpreadElement(node as SpreadElement);
1795                    case SyntaxKind.ClassExpression:
1796                        return emitClassExpression(node as ClassExpression);
1797                    case SyntaxKind.OmittedExpression:
1798                        return;
1799                    case SyntaxKind.AsExpression:
1800                        return emitAsExpression(node as AsExpression);
1801                    case SyntaxKind.NonNullExpression:
1802                        return emitNonNullExpression(node as NonNullExpression);
1803                    case SyntaxKind.ExpressionWithTypeArguments:
1804                        return emitExpressionWithTypeArguments(node as ExpressionWithTypeArguments);
1805                    case SyntaxKind.SatisfiesExpression:
1806                        return emitSatisfiesExpression(node as SatisfiesExpression);
1807                    case SyntaxKind.MetaProperty:
1808                        return emitMetaProperty(node as MetaProperty);
1809                    case SyntaxKind.SyntheticExpression:
1810                        return Debug.fail("SyntheticExpression should never be printed.");
1811
1812                    // JSX
1813                    case SyntaxKind.JsxElement:
1814                        return emitJsxElement(node as JsxElement);
1815                    case SyntaxKind.JsxSelfClosingElement:
1816                        return emitJsxSelfClosingElement(node as JsxSelfClosingElement);
1817                    case SyntaxKind.JsxFragment:
1818                        return emitJsxFragment(node as JsxFragment);
1819
1820                    // Synthesized list
1821                    case SyntaxKind.SyntaxList:
1822                        return Debug.fail("SyntaxList should not be printed");
1823
1824                    // Transformation nodes
1825                    case SyntaxKind.NotEmittedStatement:
1826                        return;
1827                    case SyntaxKind.PartiallyEmittedExpression:
1828                        return emitPartiallyEmittedExpression(node as PartiallyEmittedExpression);
1829                    case SyntaxKind.CommaListExpression:
1830                        return emitCommaList(node as CommaListExpression);
1831                    case SyntaxKind.MergeDeclarationMarker:
1832                    case SyntaxKind.EndOfDeclarationMarker:
1833                        return;
1834                    case SyntaxKind.SyntheticReferenceExpression:
1835                        return Debug.fail("SyntheticReferenceExpression should not be printed");
1836                }
1837            }
1838            if (isKeyword(node.kind)) return writeTokenNode(node, writeKeyword);
1839            if (isTokenKind(node.kind)) return writeTokenNode(node, writePunctuation);
1840            // skip emit root component expression
1841            if (findAncestor(node, (elelment: Node) => {
1842                return isEtsComponentExpression(elelment)
1843            })) {
1844                return;
1845            }
1846            Debug.fail(`Unhandled SyntaxKind: ${Debug.formatSyntaxKind(node.kind)}.`);
1847        }
1848
1849        function emitMappedTypeParameter(node: TypeParameterDeclaration): void {
1850            emit(node.name);
1851            writeSpace();
1852            writeKeyword("in");
1853            writeSpace();
1854            emit(node.constraint);
1855        }
1856
1857        function pipelineEmitWithSubstitution(hint: EmitHint, node: Node) {
1858            const pipelinePhase = getNextPipelinePhase(PipelinePhase.Substitution, hint, node);
1859            Debug.assertIsDefined(lastSubstitution);
1860            node = lastSubstitution;
1861            lastSubstitution = undefined;
1862            pipelinePhase(hint, node);
1863        }
1864
1865        function getHelpersFromBundledSourceFiles(bundle: Bundle): string[] | undefined {
1866            let result: string[] | undefined;
1867            if (moduleKind === ModuleKind.None || printerOptions.noEmitHelpers) {
1868                return undefined;
1869            }
1870            const bundledHelpers = new Map<string, boolean>();
1871            for (const sourceFile of bundle.sourceFiles) {
1872                const shouldSkip = getExternalHelpersModuleName(sourceFile) !== undefined;
1873                const helpers = getSortedEmitHelpers(sourceFile);
1874                if (!helpers) continue;
1875                for (const helper of helpers) {
1876                    if (!helper.scoped && !shouldSkip && !bundledHelpers.get(helper.name)) {
1877                        bundledHelpers.set(helper.name, true);
1878                        (result || (result = [])).push(helper.name);
1879                    }
1880                }
1881            }
1882
1883            return result;
1884        }
1885
1886        function emitHelpers(node: Node) {
1887            let helpersEmitted = false;
1888            const bundle = node.kind === SyntaxKind.Bundle ? node as Bundle : undefined;
1889            if (bundle && moduleKind === ModuleKind.None) {
1890                return;
1891            }
1892            const numPrepends = bundle ? bundle.prepends.length : 0;
1893            const numNodes = bundle ? bundle.sourceFiles.length + numPrepends : 1;
1894            for (let i = 0; i < numNodes; i++) {
1895                const currentNode = bundle ? i < numPrepends ? bundle.prepends[i] : bundle.sourceFiles[i - numPrepends] : node;
1896                const sourceFile = isSourceFile(currentNode) ? currentNode : isUnparsedSource(currentNode) ? undefined : currentSourceFile;
1897                const shouldSkip = printerOptions.noEmitHelpers || (!!sourceFile && hasRecordedExternalHelpers(sourceFile));
1898                const shouldBundle = (isSourceFile(currentNode) || isUnparsedSource(currentNode)) && !isOwnFileEmit;
1899                const helpers = isUnparsedSource(currentNode) ? currentNode.helpers : getSortedEmitHelpers(currentNode);
1900                if (helpers) {
1901                    for (const helper of helpers) {
1902                        if (!helper.scoped) {
1903                            // Skip the helper if it can be skipped and the noEmitHelpers compiler
1904                            // option is set, or if it can be imported and the importHelpers compiler
1905                            // option is set.
1906                            if (shouldSkip) continue;
1907
1908                            // Skip the helper if it can be bundled but hasn't already been emitted and we
1909                            // are emitting a bundled module.
1910                            if (shouldBundle) {
1911                                if (bundledHelpers.get(helper.name)) {
1912                                    continue;
1913                                }
1914
1915                                bundledHelpers.set(helper.name, true);
1916                            }
1917                        }
1918                        else if (bundle) {
1919                            // Skip the helper if it is scoped and we are emitting bundled helpers
1920                            continue;
1921                        }
1922                        const pos = getTextPosWithWriteLine();
1923                        if (typeof helper.text === "string") {
1924                            writeLines(helper.text);
1925                        }
1926                        else {
1927                            writeLines(helper.text(makeFileLevelOptimisticUniqueName));
1928                        }
1929                        if (bundleFileInfo) bundleFileInfo.sections.push({ pos, end: writer.getTextPos(), kind: BundleFileSectionKind.EmitHelpers, data: helper.name });
1930                        helpersEmitted = true;
1931                    }
1932                }
1933            }
1934
1935            return helpersEmitted;
1936        }
1937
1938        function getSortedEmitHelpers(node: Node) {
1939            const helpers = getEmitHelpers(node);
1940            return helpers && stableSort(helpers, compareEmitHelpers);
1941        }
1942
1943        //
1944        // Literals/Pseudo-literals
1945        //
1946
1947        // SyntaxKind.NumericLiteral
1948        // SyntaxKind.BigIntLiteral
1949        function emitNumericOrBigIntLiteral(node: NumericLiteral | BigIntLiteral) {
1950            emitLiteral(node, /*jsxAttributeEscape*/ false);
1951        }
1952
1953        // SyntaxKind.StringLiteral
1954        // SyntaxKind.RegularExpressionLiteral
1955        // SyntaxKind.NoSubstitutionTemplateLiteral
1956        // SyntaxKind.TemplateHead
1957        // SyntaxKind.TemplateMiddle
1958        // SyntaxKind.TemplateTail
1959        function emitLiteral(node: LiteralLikeNode, jsxAttributeEscape: boolean) {
1960            const text = getLiteralTextOfNode(node, printerOptions.neverAsciiEscape, jsxAttributeEscape);
1961            if ((printerOptions.sourceMap || printerOptions.inlineSourceMap)
1962                && (node.kind === SyntaxKind.StringLiteral || isTemplateLiteralKind(node.kind))) {
1963                writeLiteral(text);
1964            }
1965            else {
1966                // Quick info expects all literals to be called with writeStringLiteral, as there's no specific type for numberLiterals
1967                writeStringLiteral(text);
1968            }
1969        }
1970
1971        // SyntaxKind.UnparsedSource
1972        // SyntaxKind.UnparsedPrepend
1973        function emitUnparsedSourceOrPrepend(unparsed: UnparsedSource | UnparsedPrepend) {
1974            for (const text of unparsed.texts) {
1975                writeLine();
1976                emit(text);
1977            }
1978        }
1979
1980        // SyntaxKind.UnparsedPrologue
1981        // SyntaxKind.UnparsedText
1982        // SyntaxKind.UnparsedInternal
1983        // SyntaxKind.UnparsedSyntheticReference
1984        function writeUnparsedNode(unparsed: UnparsedNode) {
1985            writer.rawWrite(unparsed.parent.text.substring(unparsed.pos, unparsed.end));
1986        }
1987
1988        // SyntaxKind.UnparsedText
1989        // SyntaxKind.UnparsedInternal
1990        function emitUnparsedTextLike(unparsed: UnparsedTextLike) {
1991            const pos = getTextPosWithWriteLine();
1992            writeUnparsedNode(unparsed);
1993            if (bundleFileInfo) {
1994                updateOrPushBundleFileTextLike(
1995                    pos,
1996                    writer.getTextPos(),
1997                    unparsed.kind === SyntaxKind.UnparsedText ?
1998                        BundleFileSectionKind.Text :
1999                        BundleFileSectionKind.Internal
2000                );
2001            }
2002        }
2003
2004        // SyntaxKind.UnparsedSyntheticReference
2005        function emitUnparsedSyntheticReference(unparsed: UnparsedSyntheticReference) {
2006            const pos = getTextPosWithWriteLine();
2007            writeUnparsedNode(unparsed);
2008            if (bundleFileInfo) {
2009                const section = clone(unparsed.section);
2010                section.pos = pos;
2011                section.end = writer.getTextPos();
2012                bundleFileInfo.sections.push(section);
2013            }
2014        }
2015
2016        //
2017        // Snippet Elements
2018        //
2019
2020        function emitSnippetNode(hint: EmitHint, node: Node, snippet: SnippetElement) {
2021            switch (snippet.kind) {
2022                case SnippetKind.Placeholder:
2023                    emitPlaceholder(hint, node, snippet);
2024                    break;
2025                case SnippetKind.TabStop:
2026                    emitTabStop(hint, node, snippet);
2027                    break;
2028            }
2029        }
2030
2031        function emitPlaceholder(hint: EmitHint, node: Node, snippet: Placeholder) {
2032            nonEscapingWrite(`\$\{${snippet.order}:`); // `${2:`
2033            pipelineEmitWithHintWorker(hint, node, /*allowSnippets*/ false); // `...`
2034            nonEscapingWrite(`\}`); // `}`
2035            // `${2:...}`
2036        }
2037
2038        function emitTabStop(hint: EmitHint, node: Node, snippet: TabStop) {
2039            // A tab stop should only be attached to an empty node, i.e. a node that doesn't emit any text.
2040            Debug.assert(node.kind === SyntaxKind.EmptyStatement,
2041                `A tab stop cannot be attached to a node of kind ${Debug.formatSyntaxKind(node.kind)}.`);
2042            Debug.assert(hint !== EmitHint.EmbeddedStatement,
2043                `A tab stop cannot be attached to an embedded statement.`);
2044            nonEscapingWrite(`\$${snippet.order}`);
2045        }
2046
2047        //
2048        // Identifiers
2049        //
2050
2051        function emitIdentifier(node: Identifier) {
2052            const writeText = node.symbol ? writeSymbol : write;
2053            writeText(getTextOfNode(node, /*includeTrivia*/ false), node.symbol);
2054            emitList(node, node.typeArguments, ListFormat.TypeParameters); // Call emitList directly since it could be an array of TypeParameterDeclarations _or_ type arguments
2055        }
2056
2057        //
2058        // Names
2059        //
2060
2061        function emitPrivateIdentifier(node: PrivateIdentifier) {
2062            const writeText = node.symbol ? writeSymbol : write;
2063            writeText(getTextOfNode(node, /*includeTrivia*/ false), node.symbol);
2064        }
2065
2066
2067        function emitQualifiedName(node: QualifiedName) {
2068            emitEntityName(node.left);
2069            writePunctuation(".");
2070            emit(node.right);
2071        }
2072
2073        function emitEntityName(node: EntityName) {
2074            if (node.kind === SyntaxKind.Identifier) {
2075                emitExpression(node);
2076            }
2077            else {
2078                emit(node);
2079            }
2080        }
2081
2082        function emitComputedPropertyName(node: ComputedPropertyName) {
2083            writePunctuation("[");
2084            emitExpression(node.expression, parenthesizer.parenthesizeExpressionOfComputedPropertyName);
2085            writePunctuation("]");
2086        }
2087
2088        //
2089        // Signature elements
2090        //
2091
2092        function emitTypeParameter(node: TypeParameterDeclaration) {
2093            emitModifiers(node, node.modifiers);
2094            emit(node.name);
2095            if (node.constraint) {
2096                writeSpace();
2097                writeKeyword("extends");
2098                writeSpace();
2099                emit(node.constraint);
2100            }
2101            if (node.default) {
2102                writeSpace();
2103                writeOperator("=");
2104                writeSpace();
2105                emit(node.default);
2106            }
2107        }
2108
2109        function emitParameter(node: ParameterDeclaration) {
2110            emitDecoratorsAndModifiers(node, node.modifiers);
2111            emit(node.dotDotDotToken);
2112            emitNodeWithWriter(node.name, writeParameter);
2113            emit(node.questionToken);
2114            if (node.parent && node.parent.kind === SyntaxKind.JSDocFunctionType && !node.name) {
2115                emit(node.type);
2116            }
2117            else {
2118                emitTypeAnnotation(node.type);
2119            }
2120            // The comment position has to fallback to any present node within the parameterdeclaration because as it turns out, the parser can make parameter declarations with _just_ an initializer.
2121            emitInitializer(node.initializer, node.type ? node.type.end : node.questionToken ? node.questionToken.end : node.name ? node.name.end : node.modifiers ? node.modifiers.end : node.pos, node, parenthesizer.parenthesizeExpressionForDisallowedComma);
2122        }
2123
2124        function emitDecorator(decorator: Decorator) {
2125            writePunctuation("@");
2126            emitExpression(decorator.expression, parenthesizer.parenthesizeLeftSideOfAccess);
2127        }
2128
2129        //
2130        // Type members
2131        //
2132
2133        function emitPropertySignature(node: PropertySignature) {
2134            emitModifiers(node, node.modifiers);
2135            emitNodeWithWriter(node.name, writeProperty);
2136            emit(node.questionToken);
2137            emitTypeAnnotation(node.type);
2138            writeTrailingSemicolon();
2139        }
2140
2141        function emitPropertyDeclaration(node: PropertyDeclaration) {
2142            emitDecoratorsAndModifiers(node, node.modifiers);
2143            emit(node.name);
2144            emit(node.questionToken);
2145            emit(node.exclamationToken);
2146            emitTypeAnnotation(node.type);
2147            emitInitializer(node.initializer, node.type ? node.type.end : node.questionToken ? node.questionToken.end : node.name.end, node);
2148            writeTrailingSemicolon();
2149        }
2150
2151        function emitAnnotationPropertyDeclaration(node: AnnotationPropertyDeclaration) {
2152            emit(node.name);
2153            emitTypeAnnotation(node.type);
2154            emitInitializer(node.initializer, node.type ? node.type.end : node.name.end, node);
2155            writeTrailingSemicolon();
2156        }
2157
2158        function emitMethodSignature(node: MethodSignature) {
2159            pushNameGenerationScope(node);
2160            emitModifiers(node, node.modifiers);
2161            emit(node.name);
2162            emit(node.questionToken);
2163            emitTypeParameters(node, node.typeParameters);
2164            emitParameters(node, node.parameters);
2165            emitTypeAnnotation(node.type);
2166            writeTrailingSemicolon();
2167            popNameGenerationScope(node);
2168        }
2169
2170        function emitMethodDeclaration(node: MethodDeclaration) {
2171            emitDecoratorsAndModifiers(node, node.modifiers);
2172            emit(node.asteriskToken);
2173            emit(node.name);
2174            emit(node.questionToken);
2175            emitSignatureAndBody(node, emitSignatureHead);
2176        }
2177
2178        function emitClassStaticBlockDeclaration(node: ClassStaticBlockDeclaration) {
2179            writeKeyword("static");
2180            emitBlockFunctionBody(node.body);
2181        }
2182
2183        function emitConstructor(node: ConstructorDeclaration) {
2184            emitModifiers(node, node.modifiers);
2185            writeKeyword("constructor");
2186            emitSignatureAndBody(node, emitSignatureHead);
2187        }
2188
2189        function emitAccessorDeclaration(node: AccessorDeclaration) {
2190            emitDecoratorsAndModifiers(node, node.modifiers);
2191            writeKeyword(node.kind === SyntaxKind.GetAccessor ? "get" : "set");
2192            writeSpace();
2193            emit(node.name);
2194            emitSignatureAndBody(node, emitSignatureHead);
2195        }
2196
2197        function emitCallSignature(node: CallSignatureDeclaration) {
2198            pushNameGenerationScope(node);
2199            emitTypeParameters(node, node.typeParameters);
2200            emitParameters(node, node.parameters);
2201            emitTypeAnnotation(node.type);
2202            writeTrailingSemicolon();
2203            popNameGenerationScope(node);
2204        }
2205
2206        function emitConstructSignature(node: ConstructSignatureDeclaration) {
2207            pushNameGenerationScope(node);
2208            writeKeyword("new");
2209            writeSpace();
2210            emitTypeParameters(node, node.typeParameters);
2211            emitParameters(node, node.parameters);
2212            emitTypeAnnotation(node.type);
2213            writeTrailingSemicolon();
2214            popNameGenerationScope(node);
2215        }
2216
2217        function emitIndexSignature(node: IndexSignatureDeclaration) {
2218            emitModifiers(node, node.modifiers);
2219            emitParametersForIndexSignature(node, node.parameters);
2220            emitTypeAnnotation(node.type);
2221            writeTrailingSemicolon();
2222        }
2223
2224        function emitTemplateTypeSpan(node: TemplateLiteralTypeSpan) {
2225            emit(node.type);
2226            emit(node.literal);
2227        }
2228
2229        function emitSemicolonClassElement() {
2230            writeTrailingSemicolon();
2231        }
2232
2233        //
2234        // Types
2235        //
2236
2237        function emitTypePredicate(node: TypePredicateNode) {
2238            if (node.assertsModifier) {
2239                emit(node.assertsModifier);
2240                writeSpace();
2241            }
2242            emit(node.parameterName);
2243            if (node.type) {
2244                writeSpace();
2245                writeKeyword("is");
2246                writeSpace();
2247                emit(node.type);
2248            }
2249        }
2250
2251        function emitTypeReference(node: TypeReferenceNode) {
2252            emit(node.typeName);
2253            emitTypeArguments(node, node.typeArguments);
2254        }
2255
2256        function emitFunctionType(node: FunctionTypeNode) {
2257            pushNameGenerationScope(node);
2258            emitTypeParameters(node, node.typeParameters);
2259            emitParametersForArrow(node, node.parameters);
2260            writeSpace();
2261            writePunctuation("=>");
2262            writeSpace();
2263            emit(node.type);
2264            popNameGenerationScope(node);
2265        }
2266
2267        function emitJSDocFunctionType(node: JSDocFunctionType) {
2268            writeKeyword("function");
2269            emitParameters(node, node.parameters);
2270            writePunctuation(":");
2271            emit(node.type);
2272        }
2273
2274
2275        function emitJSDocNullableType(node: JSDocNullableType) {
2276            writePunctuation("?");
2277            emit(node.type);
2278        }
2279
2280        function emitJSDocNonNullableType(node: JSDocNonNullableType) {
2281            writePunctuation("!");
2282            emit(node.type);
2283        }
2284
2285        function emitJSDocOptionalType(node: JSDocOptionalType) {
2286            emit(node.type);
2287            writePunctuation("=");
2288        }
2289
2290        function emitConstructorType(node: ConstructorTypeNode) {
2291            pushNameGenerationScope(node);
2292            emitModifiers(node, node.modifiers);
2293            writeKeyword("new");
2294            writeSpace();
2295            emitTypeParameters(node, node.typeParameters);
2296            emitParameters(node, node.parameters);
2297            writeSpace();
2298            writePunctuation("=>");
2299            writeSpace();
2300            emit(node.type);
2301            popNameGenerationScope(node);
2302        }
2303
2304        function emitTypeQuery(node: TypeQueryNode) {
2305            writeKeyword("typeof");
2306            writeSpace();
2307            emit(node.exprName);
2308            emitTypeArguments(node, node.typeArguments);
2309        }
2310
2311        function emitTypeLiteral(node: TypeLiteralNode) {
2312            writePunctuation("{");
2313            const flags = getEmitFlags(node) & EmitFlags.SingleLine ? ListFormat.SingleLineTypeLiteralMembers : ListFormat.MultiLineTypeLiteralMembers;
2314            emitList(node, node.members, flags | ListFormat.NoSpaceIfEmpty);
2315            writePunctuation("}");
2316        }
2317
2318        function emitArrayType(node: ArrayTypeNode) {
2319            emit(node.elementType, parenthesizer.parenthesizeNonArrayTypeOfPostfixType);
2320            writePunctuation("[");
2321            writePunctuation("]");
2322        }
2323
2324        function emitRestOrJSDocVariadicType(node: RestTypeNode | JSDocVariadicType) {
2325            writePunctuation("...");
2326            emit(node.type);
2327        }
2328
2329        function emitTupleType(node: TupleTypeNode) {
2330            emitTokenWithComment(SyntaxKind.OpenBracketToken, node.pos, writePunctuation, node);
2331            const flags = getEmitFlags(node) & EmitFlags.SingleLine ? ListFormat.SingleLineTupleTypeElements : ListFormat.MultiLineTupleTypeElements;
2332            emitList(node, node.elements, flags | ListFormat.NoSpaceIfEmpty, parenthesizer.parenthesizeElementTypeOfTupleType);
2333            emitTokenWithComment(SyntaxKind.CloseBracketToken, node.elements.end, writePunctuation, node);
2334        }
2335
2336        function emitNamedTupleMember(node: NamedTupleMember) {
2337            emit(node.dotDotDotToken);
2338            emit(node.name);
2339            emit(node.questionToken);
2340            emitTokenWithComment(SyntaxKind.ColonToken, node.name.end, writePunctuation, node);
2341            writeSpace();
2342            emit(node.type);
2343        }
2344
2345        function emitOptionalType(node: OptionalTypeNode) {
2346            emit(node.type, parenthesizer.parenthesizeTypeOfOptionalType);
2347            writePunctuation("?");
2348        }
2349
2350        function emitUnionType(node: UnionTypeNode) {
2351            emitList(node, node.types, ListFormat.UnionTypeConstituents, parenthesizer.parenthesizeConstituentTypeOfUnionType);
2352        }
2353
2354        function emitIntersectionType(node: IntersectionTypeNode) {
2355            emitList(node, node.types, ListFormat.IntersectionTypeConstituents, parenthesizer.parenthesizeConstituentTypeOfIntersectionType);
2356        }
2357
2358        function emitConditionalType(node: ConditionalTypeNode) {
2359            emit(node.checkType, parenthesizer.parenthesizeCheckTypeOfConditionalType);
2360            writeSpace();
2361            writeKeyword("extends");
2362            writeSpace();
2363            emit(node.extendsType, parenthesizer.parenthesizeExtendsTypeOfConditionalType);
2364            writeSpace();
2365            writePunctuation("?");
2366            writeSpace();
2367            emit(node.trueType);
2368            writeSpace();
2369            writePunctuation(":");
2370            writeSpace();
2371            emit(node.falseType);
2372        }
2373
2374        function emitInferType(node: InferTypeNode) {
2375            writeKeyword("infer");
2376            writeSpace();
2377            emit(node.typeParameter);
2378        }
2379
2380        function emitParenthesizedType(node: ParenthesizedTypeNode) {
2381            writePunctuation("(");
2382            emit(node.type);
2383            writePunctuation(")");
2384        }
2385
2386        function emitThisType() {
2387            writeKeyword("this");
2388        }
2389
2390        function emitTypeOperator(node: TypeOperatorNode) {
2391            writeTokenText(node.operator, writeKeyword);
2392            writeSpace();
2393
2394            const parenthesizerRule = node.operator === SyntaxKind.ReadonlyKeyword ?
2395                parenthesizer.parenthesizeOperandOfReadonlyTypeOperator :
2396                parenthesizer.parenthesizeOperandOfTypeOperator;
2397            emit(node.type, parenthesizerRule);
2398        }
2399
2400        function emitIndexedAccessType(node: IndexedAccessTypeNode) {
2401            emit(node.objectType, parenthesizer.parenthesizeNonArrayTypeOfPostfixType);
2402            writePunctuation("[");
2403            emit(node.indexType);
2404            writePunctuation("]");
2405        }
2406
2407        function emitMappedType(node: MappedTypeNode) {
2408            const emitFlags = getEmitFlags(node);
2409            writePunctuation("{");
2410            if (emitFlags & EmitFlags.SingleLine) {
2411                writeSpace();
2412            }
2413            else {
2414                writeLine();
2415                increaseIndent();
2416            }
2417            if (node.readonlyToken) {
2418                emit(node.readonlyToken);
2419                if (node.readonlyToken.kind !== SyntaxKind.ReadonlyKeyword) {
2420                    writeKeyword("readonly");
2421                }
2422                writeSpace();
2423            }
2424            writePunctuation("[");
2425
2426            pipelineEmit(EmitHint.MappedTypeParameter, node.typeParameter);
2427            if (node.nameType) {
2428                writeSpace();
2429                writeKeyword("as");
2430                writeSpace();
2431                emit(node.nameType);
2432            }
2433
2434            writePunctuation("]");
2435            if (node.questionToken) {
2436                emit(node.questionToken);
2437                if (node.questionToken.kind !== SyntaxKind.QuestionToken) {
2438                    writePunctuation("?");
2439                }
2440            }
2441            writePunctuation(":");
2442            writeSpace();
2443            emit(node.type);
2444            writeTrailingSemicolon();
2445            if (emitFlags & EmitFlags.SingleLine) {
2446                writeSpace();
2447            }
2448            else {
2449                writeLine();
2450                decreaseIndent();
2451            }
2452            emitList(node, node.members, ListFormat.PreserveLines);
2453            writePunctuation("}");
2454        }
2455
2456        function emitLiteralType(node: LiteralTypeNode) {
2457            emitExpression(node.literal);
2458        }
2459
2460        function emitTemplateType(node: TemplateLiteralTypeNode) {
2461            emit(node.head);
2462            emitList(node, node.templateSpans, ListFormat.TemplateExpressionSpans);
2463        }
2464
2465        function emitImportTypeNode(node: ImportTypeNode) {
2466            if (node.isTypeOf) {
2467                writeKeyword("typeof");
2468                writeSpace();
2469            }
2470            writeKeyword("import");
2471            writePunctuation("(");
2472            emit(node.argument);
2473            if (node.assertions) {
2474                writePunctuation(",");
2475                writeSpace();
2476                writePunctuation("{");
2477                writeSpace();
2478                writeKeyword("assert");
2479                writePunctuation(":");
2480                writeSpace();
2481                const elements = node.assertions.assertClause.elements;
2482                emitList(node.assertions.assertClause, elements, ListFormat.ImportClauseEntries);
2483                writeSpace();
2484                writePunctuation("}");
2485            }
2486            writePunctuation(")");
2487            if (node.qualifier) {
2488                writePunctuation(".");
2489                emit(node.qualifier);
2490            }
2491            emitTypeArguments(node, node.typeArguments);
2492        }
2493
2494        //
2495        // Binding patterns
2496        //
2497
2498        function emitObjectBindingPattern(node: ObjectBindingPattern) {
2499            writePunctuation("{");
2500            emitList(node, node.elements, ListFormat.ObjectBindingPatternElements);
2501            writePunctuation("}");
2502        }
2503
2504        function emitArrayBindingPattern(node: ArrayBindingPattern) {
2505            writePunctuation("[");
2506            emitList(node, node.elements, ListFormat.ArrayBindingPatternElements);
2507            writePunctuation("]");
2508        }
2509
2510        function emitBindingElement(node: BindingElement) {
2511            emit(node.dotDotDotToken);
2512            if (node.propertyName) {
2513                emit(node.propertyName);
2514                writePunctuation(":");
2515                writeSpace();
2516            }
2517            emit(node.name);
2518            emitInitializer(node.initializer, node.name.end, node, parenthesizer.parenthesizeExpressionForDisallowedComma);
2519        }
2520
2521        //
2522        // Expressions
2523        //
2524
2525        function emitArrayLiteralExpression(node: ArrayLiteralExpression) {
2526            const elements = node.elements;
2527            const preferNewLine = node.multiLine ? ListFormat.PreferNewLine : ListFormat.None;
2528            emitExpressionList(node, elements, ListFormat.ArrayLiteralExpressionElements | preferNewLine, parenthesizer.parenthesizeExpressionForDisallowedComma);
2529        }
2530
2531        function emitObjectLiteralExpression(node: ObjectLiteralExpression) {
2532            forEach(node.properties, generateMemberNames);
2533
2534            const indentedFlag = getEmitFlags(node) & EmitFlags.Indented;
2535            if (indentedFlag) {
2536                increaseIndent();
2537            }
2538
2539            const preferNewLine = node.multiLine ? ListFormat.PreferNewLine : ListFormat.None;
2540            const allowTrailingComma = currentSourceFile && currentSourceFile.languageVersion >= ScriptTarget.ES5 && !isJsonSourceFile(currentSourceFile) ? ListFormat.AllowTrailingComma : ListFormat.None;
2541            emitList(node, node.properties, ListFormat.ObjectLiteralExpressionProperties | allowTrailingComma | preferNewLine);
2542
2543            if (indentedFlag) {
2544                decreaseIndent();
2545            }
2546        }
2547
2548        function emitPropertyAccessExpression(node: PropertyAccessExpression) {
2549            emitExpression(node.expression, parenthesizer.parenthesizeLeftSideOfAccess);
2550            const token = node.questionDotToken || setTextRangePosEnd(factory.createToken(SyntaxKind.DotToken) as DotToken, node.expression.end, node.name.pos);
2551            const linesBeforeDot = getLinesBetweenNodes(node, node.expression, token);
2552            const linesAfterDot = getLinesBetweenNodes(node, token, node.name);
2553
2554            writeLinesAndIndent(linesBeforeDot, /*writeSpaceIfNotIndenting*/ false);
2555
2556            const shouldEmitDotDot =
2557                token.kind !== SyntaxKind.QuestionDotToken &&
2558                mayNeedDotDotForPropertyAccess(node.expression) &&
2559                !writer.hasTrailingComment() &&
2560                !writer.hasTrailingWhitespace();
2561
2562            if (shouldEmitDotDot) {
2563                writePunctuation(".");
2564            }
2565
2566            if (node.questionDotToken) {
2567                emit(token);
2568            }
2569            else {
2570                emitTokenWithComment(token.kind, node.expression.end, writePunctuation, node);
2571            }
2572            writeLinesAndIndent(linesAfterDot, /*writeSpaceIfNotIndenting*/ false);
2573            emit(node.name);
2574            decreaseIndentIf(linesBeforeDot, linesAfterDot);
2575        }
2576
2577        // 1..toString is a valid property access, emit a dot after the literal
2578        // Also emit a dot if expression is a integer const enum value - it will appear in generated code as numeric literal
2579        function mayNeedDotDotForPropertyAccess(expression: Expression) {
2580            expression = skipPartiallyEmittedExpressions(expression);
2581            if (isNumericLiteral(expression)) {
2582                // check if numeric literal is a decimal literal that was originally written with a dot
2583                const text = getLiteralTextOfNode(expression as LiteralExpression, /*neverAsciiEscape*/ true, /*jsxAttributeEscape*/ false);
2584                // If he number will be printed verbatim and it doesn't already contain a dot, add one
2585                // if the expression doesn't have any comments that will be emitted.
2586                return !expression.numericLiteralFlags && !stringContains(text, tokenToString(SyntaxKind.DotToken)!);
2587            }
2588            else if (isAccessExpression(expression)) {
2589                // check if constant enum value is integer
2590                const constantValue = getConstantValue(expression);
2591                // isFinite handles cases when constantValue is undefined
2592                return typeof constantValue === "number" && isFinite(constantValue)
2593                    && Math.floor(constantValue) === constantValue;
2594            }
2595        }
2596
2597        function emitElementAccessExpression(node: ElementAccessExpression) {
2598            emitExpression(node.expression, parenthesizer.parenthesizeLeftSideOfAccess);
2599            emit(node.questionDotToken);
2600            emitTokenWithComment(SyntaxKind.OpenBracketToken, node.expression.end, writePunctuation, node);
2601            emitExpression(node.argumentExpression);
2602            emitTokenWithComment(SyntaxKind.CloseBracketToken, node.argumentExpression.end, writePunctuation, node);
2603        }
2604
2605        function emitCallExpression(node: CallExpression) {
2606            const indirectCall = getEmitFlags(node) & EmitFlags.IndirectCall;
2607            if (indirectCall) {
2608                writePunctuation("(");
2609                writeLiteral("0");
2610                writePunctuation(",");
2611                writeSpace();
2612            }
2613            emitExpression(node.expression, parenthesizer.parenthesizeLeftSideOfAccess);
2614            if (indirectCall) {
2615                writePunctuation(")");
2616            }
2617            emit(node.questionDotToken);
2618            emitTypeArguments(node, node.typeArguments);
2619            emitExpressionList(node, node.arguments, ListFormat.CallExpressionArguments, parenthesizer.parenthesizeExpressionForDisallowedComma);
2620        }
2621
2622        function emitNewExpression(node: NewExpression) {
2623            emitTokenWithComment(SyntaxKind.NewKeyword, node.pos, writeKeyword, node);
2624            writeSpace();
2625            emitExpression(node.expression, parenthesizer.parenthesizeExpressionOfNew);
2626            emitTypeArguments(node, node.typeArguments);
2627            emitExpressionList(node, node.arguments, ListFormat.NewExpressionArguments, parenthesizer.parenthesizeExpressionForDisallowedComma);
2628        }
2629
2630        function emitTaggedTemplateExpression(node: TaggedTemplateExpression) {
2631            const indirectCall = getEmitFlags(node) & EmitFlags.IndirectCall;
2632            if (indirectCall) {
2633                writePunctuation("(");
2634                writeLiteral("0");
2635                writePunctuation(",");
2636                writeSpace();
2637            }
2638            emitExpression(node.tag, parenthesizer.parenthesizeLeftSideOfAccess);
2639            if (indirectCall) {
2640                writePunctuation(")");
2641            }
2642            emitTypeArguments(node, node.typeArguments);
2643            writeSpace();
2644            emitExpression(node.template);
2645        }
2646
2647        function emitTypeAssertionExpression(node: TypeAssertion) {
2648            writePunctuation("<");
2649            emit(node.type);
2650            writePunctuation(">");
2651            emitExpression(node.expression, parenthesizer.parenthesizeOperandOfPrefixUnary);
2652        }
2653
2654        function emitParenthesizedExpression(node: ParenthesizedExpression) {
2655            const openParenPos = emitTokenWithComment(SyntaxKind.OpenParenToken, node.pos, writePunctuation, node);
2656            const indented = writeLineSeparatorsAndIndentBefore(node.expression, node);
2657            emitExpression(node.expression, /*parenthesizerRules*/ undefined);
2658            writeLineSeparatorsAfter(node.expression, node);
2659            decreaseIndentIf(indented);
2660            emitTokenWithComment(SyntaxKind.CloseParenToken, node.expression ? node.expression.end : openParenPos, writePunctuation, node);
2661        }
2662
2663        function emitFunctionExpression(node: FunctionExpression) {
2664            generateNameIfNeeded(node.name);
2665            emitFunctionDeclarationOrExpression(node);
2666        }
2667
2668        function emitArrowFunction(node: ArrowFunction) {
2669            emitModifiers(node, node.modifiers);
2670            emitSignatureAndBody(node, emitArrowFunctionHead);
2671        }
2672
2673        function emitArrowFunctionHead(node: ArrowFunction) {
2674            emitTypeParameters(node, node.typeParameters);
2675            emitParametersForArrow(node, node.parameters);
2676            emitTypeAnnotation(node.type);
2677            writeSpace();
2678            emit(node.equalsGreaterThanToken);
2679        }
2680
2681        function emitDeleteExpression(node: DeleteExpression) {
2682            emitTokenWithComment(SyntaxKind.DeleteKeyword, node.pos, writeKeyword, node);
2683            writeSpace();
2684            emitExpression(node.expression, parenthesizer.parenthesizeOperandOfPrefixUnary);
2685        }
2686
2687        function emitTypeOfExpression(node: TypeOfExpression) {
2688            emitTokenWithComment(SyntaxKind.TypeOfKeyword, node.pos, writeKeyword, node);
2689            writeSpace();
2690            emitExpression(node.expression, parenthesizer.parenthesizeOperandOfPrefixUnary);
2691        }
2692
2693        function emitVoidExpression(node: VoidExpression) {
2694            emitTokenWithComment(SyntaxKind.VoidKeyword, node.pos, writeKeyword, node);
2695            writeSpace();
2696            emitExpression(node.expression, parenthesizer.parenthesizeOperandOfPrefixUnary);
2697        }
2698
2699        function emitAwaitExpression(node: AwaitExpression) {
2700            emitTokenWithComment(SyntaxKind.AwaitKeyword, node.pos, writeKeyword, node);
2701            writeSpace();
2702            emitExpression(node.expression, parenthesizer.parenthesizeOperandOfPrefixUnary);
2703        }
2704
2705        function emitPrefixUnaryExpression(node: PrefixUnaryExpression) {
2706            writeTokenText(node.operator, writeOperator);
2707            if (shouldEmitWhitespaceBeforeOperand(node)) {
2708                writeSpace();
2709            }
2710            emitExpression(node.operand, parenthesizer.parenthesizeOperandOfPrefixUnary);
2711        }
2712
2713        function shouldEmitWhitespaceBeforeOperand(node: PrefixUnaryExpression) {
2714            // In some cases, we need to emit a space between the operator and the operand. One obvious case
2715            // is when the operator is an identifier, like delete or typeof. We also need to do this for plus
2716            // and minus expressions in certain cases. Specifically, consider the following two cases (parens
2717            // are just for clarity of exposition, and not part of the source code):
2718            //
2719            //  (+(+1))
2720            //  (+(++1))
2721            //
2722            // We need to emit a space in both cases. In the first case, the absence of a space will make
2723            // the resulting expression a prefix increment operation. And in the second, it will make the resulting
2724            // expression a prefix increment whose operand is a plus expression - (++(+x))
2725            // The same is true of minus of course.
2726            const operand = node.operand;
2727            return operand.kind === SyntaxKind.PrefixUnaryExpression
2728                && ((node.operator === SyntaxKind.PlusToken && ((operand as PrefixUnaryExpression).operator === SyntaxKind.PlusToken || (operand as PrefixUnaryExpression).operator === SyntaxKind.PlusPlusToken))
2729                    || (node.operator === SyntaxKind.MinusToken && ((operand as PrefixUnaryExpression).operator === SyntaxKind.MinusToken || (operand as PrefixUnaryExpression).operator === SyntaxKind.MinusMinusToken)));
2730        }
2731
2732        function emitPostfixUnaryExpression(node: PostfixUnaryExpression) {
2733            emitExpression(node.operand, parenthesizer.parenthesizeOperandOfPostfixUnary);
2734            writeTokenText(node.operator, writeOperator);
2735        }
2736
2737        function createEmitBinaryExpression() {
2738            interface WorkArea {
2739                stackIndex: number;
2740                preserveSourceNewlinesStack: (boolean | undefined)[];
2741                containerPosStack: number[];
2742                containerEndStack: number[];
2743                declarationListContainerEndStack: number[];
2744                shouldEmitCommentsStack: boolean[];
2745                shouldEmitSourceMapsStack: boolean[];
2746            }
2747
2748            return createBinaryExpressionTrampoline(onEnter, onLeft, onOperator, onRight, onExit, /*foldState*/ undefined);
2749
2750            function onEnter(node: BinaryExpression, state: WorkArea | undefined) {
2751                if (state) {
2752                    state.stackIndex++;
2753                    state.preserveSourceNewlinesStack[state.stackIndex] = preserveSourceNewlines;
2754                    state.containerPosStack[state.stackIndex] = containerPos;
2755                    state.containerEndStack[state.stackIndex] = containerEnd;
2756                    state.declarationListContainerEndStack[state.stackIndex] = declarationListContainerEnd;
2757                    const emitComments = state.shouldEmitCommentsStack[state.stackIndex] = shouldEmitComments(node);
2758                    const emitSourceMaps = state.shouldEmitSourceMapsStack[state.stackIndex] = shouldEmitSourceMaps(node);
2759                    onBeforeEmitNode?.(node);
2760                    if (emitComments) emitCommentsBeforeNode(node);
2761                    if (emitSourceMaps) emitSourceMapsBeforeNode(node);
2762                    beforeEmitNode(node);
2763                }
2764                else {
2765                    state = {
2766                        stackIndex: 0,
2767                        preserveSourceNewlinesStack: [undefined],
2768                        containerPosStack: [-1],
2769                        containerEndStack: [-1],
2770                        declarationListContainerEndStack: [-1],
2771                        shouldEmitCommentsStack: [false],
2772                        shouldEmitSourceMapsStack: [false],
2773                    };
2774                }
2775                return state;
2776            }
2777
2778            function onLeft(next: Expression, _workArea: WorkArea, parent: BinaryExpression) {
2779                return maybeEmitExpression(next, parent, "left");
2780            }
2781
2782            function onOperator(operatorToken: BinaryOperatorToken, _state: WorkArea, node: BinaryExpression) {
2783                const isCommaOperator = operatorToken.kind !== SyntaxKind.CommaToken;
2784                const linesBeforeOperator = getLinesBetweenNodes(node, node.left, operatorToken);
2785                const linesAfterOperator = getLinesBetweenNodes(node, operatorToken, node.right);
2786                writeLinesAndIndent(linesBeforeOperator, isCommaOperator);
2787                emitLeadingCommentsOfPosition(operatorToken.pos);
2788                writeTokenNode(operatorToken, operatorToken.kind === SyntaxKind.InKeyword ? writeKeyword : writeOperator);
2789                emitTrailingCommentsOfPosition(operatorToken.end, /*prefixSpace*/ true); // Binary operators should have a space before the comment starts
2790                writeLinesAndIndent(linesAfterOperator, /*writeSpaceIfNotIndenting*/ true);
2791            }
2792
2793            function onRight(next: Expression, _workArea: WorkArea, parent: BinaryExpression) {
2794                return maybeEmitExpression(next, parent, "right");
2795            }
2796
2797            function onExit(node: BinaryExpression, state: WorkArea) {
2798                const linesBeforeOperator = getLinesBetweenNodes(node, node.left, node.operatorToken);
2799                const linesAfterOperator = getLinesBetweenNodes(node, node.operatorToken, node.right);
2800                decreaseIndentIf(linesBeforeOperator, linesAfterOperator);
2801                if (state.stackIndex > 0) {
2802                    const savedPreserveSourceNewlines = state.preserveSourceNewlinesStack[state.stackIndex];
2803                    const savedContainerPos = state.containerPosStack[state.stackIndex];
2804                    const savedContainerEnd = state.containerEndStack[state.stackIndex];
2805                    const savedDeclarationListContainerEnd = state.declarationListContainerEndStack[state.stackIndex];
2806                    const shouldEmitComments = state.shouldEmitCommentsStack[state.stackIndex];
2807                    const shouldEmitSourceMaps = state.shouldEmitSourceMapsStack[state.stackIndex];
2808                    afterEmitNode(savedPreserveSourceNewlines);
2809                    if (shouldEmitSourceMaps) emitSourceMapsAfterNode(node);
2810                    if (shouldEmitComments) emitCommentsAfterNode(node, savedContainerPos, savedContainerEnd, savedDeclarationListContainerEnd);
2811                    onAfterEmitNode?.(node);
2812                    state.stackIndex--;
2813                }
2814            }
2815
2816            function maybeEmitExpression(next: Expression, parent: BinaryExpression, side: "left" | "right") {
2817                const parenthesizerRule = side === "left" ?
2818                    parenthesizer.getParenthesizeLeftSideOfBinaryForOperator(parent.operatorToken.kind) :
2819                    parenthesizer.getParenthesizeRightSideOfBinaryForOperator(parent.operatorToken.kind);
2820
2821                let pipelinePhase = getPipelinePhase(PipelinePhase.Notification, EmitHint.Expression, next);
2822                if (pipelinePhase === pipelineEmitWithSubstitution) {
2823                    Debug.assertIsDefined(lastSubstitution);
2824                    next = parenthesizerRule(cast(lastSubstitution, isExpression));
2825                    pipelinePhase = getNextPipelinePhase(PipelinePhase.Substitution, EmitHint.Expression, next);
2826                    lastSubstitution = undefined;
2827                }
2828
2829                if (pipelinePhase === pipelineEmitWithComments ||
2830                    pipelinePhase === pipelineEmitWithSourceMaps ||
2831                    pipelinePhase === pipelineEmitWithHint) {
2832                    if (isBinaryExpression(next)) {
2833                        return next;
2834                    }
2835                }
2836
2837                currentParenthesizerRule = parenthesizerRule;
2838                pipelinePhase(EmitHint.Expression, next);
2839            }
2840        }
2841
2842        function emitConditionalExpression(node: ConditionalExpression) {
2843            const linesBeforeQuestion = getLinesBetweenNodes(node, node.condition, node.questionToken);
2844            const linesAfterQuestion = getLinesBetweenNodes(node, node.questionToken, node.whenTrue);
2845            const linesBeforeColon = getLinesBetweenNodes(node, node.whenTrue, node.colonToken);
2846            const linesAfterColon = getLinesBetweenNodes(node, node.colonToken, node.whenFalse);
2847
2848            emitExpression(node.condition, parenthesizer.parenthesizeConditionOfConditionalExpression);
2849            writeLinesAndIndent(linesBeforeQuestion, /*writeSpaceIfNotIndenting*/ true);
2850            emit(node.questionToken);
2851            writeLinesAndIndent(linesAfterQuestion, /*writeSpaceIfNotIndenting*/ true);
2852            emitExpression(node.whenTrue, parenthesizer.parenthesizeBranchOfConditionalExpression);
2853            decreaseIndentIf(linesBeforeQuestion, linesAfterQuestion);
2854
2855            writeLinesAndIndent(linesBeforeColon, /*writeSpaceIfNotIndenting*/ true);
2856            emit(node.colonToken);
2857            writeLinesAndIndent(linesAfterColon, /*writeSpaceIfNotIndenting*/ true);
2858            emitExpression(node.whenFalse, parenthesizer.parenthesizeBranchOfConditionalExpression);
2859            decreaseIndentIf(linesBeforeColon, linesAfterColon);
2860        }
2861
2862        function emitTemplateExpression(node: TemplateExpression) {
2863            emit(node.head);
2864            emitList(node, node.templateSpans, ListFormat.TemplateExpressionSpans);
2865        }
2866
2867        function emitYieldExpression(node: YieldExpression) {
2868            emitTokenWithComment(SyntaxKind.YieldKeyword, node.pos, writeKeyword, node);
2869            emit(node.asteriskToken);
2870            emitExpressionWithLeadingSpace(node.expression && parenthesizeExpressionForNoAsi(node.expression), parenthesizeExpressionForNoAsiAndDisallowedComma);
2871        }
2872
2873        function emitSpreadElement(node: SpreadElement) {
2874            emitTokenWithComment(SyntaxKind.DotDotDotToken, node.pos, writePunctuation, node);
2875            emitExpression(node.expression, parenthesizer.parenthesizeExpressionForDisallowedComma);
2876        }
2877
2878        function emitClassExpression(node: ClassExpression) {
2879            generateNameIfNeeded(node.name);
2880            emitClassOrStructDeclarationOrExpression(node);
2881        }
2882
2883        function emitExpressionWithTypeArguments(node: ExpressionWithTypeArguments) {
2884            emitExpression(node.expression, parenthesizer.parenthesizeLeftSideOfAccess);
2885            emitTypeArguments(node, node.typeArguments);
2886        }
2887
2888        function emitAsExpression(node: AsExpression) {
2889            emitExpression(node.expression, /*parenthesizerRules*/ undefined);
2890            if (node.type) {
2891                writeSpace();
2892                writeKeyword("as");
2893                writeSpace();
2894                emit(node.type);
2895            }
2896        }
2897
2898        function emitNonNullExpression(node: NonNullExpression) {
2899            emitExpression(node.expression, parenthesizer.parenthesizeLeftSideOfAccess);
2900            writeOperator("!");
2901        }
2902
2903        function emitSatisfiesExpression(node: SatisfiesExpression) {
2904            emitExpression(node.expression, /*parenthesizerRules*/ undefined);
2905            if (node.type) {
2906                writeSpace();
2907                writeKeyword("satisfies");
2908                writeSpace();
2909                emit(node.type);
2910            }
2911        }
2912
2913        function emitMetaProperty(node: MetaProperty) {
2914            writeToken(node.keywordToken, node.pos, writePunctuation);
2915            writePunctuation(".");
2916            emit(node.name);
2917        }
2918
2919        //
2920        // Misc
2921        //
2922
2923        function emitTemplateSpan(node: TemplateSpan) {
2924            emitExpression(node.expression);
2925            emit(node.literal);
2926        }
2927
2928        //
2929        // Statements
2930        //
2931
2932        function emitBlock(node: Block) {
2933            emitBlockStatements(node, /*forceSingleLine*/ !node.multiLine && isEmptyBlock(node));
2934        }
2935
2936        function emitBlockStatements(node: BlockLike, forceSingleLine: boolean) {
2937            emitTokenWithComment(SyntaxKind.OpenBraceToken, node.pos, writePunctuation, /*contextNode*/ node);
2938            const format = forceSingleLine || getEmitFlags(node) & EmitFlags.SingleLine ? ListFormat.SingleLineBlockStatements : ListFormat.MultiLineBlockStatements;
2939            emitList(node, node.statements, format);
2940            emitTokenWithComment(SyntaxKind.CloseBraceToken, node.statements.end, writePunctuation, /*contextNode*/ node, /*indentLeading*/ !!(format & ListFormat.MultiLine));
2941        }
2942
2943        function emitVariableStatement(node: VariableStatement) {
2944            emitModifiers(node, node.modifiers);
2945            emit(node.declarationList);
2946            writeTrailingSemicolon();
2947        }
2948
2949        function emitEmptyStatement(isEmbeddedStatement: boolean) {
2950            // While most trailing semicolons are possibly insignificant, an embedded "empty"
2951            // statement is significant and cannot be elided by a trailing-semicolon-omitting writer.
2952            if (isEmbeddedStatement) {
2953                writePunctuation(";");
2954            }
2955            else {
2956                writeTrailingSemicolon();
2957            }
2958        }
2959
2960        function emitExpressionStatement(node: ExpressionStatement) {
2961            emitExpression(node.expression, parenthesizer.parenthesizeExpressionOfExpressionStatement);
2962            // Emit semicolon in non json files
2963            // or if json file that created synthesized expression(eg.define expression statement when --out and amd code generation)
2964            if (!currentSourceFile || !isJsonSourceFile(currentSourceFile) || nodeIsSynthesized(node.expression)) {
2965                writeTrailingSemicolon();
2966            }
2967        }
2968
2969        function emitIfStatement(node: IfStatement) {
2970            const openParenPos = emitTokenWithComment(SyntaxKind.IfKeyword, node.pos, writeKeyword, node);
2971            writeSpace();
2972            emitTokenWithComment(SyntaxKind.OpenParenToken, openParenPos, writePunctuation, node);
2973            emitExpression(node.expression);
2974            emitTokenWithComment(SyntaxKind.CloseParenToken, node.expression.end, writePunctuation, node);
2975            emitEmbeddedStatement(node, node.thenStatement);
2976            if (node.elseStatement) {
2977                writeLineOrSpace(node, node.thenStatement, node.elseStatement);
2978                emitTokenWithComment(SyntaxKind.ElseKeyword, node.thenStatement.end, writeKeyword, node);
2979                if (node.elseStatement.kind === SyntaxKind.IfStatement) {
2980                    writeSpace();
2981                    emit(node.elseStatement);
2982                }
2983                else {
2984                    emitEmbeddedStatement(node, node.elseStatement);
2985                }
2986            }
2987        }
2988
2989        function emitWhileClause(node: WhileStatement | DoStatement, startPos: number) {
2990            const openParenPos = emitTokenWithComment(SyntaxKind.WhileKeyword, startPos, writeKeyword, node);
2991            writeSpace();
2992            emitTokenWithComment(SyntaxKind.OpenParenToken, openParenPos, writePunctuation, node);
2993            emitExpression(node.expression);
2994            emitTokenWithComment(SyntaxKind.CloseParenToken, node.expression.end, writePunctuation, node);
2995        }
2996
2997        function emitDoStatement(node: DoStatement) {
2998            emitTokenWithComment(SyntaxKind.DoKeyword, node.pos, writeKeyword, node);
2999            emitEmbeddedStatement(node, node.statement);
3000            if (isBlock(node.statement) && !preserveSourceNewlines) {
3001                writeSpace();
3002            }
3003            else {
3004                writeLineOrSpace(node, node.statement, node.expression);
3005            }
3006
3007            emitWhileClause(node, node.statement.end);
3008            writeTrailingSemicolon();
3009        }
3010
3011        function emitWhileStatement(node: WhileStatement) {
3012            emitWhileClause(node, node.pos);
3013            emitEmbeddedStatement(node, node.statement);
3014        }
3015
3016        function emitForStatement(node: ForStatement) {
3017            const openParenPos = emitTokenWithComment(SyntaxKind.ForKeyword, node.pos, writeKeyword, node);
3018            writeSpace();
3019            let pos = emitTokenWithComment(SyntaxKind.OpenParenToken, openParenPos, writePunctuation, /*contextNode*/ node);
3020            emitForBinding(node.initializer);
3021            pos = emitTokenWithComment(SyntaxKind.SemicolonToken, node.initializer ? node.initializer.end : pos, writePunctuation, node);
3022            emitExpressionWithLeadingSpace(node.condition);
3023            pos = emitTokenWithComment(SyntaxKind.SemicolonToken, node.condition ? node.condition.end : pos, writePunctuation, node);
3024            emitExpressionWithLeadingSpace(node.incrementor);
3025            emitTokenWithComment(SyntaxKind.CloseParenToken, node.incrementor ? node.incrementor.end : pos, writePunctuation, node);
3026            emitEmbeddedStatement(node, node.statement);
3027        }
3028
3029        function emitForInStatement(node: ForInStatement) {
3030            const openParenPos = emitTokenWithComment(SyntaxKind.ForKeyword, node.pos, writeKeyword, node);
3031            writeSpace();
3032            emitTokenWithComment(SyntaxKind.OpenParenToken, openParenPos, writePunctuation, node);
3033            emitForBinding(node.initializer);
3034            writeSpace();
3035            emitTokenWithComment(SyntaxKind.InKeyword, node.initializer.end, writeKeyword, node);
3036            writeSpace();
3037            emitExpression(node.expression);
3038            emitTokenWithComment(SyntaxKind.CloseParenToken, node.expression.end, writePunctuation, node);
3039            emitEmbeddedStatement(node, node.statement);
3040        }
3041
3042        function emitForOfStatement(node: ForOfStatement) {
3043            const openParenPos = emitTokenWithComment(SyntaxKind.ForKeyword, node.pos, writeKeyword, node);
3044            writeSpace();
3045            emitWithTrailingSpace(node.awaitModifier);
3046            emitTokenWithComment(SyntaxKind.OpenParenToken, openParenPos, writePunctuation, node);
3047            emitForBinding(node.initializer);
3048            writeSpace();
3049            emitTokenWithComment(SyntaxKind.OfKeyword, node.initializer.end, writeKeyword, node);
3050            writeSpace();
3051            emitExpression(node.expression);
3052            emitTokenWithComment(SyntaxKind.CloseParenToken, node.expression.end, writePunctuation, node);
3053            emitEmbeddedStatement(node, node.statement);
3054        }
3055
3056        function emitForBinding(node: VariableDeclarationList | Expression | undefined) {
3057            if (node !== undefined) {
3058                if (node.kind === SyntaxKind.VariableDeclarationList) {
3059                    emit(node);
3060                }
3061                else {
3062                    emitExpression(node);
3063                }
3064            }
3065        }
3066
3067        function emitContinueStatement(node: ContinueStatement) {
3068            emitTokenWithComment(SyntaxKind.ContinueKeyword, node.pos, writeKeyword, node);
3069            emitWithLeadingSpace(node.label);
3070            writeTrailingSemicolon();
3071        }
3072
3073        function emitBreakStatement(node: BreakStatement) {
3074            emitTokenWithComment(SyntaxKind.BreakKeyword, node.pos, writeKeyword, node);
3075            emitWithLeadingSpace(node.label);
3076            writeTrailingSemicolon();
3077        }
3078
3079        function emitTokenWithComment(token: SyntaxKind, pos: number, writer: (s: string) => void, contextNode: Node, indentLeading?: boolean) {
3080            const node = getParseTreeNode(contextNode);
3081            const isSimilarNode = node && node.kind === contextNode.kind;
3082            const startPos = pos;
3083            if (isSimilarNode && currentSourceFile) {
3084                pos = skipTrivia(currentSourceFile.text, pos);
3085            }
3086            if (isSimilarNode && contextNode.pos !== startPos) {
3087                const needsIndent = indentLeading && currentSourceFile && !positionsAreOnSameLine(startPos, pos, currentSourceFile);
3088                if (needsIndent) {
3089                    increaseIndent();
3090                }
3091                emitLeadingCommentsOfPosition(startPos);
3092                if (needsIndent) {
3093                    decreaseIndent();
3094                }
3095            }
3096            pos = writeTokenText(token, writer, pos);
3097            if (isSimilarNode && contextNode.end !== pos) {
3098                const isJsxExprContext = contextNode.kind === SyntaxKind.JsxExpression;
3099                emitTrailingCommentsOfPosition(pos, /*prefixSpace*/ !isJsxExprContext, /*forceNoNewline*/ isJsxExprContext);
3100            }
3101            return pos;
3102        }
3103
3104        function commentWillEmitNewLine(node: CommentRange) {
3105            return node.kind === SyntaxKind.SingleLineCommentTrivia || !!node.hasTrailingNewLine;
3106        }
3107
3108        function willEmitLeadingNewLine(node: Expression): boolean {
3109            if (!currentSourceFile) return false;
3110            if (some(getLeadingCommentRanges(currentSourceFile.text, node.pos), commentWillEmitNewLine)) return true;
3111            if (some(getSyntheticLeadingComments(node), commentWillEmitNewLine)) return true;
3112            if (isPartiallyEmittedExpression(node)) {
3113                if (node.pos !== node.expression.pos) {
3114                    if (some(getTrailingCommentRanges(currentSourceFile.text, node.expression.pos), commentWillEmitNewLine)) return true;
3115                }
3116                return willEmitLeadingNewLine(node.expression);
3117            }
3118            return false;
3119        }
3120
3121        /**
3122         * Wraps an expression in parens if we would emit a leading comment that would introduce a line separator
3123         * between the node and its parent.
3124         */
3125        function parenthesizeExpressionForNoAsi(node: Expression) {
3126            if (!commentsDisabled && isPartiallyEmittedExpression(node) && willEmitLeadingNewLine(node)) {
3127                const parseNode = getParseTreeNode(node);
3128                if (parseNode && isParenthesizedExpression(parseNode)) {
3129                    // If the original node was a parenthesized expression, restore it to preserve comment and source map emit
3130                    const parens = factory.createParenthesizedExpression(node.expression);
3131                    setOriginalNode(parens, node);
3132                    setTextRange(parens, parseNode);
3133                    return parens;
3134                }
3135                return factory.createParenthesizedExpression(node);
3136            }
3137            return node;
3138        }
3139
3140        function parenthesizeExpressionForNoAsiAndDisallowedComma(node: Expression) {
3141            return parenthesizeExpressionForNoAsi(parenthesizer.parenthesizeExpressionForDisallowedComma(node));
3142        }
3143
3144        function emitReturnStatement(node: ReturnStatement) {
3145            emitTokenWithComment(SyntaxKind.ReturnKeyword, node.pos, writeKeyword, /*contextNode*/ node);
3146            emitExpressionWithLeadingSpace(node.expression && parenthesizeExpressionForNoAsi(node.expression), parenthesizeExpressionForNoAsi);
3147            writeTrailingSemicolon();
3148        }
3149
3150        function emitWithStatement(node: WithStatement) {
3151            const openParenPos = emitTokenWithComment(SyntaxKind.WithKeyword, node.pos, writeKeyword, node);
3152            writeSpace();
3153            emitTokenWithComment(SyntaxKind.OpenParenToken, openParenPos, writePunctuation, node);
3154            emitExpression(node.expression);
3155            emitTokenWithComment(SyntaxKind.CloseParenToken, node.expression.end, writePunctuation, node);
3156            emitEmbeddedStatement(node, node.statement);
3157        }
3158
3159        function emitSwitchStatement(node: SwitchStatement) {
3160            const openParenPos = emitTokenWithComment(SyntaxKind.SwitchKeyword, node.pos, writeKeyword, node);
3161            writeSpace();
3162            emitTokenWithComment(SyntaxKind.OpenParenToken, openParenPos, writePunctuation, node);
3163            emitExpression(node.expression);
3164            emitTokenWithComment(SyntaxKind.CloseParenToken, node.expression.end, writePunctuation, node);
3165            writeSpace();
3166            emit(node.caseBlock);
3167        }
3168
3169        function emitLabeledStatement(node: LabeledStatement) {
3170            emit(node.label);
3171            emitTokenWithComment(SyntaxKind.ColonToken, node.label.end, writePunctuation, node);
3172            writeSpace();
3173            emit(node.statement);
3174        }
3175
3176        function emitThrowStatement(node: ThrowStatement) {
3177            emitTokenWithComment(SyntaxKind.ThrowKeyword, node.pos, writeKeyword, node);
3178            emitExpressionWithLeadingSpace(parenthesizeExpressionForNoAsi(node.expression), parenthesizeExpressionForNoAsi);
3179            writeTrailingSemicolon();
3180        }
3181
3182        function emitTryStatement(node: TryStatement) {
3183            emitTokenWithComment(SyntaxKind.TryKeyword, node.pos, writeKeyword, node);
3184            writeSpace();
3185            emit(node.tryBlock);
3186            if (node.catchClause) {
3187                writeLineOrSpace(node, node.tryBlock, node.catchClause);
3188                emit(node.catchClause);
3189            }
3190            if (node.finallyBlock) {
3191                writeLineOrSpace(node, node.catchClause || node.tryBlock, node.finallyBlock);
3192                emitTokenWithComment(SyntaxKind.FinallyKeyword, (node.catchClause || node.tryBlock).end, writeKeyword, node);
3193                writeSpace();
3194                emit(node.finallyBlock);
3195            }
3196        }
3197
3198        function emitDebuggerStatement(node: DebuggerStatement) {
3199            writeToken(SyntaxKind.DebuggerKeyword, node.pos, writeKeyword);
3200            writeTrailingSemicolon();
3201        }
3202
3203        //
3204        // Declarations
3205        //
3206
3207        function emitVariableDeclaration(node: VariableDeclaration) {
3208            emit(node.name);
3209            emit(node.exclamationToken);
3210            emitTypeAnnotation(node.type);
3211            emitInitializer(node.initializer, node.type?.end ?? node.name.emitNode?.typeNode?.end ?? node.name.end, node, parenthesizer.parenthesizeExpressionForDisallowedComma);
3212        }
3213
3214        function emitVariableDeclarationList(node: VariableDeclarationList) {
3215            writeKeyword(isLet(node) ? "let" : isVarConst(node) ? "const" : "var");
3216            writeSpace();
3217            emitList(node, node.declarations, ListFormat.VariableDeclarationList);
3218        }
3219
3220        function emitFunctionDeclaration(node: FunctionDeclaration) {
3221            emitFunctionDeclarationOrExpression(node);
3222        }
3223
3224        function emitFunctionDeclarationOrExpression(node: FunctionDeclaration | FunctionExpression) {
3225            if (isFunctionDeclaration(node) && (isInEtsFile(node) || isInEtsFileWithOriginal(node))) {
3226                emitDecorators(node, node.illegalDecorators);
3227                getSourceFileOfNode
3228            }
3229            emitModifiers(node, factory.createNodeArray(getModifiers(node)));
3230            writeKeyword("function");
3231            emit(node.asteriskToken);
3232            writeSpace();
3233            emitIdentifierName(node.name);
3234            emitSignatureAndBody(node, emitSignatureHead);
3235        }
3236
3237        function emitSignatureAndBody(node: FunctionLikeDeclaration, emitSignatureHead: (node: SignatureDeclaration) => void) {
3238            const body = node.body;
3239            if (body) {
3240                if (isBlock(body)) {
3241                    const indentedFlag = getEmitFlags(node) & EmitFlags.Indented;
3242                    if (indentedFlag) {
3243                        increaseIndent();
3244                    }
3245
3246                    pushNameGenerationScope(node);
3247                    forEach(node.parameters, generateNames);
3248                    generateNames(node.body);
3249
3250                    emitSignatureHead(node);
3251                    emitBlockFunctionBody(body);
3252                    popNameGenerationScope(node);
3253
3254                    if (indentedFlag) {
3255                        decreaseIndent();
3256                    }
3257                }
3258                else {
3259                    emitSignatureHead(node);
3260                    writeSpace();
3261                    emitExpression(body, parenthesizer.parenthesizeConciseBodyOfArrowFunction);
3262                }
3263            }
3264            else {
3265                emitSignatureHead(node);
3266                writeTrailingSemicolon();
3267            }
3268
3269        }
3270
3271        function emitSignatureHead(node: FunctionDeclaration | FunctionExpression | MethodDeclaration | AccessorDeclaration | ConstructorDeclaration) {
3272            emitTypeParameters(node, node.typeParameters);
3273            emitParameters(node, node.parameters);
3274            emitTypeAnnotation(node.type);
3275        }
3276
3277        function shouldEmitBlockFunctionBodyOnSingleLine(body: Block) {
3278            // We must emit a function body as a single-line body in the following case:
3279            // * The body has NodeEmitFlags.SingleLine specified.
3280
3281            // We must emit a function body as a multi-line body in the following cases:
3282            // * The body is explicitly marked as multi-line.
3283            // * A non-synthesized body's start and end position are on different lines.
3284            // * Any statement in the body starts on a new line.
3285
3286            if (getEmitFlags(body) & EmitFlags.SingleLine) {
3287                return true;
3288            }
3289
3290            if (body.multiLine) {
3291                return false;
3292            }
3293
3294            if (!nodeIsSynthesized(body) && currentSourceFile && !rangeIsOnSingleLine(body, currentSourceFile)) {
3295                return false;
3296            }
3297
3298            if (getLeadingLineTerminatorCount(body, firstOrUndefined(body.statements), ListFormat.PreserveLines)
3299                || getClosingLineTerminatorCount(body, lastOrUndefined(body.statements), ListFormat.PreserveLines, body.statements)) {
3300                return false;
3301            }
3302
3303            let previousStatement: Statement | undefined;
3304            for (const statement of body.statements) {
3305                if (getSeparatingLineTerminatorCount(previousStatement, statement, ListFormat.PreserveLines) > 0) {
3306                    return false;
3307                }
3308
3309                previousStatement = statement;
3310            }
3311
3312            return true;
3313        }
3314
3315        function emitBlockFunctionBody(body: Block) {
3316            onBeforeEmitNode?.(body);
3317            writeSpace();
3318            writePunctuation("{");
3319            increaseIndent();
3320
3321            const emitBlockFunctionBody = shouldEmitBlockFunctionBodyOnSingleLine(body)
3322                ? emitBlockFunctionBodyOnSingleLine
3323                : emitBlockFunctionBodyWorker;
3324
3325            emitBodyWithDetachedComments(body, body.statements, emitBlockFunctionBody);
3326
3327            decreaseIndent();
3328            writeToken(SyntaxKind.CloseBraceToken, body.statements.end, writePunctuation, body);
3329            onAfterEmitNode?.(body);
3330        }
3331
3332        function emitBlockFunctionBodyOnSingleLine(body: Block) {
3333            emitBlockFunctionBodyWorker(body, /*emitBlockFunctionBodyOnSingleLine*/ true);
3334        }
3335
3336        function emitBlockFunctionBodyWorker(body: Block, emitBlockFunctionBodyOnSingleLine?: boolean) {
3337            // Emit all the prologue directives (like "use strict").
3338            const statementOffset = emitPrologueDirectives(body.statements);
3339            const pos = writer.getTextPos();
3340            emitHelpers(body);
3341            if (statementOffset === 0 && pos === writer.getTextPos() && emitBlockFunctionBodyOnSingleLine) {
3342                decreaseIndent();
3343                emitList(body, body.statements, ListFormat.SingleLineFunctionBodyStatements);
3344                increaseIndent();
3345            }
3346            else {
3347                emitList(body, body.statements, ListFormat.MultiLineFunctionBodyStatements, /*parenthesizerRule*/ undefined, statementOffset);
3348            }
3349        }
3350
3351        function emitClassOrStructDeclaration(node: ClassDeclaration | StructDeclaration) {
3352            emitClassOrStructDeclarationOrExpression(node);
3353        }
3354
3355        function emitClassOrStructDeclarationOrExpression(node: ClassDeclaration | ClassExpression | StructDeclaration) {
3356            forEach(node.members, generateMemberNames);
3357
3358            emitDecoratorsAndModifiers(node, node.modifiers);
3359            if (isStructDeclaration(node)) {
3360                writeKeyword("struct");
3361            }
3362            else {
3363                writeKeyword("class");
3364            }
3365            if (node.name) {
3366                writeSpace();
3367                emitIdentifierName(node.name);
3368            }
3369
3370            const indentedFlag = getEmitFlags(node) & EmitFlags.Indented;
3371            if (indentedFlag) {
3372                increaseIndent();
3373            }
3374
3375            emitTypeParameters(node, node.typeParameters);
3376            emitList(node, node.heritageClauses, ListFormat.ClassHeritageClauses);
3377
3378            writeSpace();
3379            writePunctuation("{");
3380            emitList(node, node.members, ListFormat.ClassMembers);
3381            writePunctuation("}");
3382
3383            if (indentedFlag) {
3384                decreaseIndent();
3385            }
3386        }
3387
3388        function emitInterfaceDeclaration(node: InterfaceDeclaration) {
3389            emitModifiers(node, node.modifiers);
3390            writeKeyword("interface");
3391            writeSpace();
3392            emit(node.name);
3393            emitTypeParameters(node, node.typeParameters);
3394            emitList(node, node.heritageClauses, ListFormat.HeritageClauses);
3395            writeSpace();
3396            writePunctuation("{");
3397            emitList(node, node.members, ListFormat.InterfaceMembers);
3398            writePunctuation("}");
3399        }
3400
3401        function emitAnnotationDeclaration(node: AnnotationDeclaration) {
3402            emitDecoratorsAndModifiers(node, node.modifiers);
3403            writeKeyword("@interface");
3404
3405            if (node.name) {
3406                writeSpace();
3407                emitIdentifierName(node.name);
3408            }
3409
3410            const indentedFlag = getEmitFlags(node) & EmitFlags.Indented;
3411            if (indentedFlag) {
3412                increaseIndent();
3413            }
3414
3415            writeSpace();
3416            writePunctuation("{");
3417            emitList(node, node.members, ListFormat.ClassMembers);
3418            writePunctuation("}");
3419
3420            if (indentedFlag) {
3421                decreaseIndent();
3422            }
3423        }
3424
3425        function emitTypeAliasDeclaration(node: TypeAliasDeclaration) {
3426            if (isSendableFunctionOrType(node, /*maybeNotOriginalNode*/ true)) {
3427                emitDecorators(node, node.illegalDecorators);
3428            }
3429            emitModifiers(node, node.modifiers);
3430            writeKeyword("type");
3431            writeSpace();
3432            emit(node.name);
3433            emitTypeParameters(node, node.typeParameters);
3434            writeSpace();
3435            writePunctuation("=");
3436            writeSpace();
3437            emit(node.type);
3438            writeTrailingSemicolon();
3439        }
3440
3441        function emitEnumDeclaration(node: EnumDeclaration) {
3442            emitModifiers(node, node.modifiers);
3443            writeKeyword("enum");
3444            writeSpace();
3445            emit(node.name);
3446
3447            writeSpace();
3448            writePunctuation("{");
3449            emitList(node, node.members, ListFormat.EnumMembers);
3450            writePunctuation("}");
3451        }
3452
3453        function emitModuleDeclaration(node: ModuleDeclaration) {
3454            emitModifiers(node, node.modifiers);
3455            if (~node.flags & NodeFlags.GlobalAugmentation) {
3456                writeKeyword(node.flags & NodeFlags.Namespace ? "namespace" : "module");
3457                writeSpace();
3458            }
3459            emit(node.name);
3460
3461            let body = node.body;
3462            if (!body) return writeTrailingSemicolon();
3463            while (body && isModuleDeclaration(body)) {
3464                writePunctuation(".");
3465                emit(body.name);
3466                body = body.body;
3467            }
3468
3469            writeSpace();
3470            emit(body);
3471        }
3472
3473        function emitModuleBlock(node: ModuleBlock) {
3474            pushNameGenerationScope(node);
3475            forEach(node.statements, generateNames);
3476            emitBlockStatements(node, /*forceSingleLine*/ isEmptyBlock(node));
3477            popNameGenerationScope(node);
3478        }
3479
3480        function emitCaseBlock(node: CaseBlock) {
3481            emitTokenWithComment(SyntaxKind.OpenBraceToken, node.pos, writePunctuation, node);
3482            emitList(node, node.clauses, ListFormat.CaseBlockClauses);
3483            emitTokenWithComment(SyntaxKind.CloseBraceToken, node.clauses.end, writePunctuation, node, /*indentLeading*/ true);
3484        }
3485
3486        function emitImportEqualsDeclaration(node: ImportEqualsDeclaration) {
3487            emitModifiers(node, node.modifiers);
3488            emitTokenWithComment(SyntaxKind.ImportKeyword, node.modifiers ? node.modifiers.end : node.pos, writeKeyword, node);
3489            writeSpace();
3490            if (node.isTypeOnly) {
3491                emitTokenWithComment(SyntaxKind.TypeKeyword, node.pos, writeKeyword, node);
3492                writeSpace();
3493            }
3494            emit(node.name);
3495            writeSpace();
3496            emitTokenWithComment(SyntaxKind.EqualsToken, node.name.end, writePunctuation, node);
3497            writeSpace();
3498            emitModuleReference(node.moduleReference);
3499            writeTrailingSemicolon();
3500        }
3501
3502        function emitModuleReference(node: ModuleReference) {
3503            if (node.kind === SyntaxKind.Identifier) {
3504                emitExpression(node);
3505            }
3506            else {
3507                emit(node);
3508            }
3509        }
3510
3511        function emitImportDeclaration(node: ImportDeclaration) {
3512            emitModifiers(node, node.modifiers);
3513            emitTokenWithComment(SyntaxKind.ImportKeyword, node.modifiers ? node.modifiers.end : node.pos, writeKeyword, node);
3514            writeSpace();
3515            if (node.importClause) {
3516                emit(node.importClause);
3517                writeSpace();
3518                emitTokenWithComment(SyntaxKind.FromKeyword, node.importClause.end, writeKeyword, node);
3519                writeSpace();
3520            }
3521            emitExpression(node.moduleSpecifier);
3522            if (node.assertClause) {
3523                emitWithLeadingSpace(node.assertClause);
3524            }
3525            writeTrailingSemicolon();
3526        }
3527
3528        function emitImportClause(node: ImportClause) {
3529            if (node.isTypeOnly) {
3530                emitTokenWithComment(SyntaxKind.TypeKeyword, node.pos, writeKeyword, node);
3531                writeSpace();
3532            }
3533            else if (node.isLazy) {
3534                emitTokenWithComment(SyntaxKind.LazyKeyword, node.pos, writeKeyword, node);
3535                writeSpace();
3536            }
3537            emit(node.name);
3538            if (node.name && node.namedBindings) {
3539                emitTokenWithComment(SyntaxKind.CommaToken, node.name.end, writePunctuation, node);
3540                writeSpace();
3541            }
3542            emit(node.namedBindings);
3543        }
3544
3545        function emitNamespaceImport(node: NamespaceImport) {
3546            const asPos = emitTokenWithComment(SyntaxKind.AsteriskToken, node.pos, writePunctuation, node);
3547            writeSpace();
3548            emitTokenWithComment(SyntaxKind.AsKeyword, asPos, writeKeyword, node);
3549            writeSpace();
3550            emit(node.name);
3551        }
3552
3553        function emitNamedImports(node: NamedImports) {
3554            emitNamedImportsOrExports(node);
3555        }
3556
3557        function emitImportSpecifier(node: ImportSpecifier) {
3558            emitImportOrExportSpecifier(node);
3559        }
3560
3561        function emitExportAssignment(node: ExportAssignment) {
3562            const nextPos = emitTokenWithComment(SyntaxKind.ExportKeyword, node.pos, writeKeyword, node);
3563            writeSpace();
3564            if (node.isExportEquals) {
3565                emitTokenWithComment(SyntaxKind.EqualsToken, nextPos, writeOperator, node);
3566            }
3567            else {
3568                emitTokenWithComment(SyntaxKind.DefaultKeyword, nextPos, writeKeyword, node);
3569            }
3570            writeSpace();
3571            emitExpression(node.expression, node.isExportEquals ?
3572                parenthesizer.getParenthesizeRightSideOfBinaryForOperator(SyntaxKind.EqualsToken) :
3573                parenthesizer.parenthesizeExpressionOfExportDefault);
3574            writeTrailingSemicolon();
3575        }
3576
3577        function emitExportDeclaration(node: ExportDeclaration) {
3578            emitModifiers(node, node.modifiers);
3579            let nextPos = emitTokenWithComment(SyntaxKind.ExportKeyword, node.pos, writeKeyword, node);
3580            writeSpace();
3581            if (node.isTypeOnly) {
3582                nextPos = emitTokenWithComment(SyntaxKind.TypeKeyword, nextPos, writeKeyword, node);
3583                writeSpace();
3584            }
3585            if (node.exportClause) {
3586                emit(node.exportClause);
3587            }
3588            else {
3589                nextPos = emitTokenWithComment(SyntaxKind.AsteriskToken, nextPos, writePunctuation, node);
3590            }
3591            if (node.moduleSpecifier) {
3592                writeSpace();
3593                const fromPos = node.exportClause ? node.exportClause.end : nextPos;
3594                emitTokenWithComment(SyntaxKind.FromKeyword, fromPos, writeKeyword, node);
3595                writeSpace();
3596                emitExpression(node.moduleSpecifier);
3597            }
3598            if (node.assertClause) {
3599                emitWithLeadingSpace(node.assertClause);
3600            }
3601            writeTrailingSemicolon();
3602        }
3603
3604        function emitAssertClause(node: AssertClause) {
3605            emitTokenWithComment(SyntaxKind.AssertKeyword, node.pos, writeKeyword, node);
3606            writeSpace();
3607            const elements = node.elements;
3608            emitList(node, elements, ListFormat.ImportClauseEntries);
3609        }
3610
3611        function emitAssertEntry(node: AssertEntry) {
3612            emit(node.name);
3613            writePunctuation(":");
3614            writeSpace();
3615
3616            const value = node.value;
3617            /** @see {emitPropertyAssignment} */
3618            if ((getEmitFlags(value) & EmitFlags.NoLeadingComments) === 0) {
3619                const commentRange = getCommentRange(value);
3620                emitTrailingCommentsOfPosition(commentRange.pos);
3621            }
3622            emit(value);
3623        }
3624
3625        function emitNamespaceExportDeclaration(node: NamespaceExportDeclaration) {
3626            let nextPos = emitTokenWithComment(SyntaxKind.ExportKeyword, node.pos, writeKeyword, node);
3627            writeSpace();
3628            nextPos = emitTokenWithComment(SyntaxKind.AsKeyword, nextPos, writeKeyword, node);
3629            writeSpace();
3630            nextPos = emitTokenWithComment(SyntaxKind.NamespaceKeyword, nextPos, writeKeyword, node);
3631            writeSpace();
3632            emit(node.name);
3633            writeTrailingSemicolon();
3634        }
3635
3636        function emitNamespaceExport(node: NamespaceExport) {
3637            const asPos = emitTokenWithComment(SyntaxKind.AsteriskToken, node.pos, writePunctuation, node);
3638            writeSpace();
3639            emitTokenWithComment(SyntaxKind.AsKeyword, asPos, writeKeyword, node);
3640            writeSpace();
3641            emit(node.name);
3642        }
3643
3644        function emitNamedExports(node: NamedExports) {
3645            emitNamedImportsOrExports(node);
3646        }
3647
3648        function emitExportSpecifier(node: ExportSpecifier) {
3649            emitImportOrExportSpecifier(node);
3650        }
3651
3652        function emitNamedImportsOrExports(node: NamedImportsOrExports) {
3653            writePunctuation("{");
3654            emitList(node, node.elements, ListFormat.NamedImportsOrExportsElements);
3655            writePunctuation("}");
3656        }
3657
3658        function emitImportOrExportSpecifier(node: ImportOrExportSpecifier) {
3659            if (node.isTypeOnly) {
3660                writeKeyword("type");
3661                writeSpace();
3662            }
3663            if (node.propertyName) {
3664                emit(node.propertyName);
3665                writeSpace();
3666                emitTokenWithComment(SyntaxKind.AsKeyword, node.propertyName.end, writeKeyword, node);
3667                writeSpace();
3668            }
3669
3670            emit(node.name);
3671        }
3672
3673        //
3674        // Module references
3675        //
3676
3677        function emitExternalModuleReference(node: ExternalModuleReference) {
3678            writeKeyword("require");
3679            writePunctuation("(");
3680            emitExpression(node.expression);
3681            writePunctuation(")");
3682        }
3683
3684        //
3685        // JSX
3686        //
3687
3688        function emitJsxElement(node: JsxElement) {
3689            emit(node.openingElement);
3690            emitList(node, node.children, ListFormat.JsxElementOrFragmentChildren);
3691            emit(node.closingElement);
3692        }
3693
3694        function emitJsxSelfClosingElement(node: JsxSelfClosingElement) {
3695            writePunctuation("<");
3696            emitJsxTagName(node.tagName);
3697            emitTypeArguments(node, node.typeArguments);
3698            writeSpace();
3699            emit(node.attributes);
3700            writePunctuation("/>");
3701        }
3702
3703        function emitJsxFragment(node: JsxFragment) {
3704            emit(node.openingFragment);
3705            emitList(node, node.children, ListFormat.JsxElementOrFragmentChildren);
3706            emit(node.closingFragment);
3707        }
3708
3709        function emitJsxOpeningElementOrFragment(node: JsxOpeningElement | JsxOpeningFragment) {
3710            writePunctuation("<");
3711
3712            if (isJsxOpeningElement(node)) {
3713                const indented = writeLineSeparatorsAndIndentBefore(node.tagName, node);
3714                emitJsxTagName(node.tagName);
3715                emitTypeArguments(node, node.typeArguments);
3716                if (node.attributes.properties && node.attributes.properties.length > 0) {
3717                    writeSpace();
3718                }
3719                emit(node.attributes);
3720                writeLineSeparatorsAfter(node.attributes, node);
3721                decreaseIndentIf(indented);
3722            }
3723
3724            writePunctuation(">");
3725        }
3726
3727        function emitJsxText(node: JsxText) {
3728            writer.writeLiteral(node.text);
3729        }
3730
3731        function emitJsxClosingElementOrFragment(node: JsxClosingElement | JsxClosingFragment) {
3732            writePunctuation("</");
3733            if (isJsxClosingElement(node)) {
3734                emitJsxTagName(node.tagName);
3735            }
3736            writePunctuation(">");
3737        }
3738
3739        function emitJsxAttributes(node: JsxAttributes) {
3740            emitList(node, node.properties, ListFormat.JsxElementAttributes);
3741        }
3742
3743        function emitJsxAttribute(node: JsxAttribute) {
3744            emit(node.name);
3745            emitNodeWithPrefix("=", writePunctuation, node.initializer, emitJsxAttributeValue);
3746        }
3747
3748        function emitJsxSpreadAttribute(node: JsxSpreadAttribute) {
3749            writePunctuation("{...");
3750            emitExpression(node.expression);
3751            writePunctuation("}");
3752        }
3753
3754        function hasTrailingCommentsAtPosition(pos: number) {
3755            let result = false;
3756            forEachTrailingCommentRange(currentSourceFile?.text || "", pos + 1, () => result = true);
3757            return result;
3758        }
3759
3760        function hasLeadingCommentsAtPosition(pos: number) {
3761            let result = false;
3762            forEachLeadingCommentRange(currentSourceFile?.text || "", pos + 1, () => result = true);
3763            return result;
3764        }
3765
3766        function hasCommentsAtPosition(pos: number) {
3767            return hasTrailingCommentsAtPosition(pos) || hasLeadingCommentsAtPosition(pos);
3768        }
3769
3770        function emitJsxExpression(node: JsxExpression) {
3771            if (node.expression || (!commentsDisabled && !nodeIsSynthesized(node) && hasCommentsAtPosition(node.pos))) { // preserve empty expressions if they contain comments!
3772                const isMultiline = currentSourceFile && !nodeIsSynthesized(node) && getLineAndCharacterOfPosition(currentSourceFile, node.pos).line !== getLineAndCharacterOfPosition(currentSourceFile, node.end).line;
3773                if (isMultiline) {
3774                    writer.increaseIndent();
3775                }
3776                const end = emitTokenWithComment(SyntaxKind.OpenBraceToken, node.pos, writePunctuation, node);
3777                emit(node.dotDotDotToken);
3778                emitExpression(node.expression);
3779                emitTokenWithComment(SyntaxKind.CloseBraceToken, node.expression?.end || end, writePunctuation, node);
3780                if (isMultiline) {
3781                    writer.decreaseIndent();
3782                }
3783            }
3784        }
3785
3786        function emitJsxTagName(node: JsxTagNameExpression) {
3787            if (node.kind === SyntaxKind.Identifier) {
3788                emitExpression(node);
3789            }
3790            else {
3791                emit(node);
3792            }
3793        }
3794
3795        //
3796        // Clauses
3797        //
3798
3799        function emitCaseClause(node: CaseClause) {
3800            emitTokenWithComment(SyntaxKind.CaseKeyword, node.pos, writeKeyword, node);
3801            writeSpace();
3802            emitExpression(node.expression, parenthesizer.parenthesizeExpressionForDisallowedComma);
3803
3804            emitCaseOrDefaultClauseRest(node, node.statements, node.expression.end);
3805        }
3806
3807        function emitDefaultClause(node: DefaultClause) {
3808            const pos = emitTokenWithComment(SyntaxKind.DefaultKeyword, node.pos, writeKeyword, node);
3809            emitCaseOrDefaultClauseRest(node, node.statements, pos);
3810        }
3811
3812        function emitCaseOrDefaultClauseRest(parentNode: Node, statements: NodeArray<Statement>, colonPos: number) {
3813            const emitAsSingleStatement =
3814                statements.length === 1 &&
3815                (
3816                    // treat synthesized nodes as located on the same line for emit purposes
3817                    !currentSourceFile ||
3818                    nodeIsSynthesized(parentNode) ||
3819                    nodeIsSynthesized(statements[0]) ||
3820                    rangeStartPositionsAreOnSameLine(parentNode, statements[0], currentSourceFile)
3821                );
3822
3823            let format = ListFormat.CaseOrDefaultClauseStatements;
3824            if (emitAsSingleStatement) {
3825                writeToken(SyntaxKind.ColonToken, colonPos, writePunctuation, parentNode);
3826                writeSpace();
3827                format &= ~(ListFormat.MultiLine | ListFormat.Indented);
3828            }
3829            else {
3830                emitTokenWithComment(SyntaxKind.ColonToken, colonPos, writePunctuation, parentNode);
3831            }
3832            emitList(parentNode, statements, format);
3833        }
3834
3835        function emitHeritageClause(node: HeritageClause) {
3836            writeSpace();
3837            writeTokenText(node.token, writeKeyword);
3838            writeSpace();
3839            emitList(node, node.types, ListFormat.HeritageClauseTypes);
3840        }
3841
3842        function emitCatchClause(node: CatchClause) {
3843            const openParenPos = emitTokenWithComment(SyntaxKind.CatchKeyword, node.pos, writeKeyword, node);
3844            writeSpace();
3845            if (node.variableDeclaration) {
3846                emitTokenWithComment(SyntaxKind.OpenParenToken, openParenPos, writePunctuation, node);
3847                emit(node.variableDeclaration);
3848                emitTokenWithComment(SyntaxKind.CloseParenToken, node.variableDeclaration.end, writePunctuation, node);
3849                writeSpace();
3850            }
3851            emit(node.block);
3852        }
3853
3854        //
3855        // Property assignments
3856        //
3857
3858        function emitPropertyAssignment(node: PropertyAssignment) {
3859            emit(node.name);
3860            writePunctuation(":");
3861            writeSpace();
3862            // This is to ensure that we emit comment in the following case:
3863            //      For example:
3864            //          obj = {
3865            //              id: /*comment1*/ ()=>void
3866            //          }
3867            // "comment1" is not considered to be leading comment for node.initializer
3868            // but rather a trailing comment on the previous node.
3869            const initializer = node.initializer;
3870            if ((getEmitFlags(initializer) & EmitFlags.NoLeadingComments) === 0) {
3871                const commentRange = getCommentRange(initializer);
3872                emitTrailingCommentsOfPosition(commentRange.pos);
3873            }
3874            emitExpression(initializer, parenthesizer.parenthesizeExpressionForDisallowedComma);
3875        }
3876
3877        function emitShorthandPropertyAssignment(node: ShorthandPropertyAssignment) {
3878            emit(node.name);
3879            if (node.objectAssignmentInitializer) {
3880                writeSpace();
3881                writePunctuation("=");
3882                writeSpace();
3883                emitExpression(node.objectAssignmentInitializer, parenthesizer.parenthesizeExpressionForDisallowedComma);
3884            }
3885        }
3886
3887        function emitSpreadAssignment(node: SpreadAssignment) {
3888            if (node.expression) {
3889                emitTokenWithComment(SyntaxKind.DotDotDotToken, node.pos, writePunctuation, node);
3890                emitExpression(node.expression, parenthesizer.parenthesizeExpressionForDisallowedComma);
3891            }
3892        }
3893
3894        //
3895        // Enum
3896        //
3897
3898        function emitEnumMember(node: EnumMember) {
3899            emit(node.name);
3900            emitInitializer(node.initializer, node.name.end, node, parenthesizer.parenthesizeExpressionForDisallowedComma);
3901        }
3902
3903        //
3904        // JSDoc
3905        //
3906        function emitJSDoc(node: JSDoc) {
3907            write("/**");
3908            if (node.comment) {
3909                const text = getTextOfJSDocComment(node.comment);
3910                if (text) {
3911                    const lines = text.split(/\r\n?|\n/g);
3912                    for (const line of lines) {
3913                        writeLine();
3914                        writeSpace();
3915                        writePunctuation("*");
3916                        writeSpace();
3917                        write(line);
3918                    }
3919                }
3920            }
3921            if (node.tags) {
3922                if (node.tags.length === 1 && node.tags[0].kind === SyntaxKind.JSDocTypeTag && !node.comment) {
3923                    writeSpace();
3924                    emit(node.tags[0]);
3925                }
3926                else {
3927                    emitList(node, node.tags, ListFormat.JSDocComment);
3928                }
3929            }
3930            writeSpace();
3931            write("*/");
3932        }
3933
3934        function emitJSDocSimpleTypedTag(tag: JSDocTypeTag | JSDocThisTag | JSDocEnumTag | JSDocReturnTag) {
3935            emitJSDocTagName(tag.tagName);
3936            emitJSDocTypeExpression(tag.typeExpression);
3937            emitJSDocComment(tag.comment);
3938        }
3939
3940        function emitJSDocSeeTag(tag: JSDocSeeTag) {
3941            emitJSDocTagName(tag.tagName);
3942            emit(tag.name);
3943            emitJSDocComment(tag.comment);
3944        }
3945
3946        function emitJSDocNameReference(node: JSDocNameReference) {
3947            writeSpace();
3948            writePunctuation("{");
3949            emit(node.name);
3950            writePunctuation("}");
3951        }
3952
3953        function emitJSDocHeritageTag(tag: JSDocImplementsTag | JSDocAugmentsTag) {
3954            emitJSDocTagName(tag.tagName);
3955            writeSpace();
3956            writePunctuation("{");
3957            emit(tag.class);
3958            writePunctuation("}");
3959            emitJSDocComment(tag.comment);
3960        }
3961
3962        function emitJSDocTemplateTag(tag: JSDocTemplateTag) {
3963            emitJSDocTagName(tag.tagName);
3964            emitJSDocTypeExpression(tag.constraint);
3965            writeSpace();
3966            emitList(tag, tag.typeParameters, ListFormat.CommaListElements);
3967            emitJSDocComment(tag.comment);
3968        }
3969
3970        function emitJSDocTypedefTag(tag: JSDocTypedefTag) {
3971            emitJSDocTagName(tag.tagName);
3972            if (tag.typeExpression) {
3973                if (tag.typeExpression.kind === SyntaxKind.JSDocTypeExpression) {
3974                    emitJSDocTypeExpression(tag.typeExpression);
3975                }
3976                else {
3977                    writeSpace();
3978                    writePunctuation("{");
3979                    write("Object");
3980                    if (tag.typeExpression.isArrayType) {
3981                        writePunctuation("[");
3982                        writePunctuation("]");
3983                    }
3984                    writePunctuation("}");
3985                }
3986            }
3987            if (tag.fullName) {
3988                writeSpace();
3989                emit(tag.fullName);
3990            }
3991            emitJSDocComment(tag.comment);
3992            if (tag.typeExpression && tag.typeExpression.kind === SyntaxKind.JSDocTypeLiteral) {
3993                emitJSDocTypeLiteral(tag.typeExpression);
3994            }
3995        }
3996
3997        function emitJSDocCallbackTag(tag: JSDocCallbackTag) {
3998            emitJSDocTagName(tag.tagName);
3999            if (tag.name) {
4000                writeSpace();
4001                emit(tag.name);
4002            }
4003            emitJSDocComment(tag.comment);
4004            emitJSDocSignature(tag.typeExpression);
4005        }
4006
4007        function emitJSDocSimpleTag(tag: JSDocTag) {
4008            emitJSDocTagName(tag.tagName);
4009            emitJSDocComment(tag.comment);
4010        }
4011
4012        function emitJSDocTypeLiteral(lit: JSDocTypeLiteral) {
4013            emitList(lit, factory.createNodeArray(lit.jsDocPropertyTags), ListFormat.JSDocComment);
4014        }
4015
4016        function emitJSDocSignature(sig: JSDocSignature) {
4017            if (sig.typeParameters) {
4018                emitList(sig, factory.createNodeArray(sig.typeParameters), ListFormat.JSDocComment);
4019            }
4020            if (sig.parameters) {
4021                emitList(sig, factory.createNodeArray(sig.parameters), ListFormat.JSDocComment);
4022            }
4023            if (sig.type) {
4024                writeLine();
4025                writeSpace();
4026                writePunctuation("*");
4027                writeSpace();
4028                emit(sig.type);
4029            }
4030        }
4031
4032        function emitJSDocPropertyLikeTag(param: JSDocPropertyLikeTag) {
4033            emitJSDocTagName(param.tagName);
4034            emitJSDocTypeExpression(param.typeExpression);
4035            writeSpace();
4036            if (param.isBracketed) {
4037                writePunctuation("[");
4038            }
4039            emit(param.name);
4040            if (param.isBracketed) {
4041                writePunctuation("]");
4042            }
4043            emitJSDocComment(param.comment);
4044        }
4045
4046        function emitJSDocTagName(tagName: Identifier) {
4047            writePunctuation("@");
4048            emit(tagName);
4049        }
4050
4051        function emitJSDocComment(comment: string | NodeArray<JSDocComment> | undefined) {
4052            const text = getTextOfJSDocComment(comment);
4053            if (text) {
4054                writeSpace();
4055                write(text);
4056            }
4057        }
4058
4059        function emitJSDocTypeExpression(typeExpression: JSDocTypeExpression | undefined) {
4060            if (typeExpression) {
4061                writeSpace();
4062                writePunctuation("{");
4063                emit(typeExpression.type);
4064                writePunctuation("}");
4065            }
4066        }
4067
4068        //
4069        // Top-level nodes
4070        //
4071
4072        function emitSourceFile(node: SourceFile) {
4073            writeLine();
4074            // @ts-ignore
4075            if (node.writeTsHarComments) {
4076                writeComment('// @keepTs\n// @ts-nocheck\n');
4077            }
4078            const statements = node.statements;
4079            // Emit detached comment if there are no prologue directives or if the first node is synthesized.
4080            // The synthesized node will have no leading comment so some comments may be missed.
4081            const shouldEmitDetachedComment = statements.length === 0 ||
4082                !isPrologueDirective(statements[0]) ||
4083                nodeIsSynthesized(statements[0]);
4084            if (shouldEmitDetachedComment) {
4085                emitBodyWithDetachedComments(node, statements, emitSourceFileWorker);
4086                return;
4087            }
4088            emitSourceFileWorker(node);
4089        }
4090
4091        function emitSyntheticTripleSlashReferencesIfNeeded(node: Bundle) {
4092            emitTripleSlashDirectives(!!node.hasNoDefaultLib, node.syntheticFileReferences || [], node.syntheticTypeReferences || [], node.syntheticLibReferences || []);
4093            for (const prepend of node.prepends) {
4094                if (isUnparsedSource(prepend) && prepend.syntheticReferences) {
4095                    for (const ref of prepend.syntheticReferences) {
4096                        emit(ref);
4097                        writeLine();
4098                    }
4099                }
4100            }
4101        }
4102
4103        function emitTripleSlashDirectivesIfNeeded(node: SourceFile) {
4104            if (node.isDeclarationFile) emitTripleSlashDirectives(node.hasNoDefaultLib, node.referencedFiles, node.typeReferenceDirectives, node.libReferenceDirectives);
4105        }
4106
4107        function emitTripleSlashDirectives(hasNoDefaultLib: boolean, files: readonly FileReference[], types: readonly FileReference[], libs: readonly FileReference[]) {
4108            if (hasNoDefaultLib) {
4109                const pos = writer.getTextPos();
4110                writeComment(`/// <reference no-default-lib="true"/>`);
4111                if (bundleFileInfo) bundleFileInfo.sections.push({ pos, end: writer.getTextPos(), kind: BundleFileSectionKind.NoDefaultLib });
4112                writeLine();
4113            }
4114            if (currentSourceFile && currentSourceFile.moduleName) {
4115                writeComment(`/// <amd-module name="${currentSourceFile.moduleName}" />`);
4116                writeLine();
4117            }
4118            if (currentSourceFile && currentSourceFile.amdDependencies) {
4119                for (const dep of currentSourceFile.amdDependencies) {
4120                    if (dep.name) {
4121                        writeComment(`/// <amd-dependency name="${dep.name}" path="${dep.path}" />`);
4122                    }
4123                    else {
4124                        writeComment(`/// <amd-dependency path="${dep.path}" />`);
4125                    }
4126                    writeLine();
4127                }
4128            }
4129            for (const directive of files) {
4130                const pos = writer.getTextPos();
4131                writeComment(`/// <reference path="${directive.fileName}" />`);
4132                if (bundleFileInfo) bundleFileInfo.sections.push({ pos, end: writer.getTextPos(), kind: BundleFileSectionKind.Reference, data: directive.fileName });
4133                writeLine();
4134            }
4135            for (const directive of types) {
4136                const pos = writer.getTextPos();
4137                const resolutionMode = directive.resolutionMode && directive.resolutionMode !== currentSourceFile?.impliedNodeFormat
4138                    ? `resolution-mode="${directive.resolutionMode === ModuleKind.ESNext ? "import" : "require"}"`
4139                    : "";
4140                writeComment(`/// <reference types="${directive.fileName}" ${resolutionMode}/>`);
4141                if (bundleFileInfo) bundleFileInfo.sections.push({ pos, end: writer.getTextPos(), kind: !directive.resolutionMode ? BundleFileSectionKind.Type : directive.resolutionMode === ModuleKind.ESNext ? BundleFileSectionKind.TypeResolutionModeImport : BundleFileSectionKind.TypeResolutionModeRequire, data: directive.fileName });
4142                writeLine();
4143            }
4144            for (const directive of libs) {
4145                const pos = writer.getTextPos();
4146                writeComment(`/// <reference lib="${directive.fileName}" />`);
4147                if (bundleFileInfo) bundleFileInfo.sections.push({ pos, end: writer.getTextPos(), kind: BundleFileSectionKind.Lib, data: directive.fileName });
4148                writeLine();
4149            }
4150        }
4151
4152        function emitSourceFileWorker(node: SourceFile) {
4153            const statements = node.statements;
4154            pushNameGenerationScope(node);
4155            forEach(node.statements, generateNames);
4156            emitHelpers(node);
4157            const index = findIndex(statements, statement => !isPrologueDirective(statement));
4158            emitTripleSlashDirectivesIfNeeded(node);
4159            emitList(node, statements, ListFormat.MultiLine, /*parenthesizerRule*/ undefined, index === -1 ? statements.length : index);
4160            popNameGenerationScope(node);
4161        }
4162
4163        // Transformation nodes
4164
4165        function emitPartiallyEmittedExpression(node: PartiallyEmittedExpression) {
4166            const emitFlags = getEmitFlags(node);
4167            if (!(emitFlags & EmitFlags.NoLeadingComments) && node.pos !== node.expression.pos) {
4168                emitTrailingCommentsOfPosition(node.expression.pos);
4169            }
4170            emitExpression(node.expression);
4171            if (!(emitFlags & EmitFlags.NoTrailingComments) && node.end !== node.expression.end) {
4172                emitLeadingCommentsOfPosition(node.expression.end);
4173            }
4174        }
4175
4176        function emitCommaList(node: CommaListExpression) {
4177            emitExpressionList(node, node.elements, ListFormat.CommaListElements, /*parenthesizerRule*/ undefined);
4178        }
4179
4180        /**
4181         * Emits any prologue directives at the start of a Statement list, returning the
4182         * number of prologue directives written to the output.
4183         */
4184        function emitPrologueDirectives(statements: readonly Node[], sourceFile?: SourceFile, seenPrologueDirectives?: Set<string>, recordBundleFileSection?: true): number {
4185            let needsToSetSourceFile = !!sourceFile;
4186            for (let i = 0; i < statements.length; i++) {
4187                const statement = statements[i];
4188                if (isPrologueDirective(statement)) {
4189                    const shouldEmitPrologueDirective = seenPrologueDirectives ? !seenPrologueDirectives.has(statement.expression.text) : true;
4190                    if (shouldEmitPrologueDirective) {
4191                        if (needsToSetSourceFile) {
4192                            needsToSetSourceFile = false;
4193                            setSourceFile(sourceFile);
4194                        }
4195                        writeLine();
4196                        const pos = writer.getTextPos();
4197                        emit(statement);
4198                        if (recordBundleFileSection && bundleFileInfo) bundleFileInfo.sections.push({ pos, end: writer.getTextPos(), kind: BundleFileSectionKind.Prologue, data: statement.expression.text });
4199                        if (seenPrologueDirectives) {
4200                            seenPrologueDirectives.add(statement.expression.text);
4201                        }
4202                    }
4203                }
4204                else {
4205                    // return index of the first non prologue directive
4206                    return i;
4207                }
4208            }
4209
4210            return statements.length;
4211        }
4212
4213        function emitUnparsedPrologues(prologues: readonly UnparsedPrologue[], seenPrologueDirectives: Set<string>) {
4214            for (const prologue of prologues) {
4215                if (!seenPrologueDirectives.has(prologue.data)) {
4216                    writeLine();
4217                    const pos = writer.getTextPos();
4218                    emit(prologue);
4219                    if (bundleFileInfo) bundleFileInfo.sections.push({ pos, end: writer.getTextPos(), kind: BundleFileSectionKind.Prologue, data: prologue.data });
4220                    if (seenPrologueDirectives) {
4221                        seenPrologueDirectives.add(prologue.data);
4222                    }
4223                }
4224            }
4225        }
4226
4227        function emitPrologueDirectivesIfNeeded(sourceFileOrBundle: Bundle | SourceFile) {
4228            if (isSourceFile(sourceFileOrBundle)) {
4229                emitPrologueDirectives(sourceFileOrBundle.statements, sourceFileOrBundle);
4230            }
4231            else {
4232                const seenPrologueDirectives = new Set<string>();
4233                for (const prepend of sourceFileOrBundle.prepends) {
4234                    emitUnparsedPrologues((prepend as UnparsedSource).prologues, seenPrologueDirectives);
4235                }
4236                for (const sourceFile of sourceFileOrBundle.sourceFiles) {
4237                    emitPrologueDirectives(sourceFile.statements, sourceFile, seenPrologueDirectives, /*recordBundleFileSection*/ true);
4238                }
4239                setSourceFile(undefined);
4240            }
4241        }
4242
4243        function getPrologueDirectivesFromBundledSourceFiles(bundle: Bundle): SourceFilePrologueInfo[] | undefined {
4244            const seenPrologueDirectives = new Set<string>();
4245            let prologues: SourceFilePrologueInfo[] | undefined;
4246            for (let index = 0; index < bundle.sourceFiles.length; index++) {
4247                const sourceFile = bundle.sourceFiles[index];
4248                let directives: SourceFilePrologueDirective[] | undefined;
4249                let end = 0;
4250                for (const statement of sourceFile.statements) {
4251                    if (!isPrologueDirective(statement)) break;
4252                    if (seenPrologueDirectives.has(statement.expression.text)) continue;
4253                    seenPrologueDirectives.add(statement.expression.text);
4254                    (directives || (directives = [])).push({
4255                        pos: statement.pos,
4256                        end: statement.end,
4257                        expression: {
4258                            pos: statement.expression.pos,
4259                            end: statement.expression.end,
4260                            text: statement.expression.text
4261                        }
4262                    });
4263                    end = end < statement.end ? statement.end : end;
4264                }
4265                if (directives) (prologues || (prologues = [])).push({ file: index, text: sourceFile.text.substring(0, end), directives });
4266            }
4267            return prologues;
4268        }
4269
4270        function emitShebangIfNeeded(sourceFileOrBundle: Bundle | SourceFile | UnparsedSource) {
4271            if (isSourceFile(sourceFileOrBundle) || isUnparsedSource(sourceFileOrBundle)) {
4272                const shebang = getShebang(sourceFileOrBundle.text);
4273                if (shebang) {
4274                    writeComment(shebang);
4275                    writeLine();
4276                    return true;
4277                }
4278            }
4279            else {
4280                for (const prepend of sourceFileOrBundle.prepends) {
4281                    Debug.assertNode(prepend, isUnparsedSource);
4282                    if (emitShebangIfNeeded(prepend)) {
4283                        return true;
4284                    }
4285                }
4286                for (const sourceFile of sourceFileOrBundle.sourceFiles) {
4287                    // Emit only the first encountered shebang
4288                    if (emitShebangIfNeeded(sourceFile)) {
4289                        return true;
4290                    }
4291                }
4292            }
4293        }
4294
4295        //
4296        // Helpers
4297        //
4298
4299        function emitNodeWithWriter(node: Node | undefined, writer: typeof write) {
4300            if (!node) return;
4301            const savedWrite = write;
4302            write = writer;
4303            emit(node);
4304            write = savedWrite;
4305        }
4306
4307        function emitDecoratorsAndModifiers(node: Node, modifiers: NodeArray<ModifierLike> | undefined) {
4308            if (modifiers?.length) {
4309                if (every(modifiers, isModifier)) {
4310                    // if all modifier-likes are `Modifier`, simply emit the array as modifiers.
4311                    return emitModifiers(node, modifiers as NodeArray<Modifier>);
4312                }
4313
4314                if (every(modifiers, isDecorator)) {
4315                    // if all modifier-likes are `Decorator`, simply emit the array as decorators.
4316                    return emitDecorators(node, modifiers as NodeArray<Decorator>);
4317                }
4318
4319                onBeforeEmitNodeArray?.(modifiers);
4320
4321                // partition modifiers into contiguous chunks of `Modifier` or `Decorator`
4322                let lastMode: "modifiers" | "decorators" | undefined;
4323                let mode: "modifiers" | "decorators" | undefined;
4324                let start = 0;
4325                let pos = 0;
4326                while (start < modifiers.length) {
4327                    while (pos < modifiers.length) {
4328                        const modifier = modifiers[pos];
4329                        mode = isDecorator(modifier) ? "decorators" : "modifiers";
4330                        if (lastMode === undefined) {
4331                            lastMode = mode;
4332                        }
4333                        else if (mode !== lastMode) {
4334                            break;
4335                        }
4336
4337                        pos++;
4338                    }
4339
4340                    const textRange: TextRange = { pos: -1, end: -1 };
4341                    if (start === 0) textRange.pos = modifiers.pos;
4342                    if (pos === modifiers.length - 1) textRange.end = modifiers.end;
4343                    emitNodeListItems(
4344                        emit,
4345                        node,
4346                        modifiers,
4347                        lastMode === "modifiers" ? ListFormat.Modifiers : ListFormat.Decorators,
4348                        /*parenthesizerRule*/ undefined,
4349                        start,
4350                        pos - start,
4351                        /*hasTrailingComma*/ false,
4352                        textRange);
4353                    start = pos;
4354                    lastMode = mode;
4355                    pos++;
4356                }
4357
4358                onAfterEmitNodeArray?.(modifiers);
4359            }
4360        }
4361
4362        function emitModifiers(node: Node, modifiers: NodeArray<Modifier> | undefined): void {
4363            emitList(node, modifiers, ListFormat.Modifiers);
4364        }
4365
4366        function emitTypeAnnotation(node: TypeNode | undefined) {
4367            if (node) {
4368                writePunctuation(":");
4369                writeSpace();
4370                emit(node);
4371            }
4372        }
4373
4374        function emitInitializer(node: Expression | undefined, equalCommentStartPos: number, container: Node, parenthesizerRule?: (node: Expression) => Expression) {
4375            if (node) {
4376                writeSpace();
4377                emitTokenWithComment(SyntaxKind.EqualsToken, equalCommentStartPos, writeOperator, container);
4378                writeSpace();
4379                emitExpression(node, parenthesizerRule);
4380            }
4381        }
4382
4383        function emitNodeWithPrefix<T extends Node>(prefix: string, prefixWriter: (s: string) => void, node: T | undefined, emit: (node: T) => void) {
4384            if (node) {
4385                prefixWriter(prefix);
4386                emit(node);
4387            }
4388        }
4389
4390        function emitWithLeadingSpace(node: Node | undefined) {
4391            if (node) {
4392                writeSpace();
4393                emit(node);
4394            }
4395        }
4396
4397        function emitExpressionWithLeadingSpace(node: Expression | undefined, parenthesizerRule?: (node: Expression) => Expression) {
4398            if (node) {
4399                writeSpace();
4400                emitExpression(node, parenthesizerRule);
4401            }
4402        }
4403
4404        function emitWithTrailingSpace(node: Node | undefined) {
4405            if (node) {
4406                emit(node);
4407                writeSpace();
4408            }
4409        }
4410
4411        function emitEmbeddedStatement(parent: Node, node: Statement) {
4412            if (isBlock(node) || getEmitFlags(parent) & EmitFlags.SingleLine) {
4413                writeSpace();
4414                emit(node);
4415            }
4416            else {
4417                writeLine();
4418                increaseIndent();
4419                if (isEmptyStatement(node)) {
4420                    pipelineEmit(EmitHint.EmbeddedStatement, node);
4421                }
4422                else {
4423                    emit(node);
4424                }
4425                decreaseIndent();
4426            }
4427        }
4428
4429        function emitDecorators(parentNode: Node, decorators: NodeArray<Decorator> | undefined): void {
4430            emitList(parentNode, decorators, ListFormat.Decorators);
4431        }
4432
4433        function emitTypeArguments(parentNode: Node, typeArguments: NodeArray<TypeNode> | undefined) {
4434            emitList(parentNode, typeArguments, ListFormat.TypeArguments, typeArgumentParenthesizerRuleSelector);
4435        }
4436
4437        function emitTypeParameters(parentNode: SignatureDeclaration | InterfaceDeclaration | TypeAliasDeclaration | ClassDeclaration | ClassExpression | StructDeclaration, typeParameters: NodeArray<TypeParameterDeclaration> | undefined) {
4438            if (isFunctionLike(parentNode) && parentNode.typeArguments) { // Quick info uses type arguments in place of type parameters on instantiated signatures
4439                return emitTypeArguments(parentNode, parentNode.typeArguments);
4440            }
4441            emitList(parentNode, typeParameters, ListFormat.TypeParameters);
4442        }
4443
4444        function emitParameters(parentNode: Node, parameters: NodeArray<ParameterDeclaration>) {
4445            emitList(parentNode, parameters, ListFormat.Parameters);
4446        }
4447
4448        function canEmitSimpleArrowHead(parentNode: FunctionTypeNode | ArrowFunction, parameters: NodeArray<ParameterDeclaration>) {
4449            const parameter = singleOrUndefined(parameters);
4450            return parameter
4451                && parameter.pos === parentNode.pos // may not have parsed tokens between parent and parameter
4452                && isArrowFunction(parentNode)      // only arrow functions may have simple arrow head
4453                && !parentNode.type                 // arrow function may not have return type annotation
4454                && !some(parentNode.modifiers)      // parent may not have decorators or modifiers
4455                && !some(parentNode.typeParameters) // parent may not have type parameters
4456                && !some(parameter.modifiers)       // parameter may not have decorators or modifiers
4457                && !parameter.dotDotDotToken        // parameter may not be rest
4458                && !parameter.questionToken         // parameter may not be optional
4459                && !parameter.type                  // parameter may not have a type annotation
4460                && !parameter.initializer           // parameter may not have an initializer
4461                && isIdentifier(parameter.name);    // parameter name must be identifier
4462        }
4463
4464        function emitParametersForArrow(parentNode: FunctionTypeNode | ArrowFunction, parameters: NodeArray<ParameterDeclaration>) {
4465            if (canEmitSimpleArrowHead(parentNode, parameters)) {
4466                emitList(parentNode, parameters, ListFormat.Parameters & ~ListFormat.Parenthesis);
4467            }
4468            else {
4469                emitParameters(parentNode, parameters);
4470            }
4471        }
4472
4473        function emitParametersForIndexSignature(parentNode: Node, parameters: NodeArray<ParameterDeclaration>) {
4474            emitList(parentNode, parameters, ListFormat.IndexSignatureParameters);
4475        }
4476
4477        function writeDelimiter(format: ListFormat) {
4478            switch (format & ListFormat.DelimitersMask) {
4479                case ListFormat.None:
4480                    break;
4481                case ListFormat.CommaDelimited:
4482                    writePunctuation(",");
4483                    break;
4484                case ListFormat.BarDelimited:
4485                    writeSpace();
4486                    writePunctuation("|");
4487                    break;
4488                case ListFormat.AsteriskDelimited:
4489                    writeSpace();
4490                    writePunctuation("*");
4491                    writeSpace();
4492                    break;
4493                case ListFormat.AmpersandDelimited:
4494                    writeSpace();
4495                    writePunctuation("&");
4496                    break;
4497            }
4498        }
4499
4500        function emitList(parentNode: Node | undefined, children: NodeArray<Node> | undefined, format: ListFormat, parenthesizerRule?: ParenthesizerRuleOrSelector<Node>, start?: number, count?: number) {
4501            emitNodeList(emit, parentNode, children, format, parenthesizerRule, start, count);
4502        }
4503
4504        function emitExpressionList(parentNode: Node | undefined, children: NodeArray<Node> | undefined, format: ListFormat, parenthesizerRule?: ParenthesizerRuleOrSelector<Expression>, start?: number, count?: number) {
4505            emitNodeList(emitExpression, parentNode, children, format, parenthesizerRule, start, count);
4506        }
4507
4508        function emitNodeList(emit: (node: Node, parenthesizerRule?: ((node: Node) => Node) | undefined) => void, parentNode: Node | undefined, children: NodeArray<Node> | undefined, format: ListFormat, parenthesizerRule: ParenthesizerRuleOrSelector<Node> | undefined, start = 0, count = children ? children.length - start : 0) {
4509            const isUndefined = children === undefined;
4510            if (isUndefined && format & ListFormat.OptionalIfUndefined) {
4511                return;
4512            }
4513
4514            const isEmpty = children === undefined || start >= children.length || count === 0;
4515            if (isEmpty && format & ListFormat.OptionalIfEmpty) {
4516                onBeforeEmitNodeArray?.(children);
4517                onAfterEmitNodeArray?.(children);
4518                return;
4519            }
4520
4521            if (format & ListFormat.BracketsMask) {
4522                writePunctuation(getOpeningBracket(format));
4523                if (isEmpty && children) {
4524                    emitTrailingCommentsOfPosition(children.pos, /*prefixSpace*/ true); // Emit comments within empty bracketed lists
4525                }
4526            }
4527
4528            onBeforeEmitNodeArray?.(children);
4529
4530            if (isEmpty) {
4531                // Write a line terminator if the parent node was multi-line
4532                if (format & ListFormat.MultiLine && !(preserveSourceNewlines && (!parentNode || currentSourceFile && rangeIsOnSingleLine(parentNode, currentSourceFile)))) {
4533                    writeLine();
4534                }
4535                else if (format & ListFormat.SpaceBetweenBraces && !(format & ListFormat.NoSpaceIfEmpty)) {
4536                    writeSpace();
4537                }
4538            }
4539            else {
4540                emitNodeListItems(emit, parentNode, children, format, parenthesizerRule, start, count, children.hasTrailingComma, children);
4541            }
4542
4543            onAfterEmitNodeArray?.(children);
4544
4545            if (format & ListFormat.BracketsMask) {
4546                if (isEmpty && children) {
4547                    emitLeadingCommentsOfPosition(children.end); // Emit leading comments within empty lists
4548                }
4549                writePunctuation(getClosingBracket(format));
4550            }
4551        }
4552
4553        /**
4554         * Emits a list without brackets or raising events.
4555         *
4556         * NOTE: You probably don't want to call this directly and should be using `emitList` or `emitExpressionList` instead.
4557         */
4558        function emitNodeListItems(emit: (node: Node, parenthesizerRule?: ((node: Node) => Node) | undefined) => void, parentNode: Node | undefined, children: readonly Node[], format: ListFormat, parenthesizerRule: ParenthesizerRuleOrSelector<Node> | undefined, start: number, count: number, hasTrailingComma: boolean, childrenTextRange: TextRange | undefined) {
4559            // Write the opening line terminator or leading whitespace.
4560            const mayEmitInterveningComments = (format & ListFormat.NoInterveningComments) === 0;
4561            let shouldEmitInterveningComments = mayEmitInterveningComments;
4562
4563            const leadingLineTerminatorCount = getLeadingLineTerminatorCount(parentNode, children[start], format);
4564            if (leadingLineTerminatorCount) {
4565                writeLine(leadingLineTerminatorCount);
4566                shouldEmitInterveningComments = false;
4567            }
4568            else if (format & ListFormat.SpaceBetweenBraces) {
4569                writeSpace();
4570            }
4571
4572            // Increase the indent, if requested.
4573            if (format & ListFormat.Indented) {
4574                increaseIndent();
4575            }
4576
4577            const emitListItem = getEmitListItem(emit, parenthesizerRule);
4578
4579            // Emit each child.
4580            let previousSibling: Node | undefined;
4581            let previousSourceFileTextKind: ReturnType<typeof recordBundleFileInternalSectionStart>;
4582            let shouldDecreaseIndentAfterEmit = false;
4583            for (let i = 0; i < count; i++) {
4584                const child = children[start + i];
4585
4586                // Write the delimiter if this is not the first node.
4587                if (format & ListFormat.AsteriskDelimited) {
4588                    // always write JSDoc in the format "\n *"
4589                    writeLine();
4590                    writeDelimiter(format);
4591                }
4592                else if (previousSibling) {
4593                    // i.e
4594                    //      function commentedParameters(
4595                    //          /* Parameter a */
4596                    //          a
4597                    //          /* End of parameter a */ -> this comment isn't considered to be trailing comment of parameter "a" due to newline
4598                    //          ,
4599                    if (format & ListFormat.DelimitersMask && previousSibling.end !== (parentNode ? parentNode.end : -1)) {
4600                        emitLeadingCommentsOfPosition(previousSibling.end);
4601                    }
4602                    writeDelimiter(format);
4603                    recordBundleFileInternalSectionEnd(previousSourceFileTextKind);
4604
4605                    // Write either a line terminator or whitespace to separate the elements.
4606                    const separatingLineTerminatorCount = getSeparatingLineTerminatorCount(previousSibling, child, format);
4607                    if (separatingLineTerminatorCount > 0) {
4608                        // If a synthesized node in a single-line list starts on a new
4609                        // line, we should increase the indent.
4610                        if ((format & (ListFormat.LinesMask | ListFormat.Indented)) === ListFormat.SingleLine) {
4611                            increaseIndent();
4612                            shouldDecreaseIndentAfterEmit = true;
4613                        }
4614
4615                        writeLine(separatingLineTerminatorCount);
4616                        shouldEmitInterveningComments = false;
4617                    }
4618                    else if (previousSibling && format & ListFormat.SpaceBetweenSiblings) {
4619                        writeSpace();
4620                    }
4621                }
4622
4623                // Emit this child.
4624                previousSourceFileTextKind = recordBundleFileInternalSectionStart(child);
4625                if (shouldEmitInterveningComments) {
4626                    const commentRange = getCommentRange(child);
4627                    emitTrailingCommentsOfPosition(commentRange.pos);
4628                }
4629                else {
4630                    shouldEmitInterveningComments = mayEmitInterveningComments;
4631                }
4632
4633                nextListElementPos = child.pos;
4634                emitListItem(child, emit, parenthesizerRule, i);
4635
4636                if (shouldDecreaseIndentAfterEmit) {
4637                    decreaseIndent();
4638                    shouldDecreaseIndentAfterEmit = false;
4639                }
4640
4641                previousSibling = child;
4642            }
4643
4644            // Write a trailing comma, if requested.
4645            const emitFlags = previousSibling ? getEmitFlags(previousSibling) : 0;
4646            const skipTrailingComments = commentsDisabled || !!(emitFlags & EmitFlags.NoTrailingComments);
4647            const emitTrailingComma = hasTrailingComma && (format & ListFormat.AllowTrailingComma) && (format & ListFormat.CommaDelimited);
4648            if (emitTrailingComma) {
4649                if (previousSibling && !skipTrailingComments) {
4650                    emitTokenWithComment(SyntaxKind.CommaToken, previousSibling.end, writePunctuation, previousSibling);
4651                }
4652                else {
4653                    writePunctuation(",");
4654                }
4655            }
4656
4657            // Emit any trailing comment of the last element in the list
4658            // i.e
4659            //       var array = [...
4660            //          2
4661            //          /* end of element 2 */
4662            //       ];
4663            if (previousSibling && (parentNode ? parentNode.end : -1) !== previousSibling.end && (format & ListFormat.DelimitersMask) && !skipTrailingComments) {
4664                emitLeadingCommentsOfPosition(emitTrailingComma && childrenTextRange?.end ? childrenTextRange.end : previousSibling.end);
4665            }
4666
4667            // Decrease the indent, if requested.
4668            if (format & ListFormat.Indented) {
4669                decreaseIndent();
4670            }
4671
4672            recordBundleFileInternalSectionEnd(previousSourceFileTextKind);
4673
4674            // Write the closing line terminator or closing whitespace.
4675            const closingLineTerminatorCount = getClosingLineTerminatorCount(parentNode, children[start + count - 1], format, childrenTextRange);
4676            if (closingLineTerminatorCount) {
4677                writeLine(closingLineTerminatorCount);
4678            }
4679            else if (format & (ListFormat.SpaceAfterList | ListFormat.SpaceBetweenBraces)) {
4680                writeSpace();
4681            }
4682        }
4683
4684        // Writers
4685
4686        function writeLiteral(s: string) {
4687            writer.writeLiteral(s);
4688        }
4689
4690        function writeStringLiteral(s: string) {
4691            writer.writeStringLiteral(s);
4692        }
4693
4694        function writeBase(s: string) {
4695            writer.write(s);
4696        }
4697
4698        function writeSymbol(s: string, sym: Symbol) {
4699            writer.writeSymbol(s, sym);
4700        }
4701
4702        function writePunctuation(s: string) {
4703            writer.writePunctuation(s);
4704        }
4705
4706        function writeTrailingSemicolon() {
4707            writer.writeTrailingSemicolon(";");
4708        }
4709
4710        function writeKeyword(s: string) {
4711            writer.writeKeyword(s);
4712        }
4713
4714        function writeOperator(s: string) {
4715            writer.writeOperator(s);
4716        }
4717
4718        function writeParameter(s: string) {
4719            writer.writeParameter(s);
4720        }
4721
4722        function writeComment(s: string) {
4723            writer.writeComment(s);
4724        }
4725
4726        function writeSpace() {
4727            writer.writeSpace(" ");
4728        }
4729
4730        function writeProperty(s: string) {
4731            writer.writeProperty(s);
4732        }
4733
4734        function nonEscapingWrite(s: string) {
4735            // This should be defined in a snippet-escaping text writer.
4736            if (writer.nonEscapingWrite) {
4737                writer.nonEscapingWrite(s);
4738            }
4739            else {
4740                writer.write(s);
4741            }
4742        }
4743
4744        function writeLine(count = 1) {
4745            for (let i = 0; i < count; i++) {
4746                writer.writeLine(i > 0);
4747            }
4748        }
4749
4750        function increaseIndent() {
4751            writer.increaseIndent();
4752        }
4753
4754        function decreaseIndent() {
4755            writer.decreaseIndent();
4756        }
4757
4758        function writeToken(token: SyntaxKind, pos: number, writer: (s: string) => void, contextNode?: Node) {
4759            return !sourceMapsDisabled
4760                ? emitTokenWithSourceMap(contextNode, token, writer, pos, writeTokenText)
4761                : writeTokenText(token, writer, pos);
4762        }
4763
4764        function writeTokenNode(node: Node, writer: (s: string) => void) {
4765            if (onBeforeEmitToken) {
4766                onBeforeEmitToken(node);
4767            }
4768            writer(tokenToString(node.kind)!);
4769            if (onAfterEmitToken) {
4770                onAfterEmitToken(node);
4771            }
4772        }
4773
4774        function writeTokenText(token: SyntaxKind, writer: (s: string) => void): void;
4775        function writeTokenText(token: SyntaxKind, writer: (s: string) => void, pos: number): number;
4776        function writeTokenText(token: SyntaxKind, writer: (s: string) => void, pos?: number): number {
4777            const tokenString = tokenToString(token)!;
4778            writer(tokenString);
4779            return pos! < 0 ? pos! : pos! + tokenString.length;
4780        }
4781
4782        function writeLineOrSpace(parentNode: Node, prevChildNode: Node, nextChildNode: Node) {
4783            if (getEmitFlags(parentNode) & EmitFlags.SingleLine) {
4784                writeSpace();
4785            }
4786            else if (preserveSourceNewlines) {
4787                const lines = getLinesBetweenNodes(parentNode, prevChildNode, nextChildNode);
4788                if (lines) {
4789                    writeLine(lines);
4790                }
4791                else {
4792                    writeSpace();
4793                }
4794            }
4795            else {
4796                writeLine();
4797            }
4798        }
4799
4800        function writeLines(text: string): void {
4801            const lines = text.split(/\r\n?|\n/g);
4802            const indentation = guessIndentation(lines);
4803            for (const lineText of lines) {
4804                const line = indentation ? lineText.slice(indentation) : lineText;
4805                if (line.length) {
4806                    writeLine();
4807                    write(line);
4808                }
4809            }
4810        }
4811
4812        function writeLinesAndIndent(lineCount: number, writeSpaceIfNotIndenting: boolean) {
4813            if (lineCount) {
4814                increaseIndent();
4815                writeLine(lineCount);
4816            }
4817            else if (writeSpaceIfNotIndenting) {
4818                writeSpace();
4819            }
4820        }
4821
4822        // Helper function to decrease the indent if we previously indented.  Allows multiple
4823        // previous indent values to be considered at a time.  This also allows caller to just
4824        // call this once, passing in all their appropriate indent values, instead of needing
4825        // to call this helper function multiple times.
4826        function decreaseIndentIf(value1: boolean | number | undefined, value2?: boolean | number) {
4827            if (value1) {
4828                decreaseIndent();
4829            }
4830            if (value2) {
4831                decreaseIndent();
4832            }
4833        }
4834
4835        function getLeadingLineTerminatorCount(parentNode: Node | undefined, firstChild: Node | undefined, format: ListFormat): number {
4836            if (format & ListFormat.PreserveLines || preserveSourceNewlines) {
4837                if (format & ListFormat.PreferNewLine) {
4838                    return 1;
4839                }
4840
4841                if (firstChild === undefined) {
4842                    return !parentNode || currentSourceFile && rangeIsOnSingleLine(parentNode, currentSourceFile) ? 0 : 1;
4843                }
4844                if (firstChild.pos === nextListElementPos) {
4845                    // If this child starts at the beginning of a list item in a parent list, its leading
4846                    // line terminators have already been written as the separating line terminators of the
4847                    // parent list. Example:
4848                    //
4849                    // class Foo {
4850                    //   constructor() {}
4851                    //   public foo() {}
4852                    // }
4853                    //
4854                    // The outer list is the list of class members, with one line terminator between the
4855                    // constructor and the method. The constructor is written, the separating line terminator
4856                    // is written, and then we start emitting the method. Its modifiers ([public]) constitute an inner
4857                    // list, so we look for its leading line terminators. If we didn't know that we had already
4858                    // written a newline as part of the parent list, it would appear that we need to write a
4859                    // leading newline to start the modifiers.
4860                    return 0;
4861                }
4862                if (firstChild.kind === SyntaxKind.JsxText) {
4863                    // JsxText will be written with its leading whitespace, so don't add more manually.
4864                    return 0;
4865                }
4866                if (currentSourceFile && parentNode &&
4867                    !positionIsSynthesized(parentNode.pos) &&
4868                    !nodeIsSynthesized(firstChild) &&
4869                    (!firstChild.parent || getOriginalNode(firstChild.parent) === getOriginalNode(parentNode))
4870                ) {
4871                    if (preserveSourceNewlines) {
4872                        return getEffectiveLines(
4873                            includeComments => getLinesBetweenPositionAndPrecedingNonWhitespaceCharacter(
4874                                firstChild.pos,
4875                                parentNode.pos,
4876                                currentSourceFile!,
4877                                includeComments));
4878                    }
4879                    return rangeStartPositionsAreOnSameLine(parentNode, firstChild, currentSourceFile) ? 0 : 1;
4880                }
4881                if (synthesizedNodeStartsOnNewLine(firstChild, format)) {
4882                    return 1;
4883                }
4884            }
4885            return format & ListFormat.MultiLine ? 1 : 0;
4886        }
4887
4888        function getSeparatingLineTerminatorCount(previousNode: Node | undefined, nextNode: Node, format: ListFormat): number {
4889            if (format & ListFormat.PreserveLines || preserveSourceNewlines) {
4890                if (previousNode === undefined || nextNode === undefined) {
4891                    return 0;
4892                }
4893                if (nextNode.kind === SyntaxKind.JsxText) {
4894                    // JsxText will be written with its leading whitespace, so don't add more manually.
4895                    return 0;
4896                }
4897                else if (currentSourceFile && !nodeIsSynthesized(previousNode) && !nodeIsSynthesized(nextNode)) {
4898                    if (preserveSourceNewlines && siblingNodePositionsAreComparable(previousNode, nextNode)) {
4899                        return getEffectiveLines(
4900                            includeComments => getLinesBetweenRangeEndAndRangeStart(
4901                                previousNode,
4902                                nextNode,
4903                                currentSourceFile!,
4904                                includeComments));
4905                    }
4906                    // If `preserveSourceNewlines` is `false` we do not intend to preserve the effective lines between the
4907                    // previous and next node. Instead we naively check whether nodes are on separate lines within the
4908                    // same node parent. If so, we intend to preserve a single line terminator. This is less precise and
4909                    // expensive than checking with `preserveSourceNewlines` as above, but the goal is not to preserve the
4910                    // effective source lines between two sibling nodes.
4911                    else if (!preserveSourceNewlines && originalNodesHaveSameParent(previousNode, nextNode)) {
4912                        return rangeEndIsOnSameLineAsRangeStart(previousNode, nextNode, currentSourceFile) ? 0 : 1;
4913                    }
4914                    // If the two nodes are not comparable, add a line terminator based on the format that can indicate
4915                    // whether new lines are preferred or not.
4916                    return format & ListFormat.PreferNewLine ? 1 : 0;
4917                }
4918                else if (synthesizedNodeStartsOnNewLine(previousNode, format) || synthesizedNodeStartsOnNewLine(nextNode, format)) {
4919                    return 1;
4920                }
4921            }
4922            else if (getStartsOnNewLine(nextNode)) {
4923                return 1;
4924            }
4925            return format & ListFormat.MultiLine ? 1 : 0;
4926        }
4927
4928        function getClosingLineTerminatorCount(parentNode: Node | undefined, lastChild: Node | undefined, format: ListFormat, childrenTextRange: TextRange | undefined): number {
4929            if (format & ListFormat.PreserveLines || preserveSourceNewlines) {
4930                if (format & ListFormat.PreferNewLine) {
4931                    return 1;
4932                }
4933
4934                if (lastChild === undefined) {
4935                    return !parentNode || currentSourceFile && rangeIsOnSingleLine(parentNode, currentSourceFile) ? 0 : 1;
4936                }
4937                if (currentSourceFile && parentNode && !positionIsSynthesized(parentNode.pos) && !nodeIsSynthesized(lastChild) && (!lastChild.parent || lastChild.parent === parentNode)) {
4938                    if (preserveSourceNewlines) {
4939                        const end = childrenTextRange && !positionIsSynthesized(childrenTextRange.end) ? childrenTextRange.end : lastChild.end;
4940                        return getEffectiveLines(
4941                            includeComments => getLinesBetweenPositionAndNextNonWhitespaceCharacter(
4942                                end,
4943                                parentNode.end,
4944                                currentSourceFile!,
4945                                includeComments));
4946                    }
4947                    return rangeEndPositionsAreOnSameLine(parentNode, lastChild, currentSourceFile) ? 0 : 1;
4948                }
4949                if (synthesizedNodeStartsOnNewLine(lastChild, format)) {
4950                    return 1;
4951                }
4952            }
4953            if (format & ListFormat.MultiLine && !(format & ListFormat.NoTrailingNewLine)) {
4954                return 1;
4955            }
4956            return 0;
4957        }
4958
4959        function getEffectiveLines(getLineDifference: (includeComments: boolean) => number) {
4960            // If 'preserveSourceNewlines' is disabled, we should never call this function
4961            // because it could be more expensive than alternative approximations.
4962            Debug.assert(!!preserveSourceNewlines);
4963            // We start by measuring the line difference from a position to its adjacent comments,
4964            // so that this is counted as a one-line difference, not two:
4965            //
4966            //   node1;
4967            //   // NODE2 COMMENT
4968            //   node2;
4969            const lines = getLineDifference(/*includeComments*/ true);
4970            if (lines === 0) {
4971                // However, if the line difference considering comments was 0, we might have this:
4972                //
4973                //   node1; // NODE2 COMMENT
4974                //   node2;
4975                //
4976                // in which case we should be ignoring node2's comment, so this too is counted as
4977                // a one-line difference, not zero.
4978                return getLineDifference(/*includeComments*/ false);
4979            }
4980            return lines;
4981        }
4982
4983        function writeLineSeparatorsAndIndentBefore(node: Node, parent: Node): boolean {
4984            const leadingNewlines = preserveSourceNewlines && getLeadingLineTerminatorCount(parent, node, ListFormat.None);
4985            if (leadingNewlines) {
4986                writeLinesAndIndent(leadingNewlines, /*writeSpaceIfNotIndenting*/ false);
4987            }
4988            return !!leadingNewlines;
4989        }
4990
4991        function writeLineSeparatorsAfter(node: Node, parent: Node) {
4992            const trailingNewlines = preserveSourceNewlines && getClosingLineTerminatorCount(parent, node, ListFormat.None, /*childrenTextRange*/ undefined);
4993            if (trailingNewlines) {
4994                writeLine(trailingNewlines);
4995            }
4996        }
4997
4998        function synthesizedNodeStartsOnNewLine(node: Node, format: ListFormat) {
4999            if (nodeIsSynthesized(node)) {
5000                const startsOnNewLine = getStartsOnNewLine(node);
5001                if (startsOnNewLine === undefined) {
5002                    return (format & ListFormat.PreferNewLine) !== 0;
5003                }
5004
5005                return startsOnNewLine;
5006            }
5007
5008            return (format & ListFormat.PreferNewLine) !== 0;
5009        }
5010
5011        function getLinesBetweenNodes(parent: Node, node1: Node, node2: Node): number {
5012            if (getEmitFlags(parent) & EmitFlags.NoIndentation) {
5013                return 0;
5014            }
5015
5016            parent = skipSynthesizedParentheses(parent);
5017            node1 = skipSynthesizedParentheses(node1);
5018            node2 = skipSynthesizedParentheses(node2);
5019
5020            // Always use a newline for synthesized code if the synthesizer desires it.
5021            if (getStartsOnNewLine(node2)) {
5022                return 1;
5023            }
5024
5025            if (currentSourceFile && !nodeIsSynthesized(parent) && !nodeIsSynthesized(node1) && !nodeIsSynthesized(node2)) {
5026                if (preserveSourceNewlines) {
5027                    return getEffectiveLines(
5028                        includeComments => getLinesBetweenRangeEndAndRangeStart(
5029                            node1,
5030                            node2,
5031                            currentSourceFile!,
5032                            includeComments));
5033                }
5034                return rangeEndIsOnSameLineAsRangeStart(node1, node2, currentSourceFile) ? 0 : 1;
5035            }
5036
5037            return 0;
5038        }
5039
5040        function isEmptyBlock(block: BlockLike) {
5041            return block.statements.length === 0
5042                && (!currentSourceFile || rangeEndIsOnSameLineAsRangeStart(block, block, currentSourceFile));
5043        }
5044
5045        function skipSynthesizedParentheses(node: Node) {
5046            while (node.kind === SyntaxKind.ParenthesizedExpression && nodeIsSynthesized(node)) {
5047                node = (node as ParenthesizedExpression).expression;
5048            }
5049
5050            return node;
5051        }
5052
5053        function getTextOfNode(node: Identifier | PrivateIdentifier | LiteralExpression, includeTrivia?: boolean): string {
5054            if (isGeneratedIdentifier(node) || isGeneratedPrivateIdentifier(node)) {
5055                return generateName(node);
5056            }
5057            if (isStringLiteral(node) && node.textSourceNode) {
5058                return getTextOfNode(node.textSourceNode, includeTrivia);
5059            }
5060            const sourceFile = currentSourceFile; // const needed for control flow
5061            const canUseSourceFile = !!sourceFile && !!node.parent && !nodeIsSynthesized(node) && !(node.flags & NodeFlags.NoOriginalText);
5062            if (isMemberName(node)) {
5063                if (!canUseSourceFile || getSourceFileOfNode(node) !== getOriginalNode(sourceFile)) {
5064                    return idText(node);
5065                }
5066            }
5067            else {
5068                Debug.assertNode(node, isLiteralExpression); // not strictly necessary
5069                if (!canUseSourceFile) {
5070                    return node.text;
5071                }
5072            }
5073            return getSourceTextOfNodeFromSourceFile(sourceFile, node, includeTrivia);
5074        }
5075
5076        function getLiteralTextOfNode(node: LiteralLikeNode, neverAsciiEscape: boolean | undefined, jsxAttributeEscape: boolean): string {
5077            if (node.kind === SyntaxKind.StringLiteral && (node as StringLiteral).textSourceNode) {
5078                const textSourceNode = (node as StringLiteral).textSourceNode!;
5079                if (isIdentifier(textSourceNode) || isPrivateIdentifier(textSourceNode) || isNumericLiteral(textSourceNode)) {
5080                    const text = isNumericLiteral(textSourceNode) ? textSourceNode.text : getTextOfNode(textSourceNode);
5081                    return jsxAttributeEscape ? `"${escapeJsxAttributeString(text)}"` :
5082                        neverAsciiEscape || (getEmitFlags(node) & EmitFlags.NoAsciiEscaping) ? `"${escapeString(text)}"` :
5083                        `"${escapeNonAsciiString(text)}"`;
5084                }
5085                else {
5086                    return getLiteralTextOfNode(textSourceNode, neverAsciiEscape, jsxAttributeEscape);
5087                }
5088            }
5089
5090            const flags = (neverAsciiEscape ? GetLiteralTextFlags.NeverAsciiEscape : 0)
5091                | (jsxAttributeEscape ? GetLiteralTextFlags.JsxAttributeEscape : 0)
5092                | (printerOptions.terminateUnterminatedLiterals ? GetLiteralTextFlags.TerminateUnterminatedLiterals : 0)
5093                | (printerOptions.target && printerOptions.target === ScriptTarget.ESNext ? GetLiteralTextFlags.AllowNumericSeparator : 0);
5094
5095            return getLiteralText(node, currentSourceFile, flags);
5096        }
5097
5098        /**
5099         * Push a new name generation scope.
5100         */
5101        function pushNameGenerationScope(node: Node | undefined) {
5102            if (node && getEmitFlags(node) & EmitFlags.ReuseTempVariableScope) {
5103                return;
5104            }
5105            tempFlagsStack.push(tempFlags);
5106            tempFlags = TempFlags.Auto;
5107            privateNameTempFlagsStack.push(privateNameTempFlags);
5108            privateNameTempFlags = TempFlags.Auto;
5109            formattedNameTempFlagsStack.push(formattedNameTempFlags);
5110            formattedNameTempFlags = undefined;
5111            reservedNamesStack.push(reservedNames);
5112        }
5113
5114        /**
5115         * Pop the current name generation scope.
5116         */
5117        function popNameGenerationScope(node: Node | undefined) {
5118            if (node && getEmitFlags(node) & EmitFlags.ReuseTempVariableScope) {
5119                return;
5120            }
5121            tempFlags = tempFlagsStack.pop()!;
5122            privateNameTempFlags = privateNameTempFlagsStack.pop()!;
5123            formattedNameTempFlags = formattedNameTempFlagsStack.pop();
5124            reservedNames = reservedNamesStack.pop()!;
5125        }
5126
5127        function reserveNameInNestedScopes(name: string) {
5128            if (!reservedNames || reservedNames === lastOrUndefined(reservedNamesStack)) {
5129                reservedNames = new Set();
5130            }
5131            reservedNames.add(name);
5132        }
5133
5134        function generateNames(node: Node | undefined) {
5135            if (!node) return;
5136            switch (node.kind) {
5137                case SyntaxKind.Block:
5138                    forEach((node as Block).statements, generateNames);
5139                    break;
5140                case SyntaxKind.LabeledStatement:
5141                case SyntaxKind.WithStatement:
5142                case SyntaxKind.DoStatement:
5143                case SyntaxKind.WhileStatement:
5144                    generateNames((node as LabeledStatement | WithStatement | DoStatement | WhileStatement).statement);
5145                    break;
5146                case SyntaxKind.IfStatement:
5147                    generateNames((node as IfStatement).thenStatement);
5148                    generateNames((node as IfStatement).elseStatement);
5149                    break;
5150                case SyntaxKind.ForStatement:
5151                case SyntaxKind.ForOfStatement:
5152                case SyntaxKind.ForInStatement:
5153                    generateNames((node as ForStatement | ForInOrOfStatement).initializer);
5154                    generateNames((node as ForStatement | ForInOrOfStatement).statement);
5155                    break;
5156                case SyntaxKind.SwitchStatement:
5157                    generateNames((node as SwitchStatement).caseBlock);
5158                    break;
5159                case SyntaxKind.CaseBlock:
5160                    forEach((node as CaseBlock).clauses, generateNames);
5161                    break;
5162                case SyntaxKind.CaseClause:
5163                case SyntaxKind.DefaultClause:
5164                    forEach((node as CaseOrDefaultClause).statements, generateNames);
5165                    break;
5166                case SyntaxKind.TryStatement:
5167                    generateNames((node as TryStatement).tryBlock);
5168                    generateNames((node as TryStatement).catchClause);
5169                    generateNames((node as TryStatement).finallyBlock);
5170                    break;
5171                case SyntaxKind.CatchClause:
5172                    generateNames((node as CatchClause).variableDeclaration);
5173                    generateNames((node as CatchClause).block);
5174                    break;
5175                case SyntaxKind.VariableStatement:
5176                    generateNames((node as VariableStatement).declarationList);
5177                    break;
5178                case SyntaxKind.VariableDeclarationList:
5179                    forEach((node as VariableDeclarationList).declarations, generateNames);
5180                    break;
5181                case SyntaxKind.VariableDeclaration:
5182                case SyntaxKind.Parameter:
5183                case SyntaxKind.BindingElement:
5184                case SyntaxKind.ClassDeclaration:
5185                    generateNameIfNeeded((node as NamedDeclaration).name);
5186                    break;
5187                case SyntaxKind.FunctionDeclaration:
5188                    generateNameIfNeeded((node as FunctionDeclaration).name);
5189                    if (getEmitFlags(node) & EmitFlags.ReuseTempVariableScope) {
5190                        forEach((node as FunctionDeclaration).parameters, generateNames);
5191                        generateNames((node as FunctionDeclaration).body);
5192                    }
5193                    break;
5194                case SyntaxKind.ObjectBindingPattern:
5195                case SyntaxKind.ArrayBindingPattern:
5196                    forEach((node as BindingPattern).elements, generateNames);
5197                    break;
5198                case SyntaxKind.ImportDeclaration:
5199                    generateNames((node as ImportDeclaration).importClause);
5200                    break;
5201                case SyntaxKind.ImportClause:
5202                    generateNameIfNeeded((node as ImportClause).name);
5203                    generateNames((node as ImportClause).namedBindings);
5204                    break;
5205                case SyntaxKind.NamespaceImport:
5206                    generateNameIfNeeded((node as NamespaceImport).name);
5207                    break;
5208                case SyntaxKind.NamespaceExport:
5209                    generateNameIfNeeded((node as NamespaceExport).name);
5210                    break;
5211                case SyntaxKind.NamedImports:
5212                    forEach((node as NamedImports).elements, generateNames);
5213                    break;
5214                case SyntaxKind.ImportSpecifier:
5215                    generateNameIfNeeded((node as ImportSpecifier).propertyName || (node as ImportSpecifier).name);
5216                    break;
5217            }
5218        }
5219
5220        function generateMemberNames(node: Node | undefined) {
5221            if (!node) return;
5222            switch (node.kind) {
5223                case SyntaxKind.PropertyAssignment:
5224                case SyntaxKind.ShorthandPropertyAssignment:
5225                case SyntaxKind.PropertyDeclaration:
5226                case SyntaxKind.AnnotationPropertyDeclaration:
5227                case SyntaxKind.MethodDeclaration:
5228                case SyntaxKind.GetAccessor:
5229                case SyntaxKind.SetAccessor:
5230                    generateNameIfNeeded((node as NamedDeclaration).name);
5231                    break;
5232            }
5233        }
5234
5235        function generateNameIfNeeded(name: DeclarationName | undefined) {
5236            if (name) {
5237                if (isGeneratedIdentifier(name) || isGeneratedPrivateIdentifier(name)) {
5238                    generateName(name);
5239                }
5240                else if (isBindingPattern(name)) {
5241                    generateNames(name);
5242                }
5243            }
5244        }
5245
5246        /**
5247         * Generate the text for a generated identifier.
5248         */
5249        function generateName(name: GeneratedIdentifier | GeneratedPrivateIdentifier) {
5250            if ((name.autoGenerateFlags & GeneratedIdentifierFlags.KindMask) === GeneratedIdentifierFlags.Node) {
5251                // Node names generate unique names based on their original node
5252                // and are cached based on that node's id.
5253                return generateNameCached(getNodeForGeneratedName(name), isPrivateIdentifier(name), name.autoGenerateFlags, name.autoGeneratePrefix, name.autoGenerateSuffix);
5254            }
5255            else {
5256                // Auto, Loop, and Unique names are cached based on their unique
5257                // autoGenerateId.
5258                const autoGenerateId = name.autoGenerateId!;
5259                return autoGeneratedIdToGeneratedName[autoGenerateId] || (autoGeneratedIdToGeneratedName[autoGenerateId] = makeName(name));
5260            }
5261        }
5262
5263        function generateNameCached(node: Node, privateName: boolean, flags?: GeneratedIdentifierFlags, prefix?: string | GeneratedNamePart, suffix?: string) {
5264            const nodeId = getNodeId(node);
5265            return nodeIdToGeneratedName[nodeId] || (nodeIdToGeneratedName[nodeId] = generateNameForNode(node, privateName, flags ?? GeneratedIdentifierFlags.None, formatGeneratedNamePart(prefix, generateName), formatGeneratedNamePart(suffix)));
5266        }
5267
5268        /**
5269         * Returns a value indicating whether a name is unique globally, within the current file,
5270         * or within the NameGenerator.
5271         */
5272        function isUniqueName(name: string): boolean {
5273            return isFileLevelUniqueName(name)
5274                && !generatedNames.has(name)
5275                && !(reservedNames && reservedNames.has(name));
5276        }
5277
5278        /**
5279         * Returns a value indicating whether a name is unique globally or within the current file.
5280         */
5281        function isFileLevelUniqueName(name: string) {
5282            return currentSourceFile ? ts.isFileLevelUniqueName(currentSourceFile, name, hasGlobalName) : true;
5283        }
5284
5285        /**
5286         * Returns a value indicating whether a name is unique within a container.
5287         */
5288        function isUniqueLocalName(name: string, container: Node): boolean {
5289            for (let node = container; isNodeDescendantOf(node, container); node = node.nextContainer!) {
5290                if (node.locals) {
5291                    const local = node.locals.get(escapeLeadingUnderscores(name));
5292                    // We conservatively include alias symbols to cover cases where they're emitted as locals
5293                    if (local && local.flags & (SymbolFlags.Value | SymbolFlags.ExportValue | SymbolFlags.Alias)) {
5294                        return false;
5295                    }
5296                }
5297            }
5298            return true;
5299        }
5300
5301        function getTempFlags(formattedNameKey: string) {
5302            switch (formattedNameKey) {
5303                case "":
5304                    return tempFlags;
5305                case "#":
5306                    return privateNameTempFlags;
5307                default:
5308                    return formattedNameTempFlags?.get(formattedNameKey) ?? TempFlags.Auto;
5309            }
5310        }
5311
5312        function setTempFlags(formattedNameKey: string, flags: TempFlags) {
5313            switch (formattedNameKey) {
5314                case "":
5315                    tempFlags = flags;
5316                    break;
5317                case "#":
5318                    privateNameTempFlags = flags;
5319                    break;
5320                default:
5321                    formattedNameTempFlags ??= new Map();
5322                    formattedNameTempFlags.set(formattedNameKey, flags);
5323                    break;
5324            }
5325        }
5326
5327        /**
5328         * Return the next available name in the pattern _a ... _z, _0, _1, ...
5329         * TempFlags._i or TempFlags._n may be used to express a preference for that dedicated name.
5330         * Note that names generated by makeTempVariableName and makeUniqueName will never conflict.
5331         */
5332        function makeTempVariableName(flags: TempFlags, reservedInNestedScopes: boolean, privateName: boolean, prefix: string, suffix: string): string {
5333            if (prefix.length > 0 && prefix.charCodeAt(0) === CharacterCodes.hash) {
5334                prefix = prefix.slice(1);
5335            }
5336
5337            // Generate a key to use to acquire a TempFlags counter based on the fixed portions of the generated name.
5338            const key = formatGeneratedName(privateName, prefix, "", suffix);
5339            let tempFlags = getTempFlags(key);
5340
5341            if (flags && !(tempFlags & flags)) {
5342                const name = flags === TempFlags._i ? "_i" : "_n";
5343                const fullName = formatGeneratedName(privateName, prefix, name, suffix);
5344                if (isUniqueName(fullName)) {
5345                    tempFlags |= flags;
5346                    if (reservedInNestedScopes) {
5347                        reserveNameInNestedScopes(fullName);
5348                    }
5349                    setTempFlags(key, tempFlags);
5350                    return fullName;
5351                }
5352            }
5353
5354            while (true) {
5355                const count = tempFlags & TempFlags.CountMask;
5356                tempFlags++;
5357                // Skip over 'i' and 'n'
5358                if (count !== 8 && count !== 13) {
5359                    const name = count < 26
5360                        ? "_" + String.fromCharCode(CharacterCodes.a + count)
5361                        : "_" + (count - 26);
5362                    const fullName = formatGeneratedName(privateName, prefix, name, suffix);
5363                    if (isUniqueName(fullName)) {
5364                        if (reservedInNestedScopes) {
5365                            reserveNameInNestedScopes(fullName);
5366                        }
5367                        setTempFlags(key, tempFlags);
5368                        return fullName;
5369                    }
5370                }
5371            }
5372        }
5373
5374        /**
5375         * Generate a name that is unique within the current file and doesn't conflict with any names
5376         * in global scope. The name is formed by adding an '_n' suffix to the specified base name,
5377         * where n is a positive integer. Note that names generated by makeTempVariableName and
5378         * makeUniqueName are guaranteed to never conflict.
5379         * If `optimistic` is set, the first instance will use 'baseName' verbatim instead of 'baseName_1'
5380         */
5381        function makeUniqueName(baseName: string, checkFn: (name: string) => boolean = isUniqueName, optimistic: boolean, scoped: boolean, privateName: boolean, prefix: string, suffix: string): string {
5382            if (baseName.length > 0 && baseName.charCodeAt(0) === CharacterCodes.hash) {
5383                baseName = baseName.slice(1);
5384            }
5385            if (prefix.length > 0 && prefix.charCodeAt(0) === CharacterCodes.hash) {
5386                prefix = prefix.slice(1);
5387            }
5388            if (optimistic) {
5389                const fullName = formatGeneratedName(privateName, prefix, baseName, suffix);
5390                if (checkFn(fullName)) {
5391                    if (scoped) {
5392                        reserveNameInNestedScopes(fullName);
5393                    }
5394                    else {
5395                        generatedNames.add(fullName);
5396                    }
5397                    return fullName;
5398                }
5399            }
5400            // Find the first unique 'name_n', where n is a positive number
5401            if (baseName.charCodeAt(baseName.length - 1) !== CharacterCodes._) {
5402                baseName += "_";
5403            }
5404            let i = 1;
5405            while (true) {
5406                const fullName = formatGeneratedName(privateName, prefix, baseName + i, suffix);
5407                if (checkFn(fullName)) {
5408                    if (scoped) {
5409                        reserveNameInNestedScopes(fullName);
5410                    }
5411                    else {
5412                        generatedNames.add(fullName);
5413                    }
5414                    return fullName;
5415                }
5416                i++;
5417            }
5418        }
5419
5420        function makeFileLevelOptimisticUniqueName(name: string) {
5421            return makeUniqueName(name, isFileLevelUniqueName, /*optimistic*/ true, /*scoped*/ false, /*privateName*/ false, /*prefix*/ "", /*suffix*/ "");
5422        }
5423
5424        /**
5425         * Generates a unique name for a ModuleDeclaration or EnumDeclaration.
5426         */
5427        function generateNameForModuleOrEnum(node: ModuleDeclaration | EnumDeclaration) {
5428            const name = getTextOfNode(node.name);
5429            // Use module/enum name itself if it is unique, otherwise make a unique variation
5430            return isUniqueLocalName(name, node) ? name : makeUniqueName(name, isUniqueName, /*optimistic*/ false, /*scoped*/ false, /*privateName*/ false, /*prefix*/ "", /*suffix*/ "");
5431        }
5432
5433        /**
5434         * Generates a unique name for an ImportDeclaration or ExportDeclaration.
5435         */
5436        function generateNameForImportOrExportDeclaration(node: ImportDeclaration | ExportDeclaration) {
5437            const expr = getExternalModuleName(node)!; // TODO: GH#18217
5438            const baseName = isStringLiteral(expr) ?
5439                makeIdentifierFromModuleName(expr.text) : "module";
5440            return makeUniqueName(baseName, isUniqueName, /*optimistic*/ false, /*scoped*/ false, /*privateName*/ false, /*prefix*/ "", /*suffix*/ "");
5441        }
5442
5443        /**
5444         * Generates a unique name for a default export.
5445         */
5446        function generateNameForExportDefault() {
5447            return makeUniqueName("default", isUniqueName, /*optimistic*/ false, /*scoped*/ false, /*privateName*/ false, /*prefix*/ "", /*suffix*/ "");
5448        }
5449
5450        /**
5451         * Generates a unique name for a class expression.
5452         */
5453        function generateNameForClassExpression() {
5454            return makeUniqueName("class", isUniqueName, /*optimistic*/ false, /*scoped*/ false, /*privateName*/ false, /*prefix*/ "", /*suffix*/ "");
5455        }
5456
5457        function generateNameForMethodOrAccessor(node: MethodDeclaration | AccessorDeclaration, privateName: boolean, prefix: string, suffix: string) {
5458            if (isIdentifier(node.name)) {
5459                return generateNameCached(node.name, privateName);
5460            }
5461            return makeTempVariableName(TempFlags.Auto, /*reservedInNestedScopes*/ false, privateName, prefix, suffix);
5462        }
5463
5464        /**
5465         * Generates a unique name from a node.
5466         */
5467        function generateNameForNode(node: Node, privateName: boolean, flags: GeneratedIdentifierFlags, prefix: string, suffix: string): string {
5468            switch (node.kind) {
5469                case SyntaxKind.Identifier:
5470                case SyntaxKind.PrivateIdentifier:
5471                    return makeUniqueName(
5472                        getTextOfNode(node as Identifier),
5473                        isUniqueName,
5474                        !!(flags & GeneratedIdentifierFlags.Optimistic),
5475                        !!(flags & GeneratedIdentifierFlags.ReservedInNestedScopes),
5476                        privateName,
5477                        prefix,
5478                        suffix
5479                    );
5480                case SyntaxKind.ModuleDeclaration:
5481                case SyntaxKind.EnumDeclaration:
5482                    Debug.assert(!prefix && !suffix && !privateName);
5483                    return generateNameForModuleOrEnum(node as ModuleDeclaration | EnumDeclaration);
5484                case SyntaxKind.ImportDeclaration:
5485                case SyntaxKind.ExportDeclaration:
5486                    Debug.assert(!prefix && !suffix && !privateName);
5487                    return generateNameForImportOrExportDeclaration(node as ImportDeclaration | ExportDeclaration);
5488                case SyntaxKind.FunctionDeclaration:
5489                case SyntaxKind.ClassDeclaration:
5490                case SyntaxKind.ExportAssignment:
5491                    Debug.assert(!prefix && !suffix && !privateName);
5492                    return generateNameForExportDefault();
5493                case SyntaxKind.ClassExpression:
5494                    Debug.assert(!prefix && !suffix && !privateName);
5495                    return generateNameForClassExpression();
5496                case SyntaxKind.MethodDeclaration:
5497                case SyntaxKind.GetAccessor:
5498                case SyntaxKind.SetAccessor:
5499                    return generateNameForMethodOrAccessor(node as MethodDeclaration | AccessorDeclaration, privateName, prefix, suffix);
5500                case SyntaxKind.ComputedPropertyName:
5501                    return makeTempVariableName(TempFlags.Auto, /*reserveInNestedScopes*/ true, privateName, prefix, suffix);
5502                default:
5503                    return makeTempVariableName(TempFlags.Auto, /*reserveInNestedScopes*/ false, privateName, prefix, suffix);
5504            }
5505        }
5506
5507        /**
5508         * Generates a unique identifier for a node.
5509         */
5510        function makeName(name: GeneratedIdentifier | GeneratedPrivateIdentifier) {
5511            const prefix = formatGeneratedNamePart(name.autoGeneratePrefix, generateName);
5512            const suffix = formatGeneratedNamePart (name.autoGenerateSuffix);
5513            switch (name.autoGenerateFlags & GeneratedIdentifierFlags.KindMask) {
5514                case GeneratedIdentifierFlags.Auto:
5515                    return makeTempVariableName(TempFlags.Auto, !!(name.autoGenerateFlags & GeneratedIdentifierFlags.ReservedInNestedScopes), isPrivateIdentifier(name), prefix, suffix);
5516                case GeneratedIdentifierFlags.Loop:
5517                    Debug.assertNode(name, isIdentifier);
5518                    return makeTempVariableName(TempFlags._i, !!(name.autoGenerateFlags & GeneratedIdentifierFlags.ReservedInNestedScopes), /*privateName*/ false, prefix, suffix);
5519                case GeneratedIdentifierFlags.Unique:
5520                    return makeUniqueName(
5521                        idText(name),
5522                        (name.autoGenerateFlags & GeneratedIdentifierFlags.FileLevel) ? isFileLevelUniqueName : isUniqueName,
5523                        !!(name.autoGenerateFlags & GeneratedIdentifierFlags.Optimistic),
5524                        !!(name.autoGenerateFlags & GeneratedIdentifierFlags.ReservedInNestedScopes),
5525                        isPrivateIdentifier(name),
5526                        prefix,
5527                        suffix
5528                    );
5529            }
5530
5531            return Debug.fail(`Unsupported GeneratedIdentifierKind: ${Debug.formatEnum(name.autoGenerateFlags & GeneratedIdentifierFlags.KindMask, (ts as any).GeneratedIdentifierFlags, /*isFlags*/ true)}.`);
5532        }
5533
5534        // Comments
5535
5536        function pipelineEmitWithComments(hint: EmitHint, node: Node) {
5537            const pipelinePhase = getNextPipelinePhase(PipelinePhase.Comments, hint, node);
5538            const savedContainerPos = containerPos;
5539            const savedContainerEnd = containerEnd;
5540            const savedDeclarationListContainerEnd = declarationListContainerEnd;
5541            if(needToKeepComments(node)) {
5542                emitCommentsBeforeNode(node);
5543            }
5544            pipelinePhase(hint, node);
5545            emitCommentsAfterNode(node, savedContainerPos, savedContainerEnd, savedDeclarationListContainerEnd);
5546        }
5547
5548        function needToKeepComments(node: Node) {
5549            if (removeCommentsCollection === undefined && universalRemoveCommentsCollection === undefined) {
5550                return true;
5551            }
5552            //@ts-ignore
5553            let escapedText = node?.name?.escapedText;
5554            if (!escapedText) {
5555                return false;
5556            }
5557            if (removeCommentsCollection?.includes(escapedText)) {
5558                return true;
5559            }
5560            if (universalRemoveCommentsCollection) {
5561                return isMatchWildcard(universalRemoveCommentsCollection, escapedText);
5562            }
5563            return false;
5564        }
5565
5566        function isMatchWildcard(wildcardArray: RegExp[], item: string): boolean {
5567            for (const wildcard of wildcardArray) {
5568                if (wildcard.test(item)) {
5569                    return true;
5570                }
5571            }
5572            return false;
5573        }
5574
5575        function emitCommentsBeforeNode(node: Node) {
5576            const emitFlags = getEmitFlags(node);
5577            const commentRange = getCommentRange(node);
5578
5579            // Emit leading comments
5580            emitLeadingCommentsOfNode(node, emitFlags, commentRange.pos, commentRange.end);
5581            if (emitFlags & EmitFlags.NoNestedComments) {
5582                commentsDisabled = true;
5583            }
5584        }
5585
5586        function emitCommentsAfterNode(node: Node, savedContainerPos: number, savedContainerEnd: number, savedDeclarationListContainerEnd: number) {
5587            const emitFlags = getEmitFlags(node);
5588            const commentRange = getCommentRange(node);
5589
5590            // Emit trailing comments
5591            if (emitFlags & EmitFlags.NoNestedComments) {
5592                commentsDisabled = false;
5593            }
5594            emitTrailingCommentsOfNode(node, emitFlags, commentRange.pos, commentRange.end, savedContainerPos, savedContainerEnd, savedDeclarationListContainerEnd);
5595            const typeNode = getTypeNode(node);
5596            if (typeNode) {
5597                emitTrailingCommentsOfNode(node, emitFlags, typeNode.pos, typeNode.end, savedContainerPos, savedContainerEnd, savedDeclarationListContainerEnd);
5598            }
5599        }
5600
5601        function emitLeadingCommentsOfNode(node: Node, emitFlags: EmitFlags, pos: number, end: number) {
5602            enterComment();
5603            hasWrittenComment = false;
5604
5605            // We have to explicitly check that the node is JsxText because if the compilerOptions.jsx is "preserve" we will not do any transformation.
5606            // It is expensive to walk entire tree just to set one kind of node to have no comments.
5607            const skipLeadingComments = pos < 0 || (emitFlags & EmitFlags.NoLeadingComments) !== 0 || node.kind === SyntaxKind.JsxText;
5608            const skipTrailingComments = end < 0 || (emitFlags & EmitFlags.NoTrailingComments) !== 0 || node.kind === SyntaxKind.JsxText;
5609
5610            // Save current container state on the stack.
5611            if ((pos > 0 || end > 0) && pos !== end) {
5612                // Emit leading comments if the position is not synthesized and the node
5613                // has not opted out from emitting leading comments.
5614                if (!skipLeadingComments) {
5615                    emitLeadingComments(pos, /*isEmittedNode*/ node.kind !== SyntaxKind.NotEmittedStatement);
5616                }
5617
5618                if (!skipLeadingComments || (pos >= 0 && (emitFlags & EmitFlags.NoLeadingComments) !== 0)) {
5619                    // Advance the container position if comments get emitted or if they've been disabled explicitly using NoLeadingComments.
5620                    containerPos = pos;
5621                }
5622
5623                if (!skipTrailingComments || (end >= 0 && (emitFlags & EmitFlags.NoTrailingComments) !== 0)) {
5624                    // As above.
5625                    containerEnd = end;
5626
5627                    // To avoid invalid comment emit in a down-level binding pattern, we
5628                    // keep track of the last declaration list container's end
5629                    if (node.kind === SyntaxKind.VariableDeclarationList) {
5630                        declarationListContainerEnd = end;
5631                    }
5632                }
5633            }
5634            forEach(getSyntheticLeadingComments(node), emitLeadingSynthesizedComment);
5635            exitComment();
5636        }
5637
5638        function emitTrailingCommentsOfNode(node: Node, emitFlags: EmitFlags, pos: number, end: number, savedContainerPos: number, savedContainerEnd: number, savedDeclarationListContainerEnd: number) {
5639            enterComment();
5640            const skipTrailingComments = end < 0 || (emitFlags & EmitFlags.NoTrailingComments) !== 0 || node.kind === SyntaxKind.JsxText;
5641            forEach(getSyntheticTrailingComments(node), emitTrailingSynthesizedComment);
5642            if ((pos > 0 || end > 0) && pos !== end) {
5643                // Restore previous container state.
5644                containerPos = savedContainerPos;
5645                containerEnd = savedContainerEnd;
5646                declarationListContainerEnd = savedDeclarationListContainerEnd;
5647
5648                // Emit trailing comments if the position is not synthesized and the node
5649                // has not opted out from emitting leading comments and is an emitted node.
5650                if (!skipTrailingComments && node.kind !== SyntaxKind.NotEmittedStatement) {
5651                    emitTrailingComments(end);
5652                }
5653            }
5654            exitComment();
5655        }
5656
5657        function emitLeadingSynthesizedComment(comment: SynthesizedComment) {
5658            if (comment.hasLeadingNewline || comment.kind === SyntaxKind.SingleLineCommentTrivia) {
5659                writer.writeLine();
5660            }
5661            writeSynthesizedComment(comment);
5662            if (comment.hasTrailingNewLine || comment.kind === SyntaxKind.SingleLineCommentTrivia) {
5663                writer.writeLine();
5664            }
5665            else {
5666                writer.writeSpace(" ");
5667            }
5668        }
5669
5670        function emitTrailingSynthesizedComment(comment: SynthesizedComment) {
5671            if (!writer.isAtStartOfLine()) {
5672                writer.writeSpace(" ");
5673            }
5674            writeSynthesizedComment(comment);
5675            if (comment.hasTrailingNewLine) {
5676                writer.writeLine();
5677            }
5678        }
5679
5680        function writeSynthesizedComment(comment: SynthesizedComment) {
5681            const text = formatSynthesizedComment(comment);
5682            const lineMap = comment.kind === SyntaxKind.MultiLineCommentTrivia ? computeLineStarts(text) : undefined;
5683            writeCommentRange(text, lineMap!, writer, 0, text.length, newLine);
5684        }
5685
5686        function formatSynthesizedComment(comment: SynthesizedComment) {
5687            return comment.kind === SyntaxKind.MultiLineCommentTrivia
5688                ? `/*${comment.text}*/`
5689                : `//${comment.text}`;
5690        }
5691
5692        function emitBodyWithDetachedComments(node: Node, detachedRange: TextRange, emitCallback: (node: Node) => void) {
5693            enterComment();
5694            const { pos, end } = detachedRange;
5695            const emitFlags = getEmitFlags(node);
5696            const skipLeadingComments = pos < 0 || (emitFlags & EmitFlags.NoLeadingComments) !== 0;
5697            const skipTrailingComments = commentsDisabled || end < 0 || (emitFlags & EmitFlags.NoTrailingComments) !== 0;
5698            if (!skipLeadingComments) {
5699                emitDetachedCommentsAndUpdateCommentsInfo(detachedRange);
5700            }
5701
5702            exitComment();
5703            if (emitFlags & EmitFlags.NoNestedComments && !commentsDisabled) {
5704                commentsDisabled = true;
5705                emitCallback(node);
5706                commentsDisabled = false;
5707            }
5708            else {
5709                emitCallback(node);
5710            }
5711
5712            enterComment();
5713            if (!skipTrailingComments) {
5714                emitLeadingComments(detachedRange.end, /*isEmittedNode*/ true);
5715                if (hasWrittenComment && !writer.isAtStartOfLine()) {
5716                    writer.writeLine();
5717                }
5718            }
5719            exitComment();
5720
5721        }
5722
5723        function originalNodesHaveSameParent(nodeA: Node, nodeB: Node) {
5724            nodeA = getOriginalNode(nodeA);
5725            // For performance, do not call `getOriginalNode` for `nodeB` if `nodeA` doesn't even
5726            // have a parent node.
5727            return nodeA.parent && nodeA.parent === getOriginalNode(nodeB).parent;
5728        }
5729
5730        function siblingNodePositionsAreComparable(previousNode: Node, nextNode: Node) {
5731            if (nextNode.pos < previousNode.end) {
5732                return false;
5733            }
5734
5735            previousNode = getOriginalNode(previousNode);
5736            nextNode = getOriginalNode(nextNode);
5737            const parent = previousNode.parent;
5738            if (!parent || parent !== nextNode.parent) {
5739                return false;
5740            }
5741
5742            const parentNodeArray = getContainingNodeArray(previousNode);
5743            const prevNodeIndex = parentNodeArray?.indexOf(previousNode);
5744            return prevNodeIndex !== undefined && prevNodeIndex > -1 && parentNodeArray!.indexOf(nextNode) === prevNodeIndex + 1;
5745        }
5746
5747        function emitLeadingComments(pos: number, isEmittedNode: boolean) {
5748            hasWrittenComment = false;
5749
5750            if (isEmittedNode) {
5751                if (pos === 0 && currentSourceFile?.isDeclarationFile) {
5752                    forEachLeadingCommentToEmit(pos, emitNonTripleSlashLeadingComment);
5753                }
5754                else {
5755                    forEachLeadingCommentToEmit(pos, emitLeadingComment);
5756                }
5757            }
5758            else if (pos === 0) {
5759                // If the node will not be emitted in JS, remove all the comments(normal, pinned and ///) associated with the node,
5760                // unless it is a triple slash comment at the top of the file.
5761                // For Example:
5762                //      /// <reference-path ...>
5763                //      declare var x;
5764                //      /// <reference-path ...>
5765                //      interface F {}
5766                //  The first /// will NOT be removed while the second one will be removed even though both node will not be emitted
5767                forEachLeadingCommentToEmit(pos, emitTripleSlashLeadingComment);
5768            }
5769        }
5770
5771        function emitTripleSlashLeadingComment(commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean, rangePos: number) {
5772            if (isTripleSlashComment(commentPos, commentEnd)) {
5773                emitLeadingComment(commentPos, commentEnd, kind, hasTrailingNewLine, rangePos);
5774            }
5775        }
5776
5777        function emitNonTripleSlashLeadingComment(commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean, rangePos: number) {
5778            if (!isTripleSlashComment(commentPos, commentEnd)) {
5779                emitLeadingComment(commentPos, commentEnd, kind, hasTrailingNewLine, rangePos);
5780            }
5781        }
5782
5783        function shouldWriteComment(text: string, pos: number) {
5784            if (printerOptions.onlyPrintJsDocStyle) {
5785                return (isJSDocLikeText(text, pos) || isPinnedComment(text, pos));
5786            }
5787            return true;
5788        }
5789
5790        function emitLeadingComment(commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean, rangePos: number) {
5791            if (!currentSourceFile || !shouldWriteComment(currentSourceFile.text, commentPos)) return;
5792            if (!hasWrittenComment) {
5793                emitNewLineBeforeLeadingCommentOfPosition(getCurrentLineMap(), writer, rangePos, commentPos);
5794                hasWrittenComment = true;
5795            }
5796
5797            // Leading comments are emitted at /*leading comment1 */space/*leading comment*/space
5798            emitPos(commentPos);
5799            writeCommentRange(currentSourceFile.text, getCurrentLineMap(), writer, commentPos, commentEnd, newLine);
5800            emitPos(commentEnd);
5801
5802            if (hasTrailingNewLine) {
5803                writer.writeLine();
5804            }
5805            else if (kind === SyntaxKind.MultiLineCommentTrivia) {
5806                writer.writeSpace(" ");
5807            }
5808        }
5809
5810        function emitLeadingCommentsOfPosition(pos: number) {
5811            if (commentsDisabled || pos === -1) {
5812                return;
5813            }
5814
5815            emitLeadingComments(pos, /*isEmittedNode*/ true);
5816        }
5817
5818        function emitTrailingComments(pos: number) {
5819            forEachTrailingCommentToEmit(pos, emitTrailingComment);
5820        }
5821
5822        function emitTrailingComment(commentPos: number, commentEnd: number, _kind: SyntaxKind, hasTrailingNewLine: boolean) {
5823            if (!currentSourceFile || !shouldWriteComment(currentSourceFile.text, commentPos)) return;
5824            // trailing comments are emitted at space/*trailing comment1 */space/*trailing comment2*/
5825            if (!writer.isAtStartOfLine()) {
5826                writer.writeSpace(" ");
5827            }
5828
5829            emitPos(commentPos);
5830            writeCommentRange(currentSourceFile.text, getCurrentLineMap(), writer, commentPos, commentEnd, newLine);
5831            emitPos(commentEnd);
5832
5833            if (hasTrailingNewLine) {
5834                writer.writeLine();
5835            }
5836        }
5837
5838        function emitTrailingCommentsOfPosition(pos: number, prefixSpace?: boolean, forceNoNewline?: boolean) {
5839            if (commentsDisabled) {
5840                return;
5841            }
5842            enterComment();
5843            forEachTrailingCommentToEmit(pos, prefixSpace ? emitTrailingComment : forceNoNewline ? emitTrailingCommentOfPositionNoNewline : emitTrailingCommentOfPosition);
5844            exitComment();
5845        }
5846
5847        function emitTrailingCommentOfPositionNoNewline(commentPos: number, commentEnd: number, kind: SyntaxKind) {
5848            if (!currentSourceFile) return;
5849            // trailing comments of a position are emitted at /*trailing comment1 */space/*trailing comment*/space
5850
5851            emitPos(commentPos);
5852            writeCommentRange(currentSourceFile.text, getCurrentLineMap(), writer, commentPos, commentEnd, newLine);
5853            emitPos(commentEnd);
5854
5855            if (kind === SyntaxKind.SingleLineCommentTrivia) {
5856                writer.writeLine(); // still write a newline for single-line comments, so closing tokens aren't written on the same line
5857            }
5858        }
5859
5860        function emitTrailingCommentOfPosition(commentPos: number, commentEnd: number, _kind: SyntaxKind, hasTrailingNewLine: boolean) {
5861            if(!currentSourceFile) return;
5862            // trailing comments of a position are emitted at /*trailing comment1 */space/*trailing comment*/space
5863
5864            emitPos(commentPos);
5865            writeCommentRange(currentSourceFile.text, getCurrentLineMap(), writer, commentPos, commentEnd, newLine);
5866            emitPos(commentEnd);
5867
5868            if (hasTrailingNewLine) {
5869                writer.writeLine();
5870            }
5871            else {
5872                writer.writeSpace(" ");
5873            }
5874        }
5875
5876        function forEachLeadingCommentToEmit(pos: number, cb: (commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean, rangePos: number) => void) {
5877            // Emit the leading comments only if the container's pos doesn't match because the container should take care of emitting these comments
5878            if (currentSourceFile && (containerPos === -1 || pos !== containerPos)) {
5879                if (hasDetachedComments(pos)) {
5880                    forEachLeadingCommentWithoutDetachedComments(cb);
5881                }
5882                else {
5883                    forEachLeadingCommentRange(currentSourceFile.text, pos, cb, /*state*/ pos);
5884                }
5885            }
5886        }
5887
5888        function forEachTrailingCommentToEmit(end: number, cb: (commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean) => void) {
5889            // Emit the trailing comments only if the container's end doesn't match because the container should take care of emitting these comments
5890            if (currentSourceFile && (containerEnd === -1 || (end !== containerEnd && end !== declarationListContainerEnd))) {
5891                forEachTrailingCommentRange(currentSourceFile.text, end, cb);
5892            }
5893        }
5894
5895        function hasDetachedComments(pos: number) {
5896            return detachedCommentsInfo !== undefined && last(detachedCommentsInfo).nodePos === pos;
5897        }
5898
5899        function forEachLeadingCommentWithoutDetachedComments(cb: (commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean, rangePos: number) => void) {
5900            if (!currentSourceFile) return;
5901            // get the leading comments from detachedPos
5902            const pos = last(detachedCommentsInfo!).detachedCommentEndPos;
5903            if (detachedCommentsInfo!.length - 1) {
5904                detachedCommentsInfo!.pop();
5905            }
5906            else {
5907                detachedCommentsInfo = undefined;
5908            }
5909
5910            forEachLeadingCommentRange(currentSourceFile.text, pos, cb, /*state*/ pos);
5911        }
5912
5913        function emitDetachedCommentsAndUpdateCommentsInfo(range: TextRange) {
5914            const currentDetachedCommentInfo = currentSourceFile && emitDetachedComments(currentSourceFile.text, getCurrentLineMap(), writer, emitComment, range, newLine, commentsDisabled);
5915            if (currentDetachedCommentInfo) {
5916                if (detachedCommentsInfo) {
5917                    detachedCommentsInfo.push(currentDetachedCommentInfo);
5918                }
5919                else {
5920                    detachedCommentsInfo = [currentDetachedCommentInfo];
5921                }
5922            }
5923        }
5924
5925        function emitComment(text: string, lineMap: number[], writer: EmitTextWriter, commentPos: number, commentEnd: number, newLine: string) {
5926            if (!currentSourceFile || !shouldWriteComment(currentSourceFile.text, commentPos)) return;
5927            emitPos(commentPos);
5928            writeCommentRange(text, lineMap, writer, commentPos, commentEnd, newLine);
5929            emitPos(commentEnd);
5930        }
5931
5932        /**
5933         * Determine if the given comment is a triple-slash
5934         *
5935         * @return true if the comment is a triple-slash comment else false
5936         */
5937        function isTripleSlashComment(commentPos: number, commentEnd: number) {
5938            return !!currentSourceFile && isRecognizedTripleSlashComment(currentSourceFile.text, commentPos, commentEnd);
5939        }
5940
5941        // Source Maps
5942
5943        function getParsedSourceMap(node: UnparsedSource) {
5944            if (node.parsedSourceMap === undefined && node.sourceMapText !== undefined) {
5945                node.parsedSourceMap = tryParseRawSourceMap(node.sourceMapText) || false;
5946            }
5947            return node.parsedSourceMap || undefined;
5948        }
5949
5950        function pipelineEmitWithSourceMaps(hint: EmitHint, node: Node) {
5951            const pipelinePhase = getNextPipelinePhase(PipelinePhase.SourceMaps, hint, node);
5952            emitSourceMapsBeforeNode(node);
5953            pipelinePhase(hint, node);
5954            emitSourceMapsAfterNode(node);
5955        }
5956
5957        function emitSourceMapsBeforeNode(node: Node) {
5958            const emitFlags = getEmitFlags(node);
5959            const sourceMapRange = getSourceMapRange(node);
5960
5961            // Emit leading sourcemap
5962            if (isUnparsedNode(node)) {
5963                Debug.assertIsDefined(node.parent, "UnparsedNodes must have parent pointers");
5964                const parsed = getParsedSourceMap(node.parent);
5965                if (parsed && sourceMapGenerator) {
5966                    sourceMapGenerator.appendSourceMap(
5967                        writer.getLine(),
5968                        writer.getColumn(),
5969                        parsed,
5970                        node.parent.sourceMapPath!,
5971                        node.parent.getLineAndCharacterOfPosition(node.pos),
5972                        node.parent.getLineAndCharacterOfPosition(node.end)
5973                    );
5974                }
5975            }
5976            else {
5977                const source = sourceMapRange.source || sourceMapSource;
5978                if (node.kind !== SyntaxKind.NotEmittedStatement
5979                    && (emitFlags & EmitFlags.NoLeadingSourceMap) === 0
5980                    && sourceMapRange.pos >= 0) {
5981                    emitSourcePos(sourceMapRange.source || sourceMapSource, skipSourceTrivia(source, sourceMapRange.pos));
5982                }
5983                if (emitFlags & EmitFlags.NoNestedSourceMaps) {
5984                    sourceMapsDisabled = true;
5985                }
5986            }
5987        }
5988
5989        function emitSourceMapsAfterNode(node: Node) {
5990            const emitFlags = getEmitFlags(node);
5991            const sourceMapRange = getSourceMapRange(node);
5992
5993            // Emit trailing sourcemap
5994            if (!isUnparsedNode(node)) {
5995                if (emitFlags & EmitFlags.NoNestedSourceMaps) {
5996                    sourceMapsDisabled = false;
5997                }
5998                if (node.kind !== SyntaxKind.NotEmittedStatement
5999                    && (emitFlags & EmitFlags.NoTrailingSourceMap) === 0
6000                    && sourceMapRange.end >= 0) {
6001                    emitSourcePos(sourceMapRange.source || sourceMapSource, sourceMapRange.end);
6002                }
6003            }
6004        }
6005
6006        /**
6007         * Skips trivia such as comments and white-space that can be optionally overridden by the source-map source
6008         */
6009        function skipSourceTrivia(source: SourceMapSource, pos: number): number {
6010            return source.skipTrivia ? source.skipTrivia(pos) : skipTrivia(source.text, pos);
6011        }
6012
6013        /**
6014         * Emits a mapping.
6015         *
6016         * If the position is synthetic (undefined or a negative value), no mapping will be
6017         * created.
6018         *
6019         * @param pos The position.
6020         */
6021        function emitPos(pos: number) {
6022            if (sourceMapsDisabled || positionIsSynthesized(pos) || isJsonSourceMapSource(sourceMapSource)) {
6023                return;
6024            }
6025
6026            const { line: sourceLine, character: sourceCharacter } = getLineAndCharacterOfPosition(sourceMapSource, pos);
6027            sourceMapGenerator!.addMapping(
6028                writer.getLine(),
6029                writer.getColumn(),
6030                sourceMapSourceIndex,
6031                sourceLine,
6032                sourceCharacter,
6033                /*nameIndex*/ undefined);
6034        }
6035
6036        function emitSourcePos(source: SourceMapSource, pos: number) {
6037            if (source !== sourceMapSource) {
6038                const savedSourceMapSource = sourceMapSource;
6039                const savedSourceMapSourceIndex = sourceMapSourceIndex;
6040                setSourceMapSource(source);
6041                emitPos(pos);
6042                resetSourceMapSource(savedSourceMapSource, savedSourceMapSourceIndex);
6043            }
6044            else {
6045                emitPos(pos);
6046            }
6047        }
6048
6049        /**
6050         * Emits a token of a node with possible leading and trailing source maps.
6051         *
6052         * @param node The node containing the token.
6053         * @param token The token to emit.
6054         * @param tokenStartPos The start pos of the token.
6055         * @param emitCallback The callback used to emit the token.
6056         */
6057        function emitTokenWithSourceMap(node: Node | undefined, token: SyntaxKind, writer: (s: string) => void, tokenPos: number, emitCallback: (token: SyntaxKind, writer: (s: string) => void, tokenStartPos: number) => number) {
6058            if (sourceMapsDisabled || node && isInJsonFile(node)) {
6059                return emitCallback(token, writer, tokenPos);
6060            }
6061
6062            const emitNode = node && node.emitNode;
6063            const emitFlags = emitNode && emitNode.flags || EmitFlags.None;
6064            const range = emitNode && emitNode.tokenSourceMapRanges && emitNode.tokenSourceMapRanges[token];
6065            const source = range && range.source || sourceMapSource;
6066
6067            tokenPos = skipSourceTrivia(source, range ? range.pos : tokenPos);
6068            if ((emitFlags & EmitFlags.NoTokenLeadingSourceMaps) === 0 && tokenPos >= 0) {
6069                emitSourcePos(source, tokenPos);
6070            }
6071
6072            tokenPos = emitCallback(token, writer, tokenPos);
6073
6074            if (range) tokenPos = range.end;
6075            if ((emitFlags & EmitFlags.NoTokenTrailingSourceMaps) === 0 && tokenPos >= 0) {
6076                emitSourcePos(source, tokenPos);
6077            }
6078
6079            return tokenPos;
6080        }
6081
6082        function setSourceMapSource(source: SourceMapSource) {
6083            if (sourceMapsDisabled) {
6084                return;
6085            }
6086
6087            sourceMapSource = source;
6088
6089            if (source === mostRecentlyAddedSourceMapSource) {
6090                // Fast path for when the new source map is the most recently added, in which case
6091                // we use its captured index without going through the source map generator.
6092                sourceMapSourceIndex = mostRecentlyAddedSourceMapSourceIndex;
6093                return;
6094            }
6095
6096            if (isJsonSourceMapSource(source)) {
6097                return;
6098            }
6099
6100            sourceMapSourceIndex = sourceMapGenerator!.addSource(source.fileName);
6101            if (printerOptions.inlineSources) {
6102                sourceMapGenerator!.setSourceContent(sourceMapSourceIndex, source.text);
6103            }
6104
6105            mostRecentlyAddedSourceMapSource = source;
6106            mostRecentlyAddedSourceMapSourceIndex = sourceMapSourceIndex;
6107        }
6108
6109        function resetSourceMapSource(source: SourceMapSource, sourceIndex: number) {
6110            sourceMapSource = source;
6111            sourceMapSourceIndex = sourceIndex;
6112        }
6113
6114        function isJsonSourceMapSource(sourceFile: SourceMapSource) {
6115            return fileExtensionIs(sourceFile.fileName, Extension.Json);
6116        }
6117    }
6118
6119    function createBracketsMap() {
6120        const brackets: string[][] = [];
6121        brackets[ListFormat.Braces] = ["{", "}"];
6122        brackets[ListFormat.Parenthesis] = ["(", ")"];
6123        brackets[ListFormat.AngleBrackets] = ["<", ">"];
6124        brackets[ListFormat.SquareBrackets] = ["[", "]"];
6125        return brackets;
6126    }
6127
6128    function getOpeningBracket(format: ListFormat) {
6129        return brackets[format & ListFormat.BracketsMask][0];
6130    }
6131
6132    function getClosingBracket(format: ListFormat) {
6133        return brackets[format & ListFormat.BracketsMask][1];
6134    }
6135
6136    // Flags enum to track count of temp variables and a few dedicated names
6137    const enum TempFlags {
6138        Auto = 0x00000000,  // No preferred name
6139        CountMask = 0x0FFFFFFF,  // Temp variable counter
6140        _i = 0x10000000,  // Use/preference flag for '_i'
6141    }
6142
6143    interface OrdinalParentheizerRuleSelector<T extends Node> {
6144        select(index: number): ((node: T) => T) | undefined;
6145    }
6146
6147    type ParenthesizerRule<T extends Node> = (node: T) => T;
6148
6149    type ParenthesizerRuleOrSelector<T extends Node> = OrdinalParentheizerRuleSelector<T> | ParenthesizerRule<T>;
6150
6151    function emitListItemNoParenthesizer(node: Node, emit: (node: Node, parenthesizerRule?: ((node: Node) => Node) | undefined) => void, _parenthesizerRule: ParenthesizerRuleOrSelector<Node> | undefined, _index: number) {
6152        emit(node);
6153    }
6154
6155    function emitListItemWithParenthesizerRuleSelector(node: Node, emit: (node: Node, parenthesizerRule?: ((node: Node) => Node) | undefined) => void, parenthesizerRuleSelector: OrdinalParentheizerRuleSelector<Node>, index: number) {
6156        emit(node, parenthesizerRuleSelector.select(index));
6157    }
6158
6159    function emitListItemWithParenthesizerRule(node: Node, emit: (node: Node, parenthesizerRule?: ((node: Node) => Node) | undefined) => void, parenthesizerRule: ParenthesizerRule<Node> | undefined, _index: number) {
6160        emit(node, parenthesizerRule);
6161    }
6162
6163    function getEmitListItem<T extends Node, R extends ParenthesizerRuleOrSelector<T> | undefined>(emit: (node: Node, parenthesizerRule?: ((node: Node) => Node) | undefined) => void, parenthesizerRule: R): (node: Node, emit: (node: Node, parenthesizerRule?: ((node: Node) => Node) | undefined) => void, parenthesizerRule: R, index: number) => void {
6164        return emit.length === 1 ? emitListItemNoParenthesizer :
6165            typeof parenthesizerRule === "object" ? emitListItemWithParenthesizerRuleSelector :
6166            emitListItemWithParenthesizerRule;
6167    }
6168}
6169