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