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