Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
70 contributors

Users who have contributed to this file

@sheetalkamat @rbuckton @vladima @DanielRosenwasser @CyrusNajmabadi @ahejlsberg @RyanCavanaugh @weswigham @andy-ms @JsonFreeman @sandersn @ivogabe @a-tarasyuk @tinganho @vvakame @evansb @danquirk @nojvek @v3nom @plantain-00 @andrewbranch @andrejbaran @pcan @Kingwl @vilic @holtwick
5179 lines (4597 sloc) 230 KB
namespace ts {
const brackets = createBracketsMap();
const syntheticParent: TextRange = { pos: -1, end: -1 };
/*@internal*/
export function isBuildInfoFile(file: string) {
return fileExtensionIs(file, Extension.TsBuildInfo);
}
/*@internal*/
/**
* Iterates over the source files that are expected to have an emit output.
*
* @param host An EmitHost.
* @param action The action to execute.
* @param sourceFilesOrTargetSourceFile
* If an array, the full list of source files to emit.
* Else, calls `getSourceFilesToEmit` with the (optional) target source file to determine the list of source files to emit.
*/
export function forEachEmittedFile<T>(
host: EmitHost, action: (emitFileNames: EmitFileNames, sourceFileOrBundle: SourceFile | Bundle | undefined) => T,
sourceFilesOrTargetSourceFile?: readonly SourceFile[] | SourceFile,
forceDtsEmit = false,
onlyBuildInfo?: boolean,
includeBuildInfo?: boolean) {
const sourceFiles = isArray(sourceFilesOrTargetSourceFile) ? sourceFilesOrTargetSourceFile : getSourceFilesToEmit(host, sourceFilesOrTargetSourceFile);
const options = host.getCompilerOptions();
if (options.outFile || options.out) {
const prepends = host.getPrependNodes();
if (sourceFiles.length || prepends.length) {
const bundle = createBundle(sourceFiles, prepends);
const result = action(getOutputPathsFor(bundle, host, forceDtsEmit), bundle);
if (result) {
return result;
}
}
}
else {
if (!onlyBuildInfo) {
for (const sourceFile of sourceFiles) {
const result = action(getOutputPathsFor(sourceFile, host, forceDtsEmit), sourceFile);
if (result) {
return result;
}
}
}
if (includeBuildInfo) {
const buildInfoPath = getTsBuildInfoEmitOutputFilePath(host.getCompilerOptions());
if (buildInfoPath) return action({ buildInfoPath }, /*sourceFileOrBundle*/ undefined);
}
}
}
export function getTsBuildInfoEmitOutputFilePath(options: CompilerOptions) {
const configFile = options.configFilePath;
if (!isIncrementalCompilation(options)) return undefined;
if (options.tsBuildInfoFile) return options.tsBuildInfoFile;
const outPath = options.outFile || options.out;
let buildInfoExtensionLess: string;
if (outPath) {
buildInfoExtensionLess = removeFileExtension(outPath);
}
else {
if (!configFile) return undefined;
const configFileExtensionLess = removeFileExtension(configFile);
buildInfoExtensionLess = options.outDir ?
options.rootDir ?
resolvePath(options.outDir, getRelativePathFromDirectory(options.rootDir, configFileExtensionLess, /*ignoreCase*/ true)) :
combinePaths(options.outDir, getBaseFileName(configFileExtensionLess)) :
configFileExtensionLess;
}
return buildInfoExtensionLess + Extension.TsBuildInfo;
}
/*@internal*/
export function getOutputPathsForBundle(options: CompilerOptions, forceDtsPaths: boolean): EmitFileNames {
const outPath = options.outFile || options.out!;
const jsFilePath = options.emitDeclarationOnly ? undefined : outPath;
const sourceMapFilePath = jsFilePath && getSourceMapFilePath(jsFilePath, options);
const declarationFilePath = (forceDtsPaths || getEmitDeclarations(options)) ? removeFileExtension(outPath) + Extension.Dts : undefined;
const declarationMapPath = declarationFilePath && getAreDeclarationMapsEnabled(options) ? declarationFilePath + ".map" : undefined;
const buildInfoPath = getTsBuildInfoEmitOutputFilePath(options);
return { jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath, buildInfoPath };
}
/*@internal*/
export function getOutputPathsFor(sourceFile: SourceFile | Bundle, host: EmitHost, forceDtsPaths: boolean): EmitFileNames {
const options = host.getCompilerOptions();
if (sourceFile.kind === SyntaxKind.Bundle) {
return getOutputPathsForBundle(options, forceDtsPaths);
}
else {
const ownOutputFilePath = getOwnEmitOutputFilePath(sourceFile.fileName, host, getOutputExtension(sourceFile, options));
// If json file emits to the same location skip writing it, if emitDeclarationOnly skip writing it
const isJsonEmittedToSameLocation = isJsonSourceFile(sourceFile) &&
comparePaths(sourceFile.fileName, ownOutputFilePath, host.getCurrentDirectory(), !host.useCaseSensitiveFileNames()) === Comparison.EqualTo;
const jsFilePath = options.emitDeclarationOnly || isJsonEmittedToSameLocation ? undefined : ownOutputFilePath;
const sourceMapFilePath = !jsFilePath || isJsonSourceFile(sourceFile) ? undefined : getSourceMapFilePath(jsFilePath, options);
const declarationFilePath = (forceDtsPaths || getEmitDeclarations(options)) ? getDeclarationEmitOutputFilePath(sourceFile.fileName, host) : undefined;
const declarationMapPath = declarationFilePath && getAreDeclarationMapsEnabled(options) ? declarationFilePath + ".map" : undefined;
return { jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath, buildInfoPath: undefined };
}
}
function getSourceMapFilePath(jsFilePath: string, options: CompilerOptions) {
return (options.sourceMap && !options.inlineSourceMap) ? jsFilePath + ".map" : undefined;
}
// JavaScript files are always LanguageVariant.JSX, as JSX syntax is allowed in .js files also.
// So for JavaScript files, '.jsx' is only emitted if the input was '.jsx', and JsxEmit.Preserve.
// For TypeScript, the only time to emit with a '.jsx' extension, is on JSX input, and JsxEmit.Preserve
/* @internal */
export function getOutputExtension(sourceFile: SourceFile, options: CompilerOptions): Extension {
if (isJsonSourceFile(sourceFile)) {
return Extension.Json;
}
if (options.jsx === JsxEmit.Preserve) {
if (isSourceFileJS(sourceFile)) {
if (fileExtensionIs(sourceFile.fileName, Extension.Jsx)) {
return Extension.Jsx;
}
}
else if (sourceFile.languageVariant === LanguageVariant.JSX) {
// TypeScript source file preserving JSX syntax
return Extension.Jsx;
}
}
return Extension.Js;
}
function rootDirOfOptions(configFile: ParsedCommandLine) {
return configFile.options.rootDir || getDirectoryPath(Debug.assertDefined(configFile.options.configFilePath));
}
function getOutputPathWithoutChangingExt(inputFileName: string, configFile: ParsedCommandLine, ignoreCase: boolean, outputDir: string | undefined) {
return outputDir ?
resolvePath(
outputDir,
getRelativePathFromDirectory(rootDirOfOptions(configFile), inputFileName, ignoreCase)
) :
inputFileName;
}
/* @internal */
export function getOutputDeclarationFileName(inputFileName: string, configFile: ParsedCommandLine, ignoreCase: boolean) {
Debug.assert(!fileExtensionIs(inputFileName, Extension.Dts));
return changeExtension(
getOutputPathWithoutChangingExt(inputFileName, configFile, ignoreCase, configFile.options.declarationDir || configFile.options.outDir),
Extension.Dts
);
}
function getOutputJSFileName(inputFileName: string, configFile: ParsedCommandLine, ignoreCase: boolean) {
if (configFile.options.emitDeclarationOnly) return undefined;
const isJsonFile = fileExtensionIs(inputFileName, Extension.Json);
const outputFileName = changeExtension(
getOutputPathWithoutChangingExt(inputFileName, configFile, ignoreCase, configFile.options.outDir),
isJsonFile ?
Extension.Json :
fileExtensionIs(inputFileName, Extension.Tsx) && configFile.options.jsx === JsxEmit.Preserve ?
Extension.Jsx :
Extension.Js
);
return !isJsonFile || comparePaths(inputFileName, outputFileName, Debug.assertDefined(configFile.options.configFilePath), ignoreCase) !== Comparison.EqualTo ?
outputFileName :
undefined;
}
function createAddOutput() {
let outputs: string[] | undefined;
return { addOutput, getOutputs };
function addOutput(path: string | undefined) {
if (path) {
(outputs || (outputs = [])).push(path);
}
}
function getOutputs(): readonly string[] {
return outputs || emptyArray;
}
}
function getSingleOutputFileNames(configFile: ParsedCommandLine, addOutput: ReturnType<typeof createAddOutput>["addOutput"]) {
const { jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath, buildInfoPath } = getOutputPathsForBundle(configFile.options, /*forceDtsPaths*/ false);
addOutput(jsFilePath);
addOutput(sourceMapFilePath);
addOutput(declarationFilePath);
addOutput(declarationMapPath);
addOutput(buildInfoPath);
}
function getOwnOutputFileNames(configFile: ParsedCommandLine, inputFileName: string, ignoreCase: boolean, addOutput: ReturnType<typeof createAddOutput>["addOutput"]) {
if (fileExtensionIs(inputFileName, Extension.Dts)) return;
const js = getOutputJSFileName(inputFileName, configFile, ignoreCase);
addOutput(js);
if (fileExtensionIs(inputFileName, Extension.Json)) return;
if (js && configFile.options.sourceMap) {
addOutput(`${js}.map`);
}
if (getEmitDeclarations(configFile.options)) {
const dts = getOutputDeclarationFileName(inputFileName, configFile, ignoreCase);
addOutput(dts);
if (configFile.options.declarationMap) {
addOutput(`${dts}.map`);
}
}
}
/*@internal*/
export function getAllProjectOutputs(configFile: ParsedCommandLine, ignoreCase: boolean): readonly string[] {
const { addOutput, getOutputs } = createAddOutput();
if (configFile.options.outFile || configFile.options.out) {
getSingleOutputFileNames(configFile, addOutput);
}
else {
for (const inputFileName of configFile.fileNames) {
getOwnOutputFileNames(configFile, inputFileName, ignoreCase, addOutput);
}
addOutput(getTsBuildInfoEmitOutputFilePath(configFile.options));
}
return getOutputs();
}
export function getOutputFileNames(commandLine: ParsedCommandLine, inputFileName: string, ignoreCase: boolean): readonly string[] {
inputFileName = normalizePath(inputFileName);
Debug.assert(contains(commandLine.fileNames, inputFileName), `Expected fileName to be present in command line`);
const { addOutput, getOutputs } = createAddOutput();
if (commandLine.options.outFile || commandLine.options.out) {
getSingleOutputFileNames(commandLine, addOutput);
}
else {
getOwnOutputFileNames(commandLine, inputFileName, ignoreCase, addOutput);
}
return getOutputs();
}
/*@internal*/
export function getFirstProjectOutput(configFile: ParsedCommandLine, ignoreCase: boolean): string {
if (configFile.options.outFile || configFile.options.out) {
const { jsFilePath } = getOutputPathsForBundle(configFile.options, /*forceDtsPaths*/ false);
return Debug.assertDefined(jsFilePath, `project ${configFile.options.configFilePath} expected to have at least one output`);
}
for (const inputFileName of configFile.fileNames) {
if (fileExtensionIs(inputFileName, Extension.Dts)) continue;
const jsFilePath = getOutputJSFileName(inputFileName, configFile, ignoreCase);
if (jsFilePath) return jsFilePath;
if (fileExtensionIs(inputFileName, Extension.Json)) continue;
if (getEmitDeclarations(configFile.options)) {
return getOutputDeclarationFileName(inputFileName, configFile, ignoreCase);
}
}
const buildInfoPath = getTsBuildInfoEmitOutputFilePath(configFile.options);
if (buildInfoPath) return buildInfoPath;
return Debug.fail(`project ${configFile.options.configFilePath} expected to have at least one output`);
}
/*@internal*/
// targetSourceFile is when users only want one file in entire project to be emitted. This is used in compileOnSave feature
export function emitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFile: SourceFile | undefined, { scriptTransformers, declarationTransformers }: EmitTransformers, emitOnlyDtsFiles?: boolean, onlyBuildInfo?: boolean, forceDtsEmit?: boolean): EmitResult {
const compilerOptions = host.getCompilerOptions();
const sourceMapDataList: SourceMapEmitResult[] | undefined = (compilerOptions.sourceMap || compilerOptions.inlineSourceMap || getAreDeclarationMapsEnabled(compilerOptions)) ? [] : undefined;
const emittedFilesList: string[] | undefined = compilerOptions.listEmittedFiles ? [] : undefined;
const emitterDiagnostics = createDiagnosticCollection();
const newLine = getNewLineCharacter(compilerOptions, () => host.getNewLine());
const writer = createTextWriter(newLine);
const { enter, exit } = performance.createTimer("printTime", "beforePrint", "afterPrint");
let bundleBuildInfo: BundleBuildInfo | undefined;
let emitSkipped = false;
let exportedModulesFromDeclarationEmit: ExportedModulesFromDeclarationEmit | undefined;
// Emit each output file
enter();
forEachEmittedFile(
host,
emitSourceFileOrBundle,
getSourceFilesToEmit(host, targetSourceFile),
forceDtsEmit,
onlyBuildInfo,
!targetSourceFile
);
exit();
return {
emitSkipped,
diagnostics: emitterDiagnostics.getDiagnostics(),
emittedFiles: emittedFilesList,
sourceMaps: sourceMapDataList,
exportedModulesFromDeclarationEmit
};
function emitSourceFileOrBundle({ jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath, buildInfoPath }: EmitFileNames, sourceFileOrBundle: SourceFile | Bundle | undefined) {
let buildInfoDirectory: string | undefined;
if (buildInfoPath && sourceFileOrBundle && isBundle(sourceFileOrBundle)) {
buildInfoDirectory = getDirectoryPath(getNormalizedAbsolutePath(buildInfoPath, host.getCurrentDirectory()));
bundleBuildInfo = {
commonSourceDirectory: relativeToBuildInfo(host.getCommonSourceDirectory()),
sourceFiles: sourceFileOrBundle.sourceFiles.map(file => relativeToBuildInfo(getNormalizedAbsolutePath(file.fileName, host.getCurrentDirectory())))
};
}
emitJsFileOrBundle(sourceFileOrBundle, jsFilePath, sourceMapFilePath, relativeToBuildInfo);
emitDeclarationFileOrBundle(sourceFileOrBundle, declarationFilePath, declarationMapPath, relativeToBuildInfo);
emitBuildInfo(bundleBuildInfo, buildInfoPath);
if (!emitSkipped && emittedFilesList) {
if (!emitOnlyDtsFiles) {
if (jsFilePath) {
emittedFilesList.push(jsFilePath);
}
if (sourceMapFilePath) {
emittedFilesList.push(sourceMapFilePath);
}
if (buildInfoPath) {
emittedFilesList.push(buildInfoPath);
}
}
if (declarationFilePath) {
emittedFilesList.push(declarationFilePath);
}
if (declarationMapPath) {
emittedFilesList.push(declarationMapPath);
}
}
function relativeToBuildInfo(path: string) {
return ensurePathIsNonModuleName(getRelativePathFromDirectory(buildInfoDirectory!, path, host.getCanonicalFileName));
}
}
function emitBuildInfo(bundle: BundleBuildInfo | undefined, buildInfoPath: string | undefined) {
// Write build information if applicable
if (!buildInfoPath || targetSourceFile || emitSkipped) return;
const program = host.getProgramBuildInfo();
if (host.isEmitBlocked(buildInfoPath) || compilerOptions.noEmit) {
emitSkipped = true;
return;
}
writeFile(host, emitterDiagnostics, buildInfoPath, getBuildInfoText({ bundle, program, version }), /*writeByteOrderMark*/ false);
}
function emitJsFileOrBundle(
sourceFileOrBundle: SourceFile | Bundle | undefined,
jsFilePath: string | undefined,
sourceMapFilePath: string | undefined,
relativeToBuildInfo: (path: string) => string) {
if (!sourceFileOrBundle || emitOnlyDtsFiles || !jsFilePath) {
return;
}
// Make sure not to write js file and source map file if any of them cannot be written
if ((jsFilePath && host.isEmitBlocked(jsFilePath)) || compilerOptions.noEmit) {
emitSkipped = true;
return;
}
// Transform the source files
const transform = transformNodes(resolver, host, compilerOptions, [sourceFileOrBundle], scriptTransformers, /*allowDtsFiles*/ false);
const printerOptions: PrinterOptions = {
removeComments: compilerOptions.removeComments,
newLine: compilerOptions.newLine,
noEmitHelpers: compilerOptions.noEmitHelpers,
module: compilerOptions.module,
target: compilerOptions.target,
sourceMap: compilerOptions.sourceMap,
inlineSourceMap: compilerOptions.inlineSourceMap,
inlineSources: compilerOptions.inlineSources,
extendedDiagnostics: compilerOptions.extendedDiagnostics,
writeBundleFileInfo: !!bundleBuildInfo,
relativeToBuildInfo
};
// Create a printer to print the nodes
const printer = createPrinter(printerOptions, {
// resolver hooks
hasGlobalName: resolver.hasGlobalName,
// transform hooks
onEmitNode: transform.emitNodeWithNotification,
substituteNode: transform.substituteNode,
});
Debug.assert(transform.transformed.length === 1, "Should only see one output from the transform");
printSourceFileOrBundle(jsFilePath, sourceMapFilePath, transform.transformed[0], printer, compilerOptions);
// Clean up emit nodes on parse tree
transform.dispose();
if (bundleBuildInfo) bundleBuildInfo.js = printer.bundleFileInfo;
}
function emitDeclarationFileOrBundle(
sourceFileOrBundle: SourceFile | Bundle | undefined,
declarationFilePath: string | undefined,
declarationMapPath: string | undefined,
relativeToBuildInfo: (path: string) => string) {
if (!sourceFileOrBundle) return;
if (!declarationFilePath) {
if (emitOnlyDtsFiles || compilerOptions.emitDeclarationOnly) emitSkipped = true;
return;
}
const sourceFiles = isSourceFile(sourceFileOrBundle) ? [sourceFileOrBundle] : sourceFileOrBundle.sourceFiles;
// Setup and perform the transformation to retrieve declarations from the input files
const inputListOrBundle = (compilerOptions.outFile || compilerOptions.out) ? [createBundle(sourceFiles, !isSourceFile(sourceFileOrBundle) ? sourceFileOrBundle.prepends : undefined)] : sourceFiles;
if (emitOnlyDtsFiles && !getEmitDeclarations(compilerOptions)) {
// Checker wont collect the linked aliases since thats only done when declaration is enabled.
// Do that here when emitting only dts files
sourceFiles.forEach(collectLinkedAliases);
}
const declarationTransform = transformNodes(resolver, host, compilerOptions, inputListOrBundle, declarationTransformers, /*allowDtsFiles*/ false);
if (length(declarationTransform.diagnostics)) {
for (const diagnostic of declarationTransform.diagnostics!) {
emitterDiagnostics.add(diagnostic);
}
}
const printerOptions: PrinterOptions = {
removeComments: compilerOptions.removeComments,
newLine: compilerOptions.newLine,
noEmitHelpers: true,
module: compilerOptions.module,
target: compilerOptions.target,
sourceMap: compilerOptions.sourceMap,
inlineSourceMap: compilerOptions.inlineSourceMap,
extendedDiagnostics: compilerOptions.extendedDiagnostics,
onlyPrintJsDocStyle: true,
writeBundleFileInfo: !!bundleBuildInfo,
recordInternalSection: !!bundleBuildInfo,
relativeToBuildInfo
};
const declarationPrinter = createPrinter(printerOptions, {
// resolver hooks
hasGlobalName: resolver.hasGlobalName,
// transform hooks
onEmitNode: declarationTransform.emitNodeWithNotification,
substituteNode: declarationTransform.substituteNode,
});
const declBlocked = (!!declarationTransform.diagnostics && !!declarationTransform.diagnostics.length) || !!host.isEmitBlocked(declarationFilePath) || !!compilerOptions.noEmit;
emitSkipped = emitSkipped || declBlocked;
if (!declBlocked || forceDtsEmit) {
Debug.assert(declarationTransform.transformed.length === 1, "Should only see one output from the decl transform");
printSourceFileOrBundle(
declarationFilePath,
declarationMapPath,
declarationTransform.transformed[0],
declarationPrinter,
{
sourceMap: compilerOptions.declarationMap,
sourceRoot: compilerOptions.sourceRoot,
mapRoot: compilerOptions.mapRoot,
extendedDiagnostics: compilerOptions.extendedDiagnostics,
// Explicitly do not passthru either `inline` option
}
);
if (forceDtsEmit && declarationTransform.transformed[0].kind === SyntaxKind.SourceFile) {
const sourceFile = declarationTransform.transformed[0];
exportedModulesFromDeclarationEmit = sourceFile.exportedModulesFromDeclarationEmit;
}
}
declarationTransform.dispose();
if (bundleBuildInfo) bundleBuildInfo.dts = declarationPrinter.bundleFileInfo;
}
function collectLinkedAliases(node: Node) {
if (isExportAssignment(node)) {
if (node.expression.kind === SyntaxKind.Identifier) {
resolver.collectLinkedAliases(node.expression as Identifier, /*setVisibility*/ true);
}
return;
}
else if (isExportSpecifier(node)) {
resolver.collectLinkedAliases(node.propertyName || node.name, /*setVisibility*/ true);
return;
}
forEachChild(node, collectLinkedAliases);
}
function printSourceFileOrBundle(jsFilePath: string, sourceMapFilePath: string | undefined, sourceFileOrBundle: SourceFile | Bundle, printer: Printer, mapOptions: SourceMapOptions) {
const bundle = sourceFileOrBundle.kind === SyntaxKind.Bundle ? sourceFileOrBundle : undefined;
const sourceFile = sourceFileOrBundle.kind === SyntaxKind.SourceFile ? sourceFileOrBundle : undefined;
const sourceFiles = bundle ? bundle.sourceFiles : [sourceFile!];
let sourceMapGenerator: SourceMapGenerator | undefined;
if (shouldEmitSourceMaps(mapOptions, sourceFileOrBundle)) {
sourceMapGenerator = createSourceMapGenerator(
host,
getBaseFileName(normalizeSlashes(jsFilePath)),
getSourceRoot(mapOptions),
getSourceMapDirectory(mapOptions, jsFilePath, sourceFile),
mapOptions);
}
if (bundle) {
printer.writeBundle(bundle, writer, sourceMapGenerator);
}
else {
printer.writeFile(sourceFile!, writer, sourceMapGenerator);
}
if (sourceMapGenerator) {
if (sourceMapDataList) {
sourceMapDataList.push({
inputSourceFileNames: sourceMapGenerator.getSources(),
sourceMap: sourceMapGenerator.toJSON()
});
}
const sourceMappingURL = getSourceMappingURL(
mapOptions,
sourceMapGenerator,
jsFilePath,
sourceMapFilePath,
sourceFile);
if (sourceMappingURL) {
if (!writer.isAtStartOfLine()) writer.rawWrite(newLine);
writer.writeComment(`//# ${"sourceMappingURL"}=${sourceMappingURL}`); // Tools can sometimes see this line as a source mapping url comment
}
// Write the source map
if (sourceMapFilePath) {
const sourceMap = sourceMapGenerator.toString();
writeFile(host, emitterDiagnostics, sourceMapFilePath, sourceMap, /*writeByteOrderMark*/ false, sourceFiles);
}
}
else {
writer.writeLine();
}
// Write the output file
writeFile(host, emitterDiagnostics, jsFilePath, writer.getText(), !!compilerOptions.emitBOM, sourceFiles);
// Reset state
writer.clear();
}
interface SourceMapOptions {
sourceMap?: boolean;
inlineSourceMap?: boolean;
inlineSources?: boolean;
sourceRoot?: string;
mapRoot?: string;
extendedDiagnostics?: boolean;
}
function shouldEmitSourceMaps(mapOptions: SourceMapOptions, sourceFileOrBundle: SourceFile | Bundle) {
return (mapOptions.sourceMap || mapOptions.inlineSourceMap)
&& (sourceFileOrBundle.kind !== SyntaxKind.SourceFile || !fileExtensionIs(sourceFileOrBundle.fileName, Extension.Json));
}
function getSourceRoot(mapOptions: SourceMapOptions) {
// Normalize source root and make sure it has trailing "/" so that it can be used to combine paths with the
// relative paths of the sources list in the sourcemap
const sourceRoot = normalizeSlashes(mapOptions.sourceRoot || "");
return sourceRoot ? ensureTrailingDirectorySeparator(sourceRoot) : sourceRoot;
}
function getSourceMapDirectory(mapOptions: SourceMapOptions, filePath: string, sourceFile: SourceFile | undefined) {
if (mapOptions.sourceRoot) return host.getCommonSourceDirectory();
if (mapOptions.mapRoot) {
let sourceMapDir = normalizeSlashes(mapOptions.mapRoot);
if (sourceFile) {
// For modules or multiple emit files the mapRoot will have directory structure like the sources
// 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
sourceMapDir = getDirectoryPath(getSourceFilePathInNewDir(sourceFile.fileName, host, sourceMapDir));
}
if (getRootLength(sourceMapDir) === 0) {
// The relative paths are relative to the common directory
sourceMapDir = combinePaths(host.getCommonSourceDirectory(), sourceMapDir);
}
return sourceMapDir;
}
return getDirectoryPath(normalizePath(filePath));
}
function getSourceMappingURL(mapOptions: SourceMapOptions, sourceMapGenerator: SourceMapGenerator, filePath: string, sourceMapFilePath: string | undefined, sourceFile: SourceFile | undefined) {
if (mapOptions.inlineSourceMap) {
// Encode the sourceMap into the sourceMap url
const sourceMapText = sourceMapGenerator.toString();
const base64SourceMapText = base64encode(sys, sourceMapText);
return `data:application/json;base64,${base64SourceMapText}`;
}
const sourceMapFile = getBaseFileName(normalizeSlashes(Debug.assertDefined(sourceMapFilePath)));
if (mapOptions.mapRoot) {
let sourceMapDir = normalizeSlashes(mapOptions.mapRoot);
if (sourceFile) {
// For modules or multiple emit files the mapRoot will have directory structure like the sources
// 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
sourceMapDir = getDirectoryPath(getSourceFilePathInNewDir(sourceFile.fileName, host, sourceMapDir));
}
if (getRootLength(sourceMapDir) === 0) {
// The relative paths are relative to the common directory
sourceMapDir = combinePaths(host.getCommonSourceDirectory(), sourceMapDir);
return getRelativePathToDirectoryOrUrl(
getDirectoryPath(normalizePath(filePath)), // get the relative sourceMapDir path based on jsFilePath
combinePaths(sourceMapDir, sourceMapFile), // this is where user expects to see sourceMap
host.getCurrentDirectory(),
host.getCanonicalFileName,
/*isAbsolutePathAnUrl*/ true);
}
else {
return combinePaths(sourceMapDir, sourceMapFile);
}
}
return sourceMapFile;
}
}
/*@internal*/
export function getBuildInfoText(buildInfo: BuildInfo) {
return JSON.stringify(buildInfo, undefined, 2);
}
/*@internal*/
export function getBuildInfo(buildInfoText: string) {
return JSON.parse(buildInfoText) as BuildInfo;
}
/*@internal*/
export const notImplementedResolver: EmitResolver = {
hasGlobalName: notImplemented,
getReferencedExportContainer: notImplemented,
getReferencedImportDeclaration: notImplemented,
getReferencedDeclarationWithCollidingName: notImplemented,
isDeclarationWithCollidingName: notImplemented,
isValueAliasDeclaration: notImplemented,
isReferencedAliasDeclaration: notImplemented,
isTopLevelValueImportEqualsWithEntityName: notImplemented,
getNodeCheckFlags: notImplemented,
isDeclarationVisible: notImplemented,
isLateBound: (_node): _node is LateBoundDeclaration => false,
collectLinkedAliases: notImplemented,
isImplementationOfOverload: notImplemented,
isRequiredInitializedParameter: notImplemented,
isOptionalUninitializedParameterProperty: notImplemented,
isExpandoFunctionDeclaration: notImplemented,
getPropertiesOfContainerFunction: notImplemented,
createTypeOfDeclaration: notImplemented,
createReturnTypeOfSignatureDeclaration: notImplemented,
createTypeOfExpression: notImplemented,
createLiteralConstValue: notImplemented,
isSymbolAccessible: notImplemented,
isEntityNameVisible: notImplemented,
// Returns the constant value this property access resolves to: notImplemented, or 'undefined' for a non-constant
getConstantValue: notImplemented,
getReferencedValueDeclaration: notImplemented,
getTypeReferenceSerializationKind: notImplemented,
isOptionalParameter: notImplemented,
moduleExportsSomeValue: notImplemented,
isArgumentsLocalBinding: notImplemented,
getExternalModuleFileFromDeclaration: notImplemented,
getTypeReferenceDirectivesForEntityName: notImplemented,
getTypeReferenceDirectivesForSymbol: notImplemented,
isLiteralConstDeclaration: notImplemented,
getJsxFactoryEntity: notImplemented,
getAllAccessorDeclarations: notImplemented,
getSymbolOfExternalModuleSpecifier: notImplemented,
isBindingCapturedByNode: notImplemented,
getDeclarationStatementsForSourceFile: notImplemented,
};
/*@internal*/
/** File that isnt present resulting in error or output files */
export type EmitUsingBuildInfoResult = string | readonly OutputFile[];
/*@internal*/
export interface EmitUsingBuildInfoHost extends ModuleResolutionHost {
getCurrentDirectory(): string;
getCanonicalFileName(fileName: string): string;
useCaseSensitiveFileNames(): boolean;
getNewLine(): string;
}
function createSourceFilesFromBundleBuildInfo(bundle: BundleBuildInfo, buildInfoDirectory: string, host: EmitUsingBuildInfoHost): readonly SourceFile[] {
const sourceFiles = bundle.sourceFiles.map(fileName => {
const sourceFile = createNode(SyntaxKind.SourceFile, 0, 0) as SourceFile;
sourceFile.fileName = getRelativePathFromDirectory(
host.getCurrentDirectory(),
getNormalizedAbsolutePath(fileName, buildInfoDirectory),
!host.useCaseSensitiveFileNames()
);
sourceFile.text = "";
sourceFile.statements = createNodeArray();
return sourceFile;
});
const jsBundle = Debug.assertDefined(bundle.js);
forEach(jsBundle.sources && jsBundle.sources.prologues, prologueInfo => {
const sourceFile = sourceFiles[prologueInfo.file];
sourceFile.text = prologueInfo.text;
sourceFile.end = prologueInfo.text.length;
sourceFile.statements = createNodeArray(prologueInfo.directives.map(directive => {
const statement = createNode(SyntaxKind.ExpressionStatement, directive.pos, directive.end) as PrologueDirective;
statement.expression = createNode(SyntaxKind.StringLiteral, directive.expression.pos, directive.expression.end) as StringLiteral;
statement.expression.text = directive.expression.text;
return statement;
}));
});
return sourceFiles;
}
/*@internal*/
export function emitUsingBuildInfo(
config: ParsedCommandLine,
host: EmitUsingBuildInfoHost,
getCommandLine: (ref: ProjectReference) => ParsedCommandLine | undefined,
customTransformers?: CustomTransformers
): EmitUsingBuildInfoResult {
const { buildInfoPath, jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath } = getOutputPathsForBundle(config.options, /*forceDtsPaths*/ false);
const buildInfoText = host.readFile(Debug.assertDefined(buildInfoPath));
if (!buildInfoText) return buildInfoPath!;
const jsFileText = host.readFile(Debug.assertDefined(jsFilePath));
if (!jsFileText) return jsFilePath!;
const sourceMapText = sourceMapFilePath && host.readFile(sourceMapFilePath);
// error if no source map or for now if inline sourcemap
if ((sourceMapFilePath && !sourceMapText) || config.options.inlineSourceMap) return sourceMapFilePath || "inline sourcemap decoding";
// read declaration text
const declarationText = declarationFilePath && host.readFile(declarationFilePath);
if (declarationFilePath && !declarationText) return declarationFilePath;
const declarationMapText = declarationMapPath && host.readFile(declarationMapPath);
// error if no source map or for now if inline sourcemap
if ((declarationMapPath && !declarationMapText) || config.options.inlineSourceMap) return declarationMapPath || "inline sourcemap decoding";
const buildInfo = getBuildInfo(buildInfoText);
if (!buildInfo.bundle || !buildInfo.bundle.js || (declarationText && !buildInfo.bundle.dts)) return buildInfoPath!;
const buildInfoDirectory = getDirectoryPath(getNormalizedAbsolutePath(buildInfoPath!, host.getCurrentDirectory()));
const ownPrependInput = createInputFiles(
jsFileText,
declarationText!,
sourceMapFilePath,
sourceMapText,
declarationMapPath,
declarationMapText,
jsFilePath,
declarationFilePath,
buildInfoPath,
buildInfo,
/*onlyOwnText*/ true
);
const outputFiles: OutputFile[] = [];
const prependNodes = createPrependNodes(config.projectReferences, getCommandLine, f => host.readFile(f));
const sourceFilesForJsEmit = createSourceFilesFromBundleBuildInfo(buildInfo.bundle, buildInfoDirectory, host);
const emitHost: EmitHost = {
getPrependNodes: memoize(() => [...prependNodes, ownPrependInput]),
getCanonicalFileName: host.getCanonicalFileName,
getCommonSourceDirectory: () => getNormalizedAbsolutePath(buildInfo.bundle!.commonSourceDirectory, buildInfoDirectory),
getCompilerOptions: () => config.options,
getCurrentDirectory: () => host.getCurrentDirectory(),
getNewLine: () => host.getNewLine(),
getSourceFile: returnUndefined,
getSourceFileByPath: returnUndefined,
getSourceFiles: () => sourceFilesForJsEmit,
getLibFileFromReference: notImplemented,
isSourceFileFromExternalLibrary: returnFalse,
getResolvedProjectReferenceToRedirect: returnUndefined,
writeFile: (name, text, writeByteOrderMark) => {
switch (name) {
case jsFilePath:
if (jsFileText === text) return;
break;
case sourceMapFilePath:
if (sourceMapText === text) return;
break;
case buildInfoPath:
const newBuildInfo = getBuildInfo(text);
newBuildInfo.program = buildInfo.program;
// Update sourceFileInfo
const { js, dts, sourceFiles } = buildInfo.bundle!;
newBuildInfo.bundle!.js!.sources = js!.sources;
if (dts) {
newBuildInfo.bundle!.dts!.sources = dts.sources;
}
newBuildInfo.bundle!.sourceFiles = sourceFiles;
outputFiles.push({ name, text: getBuildInfoText(newBuildInfo), writeByteOrderMark });
return;
case declarationFilePath:
if (declarationText === text) return;
break;
case declarationMapPath:
if (declarationMapText === text) return;
break;
default:
Debug.fail(`Unexpected path: ${name}`);
}
outputFiles.push({ name, text, writeByteOrderMark });
},
isEmitBlocked: returnFalse,
readFile: f => host.readFile(f),
fileExists: f => host.fileExists(f),
directoryExists: host.directoryExists && (f => host.directoryExists!(f)),
useCaseSensitiveFileNames: () => host.useCaseSensitiveFileNames(),
getProgramBuildInfo: returnUndefined,
getSourceFileFromReference: returnUndefined,
redirectTargetsMap: createMultiMap()
};
emitFiles(
notImplementedResolver,
emitHost,
/*targetSourceFile*/ undefined,
getTransformers(config.options, customTransformers)
);
return outputFiles;
}
const enum PipelinePhase {
Notification,
Substitution,
Comments,
SourceMaps,
Emit
}
export function createPrinter(printerOptions: PrinterOptions = {}, handlers: PrintHandlers = {}): Printer {
const {
hasGlobalName,
onEmitNode = noEmitNotification,
substituteNode = noEmitSubstitution,
onBeforeEmitNodeArray,
onAfterEmitNodeArray,
onBeforeEmitToken,
onAfterEmitToken
} = handlers;
const extendedDiagnostics = !!printerOptions.extendedDiagnostics;
const newLine = getNewLineCharacter(printerOptions);
const moduleKind = getEmitModuleKind(printerOptions);
const bundledHelpers = createMap<boolean>();
let currentSourceFile: SourceFile | undefined;
let nodeIdToGeneratedName: string[]; // Map of generated names for specific nodes.
let autoGeneratedIdToGeneratedName: string[]; // Map of generated names for temp and loop variables.
let generatedNames: Map<true>; // Set of names generated by the NameGenerator.
let tempFlagsStack: TempFlags[]; // Stack of enclosing name generation scopes.
let tempFlags: TempFlags; // TempFlags for the current name generation scope.
let reservedNamesStack: Map<true>[]; // Stack of TempFlags reserved in enclosing name generation scopes.
let reservedNames: Map<true>; // TempFlags to reserve in nested name generation scopes.
let writer: EmitTextWriter;
let ownWriter: EmitTextWriter; // Reusable `EmitTextWriter` for basic printing.
let write = writeBase;
let isOwnFileEmit: boolean;
const bundleFileInfo = printerOptions.writeBundleFileInfo ? { sections: [] } as BundleFileInfo : undefined;
const relativeToBuildInfo = bundleFileInfo ? Debug.assertDefined(printerOptions.relativeToBuildInfo) : undefined;
const recordInternalSection = printerOptions.recordInternalSection;
let sourceFileTextPos = 0;
let sourceFileTextKind: BundleFileTextLikeKind = BundleFileSectionKind.Text;
// Source Maps
let sourceMapsDisabled = true;
let sourceMapGenerator: SourceMapGenerator | undefined;
let sourceMapSource: SourceMapSource;
let sourceMapSourceIndex = -1;
// Comments
let containerPos = -1;
let containerEnd = -1;
let declarationListContainerEnd = -1;
let currentLineMap: readonly number[] | undefined;
let detachedCommentsInfo: { nodePos: number, detachedCommentEndPos: number}[] | undefined;
let hasWrittenComment = false;
let commentsDisabled = !!printerOptions.removeComments;
let lastNode: Node | undefined;
let lastSubstitution: Node | undefined;
const { enter: enterComment, exit: exitComment } = performance.createTimerIf(extendedDiagnostics, "commentTime", "beforeComment", "afterComment");
reset();
return {
// public API
printNode,
printList,
printFile,
printBundle,
// internal API
writeNode,
writeList,
writeFile,
writeBundle,
bundleFileInfo
};
function printNode(hint: EmitHint, node: Node, sourceFile: SourceFile): string {
switch (hint) {
case EmitHint.SourceFile:
Debug.assert(isSourceFile(node), "Expected a SourceFile node.");
break;
case EmitHint.IdentifierName:
Debug.assert(isIdentifier(node), "Expected an Identifier node.");
break;
case EmitHint.Expression:
Debug.assert(isExpression(node), "Expected an Expression node.");
break;
}
switch (node.kind) {
case SyntaxKind.SourceFile: return printFile(<SourceFile>node);
case SyntaxKind.Bundle: return printBundle(<Bundle>node);
case SyntaxKind.UnparsedSource: return printUnparsedSource(<UnparsedSource>node);
}
writeNode(hint, node, sourceFile, beginPrint());
return endPrint();
}
function printList<T extends Node>(format: ListFormat, nodes: NodeArray<T>, sourceFile: SourceFile) {
writeList(format, nodes, sourceFile, beginPrint());
return endPrint();
}
function printBundle(bundle: Bundle): string {
writeBundle(bundle, beginPrint(), /*sourceMapEmitter*/ undefined);
return endPrint();
}
function printFile(sourceFile: SourceFile): string {
writeFile(sourceFile, beginPrint(), /*sourceMapEmitter*/ undefined);
return endPrint();
}
function printUnparsedSource(unparsed: UnparsedSource): string {
writeUnparsedSource(unparsed, beginPrint());
return endPrint();
}
/**
* If `sourceFile` is `undefined`, `node` must be a synthesized `TypeNode`.
*/
function writeNode(hint: EmitHint, node: TypeNode, sourceFile: undefined, output: EmitTextWriter): void;
function writeNode(hint: EmitHint, node: Node, sourceFile: SourceFile, output: EmitTextWriter): void;
function writeNode(hint: EmitHint, node: Node, sourceFile: SourceFile | undefined, output: EmitTextWriter) {
const previousWriter = writer;
setWriter(output, /*_sourceMapGenerator*/ undefined);
print(hint, node, sourceFile);
reset();
writer = previousWriter;
}
function writeList<T extends Node>(format: ListFormat, nodes: NodeArray<T>, sourceFile: SourceFile | undefined, output: EmitTextWriter) {
const previousWriter = writer;
setWriter(output, /*_sourceMapGenerator*/ undefined);
if (sourceFile) {
setSourceFile(sourceFile);
}
emitList(syntheticParent, nodes, format);
reset();
writer = previousWriter;
}
function getTextPosWithWriteLine() {
return writer.getTextPosWithWriteLine ? writer.getTextPosWithWriteLine() : writer.getTextPos();
}
function updateOrPushBundleFileTextLike(pos: number, end: number, kind: BundleFileTextLikeKind) {
const last = lastOrUndefined(bundleFileInfo!.sections);
if (last && last.kind === kind) {
last.end = end;
}
else {
bundleFileInfo!.sections.push({ pos, end, kind });
}
}
function recordBundleFileInternalSectionStart(node: Node) {
if (recordInternalSection &&
bundleFileInfo &&
currentSourceFile &&
(isDeclaration(node) || isVariableStatement(node)) &&
isInternalDeclaration(node, currentSourceFile) &&
sourceFileTextKind !== BundleFileSectionKind.Internal) {
const prevSourceFileTextKind = sourceFileTextKind;
recordBundleFileTextLikeSection(writer.getTextPos());
sourceFileTextPos = getTextPosWithWriteLine();
sourceFileTextKind = BundleFileSectionKind.Internal;
return prevSourceFileTextKind;
}
return undefined;
}
function recordBundleFileInternalSectionEnd(prevSourceFileTextKind: ReturnType<typeof recordBundleFileInternalSectionStart>) {
if (prevSourceFileTextKind) {
recordBundleFileTextLikeSection(writer.getTextPos());
sourceFileTextPos = getTextPosWithWriteLine();
sourceFileTextKind = prevSourceFileTextKind;
}
}
function recordBundleFileTextLikeSection(end: number) {
if (sourceFileTextPos < end) {
updateOrPushBundleFileTextLike(sourceFileTextPos, end, sourceFileTextKind);
return true;
}
return false;
}
function writeBundle(bundle: Bundle, output: EmitTextWriter, sourceMapGenerator: SourceMapGenerator | undefined) {
isOwnFileEmit = false;
const previousWriter = writer;
setWriter(output, sourceMapGenerator);
emitShebangIfNeeded(bundle);
emitPrologueDirectivesIfNeeded(bundle);
emitHelpers(bundle);
emitSyntheticTripleSlashReferencesIfNeeded(bundle);
for (const prepend of bundle.prepends) {
writeLine();
const pos = writer.getTextPos();
const savedSections = bundleFileInfo && bundleFileInfo.sections;
if (savedSections) bundleFileInfo!.sections = [];
print(EmitHint.Unspecified, prepend, /*sourceFile*/ undefined);
if (bundleFileInfo) {
const newSections = bundleFileInfo.sections;
bundleFileInfo.sections = savedSections!;
if (prepend.oldFileOfCurrentEmit) bundleFileInfo.sections.push(...newSections);
else {
newSections.forEach(section => Debug.assert(isBundleFileTextLike(section)));
bundleFileInfo.sections.push({
pos,
end: writer.getTextPos(),
kind: BundleFileSectionKind.Prepend,
data: relativeToBuildInfo!((prepend as UnparsedSource).fileName),
texts: newSections as BundleFileTextLike[]
});
}
}
}
sourceFileTextPos = getTextPosWithWriteLine();
for (const sourceFile of bundle.sourceFiles) {
print(EmitHint.SourceFile, sourceFile, sourceFile);
}
if (bundleFileInfo && bundle.sourceFiles.length) {
const end = writer.getTextPos();
if (recordBundleFileTextLikeSection(end)) {
// Store prologues
const prologues = getPrologueDirectivesFromBundledSourceFiles(bundle);
if (prologues) {
if (!bundleFileInfo.sources) bundleFileInfo.sources = {};
bundleFileInfo.sources.prologues = prologues;
}
// Store helpes
const helpers = getHelpersFromBundledSourceFiles(bundle);
if (helpers) {
if (!bundleFileInfo.sources) bundleFileInfo.sources = {};
bundleFileInfo.sources.helpers = helpers;
}
}
}
reset();
writer = previousWriter;
}
function writeUnparsedSource(unparsed: UnparsedSource, output: EmitTextWriter) {
const previousWriter = writer;
setWriter(output, /*_sourceMapGenerator*/ undefined);
print(EmitHint.Unspecified, unparsed, /*sourceFile*/ undefined);
reset();
writer = previousWriter;
}
function writeFile(sourceFile: SourceFile, output: EmitTextWriter, sourceMapGenerator: SourceMapGenerator | undefined) {
isOwnFileEmit = true;
const previousWriter = writer;
setWriter(output, sourceMapGenerator);
emitShebangIfNeeded(sourceFile);
emitPrologueDirectivesIfNeeded(sourceFile);
print(EmitHint.SourceFile, sourceFile, sourceFile);
reset();
writer = previousWriter;
}
function beginPrint() {
return ownWriter || (ownWriter = createTextWriter(newLine));
}
function endPrint() {
const text = ownWriter.getText();
ownWriter.clear();
return text;
}
function print(hint: EmitHint, node: Node, sourceFile: SourceFile | undefined) {
if (sourceFile) {
setSourceFile(sourceFile);
}
pipelineEmit(hint, node);
}
function setSourceFile(sourceFile: SourceFile | undefined) {
currentSourceFile = sourceFile;
currentLineMap = undefined;
detachedCommentsInfo = undefined;
if (sourceFile) {
setSourceMapSource(sourceFile);
}
}
function setWriter(_writer: EmitTextWriter | undefined, _sourceMapGenerator: SourceMapGenerator | undefined) {
if (_writer && printerOptions.omitTrailingSemicolon) {
_writer = getTrailingSemicolonDeferringWriter(_writer);
}
writer = _writer!; // TODO: GH#18217
sourceMapGenerator = _sourceMapGenerator;
sourceMapsDisabled = !writer || !sourceMapGenerator;
}
function reset() {
nodeIdToGeneratedName = [];
autoGeneratedIdToGeneratedName = [];
generatedNames = createMap<true>();
tempFlagsStack = [];
tempFlags = TempFlags.Auto;
reservedNamesStack = [];
currentSourceFile = undefined!;
currentLineMap = undefined!;
detachedCommentsInfo = undefined;
lastNode = undefined;
lastSubstitution = undefined;
setWriter(/*output*/ undefined, /*_sourceMapGenerator*/ undefined);
}
function getCurrentLineMap() {
return currentLineMap || (currentLineMap = getLineStarts(currentSourceFile!));
}
function emit(node: Node): Node;
function emit(node: Node | undefined): Node | undefined;
function emit(node: Node | undefined) {
if (node === undefined) return;
const prevSourceFileTextKind = recordBundleFileInternalSectionStart(node);
const substitute = pipelineEmit(EmitHint.Unspecified, node);
recordBundleFileInternalSectionEnd(prevSourceFileTextKind);
return substitute;
}
function emitIdentifierName(node: Identifier): Node;
function emitIdentifierName(node: Identifier | undefined): Node | undefined;
function emitIdentifierName(node: Identifier | undefined): Node | undefined {
if (node === undefined) return;
return pipelineEmit(EmitHint.IdentifierName, node);
}
function emitExpression(node: Expression): Node;
function emitExpression(node: Expression | undefined): Node | undefined;
function emitExpression(node: Expression | undefined): Node | undefined {
if (node === undefined) return;
return pipelineEmit(EmitHint.Expression, node);
}
function pipelineEmit(emitHint: EmitHint, node: Node) {
const savedLastNode = lastNode;
const savedLastSubstitution = lastSubstitution;
lastNode = node;
lastSubstitution = undefined;
const pipelinePhase = getPipelinePhase(PipelinePhase.Notification, node);
pipelinePhase(emitHint, node);
Debug.assert(lastNode === node);
const substitute = lastSubstitution;
lastNode = savedLastNode;
lastSubstitution = savedLastSubstitution;
return substitute || node;
}
function getPipelinePhase(phase: PipelinePhase, node: Node) {
switch (phase) {
case PipelinePhase.Notification:
if (onEmitNode !== noEmitNotification) {
return pipelineEmitWithNotification;
}
// falls through
case PipelinePhase.Substitution:
if (substituteNode !== noEmitSubstitution) {
return pipelineEmitWithSubstitution;
}
// falls through
case PipelinePhase.Comments:
if (!commentsDisabled && node.kind !== SyntaxKind.SourceFile) {
return pipelineEmitWithComments;
}
// falls through
case PipelinePhase.SourceMaps:
if (!sourceMapsDisabled && node.kind !== SyntaxKind.SourceFile && !isInJsonFile(node)) {
return pipelineEmitWithSourceMap;
}
// falls through
case PipelinePhase.Emit:
return pipelineEmitWithHint;
default:
return Debug.assertNever(phase);
}
}
function getNextPipelinePhase(currentPhase: PipelinePhase, node: Node) {
return getPipelinePhase(currentPhase + 1, node);
}
function pipelineEmitWithNotification(hint: EmitHint, node: Node) {
Debug.assert(lastNode === node);
const pipelinePhase = getNextPipelinePhase(PipelinePhase.Notification, node);
onEmitNode(hint, node, pipelinePhase);
Debug.assert(lastNode === node);
}
function pipelineEmitWithHint(hint: EmitHint, node: Node): void {
Debug.assert(lastNode === node || lastSubstitution === node);
if (hint === EmitHint.SourceFile) return emitSourceFile(cast(node, isSourceFile));
if (hint === EmitHint.IdentifierName) return emitIdentifier(cast(node, isIdentifier));
if (hint === EmitHint.MappedTypeParameter) return emitMappedTypeParameter(cast(node, isTypeParameterDeclaration));
if (hint === EmitHint.EmbeddedStatement) {
Debug.assertNode(node, isEmptyStatement);
return emitEmptyStatement(/*isEmbeddedStatement*/ true);
}
if (hint === EmitHint.Unspecified) {
if (isKeyword(node.kind)) return writeTokenNode(node, writeKeyword);
switch (node.kind) {
// Pseudo-literals
case SyntaxKind.TemplateHead:
case SyntaxKind.TemplateMiddle:
case SyntaxKind.TemplateTail:
return emitLiteral(<LiteralExpression>node);
case SyntaxKind.UnparsedSource:
case SyntaxKind.UnparsedPrepend:
return emitUnparsedSourceOrPrepend(<UnparsedSource>node);
case SyntaxKind.UnparsedPrologue:
return writeUnparsedNode(<UnparsedNode>node);
case SyntaxKind.UnparsedText:
case SyntaxKind.UnparsedInternalText:
return emitUnparsedTextLike(<UnparsedTextLike>node);
case SyntaxKind.UnparsedSyntheticReference:
return emitUnparsedSyntheticReference(<UnparsedSyntheticReference>node);
// Identifiers
case SyntaxKind.Identifier:
return emitIdentifier(<Identifier>node);
// Parse tree nodes
// Names
case SyntaxKind.QualifiedName:
return emitQualifiedName(<QualifiedName>node);
case SyntaxKind.ComputedPropertyName:
return emitComputedPropertyName(<ComputedPropertyName>node);
// Signature elements
case SyntaxKind.TypeParameter:
return emitTypeParameter(<TypeParameterDeclaration>node);
case SyntaxKind.Parameter:
return emitParameter(<ParameterDeclaration>node);
case SyntaxKind.Decorator:
return emitDecorator(<Decorator>node);
// Type members
case SyntaxKind.PropertySignature:
return emitPropertySignature(<PropertySignature>node);
case SyntaxKind.PropertyDeclaration:
return emitPropertyDeclaration(<PropertyDeclaration>node);
case SyntaxKind.MethodSignature:
return emitMethodSignature(<MethodSignature>node);
case SyntaxKind.MethodDeclaration:
return emitMethodDeclaration(<MethodDeclaration>node);
case SyntaxKind.Constructor:
return emitConstructor(<ConstructorDeclaration>node);
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
return emitAccessorDeclaration(<AccessorDeclaration>node);
case SyntaxKind.CallSignature:
return emitCallSignature(<CallSignatureDeclaration>node);
case SyntaxKind.ConstructSignature:
return emitConstructSignature(<ConstructSignatureDeclaration>node);
case SyntaxKind.IndexSignature:
return emitIndexSignature(<IndexSignatureDeclaration>node);
// Types
case SyntaxKind.TypePredicate:
return emitTypePredicate(<TypePredicateNode>node);
case SyntaxKind.TypeReference:
return emitTypeReference(<TypeReferenceNode>node);
case SyntaxKind.FunctionType:
return emitFunctionType(<FunctionTypeNode>node);
case SyntaxKind.JSDocFunctionType:
return emitJSDocFunctionType(node as JSDocFunctionType);
case SyntaxKind.ConstructorType:
return emitConstructorType(<ConstructorTypeNode>node);
case SyntaxKind.TypeQuery:
return emitTypeQuery(<TypeQueryNode>node);
case SyntaxKind.TypeLiteral:
return emitTypeLiteral(<TypeLiteralNode>node);
case SyntaxKind.ArrayType:
return emitArrayType(<ArrayTypeNode>node);
case SyntaxKind.TupleType:
return emitTupleType(<TupleTypeNode>node);
case SyntaxKind.OptionalType:
return emitOptionalType(<OptionalTypeNode>node);
case SyntaxKind.UnionType:
return emitUnionType(<UnionTypeNode>node);
case SyntaxKind.IntersectionType:
return emitIntersectionType(<IntersectionTypeNode>node);
case SyntaxKind.ConditionalType:
return emitConditionalType(<ConditionalTypeNode>node);
case SyntaxKind.InferType:
return emitInferType(<InferTypeNode>node);
case SyntaxKind.ParenthesizedType:
return emitParenthesizedType(<ParenthesizedTypeNode>node);
case SyntaxKind.ExpressionWithTypeArguments:
return emitExpressionWithTypeArguments(<ExpressionWithTypeArguments>node);
case SyntaxKind.ThisType:
return emitThisType();
case SyntaxKind.TypeOperator:
return emitTypeOperator(<TypeOperatorNode>node);
case SyntaxKind.IndexedAccessType:
return emitIndexedAccessType(<IndexedAccessTypeNode>node);
case SyntaxKind.MappedType:
return emitMappedType(<MappedTypeNode>node);
case SyntaxKind.LiteralType:
return emitLiteralType(<LiteralTypeNode>node);
case SyntaxKind.ImportType:
return emitImportTypeNode(<ImportTypeNode>node);
case SyntaxKind.JSDocAllType:
writePunctuation("*");
return;
case SyntaxKind.JSDocUnknownType:
writePunctuation("?");
return;
case SyntaxKind.JSDocNullableType:
return emitJSDocNullableType(node as JSDocNullableType);
case SyntaxKind.JSDocNonNullableType:
return emitJSDocNonNullableType(node as JSDocNonNullableType);
case SyntaxKind.JSDocOptionalType:
return emitJSDocOptionalType(node as JSDocOptionalType);
case SyntaxKind.RestType:
case SyntaxKind.JSDocVariadicType:
return emitRestOrJSDocVariadicType(node as RestTypeNode | JSDocVariadicType);
// Binding patterns
case SyntaxKind.ObjectBindingPattern:
return emitObjectBindingPattern(<ObjectBindingPattern>node);
case SyntaxKind.ArrayBindingPattern:
return emitArrayBindingPattern(<ArrayBindingPattern>node);
case SyntaxKind.BindingElement:
return emitBindingElement(<BindingElement>node);
// Misc
case SyntaxKind.TemplateSpan:
return emitTemplateSpan(<TemplateSpan>node);
case SyntaxKind.SemicolonClassElement:
return emitSemicolonClassElement();
// Statements
case SyntaxKind.Block:
return emitBlock(<Block>node);
case SyntaxKind.VariableStatement:
return emitVariableStatement(<VariableStatement>node);
case SyntaxKind.EmptyStatement:
return emitEmptyStatement(/*isEmbeddedStatement*/ false);
case SyntaxKind.ExpressionStatement:
return emitExpressionStatement(<ExpressionStatement>node);
case SyntaxKind.IfStatement:
return emitIfStatement(<IfStatement>node);
case SyntaxKind.DoStatement:
return emitDoStatement(<DoStatement>node);
case SyntaxKind.WhileStatement:
return emitWhileStatement(<WhileStatement>node);
case SyntaxKind.ForStatement:
return emitForStatement(<ForStatement>node);
case SyntaxKind.ForInStatement:
return emitForInStatement(<ForInStatement>node);
case SyntaxKind.ForOfStatement:
return emitForOfStatement(<ForOfStatement>node);
case SyntaxKind.ContinueStatement:
return emitContinueStatement(<ContinueStatement>node);
case SyntaxKind.BreakStatement:
return emitBreakStatement(<BreakStatement>node);
case SyntaxKind.ReturnStatement:
return emitReturnStatement(<ReturnStatement>node);
case SyntaxKind.WithStatement:
return emitWithStatement(<WithStatement>node);
case SyntaxKind.SwitchStatement:
return emitSwitchStatement(<SwitchStatement>node);
case SyntaxKind.LabeledStatement:
return emitLabeledStatement(<LabeledStatement>node);
case SyntaxKind.ThrowStatement:
return emitThrowStatement(<ThrowStatement>node);
case SyntaxKind.TryStatement:
return emitTryStatement(<TryStatement>node);
case SyntaxKind.DebuggerStatement:
return emitDebuggerStatement(<DebuggerStatement>node);
// Declarations
case SyntaxKind.VariableDeclaration:
return emitVariableDeclaration(<VariableDeclaration>node);
case SyntaxKind.VariableDeclarationList:
return emitVariableDeclarationList(<VariableDeclarationList>node);
case SyntaxKind.FunctionDeclaration:
return emitFunctionDeclaration(<FunctionDeclaration>node);
case SyntaxKind.ClassDeclaration:
return emitClassDeclaration(<ClassDeclaration>node);
case SyntaxKind.InterfaceDeclaration:
return emitInterfaceDeclaration(<InterfaceDeclaration>node);
case SyntaxKind.TypeAliasDeclaration:
return emitTypeAliasDeclaration(<TypeAliasDeclaration>node);
case SyntaxKind.EnumDeclaration:
return emitEnumDeclaration(<EnumDeclaration>node);
case SyntaxKind.ModuleDeclaration:
return emitModuleDeclaration(<ModuleDeclaration>node);
case SyntaxKind.ModuleBlock:
return emitModuleBlock(<ModuleBlock>node);
case SyntaxKind.CaseBlock:
return emitCaseBlock(<CaseBlock>node);
case SyntaxKind.NamespaceExportDeclaration:
return emitNamespaceExportDeclaration(<NamespaceExportDeclaration>node);
case SyntaxKind.ImportEqualsDeclaration:
return emitImportEqualsDeclaration(<ImportEqualsDeclaration>node);
case SyntaxKind.ImportDeclaration:
return emitImportDeclaration(<ImportDeclaration>node);
case SyntaxKind.ImportClause:
return emitImportClause(<ImportClause>node);
case SyntaxKind.NamespaceImport:
return emitNamespaceImport(<NamespaceImport>node);
case SyntaxKind.NamedImports:
return emitNamedImports(<NamedImports>node);
case SyntaxKind.ImportSpecifier:
return emitImportSpecifier(<ImportSpecifier>node);
case SyntaxKind.ExportAssignment:
return emitExportAssignment(<ExportAssignment>node);
case SyntaxKind.ExportDeclaration:
return emitExportDeclaration(<ExportDeclaration>node);
case SyntaxKind.NamedExports:
return emitNamedExports(<NamedExports>node);
case SyntaxKind.ExportSpecifier:
return emitExportSpecifier(<ExportSpecifier>node);
case SyntaxKind.MissingDeclaration:
return;
// Module references
case SyntaxKind.ExternalModuleReference:
return emitExternalModuleReference(<ExternalModuleReference>node);
// JSX (non-expression)
case SyntaxKind.JsxText:
return emitJsxText(<JsxText>node);
case SyntaxKind.JsxOpeningElement:
case SyntaxKind.JsxOpeningFragment:
return emitJsxOpeningElementOrFragment(<JsxOpeningElement>node);
case SyntaxKind.JsxClosingElement:
case SyntaxKind.JsxClosingFragment:
return emitJsxClosingElementOrFragment(<JsxClosingElement>node);
case SyntaxKind.JsxAttribute:
return emitJsxAttribute(<JsxAttribute>node);
case SyntaxKind.JsxAttributes:
return emitJsxAttributes(<JsxAttributes>node);
case SyntaxKind.JsxSpreadAttribute:
return emitJsxSpreadAttribute(<JsxSpreadAttribute>node);
case SyntaxKind.JsxExpression:
return emitJsxExpression(<JsxExpression>node);
// Clauses
case SyntaxKind.CaseClause:
return emitCaseClause(<CaseClause>node);
case SyntaxKind.DefaultClause:
return emitDefaultClause(<DefaultClause>node);
case SyntaxKind.HeritageClause:
return emitHeritageClause(<HeritageClause>node);
case SyntaxKind.CatchClause:
return emitCatchClause(<CatchClause>node);
// Property assignments
case SyntaxKind.PropertyAssignment:
return emitPropertyAssignment(<PropertyAssignment>node);
case SyntaxKind.ShorthandPropertyAssignment:
return emitShorthandPropertyAssignment(<ShorthandPropertyAssignment>node);
case SyntaxKind.SpreadAssignment:
return emitSpreadAssignment(node as SpreadAssignment);
// Enum
case SyntaxKind.EnumMember:
return emitEnumMember(<EnumMember>node);
// JSDoc nodes (only used in codefixes currently)
case SyntaxKind.JSDocParameterTag:
case SyntaxKind.JSDocPropertyTag:
return emitJSDocPropertyLikeTag(node as JSDocPropertyLikeTag);
case SyntaxKind.JSDocReturnTag:
case SyntaxKind.JSDocTypeTag:
case SyntaxKind.JSDocThisTag:
case SyntaxKind.JSDocEnumTag:
return emitJSDocSimpleTypedTag(node as JSDocTypeTag);
case SyntaxKind.JSDocAugmentsTag:
return emitJSDocAugmentsTag(node as JSDocAugmentsTag);
case SyntaxKind.JSDocTemplateTag:
return emitJSDocTemplateTag(node as JSDocTemplateTag);
case SyntaxKind.JSDocTypedefTag:
return emitJSDocTypedefTag(node as JSDocTypedefTag);
case SyntaxKind.JSDocCallbackTag:
return emitJSDocCallbackTag(node as JSDocCallbackTag);
case SyntaxKind.JSDocSignature:
return emitJSDocSignature(node as JSDocSignature);
case SyntaxKind.JSDocTypeLiteral:
return emitJSDocTypeLiteral(node as JSDocTypeLiteral);
case SyntaxKind.JSDocClassTag:
case SyntaxKind.JSDocTag:
return emitJSDocSimpleTag(node as JSDocTag);
case SyntaxKind.JSDocComment:
return emitJSDoc(node as JSDoc);
// Transformation nodes (ignored)
}
if (isExpression(node)) {
hint = EmitHint.Expression;
if (substituteNode !== noEmitSubstitution) {
lastSubstitution = node = substituteNode(hint, node);
}
}
else if (isToken(node)) {
return writeTokenNode(node, writePunctuation);
}
}
if (hint === EmitHint.Expression) {
switch (node.kind) {
// Literals
case SyntaxKind.NumericLiteral:
case SyntaxKind.BigIntLiteral:
return emitNumericOrBigIntLiteral(<NumericLiteral | BigIntLiteral>node);
case SyntaxKind.StringLiteral:
case SyntaxKind.RegularExpressionLiteral:
case SyntaxKind.NoSubstitutionTemplateLiteral:
return emitLiteral(<LiteralExpression>node);
// Identifiers
case SyntaxKind.Identifier:
return emitIdentifier(<Identifier>node);
// Reserved words
case SyntaxKind.FalseKeyword:
case SyntaxKind.NullKeyword:
case SyntaxKind.SuperKeyword:
case SyntaxKind.TrueKeyword:
case SyntaxKind.ThisKeyword:
case SyntaxKind.ImportKeyword:
writeTokenNode(node, writeKeyword);
return;
// Expressions
case SyntaxKind.ArrayLiteralExpression:
return emitArrayLiteralExpression(<ArrayLiteralExpression>node);
case SyntaxKind.ObjectLiteralExpression:
return emitObjectLiteralExpression(<ObjectLiteralExpression>node);
case SyntaxKind.PropertyAccessExpression:
return emitPropertyAccessExpression(<PropertyAccessExpression>node);
case SyntaxKind.ElementAccessExpression:
return emitElementAccessExpression(<ElementAccessExpression>node);
case SyntaxKind.CallExpression:
return emitCallExpression(<CallExpression>node);
case SyntaxKind.NewExpression:
return emitNewExpression(<NewExpression>node);
case SyntaxKind.TaggedTemplateExpression:
return emitTaggedTemplateExpression(<TaggedTemplateExpression>node);
case SyntaxKind.TypeAssertionExpression:
return emitTypeAssertionExpression(<TypeAssertion>node);
case SyntaxKind.ParenthesizedExpression:
return emitParenthesizedExpression(<ParenthesizedExpression>node);
case SyntaxKind.FunctionExpression:
return emitFunctionExpression(<FunctionExpression>node);
case SyntaxKind.ArrowFunction:
return emitArrowFunction(<ArrowFunction>node);
case SyntaxKind.DeleteExpression:
return emitDeleteExpression(<DeleteExpression>node);
case SyntaxKind.TypeOfExpression:
return emitTypeOfExpression(<TypeOfExpression>node);
case SyntaxKind.VoidExpression:
return emitVoidExpression(<VoidExpression>node);
case SyntaxKind.AwaitExpression:
return emitAwaitExpression(<AwaitExpression>node);
case SyntaxKind.PrefixUnaryExpression:
return emitPrefixUnaryExpression(<PrefixUnaryExpression>node);
case SyntaxKind.PostfixUnaryExpression:
return emitPostfixUnaryExpression(<PostfixUnaryExpression>node);
case SyntaxKind.BinaryExpression:
return emitBinaryExpression(<BinaryExpression>node);
case SyntaxKind.ConditionalExpression:
return emitConditionalExpression(<ConditionalExpression>node);
case SyntaxKind.TemplateExpression:
return emitTemplateExpression(<TemplateExpression>node);
case SyntaxKind.YieldExpression:
return emitYieldExpression(<YieldExpression>node);
case SyntaxKind.SpreadElement:
return emitSpreadExpression(<SpreadElement>node);
case SyntaxKind.ClassExpression:
return emitClassExpression(<ClassExpression>node);
case SyntaxKind.OmittedExpression:
return;
case SyntaxKind.AsExpression:
return emitAsExpression(<AsExpression>node);
case SyntaxKind.NonNullExpression:
return emitNonNullExpression(<NonNullExpression>node);
case SyntaxKind.MetaProperty:
return emitMetaProperty(<MetaProperty>node);
// JSX
case SyntaxKind.JsxElement:
return emitJsxElement(<JsxElement>node);
case SyntaxKind.JsxSelfClosingElement:
return emitJsxSelfClosingElement(<JsxSelfClosingElement>node);
case SyntaxKind.JsxFragment:
return emitJsxFragment(<JsxFragment>node);
// Transformation nodes
case SyntaxKind.PartiallyEmittedExpression:
return emitPartiallyEmittedExpression(<PartiallyEmittedExpression>node);
case SyntaxKind.CommaListExpression:
return emitCommaList(<CommaListExpression>node);
}
}
}
function emitMappedTypeParameter(node: TypeParameterDeclaration): void {
emit(node.name);
writeSpace();
writeKeyword("in");
writeSpace();
emit(node.constraint);
}
function pipelineEmitWithSubstitution(hint: EmitHint, node: Node) {
Debug.assert(lastNode === node || lastSubstitution === node);
const pipelinePhase = getNextPipelinePhase(PipelinePhase.Substitution, node);
lastSubstitution = substituteNode(hint, node);
pipelinePhase(hint, lastSubstitution);
Debug.assert(lastNode === node || lastSubstitution === node);
}
function getHelpersFromBundledSourceFiles(bundle: Bundle): string[] | undefined {
let result: string[] | undefined;
if (moduleKind === ModuleKind.None || printerOptions.noEmitHelpers) {
return undefined;
}
const bundledHelpers = createMap<boolean>();
for (const sourceFile of bundle.sourceFiles) {
const shouldSkip = getExternalHelpersModuleName(sourceFile) !== undefined;
const helpers = getSortedEmitHelpers(sourceFile);
if (!helpers) continue;
for (const helper of helpers) {
if (!helper.scoped && !shouldSkip && !bundledHelpers.get(helper.name)) {
bundledHelpers.set(helper.name, true);
(result || (result = [])).push(helper.name);
}
}
}
return result;
}
function emitHelpers(node: Node) {
let helpersEmitted = false;
const bundle = node.kind === SyntaxKind.Bundle ? <Bundle>node : undefined;
if (bundle && moduleKind === ModuleKind.None) {
return;
}
const numPrepends = bundle ? bundle.prepends.length : 0;
const numNodes = bundle ? bundle.sourceFiles.length + numPrepends : 1;
for (let i = 0; i < numNodes; i++) {
const currentNode = bundle ? i < numPrepends ? bundle.prepends[i] : bundle.sourceFiles[i - numPrepends] : node;
const sourceFile = isSourceFile(currentNode) ? currentNode : isUnparsedSource(currentNode) ? undefined : currentSourceFile!;
const shouldSkip = printerOptions.noEmitHelpers || (!!sourceFile && hasRecordedExternalHelpers(sourceFile));
const shouldBundle = (isSourceFile(currentNode) || isUnparsedSource(currentNode)) && !isOwnFileEmit;
const helpers = isUnparsedSource(currentNode) ? currentNode.helpers : getSortedEmitHelpers(currentNode);
if (helpers) {
for (const helper of helpers) {
if (!helper.scoped) {
// Skip the helper if it can be skipped and the noEmitHelpers compiler
// option is set, or if it can be imported and the importHelpers compiler
// option is set.
if (shouldSkip) continue;
// Skip the helper if it can be bundled but hasn't already been emitted and we
// are emitting a bundled module.
if (shouldBundle) {
if (bundledHelpers.get(helper.name)) {
continue;
}
bundledHelpers.set(helper.name, true);
}
}
else if (bundle) {
// Skip the helper if it is scoped and we are emitting bundled helpers
continue;
}
const pos = getTextPosWithWriteLine();
if (typeof helper.text === "string") {
writeLines(helper.text);
}
else {
writeLines(helper.text(makeFileLevelOptimisticUniqueName));
}
if (bundleFileInfo) bundleFileInfo.sections.push({ pos, end: writer.getTextPos(), kind: BundleFileSectionKind.EmitHelpers, data: helper.name });
helpersEmitted = true;
}
}
}
return helpersEmitted;
}
function getSortedEmitHelpers(node: Node) {
const helpers = getEmitHelpers(node);
return helpers && stableSort(helpers, compareEmitHelpers);
}
//
// Literals/Pseudo-literals
//
// SyntaxKind.NumericLiteral
// SyntaxKind.BigIntLiteral
function emitNumericOrBigIntLiteral(node: NumericLiteral | BigIntLiteral) {
emitLiteral(node);
}
// SyntaxKind.StringLiteral
// SyntaxKind.RegularExpressionLiteral
// SyntaxKind.NoSubstitutionTemplateLiteral
// SyntaxKind.TemplateHead
// SyntaxKind.TemplateMiddle
// SyntaxKind.TemplateTail
function emitLiteral(node: LiteralLikeNode) {
const text = getLiteralTextOfNode(node, printerOptions.neverAsciiEscape);
if ((printerOptions.sourceMap || printerOptions.inlineSourceMap)
&& (node.kind === SyntaxKind.StringLiteral || isTemplateLiteralKind(node.kind))) {
writeLiteral(text);
}
else {
// Quick info expects all literals to be called with writeStringLiteral, as there's no specific type for numberLiterals
writeStringLiteral(text);
}
}
// SyntaxKind.UnparsedSource
// SyntaxKind.UnparsedPrepend
function emitUnparsedSourceOrPrepend(unparsed: UnparsedSource | UnparsedPrepend) {
for (const text of unparsed.texts) {
writeLine();
emit(text);
}
}
// SyntaxKind.UnparsedPrologue
// SyntaxKind.UnparsedText
// SyntaxKind.UnparsedInternal
// SyntaxKind.UnparsedSyntheticReference
function writeUnparsedNode(unparsed: UnparsedNode) {
writer.rawWrite(unparsed.parent.text.substring(unparsed.pos, unparsed.end));
}
// SyntaxKind.UnparsedText
// SyntaxKind.UnparsedInternal
function emitUnparsedTextLike(unparsed: UnparsedTextLike) {
const pos = getTextPosWithWriteLine();
writeUnparsedNode(unparsed);
if (bundleFileInfo) {
updateOrPushBundleFileTextLike(
pos,
writer.getTextPos(),
unparsed.kind === SyntaxKind.UnparsedText ?
BundleFileSectionKind.Text :
BundleFileSectionKind.Internal
);
}
}
// SyntaxKind.UnparsedSyntheticReference
function emitUnparsedSyntheticReference(unparsed: UnparsedSyntheticReference) {
const pos = getTextPosWithWriteLine();
writeUnparsedNode(unparsed);
if (bundleFileInfo) {
const section = clone(unparsed.section);
section.pos = pos;
section.end = writer.getTextPos();
bundleFileInfo.sections.push(section);
}
}
//
// Identifiers
//
function emitIdentifier(node: Identifier) {
const writeText = node.symbol ? writeSymbol : write;
writeText(getTextOfNode(node, /*includeTrivia*/ false), node.symbol);
emitList(node, node.typeArguments, ListFormat.TypeParameters); // Call emitList directly since it could be an array of TypeParameterDeclarations _or_ type arguments
}
//
// Names
//
function emitQualifiedName(node: QualifiedName) {
emitEntityName(node.left);
writePunctuation(".");
emit(node.right);
}
function emitEntityName(node: EntityName) {
if (node.kind === SyntaxKind.Identifier) {
emitExpression(node);
}
else {
emit(node);
}
}
function emitComputedPropertyName(node: ComputedPropertyName) {
writePunctuation("[");
emitExpression(node.expression);
writePunctuation("]");
}
//
// Signature elements
//
function emitTypeParameter(node: TypeParameterDeclaration) {
emit(node.name);
if (node.constraint) {
writeSpace();
writeKeyword("extends");
writeSpace();
emit(node.constraint);
}
if (node.default) {
writeSpace();
writeOperator("=");
writeSpace();
emit(node.default);
}
}
function emitParameter(node: ParameterDeclaration) {
emitDecorators(node, node.decorators);
emitModifiers(node, node.modifiers);
emit(node.dotDotDotToken);
emitNodeWithWriter(node.name, writeParameter);
emit(node.questionToken);
if (node.parent && node.parent.kind === SyntaxKind.JSDocFunctionType && !node.name) {
emit(node.type);
}
else {
emitTypeAnnotation(node.type);
}
// 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.
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);
}
function emitDecorator(decorator: Decorator) {
writePunctuation("@");
emitExpression(decorator.expression);
}
//
// Type members
//
function emitPropertySignature(node: PropertySignature) {
emitDecorators(node, node.decorators);
emitModifiers(node, node.modifiers);
emitNodeWithWriter(node.name, writeProperty);
emit(node.questionToken);
emitTypeAnnotation(node.type);
writeTrailingSemicolon();
}
function emitPropertyDeclaration(node: PropertyDeclaration) {
emitDecorators(node, node.decorators);
emitModifiers(node, node.modifiers);
emit(node.name);
emit(node.questionToken);
emit(node.exclamationToken);
emitTypeAnnotation(node.type);
emitInitializer(node.initializer, node.type ? node.type.end : node.questionToken ? node.questionToken.end : node.name.end, node);
writeTrailingSemicolon();
}
function emitMethodSignature(node: MethodSignature) {
pushNameGenerationScope(node);
emitDecorators(node, node.decorators);
emitModifiers(node, node.modifiers);
emit(node.name);
emit(node.questionToken);
emitTypeParameters(node, node.typeParameters);
emitParameters(node, node.parameters);
emitTypeAnnotation(node.type);
writeTrailingSemicolon();
popNameGenerationScope(node);
}
function emitMethodDeclaration(node: MethodDeclaration) {
emitDecorators(node, node.decorators);
emitModifiers(node, node.modifiers);
emit(node.asteriskToken);
emit(node.name);
emit(node.questionToken);
emitSignatureAndBody(node, emitSignatureHead);
}
function emitConstructor(node: ConstructorDeclaration) {
emitModifiers(node, node.modifiers);
writeKeyword("constructor");
emitSignatureAndBody(node, emitSignatureHead);
}
function emitAccessorDeclaration(node: AccessorDeclaration) {
emitDecorators(node, node.decorators);
emitModifiers(node, node.modifiers);
writeKeyword(node.kind === SyntaxKind.GetAccessor ? "get" : "set");
writeSpace();
emit(node.name);
emitSignatureAndBody(node, emitSignatureHead);
}
function emitCallSignature(node: CallSignatureDeclaration) {
pushNameGenerationScope(node);
emitDecorators(node, node.decorators);
emitModifiers(node, node.modifiers);
emitTypeParameters(node, node.typeParameters);
emitParameters(node, node.parameters);
emitTypeAnnotation(node.type);
writeTrailingSemicolon();
popNameGenerationScope(node);
}
function emitConstructSignature(node: ConstructSignatureDeclaration) {
pushNameGenerationScope(node);
emitDecorators(node, node.decorators);
emitModifiers(node, node.modifiers);
writeKeyword("new");
writeSpace();
emitTypeParameters(node, node.typeParameters);
emitParameters(node, node.parameters);
emitTypeAnnotation(node.type);
writeTrailingSemicolon();
popNameGenerationScope(node);
}
function emitIndexSignature(node: IndexSignatureDeclaration) {
emitDecorators(node, node.decorators);
emitModifiers(node, node.modifiers);
emitParametersForIndexSignature(node, node.parameters);
emitTypeAnnotation(node.type);
writeTrailingSemicolon();
}
function emitSemicolonClassElement() {
writeTrailingSemicolon();
}
//
// Types
//
function emitTypePredicate(node: TypePredicateNode) {
if (node.assertsModifier) {
emit(node.assertsModifier);
writeSpace();
}
emit(node.parameterName);
if (node.type) {
writeSpace();
writeKeyword("is");
writeSpace();
emit(node.type);
}
}
function emitTypeReference(node: TypeReferenceNode) {
emit(node.typeName);
emitTypeArguments(node, node.typeArguments);
}
function emitFunctionType(node: FunctionTypeNode) {
pushNameGenerationScope(node);
emitTypeParameters(node, node.typeParameters);
emitParametersForArrow(node, node.parameters);
writeSpace();
writePunctuation("=>");
writeSpace();
emit(node.type);
popNameGenerationScope(node);
}
function emitJSDocFunctionType(node: JSDocFunctionType) {
writeKeyword("function");
emitParameters(node, node.parameters);
writePunctuation(":");
emit(node.type);
}
function emitJSDocNullableType(node: JSDocNullableType) {
writePunctuation("?");
emit(node.type);
}
function emitJSDocNonNullableType(node: JSDocNonNullableType) {
writePunctuation("!");
emit(node.type);
}
function emitJSDocOptionalType(node: JSDocOptionalType) {
emit(node.type);
writePunctuation("=");
}
function emitConstructorType(node: ConstructorTypeNode) {
pushNameGenerationScope(node);
writeKeyword("new");
writeSpace();
emitTypeParameters(node, node.typeParameters);
emitParameters(node, node.parameters);
writeSpace();
writePunctuation("=>");
writeSpace();
emit(node.type);
popNameGenerationScope(node);
}
function emitTypeQuery(node: TypeQueryNode) {
writeKeyword("typeof");
writeSpace();
emit(node.exprName);
}
function emitTypeLiteral(node: TypeLiteralNode) {
writePunctuation("{");
const flags = getEmitFlags(node) & EmitFlags.SingleLine ? ListFormat.SingleLineTypeLiteralMembers : ListFormat.MultiLineTypeLiteralMembers;
emitList(node, node.members, flags | ListFormat.NoSpaceIfEmpty);
writePunctuation("}");
}
function emitArrayType(node: ArrayTypeNode) {
emit(node.elementType);
writePunctuation("[");
writePunctuation("]");
}
function emitRestOrJSDocVariadicType(node: RestTypeNode | JSDocVariadicType) {
writePunctuation("...");
emit(node.type);
}
function emitTupleType(node: TupleTypeNode) {
writePunctuation("[");
emitList(node, node.elementTypes, ListFormat.TupleTypeElements);
writePunctuation("]");
}
function emitOptionalType(node: OptionalTypeNode) {
emit(node.type);
writePunctuation("?");
}
function emitUnionType(node: UnionTypeNode) {
emitList(node, node.types, ListFormat.UnionTypeConstituents);
}
function emitIntersectionType(node: IntersectionTypeNode) {
emitList(node, node.types, ListFormat.IntersectionTypeConstituents);
}
function emitConditionalType(node: ConditionalTypeNode) {
emit(node.checkType);
writeSpace();
writeKeyword("extends");
writeSpace();
emit(node.extendsType);
writeSpace();
writePunctuation("?");
writeSpace();
emit(node.trueType);
writeSpace();
writePunctuation(":");
writeSpace();
emit(node.falseType);
}
function emitInferType(node: InferTypeNode) {
writeKeyword("infer");
writeSpace();
emit(node.typeParameter);
}
function emitParenthesizedType(node: ParenthesizedTypeNode) {
writePunctuation("(");
emit(node.type);
writePunctuation(")");
}
function emitThisType() {
writeKeyword("this");
}
function emitTypeOperator(node: TypeOperatorNode) {
writeTokenText(node.operator, writeKeyword);
writeSpace();
emit(node.type);
}
function emitIndexedAccessType(node: IndexedAccessTypeNode) {
emit(node.objectType);
writePunctuation("[");
emit(node.indexType);
writePunctuation("]");
}
function emitMappedType(node: MappedTypeNode) {
const emitFlags = getEmitFlags(node);
writePunctuation("{");
if (emitFlags & EmitFlags.SingleLine) {
writeSpace();
}
else {
writeLine();
increaseIndent();
}
if (node.readonlyToken) {
emit(node.readonlyToken);
if (node.readonlyToken.kind !== SyntaxKind.ReadonlyKeyword) {
writeKeyword("readonly");
}
writeSpace();
}
writePunctuation("[");
pipelineEmit(EmitHint.MappedTypeParameter, node.typeParameter);
writePunctuation("]");
if (node.questionToken) {
emit(node.questionToken);
if (node.questionToken.kind !== SyntaxKind.QuestionToken) {
writePunctuation("?");
}
}
writePunctuation(":");
writeSpace();
emit(node.type);
writeTrailingSemicolon();
if (emitFlags & EmitFlags.SingleLine) {
writeSpace();
}
else {
writeLine();
decreaseIndent();
}
writePunctuation("}");
}
function emitLiteralType(node: LiteralTypeNode) {
emitExpression(node.literal);
}
function emitImportTypeNode(node: ImportTypeNode) {
if (node.isTypeOf) {
writeKeyword("typeof");
writeSpace();
}
writeKeyword("import");
writePunctuation("(");
emit(node.argument);
writePunctuation(")");
if (node.qualifier) {
writePunctuation(".");
emit(node.qualifier);
}
emitTypeArguments(node, node.typeArguments);
}
//
// Binding patterns
//
function emitObjectBindingPattern(node: ObjectBindingPattern) {
writePunctuation("{");
emitList(node, node.elements, ListFormat.ObjectBindingPatternElements);
writePunctuation("}");
}
function emitArrayBindingPattern(node: ArrayBindingPattern) {
writePunctuation("[");
emitList(node, node.elements, ListFormat.ArrayBindingPatternElements);
writePunctuation("]");
}
function emitBindingElement(node: BindingElement) {
emit(node.dotDotDotToken);
if (node.propertyName) {
emit(node.propertyName);
writePunctuation(":");
writeSpace();
}
emit(node.name);
emitInitializer(node.initializer, node.name.end, node);
}
//
// Expressions
//
function emitArrayLiteralExpression(node: ArrayLiteralExpression) {
const elements = node.elements;
const preferNewLine = node.multiLine ? ListFormat.PreferNewLine : ListFormat.None;
emitExpressionList(node, elements, ListFormat.ArrayLiteralExpressionElements | preferNewLine);
}
function emitObjectLiteralExpression(node: ObjectLiteralExpression) {
forEach(node.properties, generateMemberNames);
const indentedFlag = getEmitFlags(node) & EmitFlags.Indented;
if (indentedFlag) {
increaseIndent();
}
const preferNewLine = node.multiLine ? ListFormat.PreferNewLine : ListFormat.None;
const allowTrailingComma = currentSourceFile!.languageVersion >= ScriptTarget.ES5 && !isJsonSourceFile(currentSourceFile!) ? ListFormat.AllowTrailingComma : ListFormat.None;
emitList(node, node.properties, ListFormat.ObjectLiteralExpressionProperties | allowTrailingComma | preferNewLine);
if (indentedFlag) {
decreaseIndent();
}
}
function emitPropertyAccessExpression(node: PropertyAccessExpression) {
const expression = cast(emitExpression(node.expression), isExpression);
const token = getDotOrQuestionDotToken(node);
const indentBeforeDot = needsIndentation(node, node.expression, token);
const indentAfterDot = needsIndentation(node, token, node.name);
increaseIndentIf(indentBeforeDot, /*writeSpaceIfNotIndenting*/ false);
const shouldEmitDotDot =
token.kind !== SyntaxKind.QuestionDotToken &&
mayNeedDotDotForPropertyAccess(expression) &&
!writer.hasTrailingComment() &&
!writer.hasTrailingWhitespace();
if (shouldEmitDotDot) {
writePunctuation(".");
}
emitTokenWithComment(token.kind, node.expression.end, writePunctuation, node);
increaseIndentIf(indentAfterDot, /*writeSpaceIfNotIndenting*/ false);
emit(node.name);
decreaseIndentIf(indentBeforeDot, indentAfterDot);
}
// 1..toString is a valid property access, emit a dot after the literal
// Also emit a dot if expression is a integer const enum value - it will appear in generated code as numeric literal
function mayNeedDotDotForPropertyAccess(expression: Expression) {
expression = skipPartiallyEmittedExpressions(expression);
if (isNumericLiteral(expression)) {
// check if numeric literal is a decimal literal that was originally written with a dot
const text = getLiteralTextOfNode(<LiteralExpression>expression, /*neverAsciiEscape*/ true);
// If he number will be printed verbatim and it doesn't already contain a dot, add one
// if the expression doesn't have any comments that will be emitted.
return !expression.numericLiteralFlags && !stringContains(text, tokenToString(SyntaxKind.DotToken)!);
}
else if (isAccessExpression(expression)) {
// check if constant enum value is integer
const constantValue = getConstantValue(expression);
// isFinite handles cases when constantValue is undefined
return typeof constantValue === "number" && isFinite(constantValue)
&& Math.floor(constantValue) === constantValue;
}
}
function emitElementAccessExpression(node: ElementAccessExpression) {
emitExpression(node.expression);
emit(node.questionDotToken);
emitTokenWithComment(SyntaxKind.OpenBracketToken, node.expression.end, writePunctuation, node);
emitExpression(node.argumentExpression);
emitTokenWithComment(SyntaxKind.CloseBracketToken, node.argumentExpression.end, writePunctuation, node);
}
function emitCallExpression(node: CallExpression) {
emitExpression(node.expression);
emit(node.questionDotToken);
emitTypeArguments(node, node.typeArguments);
emitExpressionList(node, node.arguments, ListFormat.CallExpressionArguments);
}
function emitNewExpression(node: NewExpression) {
emitTokenWithComment(SyntaxKind.NewKeyword, node.pos, writeKeyword, node);
writeSpace();
emitExpression(node.expression);
emitTypeArguments(node, node.typeArguments);
emitExpressionList(node, node.arguments, ListFormat.NewExpressionArguments);
}
function emitTaggedTemplateExpression(node: TaggedTemplateExpression) {
emitExpression(node.tag);
emitTypeArguments(node, node.typeArguments);
writeSpace();
emitExpression(node.template);
}
function emitTypeAssertionExpression(node: TypeAssertion) {
writePunctuation("<");
emit(node.type);
writePunctuation(">");
emitExpression(node.expression);
}
function emitParenthesizedExpression(node: ParenthesizedExpression) {
const openParenPos = emitTokenWithComment(SyntaxKind.OpenParenToken, node.pos, writePunctuation, node);
emitExpression(node.expression);
emitTokenWithComment(SyntaxKind.CloseParenToken, node.expression ? node.expression.end : openParenPos, writePunctuation, node);
}
function emitFunctionExpression(node: FunctionExpression) {
generateNameIfNeeded(node.name);
emitFunctionDeclarationOrExpression(node);
}
function emitArrowFunction(node: ArrowFunction) {
emitDecorators(node, node.decorators);
emitModifiers(node, node.modifiers);
emitSignatureAndBody(node, emitArrowFunctionHead);
}
function emitArrowFunctionHead(node: ArrowFunction) {
emitTypeParameters(node, node.typeParameters);
emitParametersForArrow(node, node.parameters);
emitTypeAnnotation(node.type);
writeSpace();
emit(node.equalsGreaterThanToken);
}
function emitDeleteExpression(node: DeleteExpression) {
emitTokenWithComment(SyntaxKind.DeleteKeyword, node.pos, writeKeyword, node);
writeSpace();
emitExpression(node.expression);
}
function emitTypeOfExpression(node: TypeOfExpression) {
emitTokenWithComment(SyntaxKind.TypeOfKeyword, node.pos, writeKeyword, node);
writeSpace();
emitExpression(node.expression);
}
function emitVoidExpression(node: VoidExpression) {
emitTokenWithComment(SyntaxKind.VoidKeyword, node.pos, writeKeyword, node);
writeSpace();
emitExpression(node.expression);
}
function emitAwaitExpression(node: AwaitExpression) {
emitTokenWithComment(SyntaxKind.AwaitKeyword, node.pos, writeKeyword, node);
writeSpace();
emitExpression(node.expression);
}
function emitPrefixUnaryExpression(node: PrefixUnaryExpression) {
writeTokenText(node.operator, writeOperator);
if (shouldEmitWhitespaceBeforeOperand(node)) {
writeSpace();
}
emitExpression(node.operand);
}
function shouldEmitWhitespaceBeforeOperand(node: PrefixUnaryExpression) {
// In some cases, we need to emit a space between the operator and the operand. One obvious case
// is when the operator is an identifier, like delete or typeof. We also need to do this for plus
// and minus expressions in certain cases. Specifically, consider the following two cases (parens
// are just for clarity of exposition, and not part of the source code):
//
// (+(+1))
// (+(++1))
//
// We need to emit a space in both cases. In the first case, the absence of a space will make
// the resulting expression a prefix increment operation. And in the second, it will make the resulting
// expression a prefix increment whose operand is a plus expression - (++(+x))
// The same is true of minus of course.
const operand = node.operand;
return operand.kind === SyntaxKind.PrefixUnaryExpression
&& ((node.operator === SyntaxKind.PlusToken && ((<PrefixUnaryExpression>operand).operator === SyntaxKind.PlusToken || (<PrefixUnaryExpression>operand).operator === SyntaxKind.PlusPlusToken))
|| (node.operator === SyntaxKind.MinusToken && ((<PrefixUnaryExpression>operand).operator === SyntaxKind.MinusToken || (<PrefixUnaryExpression>operand).operator === SyntaxKind.MinusMinusToken)));
}
function emitPostfixUnaryExpression(node: PostfixUnaryExpression) {
emitExpression(node.operand);
writeTokenText(node.operator, writeOperator);
}
function emitBinaryExpression(node: BinaryExpression) {
const isCommaOperator = node.operatorToken.kind !== SyntaxKind.CommaToken;
const indentBeforeOperator = needsIndentation(node, node.left, node.operatorToken);
const indentAfterOperator = needsIndentation(node, node.operatorToken, node.right);
emitExpression(node.left);
increaseIndentIf(indentBeforeOperator, isCommaOperator);
emitLeadingCommentsOfPosition(node.operatorToken.pos);
writeTokenNode(node.operatorToken, node.operatorToken.kind === SyntaxKind.InKeyword ? writeKeyword : writeOperator);
emitTrailingCommentsOfPosition(node.operatorToken.end, /*prefixSpace*/ true); // Binary operators should have a space before the comment starts
increaseIndentIf(indentAfterOperator, /*writeSpaceIfNotIndenting*/ true);
emitExpression(node.right);
decreaseIndentIf(indentBeforeOperator, indentAfterOperator);
}
function emitConditionalExpression(node: ConditionalExpression) {
const indentBeforeQuestion = needsIndentation(node, node.condition, node.questionToken);
const indentAfterQuestion = needsIndentation(node, node.questionToken, node.whenTrue);
const indentBeforeColon = needsIndentation(node, node.whenTrue, node.colonToken);
const indentAfterColon = needsIndentation(node, node.colonToken, node.whenFalse);
emitExpression(node.condition);
increaseIndentIf(indentBeforeQuestion, /*writeSpaceIfNotIndenting*/ true);
emit(node.questionToken);
increaseIndentIf(indentAfterQuestion, /*writeSpaceIfNotIndenting*/ true);
emitExpression(node.whenTrue);
decreaseIndentIf(indentBeforeQuestion, indentAfterQuestion);
increaseIndentIf(indentBeforeColon, /*writeSpaceIfNotIndenting*/ true);
emit(node.colonToken);
increaseIndentIf(indentAfterColon, /*writeSpaceIfNotIndenting*/ true);
emitExpression(node.whenFalse);
decreaseIndentIf(indentBeforeColon, indentAfterColon);
}
function emitTemplateExpression(node: TemplateExpression) {
emit(node.head);
emitList(node, node.templateSpans, ListFormat.TemplateExpressionSpans);
}
function emitYieldExpression(node: YieldExpression) {
emitTokenWithComment(SyntaxKind.YieldKeyword, node.pos, writeKeyword, node);
emit(node.asteriskToken);
emitExpressionWithLeadingSpace(node.expression);
}
function emitSpreadExpression(node: SpreadElement) {
emitTokenWithComment(SyntaxKind.DotDotDotToken, node.pos, writePunctuation, node);
emitExpression(node.expression);
}
function emitClassExpression(node: ClassExpression) {
generateNameIfNeeded(node.name);
emitClassDeclarationOrExpression(node);
}
function emitExpressionWithTypeArguments(node: ExpressionWithTypeArguments) {
emitExpression(node.expression);
emitTypeArguments(node, node.typeArguments);
}
function emitAsExpression(node: AsExpression) {
emitExpression(node.expression);
if (node.type) {
writeSpace();
writeKeyword("as");
writeSpace();
emit(node.type);
}
}
function emitNonNullExpression(node: NonNullExpression) {
emitExpression(node.expression);
writeOperator("!");
}
function emitMetaProperty(node: MetaProperty) {
writeToken(node.keywordToken, node.pos, writePunctuation);
writePunctuation(".");
emit(node.name);
}
//
// Misc
//
function emitTemplateSpan(node: TemplateSpan) {
emitExpression(node.expression);
emit(node.literal);
}
//
// Statements
//
function emitBlock(node: Block) {
emitBlockStatements(node, /*forceSingleLine*/ !node.multiLine && isEmptyBlock(node));
}
function emitBlockStatements(node: BlockLike, forceSingleLine: boolean) {
emitTokenWithComment(SyntaxKind.OpenBraceToken, node.pos, writePunctuation, /*contextNode*/ node);
const format = forceSingleLine || getEmitFlags(node) & EmitFlags.SingleLine ? ListFormat.SingleLineBlockStatements : ListFormat.MultiLineBlockStatements;
emitList(node, node.statements, format);
emitTokenWithComment(SyntaxKind.CloseBraceToken, node.statements.end, writePunctuation, /*contextNode*/ node, /*indentLeading*/ !!(format & ListFormat.MultiLine));
}
function emitVariableStatement(node: VariableStatement) {
emitModifiers(node, node.modifiers);
emit(node.declarationList);
writeTrailingSemicolon();
}
function emitEmptyStatement(isEmbeddedStatement: boolean) {
// While most trailing semicolons are possibly insignificant, an embedded "empty"
// statement is significant and cannot be elided by a trailing-semicolon-omitting writer.
if (isEmbeddedStatement) {
writePunctuation(";");
}
else {
writeTrailingSemicolon();
}
}
function emitExpressionStatement(node: ExpressionStatement) {
emitExpression(node.expression);
// Emit semicolon in non json files
// or if json file that created synthesized expression(eg.define expression statement when --out and amd code generation)
if (!isJsonSourceFile(currentSourceFile!) || nodeIsSynthesized(node.expression)) {
writeTrailingSemicolon();
}
}
function emitIfStatement(node: IfStatement) {
const openParenPos = emitTokenWithComment(SyntaxKind.IfKeyword, node.pos, writeKeyword, node);
writeSpace();
emitTokenWithComment(SyntaxKind.OpenParenToken, openParenPos, writePunctuation, node);
emitExpression(node.expression);
emitTokenWithComment(SyntaxKind.CloseParenToken, node.expression.end, writePunctuation, node);
emitEmbeddedStatement(node, node.thenStatement);
if (node.elseStatement) {
writeLineOrSpace(node);
emitTokenWithComment(SyntaxKind.ElseKeyword, node.thenStatement.end, writeKeyword, node);
if (node.elseStatement.kind === SyntaxKind.IfStatement) {
writeSpace();
emit(node.elseStatement);
}
else {
emitEmbeddedStatement(node, node.elseStatement);
}
}
}
function emitWhileClause(node: WhileStatement | DoStatement, startPos: number) {
const openParenPos = emitTokenWithComment(SyntaxKind.WhileKeyword, startPos, writeKeyword, node);
writeSpace();
emitTokenWithComment(SyntaxKind.OpenParenToken, openParenPos, writePunctuation, node);
emitExpression(node.expression);
emitTokenWithComment(SyntaxKind.CloseParenToken, node.expression.end, writePunctuation, node);
}
function emitDoStatement(node: DoStatement) {
emitTokenWithComment(SyntaxKind.DoKeyword, node.pos, writeKeyword, node);
emitEmbeddedStatement(node, node.statement);
if (isBlock(node.statement)) {
writeSpace();
}
else {
writeLineOrSpace(node);
}
emitWhileClause(node, node.statement.end);
writeTrailingSemicolon();
}
function emitWhileStatement(node: WhileStatement) {
emitWhileClause(node, node.pos);
emitEmbeddedStatement(node, node.statement);
}
function emitForStatement(node: ForStatement) {
const openParenPos = emitTokenWithComment(SyntaxKind.ForKeyword, node.pos, writeKeyword, node);
writeSpace();
let pos = emitTokenWithComment(SyntaxKind.OpenParenToken, openParenPos, writePunctuation, /*contextNode*/ node);
emitForBinding(node.initializer);
pos = emitTokenWithComment(SyntaxKind.SemicolonToken, node.initializer ? node.initializer.end : pos, writePunctuation, node);
emitExpressionWithLeadingSpace(node.condition);
pos = emitTokenWithComment(SyntaxKind.SemicolonToken, node.condition ? node.condition.end : pos, writePunctuation, node);
emitExpressionWithLeadingSpace(node.incrementor);
emitTokenWithComment(SyntaxKind.CloseParenToken, node.incrementor ? node.incrementor.end : pos, writePunctuation, node);
emitEmbeddedStatement(node, node.statement);
}
function emitForInStatement(node: ForInStatement) {
const openParenPos = emitTokenWithComment(SyntaxKind.ForKeyword, node.pos, writeKeyword, node);
writeSpace();
emitTokenWithComment(SyntaxKind.OpenParenToken, openParenPos, writePunctuation, node);
emitForBinding(node.initializer);
writeSpace();
emitTokenWithComment(SyntaxKind.InKeyword, node.initializer.end, writeKeyword, node);
writeSpace();
emitExpression(node.expression);
emitTokenWithComment(SyntaxKind.CloseParenToken, node.expression.end, writePunctuation, node);
emitEmbeddedStatement(node, node.statement);
}
function emitForOfStatement(node: ForOfStatement) {
const openParenPos = emitTokenWithComment(SyntaxKind.ForKeyword, node.pos, writeKeyword, node);
writeSpace();
emitWithTrailingSpace(node.awaitModifier);
emitTokenWithComment(SyntaxKind.OpenParenToken, openParenPos, writePunctuation, node);
emitForBinding(node.initializer);
writeSpace();
emitTokenWithComment(SyntaxKind.OfKeyword, node.initializer.end, writeKeyword, node);
writeSpace();
emitExpression(node.expression);
emitTokenWithComment(SyntaxKind.CloseParenToken, node.expression.end, writePunctuation, node);
emitEmbeddedStatement(node, node.statement);
}
function emitForBinding(node: VariableDeclarationList | Expression | undefined) {
if (node !== undefined) {
if (node.kind === SyntaxKind.VariableDeclarationList) {
emit(node);
}
else {
emitExpression(node);
}
}
}
function emitContinueStatement(node: ContinueStatement) {
emitTokenWithComment(SyntaxKind.ContinueKeyword, node.pos, writeKeyword, node);
emitWithLeadingSpace(node.label);
writeTrailingSemicolon();
}
function emitBreakStatement(node: BreakStatement) {
emitTokenWithComment(SyntaxKind.BreakKeyword, node.pos, writeKeyword, node);
emitWithLeadingSpace(node.label);
writeTrailingSemicolon();
}
function emitTokenWithComment(token: SyntaxKind, pos: number, writer: (s: string) => void, contextNode: Node, indentLeading?: boolean) {
const node = getParseTreeNode(contextNode);
const isSimilarNode = node && node.kind === contextNode.kind;
const startPos = pos;
if (isSimilarNode) {
pos = skipTrivia(currentSourceFile!.text, pos);
}
if (emitLeadingCommentsOfPosition && isSimilarNode && contextNode.pos !== startPos) {
const needsIndent = indentLeading && !positionsAreOnSameLine(startPos, pos, currentSourceFile!);
if (needsIndent) {
increaseIndent();
}
emitLeadingCommentsOfPosition(startPos);
if (needsIndent) {
decreaseIndent();
}
}
pos = writeTokenText(token, writer, pos);
if (emitTrailingCommentsOfPosition && isSimilarNode && contextNode.end !== pos) {
emitTrailingCommentsOfPosition(pos, /*prefixSpace*/ true);
}
return pos;
}
function emitReturnStatement(node: ReturnStatement) {
emitTokenWithComment(SyntaxKind.ReturnKeyword, node.pos, writeKeyword, /*contextNode*/ node);
emitExpressionWithLeadingSpace(node.expression);
writeTrailingSemicolon();
}
function emitWithStatement(node: WithStatement) {
const openParenPos = emitTokenWithComment(SyntaxKind.WithKeyword, node.pos, writeKeyword, node);
writeSpace();
emitTokenWithComment(SyntaxKind.OpenParenToken, openParenPos, writePunctuation, node);
emitExpression(node.expression);
emitTokenWithComment(SyntaxKind.CloseParenToken, node.expression.end, writePunctuation, node);
emitEmbeddedStatement(node, node.statement);
}
function emitSwitchStatement(node: SwitchStatement) {
const openParenPos = emitTokenWithComment(SyntaxKind.SwitchKeyword, node.pos, writeKeyword, node);
writeSpace();
emitTokenWithComment(SyntaxKind.OpenParenToken, openParenPos, writePunctuation, node);
emitExpression(node.expression);
emitTokenWithComment(SyntaxKind.CloseParenToken, node.expression.end, writePunctuation, node);
writeSpace();
emit(node.caseBlock);
}
function emitLabeledStatement(node: LabeledStatement) {
emit(node.label);
emitTokenWithComment(SyntaxKind.ColonToken, node.label.end, writePunctuation, node);
writeSpace();
emit(node.statement);
}
function emitThrowStatement(node: ThrowStatement) {
emitTokenWithComment(SyntaxKind.ThrowKeyword, node.pos, writeKeyword, node);
emitExpressionWithLeadingSpace(node.expression);
writeTrailingSemicolon();
}
function emitTryStatement(node: TryStatement) {
emitTokenWithComment(SyntaxKind.TryKeyword, node.pos, writeKeyword, node);
writeSpace();
emit(node.tryBlock);
if (node.catchClause) {
writeLineOrSpace(node);
emit(node.catchClause);
}
if (node.finallyBlock) {
writeLineOrSpace(node);
emitTokenWithComment(SyntaxKind.FinallyKeyword, (node.catchClause || node.tryBlock).end, writeKeyword, node);
writeSpace();
emit(node.finallyBlock);
}
}
function emitDebuggerStatement(node: DebuggerStatement) {
writeToken(SyntaxKind.DebuggerKeyword, node.pos, writeKeyword);
writeTrailingSemicolon();
}
//
// Declarations
//
function emitVariableDeclaration(node: VariableDeclaration) {
emit(node.name);
emitTypeAnnotation(node.type);
emitInitializer(node.initializer, node.type ? node.type.end : node.name.end, node);
}
function emitVariableDeclarationList(node: VariableDeclarationList) {
writeKeyword(isLet(node) ? "let" : isVarConst(node) ? "const" : "var");
writeSpace();
emitList(node, node.declarations, ListFormat.VariableDeclarationList);
}
function emitFunctionDeclaration(node: FunctionDeclaration) {
emitFunctionDeclarationOrExpression(node);
}
function emitFunctionDeclarationOrExpression(node: FunctionDeclaration | FunctionExpression) {
emitDecorators(node, node.decorators);
emitModifiers(node, node.modifiers);
writeKeyword("function");
emit(node.asteriskToken);
writeSpace();
emitIdentifierName(node.name);
emitSignatureAndBody(node, emitSignatureHead);
}
function emitBlockCallback(_hint: EmitHint, body: Node): void {
emitBlockFunctionBody(<Block>body);
}
function emitSignatureAndBody(node: FunctionLikeDeclaration, emitSignatureHead: (node: SignatureDeclaration) => void) {
const body = node.body;
if (body) {
if (isBlock(body)) {
const indentedFlag = getEmitFlags(node) & EmitFlags.Indented;
if (indentedFlag) {
increaseIndent();
}
pushNameGenerationScope(node);
forEach(node.parameters, generateNames);
generateNames(node.body);
emitSignatureHead(node);
if (onEmitNode) {
onEmitNode(EmitHint.Unspecified, body, emitBlockCallback);
}
else {
emitBlockFunctionBody(body);
}
popNameGenerationScope(node);
if (indentedFlag) {
decreaseIndent();
}
}
else {
emitSignatureHead(node);
writeSpace();
emitExpression(body);
}
}
else {
emitSignatureHead(node);
writeTrailingSemicolon();
}
}
function emitSignatureHead(node: FunctionDeclaration | FunctionExpression | MethodDeclaration | AccessorDeclaration | ConstructorDeclaration) {
emitTypeParameters(node, node.typeParameters);
emitParameters(node, node.parameters);
emitTypeAnnotation(node.type);
}
function shouldEmitBlockFunctionBodyOnSingleLine(body: Block) {
// We must emit a function body as a single-line body in the following case:
// * The body has NodeEmitFlags.SingleLine specified.
// We must emit a function body as a multi-line body in the following cases:
// * The body is explicitly marked as multi-line.
// * A non-synthesized body's start and end position are on different lines.
// * Any statement in the body starts on a new line.
if (getEmitFlags(body) & EmitFlags.SingleLine) {
return true;
}
if (body.multiLine) {
return false;
}
if (!nodeIsSynthesized(body) && !rangeIsOnSingleLine(body, currentSourceFile!)) {
return false;
}
if (shouldWriteLeadingLineTerminator(body, body.statements, ListFormat.PreserveLines)
|| shouldWriteClosingLineTerminator(body, body.statements, ListFormat.PreserveLines)) {
return false;
}
let previousStatement: Statement | undefined;
for (const statement of body.statements) {
if (shouldWriteSeparatingLineTerminator(previousStatement, statement, ListFormat.PreserveLines)) {
return false;
}
previousStatement = statement;
}
return true;
}
function emitBlockFunctionBody(body: Block) {
writeSpace();
writePunctuation("{");
increaseIndent();
const emitBlockFunctionBody = shouldEmitBlockFunctionBodyOnSingleLine(body)
? emitBlockFunctionBodyOnSingleLine
: emitBlockFunctionBodyWorker;
if (emitBodyWithDetachedComments) {
emitBodyWithDetachedComments(body, body.statements, emitBlockFunctionBody);
}
else {
emitBlockFunctionBody(body);
}
decreaseIndent();
writeToken(SyntaxKind.CloseBraceToken, body.statements.end, writePunctuation, body);
}
function emitBlockFunctionBodyOnSingleLine(body: Block) {
emitBlockFunctionBodyWorker(body, /*emitBlockFunctionBodyOnSingleLine*/ true);
}
function emitBlockFunctionBodyWorker(body: Block, emitBlockFunctionBodyOnSingleLine?: boolean) {
// Emit all the prologue directives (like "use strict").
const statementOffset = emitPrologueDirectives(body.statements);
const pos = writer.getTextPos();
emitHelpers(body);
if (statementOffset === 0 && pos === writer.getTextPos() && emitBlockFunctionBodyOnSingleLine) {
decreaseIndent();
emitList(body, body.statements, ListFormat.SingleLineFunctionBodyStatements);
increaseIndent();
}
else {
emitList(body, body.statements, ListFormat.MultiLineFunctionBodyStatements, statementOffset);
}
}
function emitClassDeclaration(node: ClassDeclaration) {
emitClassDeclarationOrExpression(node);
}
function emitClassDeclarationOrExpression(node: ClassDeclaration | ClassExpression) {
forEach(node.members, generateMemberNames);
emitDecorators(node, node.decorators);
emitModifiers(node, node.modifiers);
writeKeyword("class");
if (node.name) {
writeSpace();
emitIdentifierName(node.name);
}
const indentedFlag = getEmitFlags(node) & EmitFlags.Indented;
if (indentedFlag) {
increaseIndent();
}
emitTypeParameters(node, node.typeParameters);
emitList(node, node.heritageClauses, ListFormat.ClassHeritageClauses);
writeSpace();
writePunctuation("{");
emitList(node, node.members, ListFormat.ClassMembers);
writePunctuation("}");
if (indentedFlag) {
decreaseIndent();
}
}
function emitInterfaceDeclaration(node: InterfaceDeclaration) {
emitDecorators(node, node.decorators);
emitModifiers(node, node.modifiers);
writeKeyword("interface");
writeSpace();
emit(node.name);
emitTypeParameters(node, node.typeParameters);
emitList(node, node.heritageClauses, ListFormat.HeritageClauses);
writeSpace();
writePunctuation("{");
emitList(node, node.members, ListFormat.InterfaceMembers);
writePunctuation("}");
}
function emitTypeAliasDeclaration(node: TypeAliasDeclaration) {
emitDecorators(node, node.decorators);
emitModifiers(node, node.modifiers);
writeKeyword("type");
writeSpace();
emit(node.name);
emitTypeParameters(node, node.typeParameters);
writeSpace();
writePunctuation("=");
writeSpace();
emit(node.type);
writeTrailingSemicolon();
}
function emitEnumDeclaration(node: EnumDeclaration) {
emitModifiers(node, node.modifiers);
writeKeyword("enum");
writeSpace();
emit(node.name);
writeSpace();
writePunctuation("{");
emitList(node, node.members, ListFormat.EnumMembers);
writePunctuation("}");
}
function emitModuleDeclaration(node: ModuleDeclaration) {
emitModifiers(node, node.modifiers);
if (~node.flags & NodeFlags.GlobalAugmentation) {
writeKeyword(node.flags & NodeFlags.Namespace ? "namespace" : "module");
writeSpace();
}
emit(node.name);
let body = node.body;
if (!body) return writeTrailingSemicolon();
while (body.kind === SyntaxKind.ModuleDeclaration) {
writePunctuation(".");
emit((<ModuleDeclaration>body).name);
body = (<ModuleDeclaration>body).body!;
}
writeSpace();
emit(body);
}
function emitModuleBlock(node: ModuleBlock) {
pushNameGenerationScope(node);
forEach(node.statements, generateNames);
emitBlockStatements(node, /*forceSingleLine*/ isEmptyBlock(node));
popNameGenerationScope(node);
}
function emitCaseBlock(node: CaseBlock) {
emitTokenWithComment(SyntaxKind.OpenBraceToken, node.pos, writePunctuation, node);
emitList(node, node.clauses, ListFormat.CaseBlockClauses);
emitTokenWithComment(SyntaxKind.CloseBraceToken, node.clauses.end, writePunctuation, node, /*indentLeading*/ true);
}
function emitImportEqualsDeclaration(node: ImportEqualsDeclaration) {
emitModifiers(node, node.modifiers);
emitTokenWithComment(SyntaxKind.ImportKeyword, node.modifiers ? node.modifiers.end : node.pos, writeKeyword, node);
writeSpace();
emit(node.name);
writeSpace();
emitTokenWithComment(SyntaxKind.EqualsToken, node.name.end, writePunctuation, node);
writeSpace();
emitModuleReference(node.moduleReference);
writeTrailingSemicolon();
}
function emitModuleReference(node: ModuleReference) {
if (node.kind === SyntaxKind.Identifier) {
emitExpression(node);
}
else {
emit(node);
}
}
function emitImportDeclaration(node: ImportDeclaration) {
emitModifiers(node, node.modifiers);
emitTokenWithComment(SyntaxKind.ImportKeyword, node.modifiers ? node.modifiers.end : node.pos, writeKeyword, node);
writeSpace();
if (node.importClause) {
emit(node.importClause);
writeSpace();
emitTokenWithComment(SyntaxKind.FromKeyword, node.importClause.end, writeKeyword, node);
writeSpace();
}
emitExpression(node.moduleSpecifier);
writeTrailingSemicolon();
}
function emitImportClause(node: ImportClause) {
emit(node.name);
if (node.name && node.namedBindings) {
emitTokenWithComment(SyntaxKind.CommaToken, node.name.end, writePunctuation, node);
writeSpace();
}
emit(node.namedBindings);
}
function emitNamespaceImport(node: NamespaceImport) {
const asPos = emitTokenWithComment(SyntaxKind.AsteriskToken, node.pos, writePunctuation, node);
writeSpace();
emitTokenWithComment(SyntaxKind.AsKeyword, asPos, writeKeyword, node);
writeSpace();
emit(node.name);
}
function emitNamedImports(node: NamedImports) {
emitNamedImportsOrExports(node);
}
function emitImportSpecifier(node: ImportSpecifier) {
emitImportOrExportSpecifier(node);
}
function emitExportAssignment(node: ExportAssignment) {
const nextPos = emitTokenWithComment(SyntaxKind.ExportKeyword, node.pos, writeKeyword, node);
writeSpace();
if (node.isExportEquals) {
emitTokenWithComment(SyntaxKind.EqualsToken, nextPos, writeOperator, node);
}
else {
emitTokenWithComment(SyntaxKind.DefaultKeyword, nextPos, writeKeyword, node);
}
writeSpace();
emitExpression(node.expression);
writeTrailingSemicolon();
}
function emitExportDeclaration(node: ExportDeclaration) {
let nextPos = emitTokenWithComment(SyntaxKind.ExportKeyword, node.pos, writeKeyword, node);
writeSpace();
if (node.exportClause) {
emit(node.exportClause);
}
else {
nextPos = emitTokenWithComment(SyntaxKind.AsteriskToken, nextPos, writePunctuation, node);
}
if (node.moduleSpecifier) {
writeSpace();
const fromPos = node.exportClause ? node.exportClause.end : nextPos;
emitTokenWithComment(SyntaxKind.FromKeyword, fromPos, writeKeyword, node);
writeSpace();
emitExpression(node.moduleSpecifier);
}
writeTrailingSemicolon();
}
function emitNamespaceExportDeclaration(node: NamespaceExportDeclaration) {
let nextPos = emitTokenWithComment(SyntaxKind.ExportKeyword, node.pos, writeKeyword, node);
writeSpace();
nextPos = emitTokenWithComment(SyntaxKind.AsKeyword, nextPos, writeKeyword, node);
writeSpace();
nextPos = emitTokenWithComment(SyntaxKind.NamespaceKeyword, nextPos, writeKeyword, node);
writeSpace();
emit(node.name);
writeTrailingSemicolon();
}
function emitNamedExports(node: NamedExports) {
emitNamedImportsOrExports(node);
}
function emitExportSpecifier(node: ExportSpecifier) {
emitImportOrExportSpecifier(node);
}
function emitNamedImportsOrExports(node: NamedImportsOrExports) {
writePunctuation("{");
emitList(node, node.elements, ListFormat.NamedImportsOrExportsElements);
writePunctuation("}");
}
function emitImportOrExportSpecifier(node: ImportOrExportSpecifier) {
if (node.