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