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