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