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