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