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