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