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