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