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