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