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