diff --git a/Jakefile.js b/Jakefile.js
index 3ee8476c842..0b3a26dbb77 100644
--- a/Jakefile.js
+++ b/Jakefile.js
@@ -269,6 +269,7 @@ var harnessSources = harnessCoreSources.concat([
"projectErrors.ts",
"matchFiles.ts",
"initializeTSConfig.ts",
+ "printer.ts",
].map(function (f) {
return path.join(unittestsDirectory, f);
})).concat([
diff --git a/src/compiler/comments.ts b/src/compiler/comments.ts
index bd9190dbec7..200158c2f5c 100644
--- a/src/compiler/comments.ts
+++ b/src/compiler/comments.ts
@@ -5,17 +5,16 @@ namespace ts {
export interface CommentWriter {
reset(): void;
setSourceFile(sourceFile: SourceFile): void;
- emitNodeWithComments(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void;
+ setWriter(writer: EmitTextWriter): void;
+ emitNodeWithComments(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void;
emitBodyWithDetachedComments(node: Node, detachedRange: TextRange, emitCallback: (node: Node) => void): void;
emitTrailingCommentsOfPosition(pos: number): void;
}
- export function createCommentWriter(host: EmitHost, writer: EmitTextWriter, sourceMap: SourceMapWriter): CommentWriter {
- const compilerOptions = host.getCompilerOptions();
- const extendedDiagnostics = compilerOptions.extendedDiagnostics;
- const newLine = host.getNewLine();
- const { emitPos } = sourceMap;
-
+ export function createCommentWriter(printerOptions: PrinterOptions, emitPos: ((pos: number) => void) | undefined): CommentWriter {
+ const extendedDiagnostics = printerOptions.extendedDiagnostics;
+ const newLine = getNewLineCharacter(printerOptions);
+ let writer: EmitTextWriter;
let containerPos = -1;
let containerEnd = -1;
let declarationListContainerEnd = -1;
@@ -24,19 +23,20 @@ namespace ts {
let currentLineMap: number[];
let detachedCommentsInfo: { nodePos: number, detachedCommentEndPos: number}[];
let hasWrittenComment = false;
- let disabled: boolean = compilerOptions.removeComments;
+ let disabled: boolean = printerOptions.removeComments;
return {
reset,
+ setWriter,
setSourceFile,
emitNodeWithComments,
emitBodyWithDetachedComments,
emitTrailingCommentsOfPosition,
};
- function emitNodeWithComments(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void) {
+ function emitNodeWithComments(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) {
if (disabled) {
- emitCallback(emitContext, node);
+ emitCallback(hint, node);
return;
}
@@ -47,11 +47,11 @@ namespace ts {
// Both pos and end are synthesized, so just emit the node without comments.
if (emitFlags & EmitFlags.NoNestedComments) {
disabled = true;
- emitCallback(emitContext, node);
+ emitCallback(hint, node);
disabled = false;
}
else {
- emitCallback(emitContext, node);
+ emitCallback(hint, node);
}
}
else {
@@ -94,11 +94,11 @@ namespace ts {
if (emitFlags & EmitFlags.NoNestedComments) {
disabled = true;
- emitCallback(emitContext, node);
+ emitCallback(hint, node);
disabled = false;
}
else {
- emitCallback(emitContext, node);
+ emitCallback(hint, node);
}
if (extendedDiagnostics) {
@@ -198,9 +198,9 @@ namespace ts {
}
// Leading comments are emitted at /*leading comment1 */space/*leading comment*/space
- emitPos(commentPos);
+ if (emitPos) emitPos(commentPos);
writeCommentRange(currentText, currentLineMap, writer, commentPos, commentEnd, newLine);
- emitPos(commentEnd);
+ if (emitPos) emitPos(commentEnd);
if (hasTrailingNewLine) {
writer.writeLine();
@@ -220,9 +220,9 @@ namespace ts {
writer.write(" ");
}
- emitPos(commentPos);
+ if (emitPos) emitPos(commentPos);
writeCommentRange(currentText, currentLineMap, writer, commentPos, commentEnd, newLine);
- emitPos(commentEnd);
+ if (emitPos) emitPos(commentEnd);
if (hasTrailingNewLine) {
writer.writeLine();
@@ -248,9 +248,9 @@ namespace ts {
function emitTrailingCommentOfPosition(commentPos: number, commentEnd: number, _kind: SyntaxKind, hasTrailingNewLine: boolean) {
// trailing comments of a position are emitted at /*trailing comment1 */space/*trailing comment*/space
- emitPos(commentPos);
+ if (emitPos) emitPos(commentPos);
writeCommentRange(currentText, currentLineMap, writer, commentPos, commentEnd, newLine);
- emitPos(commentEnd);
+ if (emitPos) emitPos(commentEnd);
if (hasTrailingNewLine) {
writer.writeLine();
@@ -286,6 +286,10 @@ namespace ts {
detachedCommentsInfo = undefined;
}
+ function setWriter(output: EmitTextWriter): void {
+ writer = output;
+ }
+
function setSourceFile(sourceFile: SourceFile) {
currentSourceFile = sourceFile;
currentText = currentSourceFile.text;
@@ -323,9 +327,9 @@ namespace ts {
}
function writeComment(text: string, lineMap: number[], writer: EmitTextWriter, commentPos: number, commentEnd: number, newLine: string) {
- emitPos(commentPos);
+ if (emitPos) emitPos(commentPos);
writeCommentRange(text, lineMap, writer, commentPos, commentEnd, newLine);
- emitPos(commentEnd);
+ if (emitPos) emitPos(commentEnd);
}
/**
diff --git a/src/compiler/core.ts b/src/compiler/core.ts
index de3472afd88..304fba69b26 100644
--- a/src/compiler/core.ts
+++ b/src/compiler/core.ts
@@ -1447,7 +1447,7 @@ namespace ts {
return /^\.\.?($|[\\/])/.test(moduleName);
}
- export function getEmitScriptTarget(compilerOptions: CompilerOptions) {
+ export function getEmitScriptTarget(compilerOptions: CompilerOptions | PrinterOptions) {
return compilerOptions.target || ScriptTarget.ES3;
}
diff --git a/src/compiler/declarationEmitter.ts b/src/compiler/declarationEmitter.ts
index 46df4fed689..4b878adaff5 100644
--- a/src/compiler/declarationEmitter.ts
+++ b/src/compiler/declarationEmitter.ts
@@ -35,13 +35,15 @@ namespace ts {
forEachEmittedFile(host, getDeclarationDiagnosticsFromFile, targetSourceFile);
return declarationDiagnostics.getDiagnostics(targetSourceFile ? targetSourceFile.fileName : undefined);
- function getDeclarationDiagnosticsFromFile({ declarationFilePath }: EmitFileNames, sources: SourceFile[], isBundledEmit: boolean) {
- emitDeclarations(host, resolver, declarationDiagnostics, declarationFilePath, sources, isBundledEmit, /*emitOnlyDtsFiles*/ false);
+ function getDeclarationDiagnosticsFromFile({ declarationFilePath }: EmitFileNames, sourceFileOrBundle: SourceFile | Bundle) {
+ emitDeclarations(host, resolver, declarationDiagnostics, declarationFilePath, sourceFileOrBundle, /*emitOnlyDtsFiles*/ false);
}
}
function emitDeclarations(host: EmitHost, resolver: EmitResolver, emitterDiagnostics: DiagnosticCollection, declarationFilePath: string,
- sourceFiles: SourceFile[], isBundledEmit: boolean, emitOnlyDtsFiles: boolean): DeclarationEmit {
+ sourceFileOrBundle: SourceFile | Bundle, emitOnlyDtsFiles: boolean): DeclarationEmit {
+ const sourceFiles = sourceFileOrBundle.kind === SyntaxKind.Bundle ? sourceFileOrBundle.sourceFiles : [sourceFileOrBundle];
+ const isBundledEmit = sourceFileOrBundle.kind === SyntaxKind.Bundle;
const newLine = host.getNewLine();
const compilerOptions = host.getCompilerOptions();
@@ -1803,8 +1805,9 @@ namespace ts {
}
return addedBundledEmitReference;
- function getDeclFileName(emitFileNames: EmitFileNames, _sourceFiles: SourceFile[], isBundledEmit: boolean) {
+ function getDeclFileName(emitFileNames: EmitFileNames, sourceFileOrBundle: SourceFile | Bundle) {
// Dont add reference path to this file if it is a bundled emit and caller asked not emit bundled file path
+ const isBundledEmit = sourceFileOrBundle.kind === SyntaxKind.Bundle;
if (isBundledEmit && !addBundledFileReference) {
return;
}
@@ -1817,10 +1820,11 @@ namespace ts {
}
/* @internal */
- export function writeDeclarationFile(declarationFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean, host: EmitHost, resolver: EmitResolver, emitterDiagnostics: DiagnosticCollection, emitOnlyDtsFiles: boolean) {
- const emitDeclarationResult = emitDeclarations(host, resolver, emitterDiagnostics, declarationFilePath, sourceFiles, isBundledEmit, emitOnlyDtsFiles);
+ export function writeDeclarationFile(declarationFilePath: string, sourceFileOrBundle: SourceFile | Bundle, host: EmitHost, resolver: EmitResolver, emitterDiagnostics: DiagnosticCollection, emitOnlyDtsFiles: boolean) {
+ const emitDeclarationResult = emitDeclarations(host, resolver, emitterDiagnostics, declarationFilePath, sourceFileOrBundle, emitOnlyDtsFiles);
const emitSkipped = emitDeclarationResult.reportedDeclarationError || host.isEmitBlocked(declarationFilePath) || host.getCompilerOptions().noEmit;
if (!emitSkipped) {
+ const sourceFiles = sourceFileOrBundle.kind === SyntaxKind.Bundle ? sourceFileOrBundle.sourceFiles : [sourceFileOrBundle];
const declarationOutput = emitDeclarationResult.referencesOutput
+ getDeclarationOutput(emitDeclarationResult.synchronousDeclarationOutput, emitDeclarationResult.moduleElementDeclarationEmitInfo);
writeFile(host, emitterDiagnostics, declarationFilePath, declarationOutput, host.getCompilerOptions().emitBOM, sourceFiles);
diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts
index 8517d16d4e7..498f65ad366 100644
--- a/src/compiler/emitter.ts
+++ b/src/compiler/emitter.ts
@@ -1,61 +1,27 @@
-///
+///
///
-///
-///
+///
+///
///
-/* @internal */
namespace ts {
- // Flags enum to track count of temp variables and a few dedicated names
- const enum TempFlags {
- Auto = 0x00000000, // No preferred name
- CountMask = 0x0FFFFFFF, // Temp variable counter
- _i = 0x10000000, // Use/preference flag for '_i'
- }
-
- const id = (s: SourceFile) => s;
- const nullTransformers: Transformer[] = [_ => id];
+ const delimiters = createDelimiterMap();
+ const brackets = createBracketsMap();
+ /*@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, emitOnlyDtsFiles?: boolean): EmitResult {
- const delimiters = createDelimiterMap();
- const brackets = createBracketsMap();
const compilerOptions = host.getCompilerOptions();
- const languageVersion = getEmitScriptTarget(compilerOptions);
const moduleKind = getEmitModuleKind(compilerOptions);
const sourceMapDataList: SourceMapData[] = compilerOptions.sourceMap || compilerOptions.inlineSourceMap ? [] : undefined;
const emittedFilesList: string[] = compilerOptions.listEmittedFiles ? [] : undefined;
const emitterDiagnostics = createDiagnosticCollection();
const newLine = host.getNewLine();
- const transformers: Transformer[] = emitOnlyDtsFiles ? nullTransformers : getTransformers(compilerOptions);
+ const transformers = emitOnlyDtsFiles ? [] : getTransformers(compilerOptions);
const writer = createTextWriter(newLine);
- const {
- write,
- writeLine,
- increaseIndent,
- decreaseIndent
- } = writer;
-
const sourceMap = createSourceMapWriter(host, writer);
- const {
- emitNodeWithSourceMap,
- emitTokenWithSourceMap
- } = sourceMap;
- const comments = createCommentWriter(host, writer, sourceMap);
- const {
- emitNodeWithComments,
- emitBodyWithDetachedComments,
- emitTrailingCommentsOfPosition
- } = comments;
-
- let nodeIdToGeneratedName: string[];
- let autoGeneratedIdToGeneratedName: string[];
- let generatedNameSet: Map;
- let tempFlags: TempFlags;
let currentSourceFile: SourceFile;
- let currentText: string;
- let currentFileIdentifiers: Map;
let bundledHelpers: Map;
let isOwnFileEmit: boolean;
let emitSkipped = false;
@@ -63,17 +29,30 @@ namespace ts {
const sourceFiles = getSourceFilesToEmit(host, targetSourceFile);
// Transform the source files
- performance.mark("beforeTransform");
- const {
- transformed,
- emitNodeWithSubstitution,
- emitNodeWithNotification
- } = transformFiles(resolver, host, sourceFiles, transformers);
- performance.measure("transformTime", "beforeTransform");
+ const transform = transformFiles(resolver, host, sourceFiles, transformers);
+
+ // Create a printer to print the nodes
+ const printer = createPrinter(compilerOptions, {
+ // resolver hooks
+ hasGlobalName: resolver.hasGlobalName,
+
+ // transform hooks
+ onEmitNode: transform.emitNodeWithNotification,
+ onSubstituteNode: transform.emitNodeWithSubstitution,
+
+ // sourcemap hooks
+ onEmitSourceMapOfNode: sourceMap.emitNodeWithSourceMap,
+ onEmitSourceMapOfToken: sourceMap.emitTokenWithSourceMap,
+ onEmitSourceMapOfPosition: sourceMap.emitPos,
+
+ // emitter hooks
+ onEmitHelpers: emitHelpers,
+ onSetSourceFile: setSourceFile,
+ });
// Emit each output file
performance.mark("beforePrint");
- forEachEmittedFile(host, emitFile, transformed, emitOnlyDtsFiles);
+ forEachEmittedFile(host, emitSourceFileOrBundle, transform.transformed, emitOnlyDtsFiles);
performance.measure("printTime", "beforePrint");
// Clean up emit nodes on parse tree
@@ -88,11 +67,11 @@ namespace ts {
sourceMaps: sourceMapDataList
};
- function emitFile({ jsFilePath, sourceMapFilePath, declarationFilePath }: EmitFileNames, sourceFiles: SourceFile[], isBundledEmit: boolean) {
+ function emitSourceFileOrBundle({ jsFilePath, sourceMapFilePath, declarationFilePath }: EmitFileNames, sourceFileOrBundle: SourceFile | Bundle) {
// Make sure not to write js file and source map file if any of them cannot be written
if (!host.isEmitBlocked(jsFilePath) && !compilerOptions.noEmit) {
if (!emitOnlyDtsFiles) {
- printFile(jsFilePath, sourceMapFilePath, sourceFiles, isBundledEmit);
+ printSourceFileOrBundle(jsFilePath, sourceMapFilePath, sourceFileOrBundle);
}
}
else {
@@ -100,7 +79,7 @@ namespace ts {
}
if (declarationFilePath) {
- emitSkipped = writeDeclarationFile(declarationFilePath, getOriginalSourceFiles(sourceFiles), isBundledEmit, host, resolver, emitterDiagnostics, emitOnlyDtsFiles) || emitSkipped;
+ emitSkipped = writeDeclarationFile(declarationFilePath, getOriginalSourceFileOrBundle(sourceFileOrBundle), host, resolver, emitterDiagnostics, emitOnlyDtsFiles) || emitSkipped;
}
if (!emitSkipped && emittedFilesList) {
@@ -116,34 +95,32 @@ namespace ts {
}
}
- function printFile(jsFilePath: string, sourceMapFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean) {
- sourceMap.initialize(jsFilePath, sourceMapFilePath, sourceFiles, isBundledEmit);
- nodeIdToGeneratedName = [];
- autoGeneratedIdToGeneratedName = [];
- generatedNameSet = createMap();
- bundledHelpers = isBundledEmit ? createMap() : undefined;
- isOwnFileEmit = !isBundledEmit;
+ function printSourceFileOrBundle(jsFilePath: string, sourceMapFilePath: string, sourceFileOrBundle: SourceFile | Bundle) {
+ const bundle = sourceFileOrBundle.kind === SyntaxKind.Bundle ? sourceFileOrBundle : undefined;
+ const sourceFile = sourceFileOrBundle.kind === SyntaxKind.SourceFile ? sourceFileOrBundle : undefined;
+ const sourceFiles = bundle ? bundle.sourceFiles : [sourceFile];
+ sourceMap.initialize(jsFilePath, sourceMapFilePath, sourceFileOrBundle);
- // Emit helpers from all the files
- if (isBundledEmit && moduleKind) {
- for (const sourceFile of sourceFiles) {
- emitHelpers(sourceFile, /*isBundle*/ true);
- }
+ if (bundle) {
+ bundledHelpers = createMap();
+ isOwnFileEmit = false;
+ printer.writeBundle(bundle, writer);
+ }
+ else {
+ isOwnFileEmit = true;
+ printer.writeFile(sourceFile, writer);
}
- // Print each transformed source file.
- forEach(sourceFiles, printSourceFile);
-
- writeLine();
+ writer.writeLine();
const sourceMappingURL = sourceMap.getSourceMappingURL();
if (sourceMappingURL) {
- write(`//# ${"sourceMappingURL"}=${sourceMappingURL}`); // Sometimes tools can sometimes see this line as a source mapping url comment
+ writer.write(`//# ${"sourceMappingURL"}=${sourceMappingURL}`); // Sometimes tools can sometimes see this line as a source mapping url comment
}
// Write the source map
if (compilerOptions.sourceMap && !compilerOptions.inlineSourceMap) {
- writeFile(host, emitterDiagnostics, sourceMapFilePath, sourceMap.getText(), /*writeByteOrderMark*/ false, sourceFiles);
+ writeFile(host, emitterDiagnostics, sourceMapFilePath, sourceMap.getText(), /*writeByteOrderMark*/ false, sourceFiles);
}
// Record source map data for the test harness.
@@ -156,153 +133,281 @@ namespace ts {
// Reset state
sourceMap.reset();
- comments.reset();
writer.reset();
- tempFlags = TempFlags.Auto;
currentSourceFile = undefined;
- currentText = undefined;
+ bundledHelpers = undefined;
isOwnFileEmit = false;
}
- function printSourceFile(node: SourceFile) {
+ function setSourceFile(node: SourceFile) {
currentSourceFile = node;
- currentText = node.text;
- currentFileIdentifiers = node.identifiers;
sourceMap.setSourceFile(node);
- comments.setSourceFile(node);
- pipelineEmitWithNotification(EmitContext.SourceFile, node);
}
- /**
- * Emits a node.
- */
- function emit(node: Node) {
- pipelineEmitWithNotification(EmitContext.Unspecified, node);
+ function emitHelpers(node: Node, writeLines: (text: string) => void) {
+ let helpersEmitted = false;
+ const bundle = node.kind === SyntaxKind.Bundle ? node : undefined;
+ if (bundle && moduleKind === ModuleKind.None) {
+ return;
+ }
+
+ const numNodes = bundle ? bundle.sourceFiles.length : 1;
+ for (let i = 0; i < numNodes; i++) {
+ const currentNode = bundle ? bundle.sourceFiles[i] : node;
+ const sourceFile = isSourceFile(currentNode) ? currentNode : currentSourceFile;
+ const shouldSkip = compilerOptions.noEmitHelpers || (sourceFile && getExternalHelpersModuleName(sourceFile) !== undefined);
+ const shouldBundle = isSourceFile(currentNode) && !isOwnFileEmit;
+ const helpers = getEmitHelpers(currentNode);
+ if (helpers) {
+ for (const helper of stableSort(helpers, compareEmitHelpers)) {
+ 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;
+ }
+
+ writeLines(helper.text);
+ helpersEmitted = true;
+ }
+ }
+ }
+
+ return helpersEmitted;
+ }
+ }
+
+ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: PrintHandlers = {}): Printer {
+ const {
+ hasGlobalName,
+ onEmitSourceMapOfNode,
+ onEmitSourceMapOfToken,
+ onEmitSourceMapOfPosition,
+ onEmitNode,
+ onEmitHelpers,
+ onSetSourceFile,
+ onSubstituteNode,
+ } = handlers;
+
+ const newLine = getNewLineCharacter(printerOptions);
+ const languageVersion = getEmitScriptTarget(printerOptions);
+ const comments = createCommentWriter(printerOptions, onEmitSourceMapOfPosition);
+ const {
+ emitNodeWithComments,
+ emitBodyWithDetachedComments,
+ emitTrailingCommentsOfPosition,
+ } = comments;
+
+ let currentSourceFile: SourceFile;
+ 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; // 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 writer: EmitTextWriter;
+ let ownWriter: EmitTextWriter;
+
+ reset();
+ return {
+ // public API
+ printNode,
+ printFile,
+ printBundle,
+
+ // internal API
+ writeNode,
+ writeFile,
+ writeBundle
+ };
+
+ 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(node);
+ case SyntaxKind.Bundle: return printBundle(node);
+ }
+ writeNode(hint, node, sourceFile, beginPrint());
+ return endPrint();
+ }
+
+ function printBundle(bundle: Bundle): string {
+ writeBundle(bundle, beginPrint());
+ return endPrint();
+ }
+
+ function printFile(sourceFile: SourceFile): string {
+ writeFile(sourceFile, beginPrint());
+ return endPrint();
+ }
+
+ function writeNode(hint: EmitHint, node: Node, sourceFile: SourceFile, output: EmitTextWriter) {
+ const previousWriter = writer;
+ setWriter(output);
+ print(hint, node, sourceFile);
+ reset();
+ writer = previousWriter;
+ }
+
+ function writeBundle(bundle: Bundle, output: EmitTextWriter) {
+ const previousWriter = writer;
+ setWriter(output);
+ emitHelpersIndirect(bundle);
+ for (const sourceFile of bundle.sourceFiles) {
+ print(EmitHint.SourceFile, sourceFile, sourceFile);
+ }
+ reset();
+ writer = previousWriter;
+ }
+
+ function writeFile(sourceFile: SourceFile, output: EmitTextWriter) {
+ const previousWriter = writer;
+ setWriter(output);
+ print(EmitHint.SourceFile, sourceFile, sourceFile);
+ reset();
+ writer = previousWriter;
+ }
+
+ function beginPrint() {
+ return ownWriter || (ownWriter = createTextWriter(newLine));
+ }
+
+ function endPrint() {
+ const text = ownWriter.getText();
+ ownWriter.reset();
+ return text;
+ }
+
+ function print(hint: EmitHint, node: Node, sourceFile: SourceFile) {
+ setSourceFile(sourceFile);
+ pipelineEmitWithNotification(hint, node);
+ }
+
+ function setSourceFile(sourceFile: SourceFile) {
+ currentSourceFile = sourceFile;
+ comments.setSourceFile(sourceFile);
+ if (onSetSourceFile) {
+ onSetSourceFile(sourceFile);
+ }
+ }
+
+ function setWriter(output: EmitTextWriter | undefined) {
+ writer = output;
+ comments.setWriter(output);
+ }
+
+ function reset() {
+ nodeIdToGeneratedName = [];
+ autoGeneratedIdToGeneratedName = [];
+ generatedNames = createMap();
+ tempFlagsStack = [];
+ tempFlags = TempFlags.Auto;
+ comments.reset();
+ setWriter(/*output*/ undefined);
+ }
+
+ function emit(node: Node, hint = EmitHint.Unspecified) {
+ pipelineEmitWithNotification(hint, node);
}
- /**
- * Emits an IdentifierName.
- */
function emitIdentifierName(node: Identifier) {
- pipelineEmitWithNotification(EmitContext.IdentifierName, node);
+ pipelineEmitWithNotification(EmitHint.IdentifierName, node);
}
- /**
- * Emits an expression node.
- */
function emitExpression(node: Expression) {
- pipelineEmitWithNotification(EmitContext.Expression, node);
+ pipelineEmitWithNotification(EmitHint.Expression, node);
}
- /**
- * Emits a node with possible notification.
- *
- * NOTE: Do not call this method directly. It is part of the emit pipeline
- * and should only be called from printSourceFile, emit, emitExpression, or
- * emitIdentifierName.
- */
- function pipelineEmitWithNotification(emitContext: EmitContext, node: Node) {
- emitNodeWithNotification(emitContext, node, pipelineEmitWithComments);
+ function pipelineEmitWithNotification(hint: EmitHint, node: Node) {
+ if (onEmitNode) {
+ onEmitNode(hint, node, pipelineEmitWithComments);
+ }
+ else {
+ pipelineEmitWithComments(hint, node);
+ }
}
- /**
- * Emits a node with comments.
- *
- * NOTE: Do not call this method directly. It is part of the emit pipeline
- * and should only be called indirectly from pipelineEmitWithNotification.
- */
- function pipelineEmitWithComments(emitContext: EmitContext, node: Node) {
- // Do not emit comments for SourceFile
- if (emitContext === EmitContext.SourceFile) {
- pipelineEmitWithSourceMap(emitContext, node);
+ function pipelineEmitWithComments(hint: EmitHint, node: Node) {
+ if (emitNodeWithComments && hint !== EmitHint.SourceFile) {
+ emitNodeWithComments(hint, node, pipelineEmitWithSourceMap);
+ }
+ else {
+ pipelineEmitWithSourceMap(hint, node);
+ }
+ }
+
+ function pipelineEmitWithSourceMap(hint: EmitHint, node: Node) {
+ if (onEmitSourceMapOfNode && hint !== EmitHint.SourceFile && hint !== EmitHint.IdentifierName) {
+ onEmitSourceMapOfNode(hint, node, pipelineEmitWithSubstitution);
+ }
+ else {
+ pipelineEmitWithSubstitution(hint, node);
+ }
+ }
+
+ function pipelineEmitWithSubstitution(hint: EmitHint, node: Node) {
+ if (onSubstituteNode) {
+ onSubstituteNode(hint, node, pipelineEmitWithHint);
+ }
+ else {
+ pipelineEmitWithHint(hint, node);
+ }
+ }
+
+ function pipelineEmitWithHint(hint: EmitHint, node: Node): void {
+ switch (hint) {
+ case EmitHint.SourceFile: return pipelineEmitSourceFile(node);
+ case EmitHint.IdentifierName: return pipelineEmitIdentifierName(node);
+ case EmitHint.Expression: return pipelineEmitExpression(node);
+ case EmitHint.Unspecified: return pipelineEmitUnspecified(node);
+ }
+ }
+
+ function pipelineEmitSourceFile(node: Node): void {
+ Debug.assertNode(node, isSourceFile);
+ emitSourceFile(node);
+ }
+
+ function pipelineEmitIdentifierName(node: Node): void {
+ Debug.assertNode(node, isIdentifier);
+ emitIdentifier(node);
+ }
+
+ function pipelineEmitUnspecified(node: Node): void {
+ const kind = node.kind;
+
+ // Reserved words
+ // Strict mode reserved words
+ // Contextual keywords
+ if (isKeyword(kind)) {
+ writeTokenText(kind);
return;
}
- emitNodeWithComments(emitContext, node, pipelineEmitWithSourceMap);
- }
-
- /**
- * Emits a node with source maps.
- *
- * NOTE: Do not call this method directly. It is part of the emit pipeline
- * and should only be called indirectly from pipelineEmitWithComments.
- */
- function pipelineEmitWithSourceMap(emitContext: EmitContext, node: Node) {
- // Do not emit source mappings for SourceFile or IdentifierName
- if (emitContext === EmitContext.SourceFile
- || emitContext === EmitContext.IdentifierName) {
- pipelineEmitWithSubstitution(emitContext, node);
- return;
- }
-
- emitNodeWithSourceMap(emitContext, node, pipelineEmitWithSubstitution);
- }
-
- /**
- * Emits a node with possible substitution.
- *
- * NOTE: Do not call this method directly. It is part of the emit pipeline
- * and should only be called indirectly from pipelineEmitWithSourceMap or
- * pipelineEmitInUnspecifiedContext (when picking a more specific context).
- */
- function pipelineEmitWithSubstitution(emitContext: EmitContext, node: Node) {
- emitNodeWithSubstitution(emitContext, node, pipelineEmitForContext);
- }
-
- /**
- * Emits a node.
- *
- * NOTE: Do not call this method directly. It is part of the emit pipeline
- * and should only be called indirectly from pipelineEmitWithSubstitution.
- */
- function pipelineEmitForContext(emitContext: EmitContext, node: Node): void {
- switch (emitContext) {
- case EmitContext.SourceFile: return pipelineEmitInSourceFileContext(node);
- case EmitContext.IdentifierName: return pipelineEmitInIdentifierNameContext(node);
- case EmitContext.Unspecified: return pipelineEmitInUnspecifiedContext(node);
- case EmitContext.Expression: return pipelineEmitInExpressionContext(node);
- }
- }
-
- /**
- * Emits a node in the SourceFile EmitContext.
- *
- * NOTE: Do not call this method directly. It is part of the emit pipeline
- * and should only be called indirectly from pipelineEmitForContext.
- */
- function pipelineEmitInSourceFileContext(node: Node): void {
- const kind = node.kind;
- switch (kind) {
- // Top-level nodes
- case SyntaxKind.SourceFile:
- return emitSourceFile(node);
- }
- }
-
- /**
- * Emits a node in the IdentifierName EmitContext.
- *
- * NOTE: Do not call this method directly. It is part of the emit pipeline
- * and should only be called indirectly from pipelineEmitForContext.
- */
- function pipelineEmitInIdentifierNameContext(node: Node): void {
- const kind = node.kind;
- switch (kind) {
- // Identifiers
- case SyntaxKind.Identifier:
- return emitIdentifier(node);
- }
- }
-
- /**
- * Emits a node in the Unspecified EmitContext.
- *
- * NOTE: Do not call this method directly. It is part of the emit pipeline
- * and should only be called indirectly from pipelineEmitForContext.
- */
- function pipelineEmitInUnspecifiedContext(node: Node): void {
- const kind = node.kind;
switch (kind) {
// Pseudo-literals
case SyntaxKind.TemplateHead:
@@ -314,46 +419,6 @@ namespace ts {
case SyntaxKind.Identifier:
return emitIdentifier(node);
- // Reserved words
- case SyntaxKind.ConstKeyword:
- case SyntaxKind.DefaultKeyword:
- case SyntaxKind.ExportKeyword:
- case SyntaxKind.VoidKeyword:
-
- // Strict mode reserved words
- case SyntaxKind.PrivateKeyword:
- case SyntaxKind.ProtectedKeyword:
- case SyntaxKind.PublicKeyword:
- case SyntaxKind.StaticKeyword:
-
- // Contextual keywords
- case SyntaxKind.AbstractKeyword:
- case SyntaxKind.AsKeyword:
- case SyntaxKind.AnyKeyword:
- case SyntaxKind.AsyncKeyword:
- case SyntaxKind.AwaitKeyword:
- case SyntaxKind.BooleanKeyword:
- case SyntaxKind.ConstructorKeyword:
- case SyntaxKind.DeclareKeyword:
- case SyntaxKind.GetKeyword:
- case SyntaxKind.IsKeyword:
- case SyntaxKind.ModuleKeyword:
- case SyntaxKind.NamespaceKeyword:
- case SyntaxKind.NeverKeyword:
- case SyntaxKind.ReadonlyKeyword:
- case SyntaxKind.RequireKeyword:
- case SyntaxKind.NumberKeyword:
- case SyntaxKind.SetKeyword:
- case SyntaxKind.StringKeyword:
- case SyntaxKind.SymbolKeyword:
- case SyntaxKind.TypeKeyword:
- case SyntaxKind.UndefinedKeyword:
- case SyntaxKind.FromKeyword:
- case SyntaxKind.GlobalKeyword:
- case SyntaxKind.OfKeyword:
- writeTokenText(kind);
- return;
-
// Parse tree nodes
// Names
@@ -572,17 +637,11 @@ namespace ts {
// If the node is an expression, try to emit it as an expression with
// substitution.
if (isExpression(node)) {
- return pipelineEmitWithSubstitution(EmitContext.Expression, node);
+ return pipelineEmitWithSubstitution(EmitHint.Expression, node);
}
}
- /**
- * Emits a node in the Expression EmitContext.
- *
- * NOTE: Do not call this method directly. It is part of the emit pipeline
- * and should only be called indirectly from pipelineEmitForContext.
- */
- function pipelineEmitInExpressionContext(node: Node): void {
+ function pipelineEmitExpression(node: Node): void {
const kind = node.kind;
switch (kind) {
// Literals
@@ -675,6 +734,21 @@ namespace ts {
}
}
+ function emitBodyIndirect(node: Node, elements: NodeArray, emitCallback: (node: Node) => void): void {
+ if (emitBodyWithDetachedComments) {
+ emitBodyWithDetachedComments(node, elements, emitCallback);
+ }
+ else {
+ emitCallback(node);
+ }
+ }
+
+ function emitHelpersIndirect(node: Node) {
+ if (onEmitHelpers) {
+ onEmitHelpers(node, writeLines);
+ }
+ }
+
//
// Literals/Pseudo-literals
//
@@ -695,7 +769,7 @@ namespace ts {
// SyntaxKind.TemplateTail
function emitLiteral(node: LiteralLikeNode) {
const text = getLiteralTextOfNode(node);
- if ((compilerOptions.sourceMap || compilerOptions.inlineSourceMap)
+ if ((printerOptions.sourceMap || printerOptions.inlineSourceMap)
&& (node.kind === SyntaxKind.StringLiteral || isTemplateLiteralKind(node.kind))) {
writer.writeLiteral(text);
}
@@ -934,17 +1008,13 @@ namespace ts {
write("{");
writeLine();
increaseIndent();
- if (node.readonlyToken) {
- write("readonly ");
- }
+ writeIfPresent(node.readonlyToken, "readonly ");
write("[");
emit(node.typeParameter.name);
write(" in ");
emit(node.typeParameter.constraint);
write("]");
- if (node.questionToken) {
- write("?");
- }
+ writeIfPresent(node.questionToken, "?");
write(": ");
emit(node.type);
write(";");
@@ -1033,7 +1103,7 @@ namespace ts {
let indentAfterDot = false;
if (!(getEmitFlags(node) & EmitFlags.NoIndentation)) {
const dotRangeStart = node.expression.end;
- const dotRangeEnd = skipTrivia(currentText, node.expression.end) + 1;
+ const dotRangeEnd = skipTrivia(currentSourceFile.text, node.expression.end) + 1;
const dotToken = { kind: SyntaxKind.DotToken, pos: dotRangeStart, end: dotRangeEnd };
indentBeforeDot = needsIndentation(node, node.expression, dotToken);
indentAfterDot = needsIndentation(node, dotToken, node.name);
@@ -1066,7 +1136,7 @@ namespace ts {
// isFinite handles cases when constantValue is undefined
return isFinite(constantValue)
&& Math.floor(constantValue) === constantValue
- && compilerOptions.removeComments;
+ && printerOptions.removeComments;
}
}
@@ -1097,12 +1167,9 @@ namespace ts {
}
function emitTypeAssertionExpression(node: TypeAssertion) {
- if (node.type) {
- write("<");
- emit(node.type);
- write(">");
- }
-
+ write("<");
+ emit(node.type);
+ write(">");
emitExpression(node.expression);
}
@@ -1502,11 +1569,10 @@ namespace ts {
emitBlockFunctionBody(body);
}
else {
- const savedTempFlags = tempFlags;
- tempFlags = 0;
+ pushNameGenerationScope();
emitSignatureHead(node);
emitBlockFunctionBody(body);
- tempFlags = savedTempFlags;
+ popNameGenerationScope();
}
if (indentedFlag) {
@@ -1574,10 +1640,11 @@ namespace ts {
write(" {");
increaseIndent();
- emitBodyWithDetachedComments(body, body.statements,
- shouldEmitBlockFunctionBodyOnSingleLine(body)
- ? emitBlockFunctionBodyOnSingleLine
- : emitBlockFunctionBodyWorker);
+ const emitBlockFunctionBody = shouldEmitBlockFunctionBodyOnSingleLine(body)
+ ? emitBlockFunctionBodyOnSingleLine
+ : emitBlockFunctionBodyWorker;
+
+ emitBodyIndirect(body, body.statements, emitBlockFunctionBody);
decreaseIndent();
writeToken(SyntaxKind.CloseBraceToken, body.statements.end, body);
@@ -1590,9 +1657,9 @@ namespace ts {
function emitBlockFunctionBodyWorker(body: Block, emitBlockFunctionBodyOnSingleLine?: boolean) {
// Emit all the prologue directives (like "use strict").
const statementOffset = emitPrologueDirectives(body.statements, /*startWithNewLine*/ true);
- const helpersEmitted = emitHelpers(body);
-
- if (statementOffset === 0 && !helpersEmitted && emitBlockFunctionBodyOnSingleLine) {
+ const pos = writer.getTextPos();
+ emitHelpersIndirect(body);
+ if (statementOffset === 0 && pos === writer.getTextPos() && emitBlockFunctionBodyOnSingleLine) {
decreaseIndent();
emitList(body, body.statements, ListFormat.SingleLineFunctionBodyStatements);
increaseIndent();
@@ -1620,18 +1687,15 @@ namespace ts {
emitTypeParameters(node, node.typeParameters);
emitList(node, node.heritageClauses, ListFormat.ClassHeritageClauses);
- const savedTempFlags = tempFlags;
- tempFlags = 0;
-
+ pushNameGenerationScope();
write(" {");
emitList(node, node.members, ListFormat.ClassMembers);
write("}");
+ popNameGenerationScope();
if (indentedFlag) {
decreaseIndent();
}
-
- tempFlags = savedTempFlags;
}
function emitInterfaceDeclaration(node: InterfaceDeclaration) {
@@ -1661,14 +1725,11 @@ namespace ts {
emitModifiers(node, node.modifiers);
write("enum ");
emit(node.name);
-
- const savedTempFlags = tempFlags;
- tempFlags = 0;
-
+ pushNameGenerationScope();
write(" {");
emitList(node, node.members, ListFormat.EnumMembers);
write("}");
- tempFlags = savedTempFlags;
+ popNameGenerationScope();
}
function emitModuleDeclaration(node: ModuleDeclaration) {
@@ -1692,13 +1753,12 @@ namespace ts {
write("{ }");
}
else {
- const savedTempFlags = tempFlags;
- tempFlags = 0;
+ pushNameGenerationScope();
write("{");
increaseIndent();
emitBlockStatements(node);
write("}");
- tempFlags = savedTempFlags;
+ popNameGenerationScope();
}
}
@@ -1947,11 +2007,10 @@ namespace ts {
// "comment1" is not considered to be leading comment for node.initializer
// but rather a trailing comment on the previous node.
const initializer = node.initializer;
- if ((getEmitFlags(initializer) & EmitFlags.NoLeadingComments) === 0) {
+ if (emitTrailingCommentsOfPosition && (getEmitFlags(initializer) & EmitFlags.NoLeadingComments) === 0) {
const commentRange = getCommentRange(initializer);
emitTrailingCommentsOfPosition(commentRange.pos);
}
-
emitExpression(initializer);
}
@@ -1986,17 +2045,16 @@ namespace ts {
function emitSourceFile(node: SourceFile) {
writeLine();
emitShebang();
- emitBodyWithDetachedComments(node, node.statements, emitSourceFileWorker);
+ emitBodyIndirect(node, node.statements, emitSourceFileWorker);
}
function emitSourceFileWorker(node: SourceFile) {
const statements = node.statements;
const statementOffset = emitPrologueDirectives(statements);
- const savedTempFlags = tempFlags;
- tempFlags = 0;
- emitHelpers(node);
+ pushNameGenerationScope();
+ emitHelpersIndirect(node);
emitList(node, statements, ListFormat.MultiLine, statementOffset);
- tempFlags = savedTempFlags;
+ popNameGenerationScope();
}
// Transformation nodes
@@ -2026,83 +2084,12 @@ namespace ts {
return statements.length;
}
- function emitHelpers(node: Node, isBundle?: boolean) {
- const sourceFile = isSourceFile(node) ? node : currentSourceFile;
- const shouldSkip = compilerOptions.noEmitHelpers || (sourceFile && getExternalHelpersModuleName(sourceFile) !== undefined);
- const shouldBundle = isSourceFile(node) && !isOwnFileEmit;
-
- let helpersEmitted = false;
- const helpers = getEmitHelpers(node);
- if (helpers) {
- for (const helper of stableSort(helpers, compareEmitHelpers)) {
- 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 (isBundle) {
- // Skip the helper if it is scoped and we are emitting bundled helpers
- continue;
- }
-
- writeLines(helper.text);
- helpersEmitted = true;
- }
- }
-
- if (helpersEmitted) {
- writeLine();
- }
-
- return helpersEmitted;
- }
-
- function writeLines(text: string): void {
- const lines = text.split(/\r\n?|\n/g);
- const indentation = guessIndentation(lines);
- for (let i = 0; i < lines.length; i++) {
- const line = indentation ? lines[i].slice(indentation) : lines[i];
- if (line.length) {
- if (i > 0) {
- writeLine();
- }
- write(line);
- }
- }
- }
-
- function guessIndentation(lines: string[]) {
- let indentation: number;
- for (const line of lines) {
- for (let i = 0; i < line.length && (indentation === undefined || i < indentation); i++) {
- if (!isWhiteSpace(line.charCodeAt(i))) {
- if (indentation === undefined || i < indentation) {
- indentation = i;
- break;
- }
- }
- }
- }
- return indentation;
- }
-
//
// Helpers
//
function emitShebang() {
- const shebang = getShebang(currentText);
+ const shebang = getShebang(currentSourceFile.text);
if (shebang) {
write(shebang);
writeLine();
@@ -2260,15 +2247,17 @@ namespace ts {
}
}
+ // Emit this child.
if (shouldEmitInterveningComments) {
- const commentRange = getCommentRange(child);
- emitTrailingCommentsOfPosition(commentRange.pos);
+ if (emitTrailingCommentsOfPosition) {
+ const commentRange = getCommentRange(child);
+ emitTrailingCommentsOfPosition(commentRange.pos);
+ }
}
else {
shouldEmitInterveningComments = mayEmitInterveningComments;
}
- // Emit this child.
emit(child);
if (shouldDecreaseIndentAfterEmit) {
@@ -2304,6 +2293,46 @@ namespace ts {
}
}
+ function write(s: string) {
+ writer.write(s);
+ }
+
+ function writeLine() {
+ writer.writeLine();
+ }
+
+ function increaseIndent() {
+ writer.increaseIndent();
+ }
+
+ function decreaseIndent() {
+ writer.decreaseIndent();
+ }
+
+ function writeIfAny(nodes: NodeArray, text: string) {
+ if (some(nodes)) {
+ write(text);
+ }
+ }
+
+ function writeIfPresent(node: Node, text: string) {
+ if (node) {
+ write(text);
+ }
+ }
+
+ function writeToken(token: SyntaxKind, pos: number, contextNode?: Node) {
+ return onEmitSourceMapOfToken
+ ? onEmitSourceMapOfToken(contextNode, token, pos, writeTokenText)
+ : writeTokenText(token, pos);
+ }
+
+ function writeTokenText(token: SyntaxKind, pos?: number) {
+ const tokenString = tokenToString(token);
+ write(tokenString);
+ return pos < 0 ? pos : pos + tokenString.length;
+ }
+
function writeLineOrSpace(node: Node) {
if (getEmitFlags(node) & EmitFlags.SingleLine) {
write(" ");
@@ -2313,26 +2342,32 @@ namespace ts {
}
}
- function writeIfAny(nodes: NodeArray, text: string) {
- if (nodes && nodes.length > 0) {
- write(text);
+ function writeLines(text: string): void {
+ const lines = text.split(/\r\n?|\n/g);
+ const indentation = guessIndentation(lines);
+ for (let i = 0; i < lines.length; i++) {
+ const line = indentation ? lines[i].slice(indentation) : lines[i];
+ if (line.length) {
+ writeLine();
+ write(line);
+ writeLine();
+ }
}
}
- function writeIfPresent(node: Node, text: string) {
- if (node !== undefined) {
- write(text);
+ function guessIndentation(lines: string[]) {
+ let indentation: number;
+ for (const line of lines) {
+ for (let i = 0; i < line.length && (indentation === undefined || i < indentation); i++) {
+ if (!isWhiteSpace(line.charCodeAt(i))) {
+ if (indentation === undefined || i < indentation) {
+ indentation = i;
+ break;
+ }
+ }
+ }
}
- }
-
- function writeToken(token: SyntaxKind, pos: number, contextNode?: Node) {
- return emitTokenWithSourceMap(contextNode, token, pos, writeTokenText);
- }
-
- function writeTokenText(token: SyntaxKind, pos?: number) {
- const tokenString = tokenToString(token);
- write(tokenString);
- return pos < 0 ? pos : pos + tokenString.length;
+ return indentation;
}
function increaseIndentIf(value: boolean, valueToWriteWhenNotIndenting?: string) {
@@ -2458,6 +2493,16 @@ namespace ts {
&& !rangeEndIsOnSameLineAsRangeStart(node1, node2, currentSourceFile);
}
+ function isSingleLineEmptyBlock(block: Block) {
+ return !block.multiLine
+ && isEmptyBlock(block);
+ }
+
+ function isEmptyBlock(block: BlockLike) {
+ return block.statements.length === 0
+ && rangeEndIsOnSameLineAsRangeStart(block, block, currentSourceFile);
+ }
+
function skipSynthesizedParentheses(node: Node) {
while (node.kind === SyntaxKind.ParenthesizedExpression && nodeIsSynthesized(node)) {
node = (node).expression;
@@ -2468,7 +2513,7 @@ namespace ts {
function getTextOfNode(node: Node, includeTrivia?: boolean): string {
if (isGeneratedIdentifier(node)) {
- return getGeneratedIdentifier(node);
+ return generateName(node);
}
else if (isIdentifier(node) && (nodeIsSynthesized(node) || !node.parent)) {
return unescapeIdentifier(node.text);
@@ -2497,22 +2542,57 @@ namespace ts {
return getLiteralText(node, currentSourceFile, languageVersion);
}
- function isSingleLineEmptyBlock(block: Block) {
- return !block.multiLine
- && isEmptyBlock(block);
+ /**
+ * Push a new name generation scope.
+ */
+ function pushNameGenerationScope() {
+ tempFlagsStack.push(tempFlags);
+ tempFlags = 0;
}
- function isEmptyBlock(block: BlockLike) {
- return block.statements.length === 0
- && rangeEndIsOnSameLineAsRangeStart(block, block, currentSourceFile);
+ /**
+ * Pop the current name generation scope.
+ */
+ function popNameGenerationScope() {
+ tempFlags = tempFlagsStack.pop();
}
+ /**
+ * Generate the text for a generated identifier.
+ */
+ function generateName(name: GeneratedIdentifier) {
+ if (name.autoGenerateKind === GeneratedIdentifierKind.Node) {
+ // Node names generate unique names based on their original node
+ // and are cached based on that node's id.
+ const node = getNodeForGeneratedName(name);
+ return generateNameCached(node, getTextOfNode);
+ }
+ else {
+ // Auto, Loop, and Unique names are cached based on their unique
+ // autoGenerateId.
+ const autoGenerateId = name.autoGenerateId;
+ return autoGeneratedIdToGeneratedName[autoGenerateId] || (autoGeneratedIdToGeneratedName[autoGenerateId] = unescapeIdentifier(makeName(name)));
+ }
+ }
+
+ function generateNameCached(node: Node, getTextOfNode: (node: Node, includeTrivia?: boolean) => string) {
+ const nodeId = getNodeId(node);
+ return nodeIdToGeneratedName[nodeId] || (nodeIdToGeneratedName[nodeId] = unescapeIdentifier(generateNameForNode(node, getTextOfNode)));
+ }
+
+ /**
+ * Returns a value indicating whether a name is unique globally, within the current file,
+ * or within the NameGenerator.
+ */
function isUniqueName(name: string): boolean {
- return !resolver.hasGlobalName(name) &&
- !currentFileIdentifiers.has(name) &&
- !generatedNameSet.has(name);
+ return !(hasGlobalName && hasGlobalName(name))
+ && !currentSourceFile.identifiers.has(name)
+ && !generatedNames.has(name);
}
+ /**
+ * Returns a value indicating whether a name is unique within a container.
+ */
function isUniqueLocalName(name: string, container: Node): boolean {
for (let node = container; isNodeDescendantOf(node, container); node = node.nextContainer) {
if (node.locals) {
@@ -2527,10 +2607,10 @@ namespace ts {
}
/**
- * Return the next available name in the pattern _a ... _z, _0, _1, ...
- * TempFlags._i or TempFlags._n may be used to express a preference for that dedicated name.
- * Note that names generated by makeTempVariableName and makeUniqueName will never conflict.
- */
+ * Return the next available name in the pattern _a ... _z, _0, _1, ...
+ * TempFlags._i or TempFlags._n may be used to express a preference for that dedicated name.
+ * Note that names generated by makeTempVariableName and makeUniqueName will never conflict.
+ */
function makeTempVariableName(flags: TempFlags): string {
if (flags && !(tempFlags & flags)) {
const name = flags === TempFlags._i ? "_i" : "_n";
@@ -2554,10 +2634,12 @@ namespace ts {
}
}
- // Generate a name that is unique within the current file and doesn't conflict with any names
- // in global scope. The name is formed by adding an '_n' suffix to the specified base name,
- // where n is a positive integer. Note that names generated by makeTempVariableName and
- // makeUniqueName are guaranteed to never conflict.
+ /**
+ * Generate a name that is unique within the current file and doesn't conflict with any names
+ * in global scope. The name is formed by adding an '_n' suffix to the specified base name,
+ * where n is a positive integer. Note that names generated by makeTempVariableName and
+ * makeUniqueName are guaranteed to never conflict.
+ */
function makeUniqueName(baseName: string): string {
// Find the first unique 'name_n', where n is a positive number
if (baseName.charCodeAt(baseName.length - 1) !== CharacterCodes._) {
@@ -2567,19 +2649,25 @@ namespace ts {
while (true) {
const generatedName = baseName + i;
if (isUniqueName(generatedName)) {
- generatedNameSet.set(generatedName, generatedName);
+ generatedNames.set(generatedName, generatedName);
return generatedName;
}
i++;
}
}
- function generateNameForModuleOrEnum(node: ModuleDeclaration | EnumDeclaration) {
+ /**
+ * Generates a unique name for a ModuleDeclaration or EnumDeclaration.
+ */
+ function generateNameForModuleOrEnum(node: ModuleDeclaration | EnumDeclaration, getTextOfNode: (node: Node, includeTrivia?: boolean) => string) {
const name = getTextOfNode(node.name);
// Use module/enum name itself if it is unique, otherwise make a unique variation
return isUniqueLocalName(name, node) ? name : makeUniqueName(name);
}
+ /**
+ * Generates a unique name for an ImportDeclaration or ExportDeclaration.
+ */
function generateNameForImportOrExportDeclaration(node: ImportDeclaration | ExportDeclaration) {
const expr = getExternalModuleName(node);
const baseName = expr.kind === SyntaxKind.StringLiteral ?
@@ -2587,33 +2675,37 @@ namespace ts {
return makeUniqueName(baseName);
}
+ /**
+ * Generates a unique name for a default export.
+ */
function generateNameForExportDefault() {
return makeUniqueName("default");
}
+ /**
+ * Generates a unique name for a class expression.
+ */
function generateNameForClassExpression() {
return makeUniqueName("class");
}
- function generateNameForMethodOrAccessor(node: MethodDeclaration | AccessorDeclaration) {
+ function generateNameForMethodOrAccessor(node: MethodDeclaration | AccessorDeclaration, getTextOfNode: (node: Node, includeTrivia?: boolean) => string) {
if (isIdentifier(node.name)) {
- return generateNameForNodeCached(node.name);
+ return generateNameCached(node.name, getTextOfNode);
}
return makeTempVariableName(TempFlags.Auto);
}
/**
* Generates a unique name from a node.
- *
- * @param node A node.
*/
- function generateNameForNode(node: Node): string {
+ function generateNameForNode(node: Node, getTextOfNode: (node: Node, includeTrivia?: boolean) => string): string {
switch (node.kind) {
case SyntaxKind.Identifier:
return makeUniqueName(getTextOfNode(node));
case SyntaxKind.ModuleDeclaration:
case SyntaxKind.EnumDeclaration:
- return generateNameForModuleOrEnum(node);
+ return generateNameForModuleOrEnum(node, getTextOfNode);
case SyntaxKind.ImportDeclaration:
case SyntaxKind.ExportDeclaration:
return generateNameForImportOrExportDeclaration(node);
@@ -2626,7 +2718,7 @@ namespace ts {
case SyntaxKind.MethodDeclaration:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
- return generateNameForMethodOrAccessor(node);
+ return generateNameForMethodOrAccessor(node, getTextOfNode);
default:
return makeTempVariableName(TempFlags.Auto);
}
@@ -2634,10 +2726,8 @@ namespace ts {
/**
* Generates a unique identifier for a node.
- *
- * @param name A generated name.
*/
- function generateName(name: Identifier) {
+ function makeName(name: GeneratedIdentifier) {
switch (name.autoGenerateKind) {
case GeneratedIdentifierKind.Auto:
return makeTempVariableName(TempFlags.Auto);
@@ -2652,10 +2742,8 @@ namespace ts {
/**
* Gets the node from which a name should be generated.
- *
- * @param name A generated name wrapper.
*/
- function getNodeForGeneratedName(name: Identifier) {
+ function getNodeForGeneratedName(name: GeneratedIdentifier) {
const autoGenerateId = name.autoGenerateId;
let node = name as Node;
let original = node.original;
@@ -2676,61 +2764,43 @@ namespace ts {
// otherwise, return the original node for the source;
return node;
}
+ }
- function generateNameForNodeCached(node: Node) {
- const nodeId = getNodeId(node);
- return nodeIdToGeneratedName[nodeId] || (nodeIdToGeneratedName[nodeId] = unescapeIdentifier(generateNameForNode(node)));
- }
+ function createDelimiterMap() {
+ const delimiters: string[] = [];
+ delimiters[ListFormat.None] = "";
+ delimiters[ListFormat.CommaDelimited] = ",";
+ delimiters[ListFormat.BarDelimited] = " |";
+ delimiters[ListFormat.AmpersandDelimited] = " &";
+ return delimiters;
+ }
- /**
- * Gets the generated identifier text from a generated identifier.
- *
- * @param name The generated identifier.
- */
- function getGeneratedIdentifier(name: Identifier) {
- if (name.autoGenerateKind === GeneratedIdentifierKind.Node) {
- // Generated names generate unique names based on their original node
- // and are cached based on that node's id
- const node = getNodeForGeneratedName(name);
- return generateNameForNodeCached(node);
- }
- else {
- // Auto, Loop, and Unique names are cached based on their unique
- // autoGenerateId.
- const autoGenerateId = name.autoGenerateId;
- return autoGeneratedIdToGeneratedName[autoGenerateId] || (autoGeneratedIdToGeneratedName[autoGenerateId] = unescapeIdentifier(generateName(name)));
- }
- }
+ function getDelimiter(format: ListFormat) {
+ return delimiters[format & ListFormat.DelimitersMask];
+ }
- function createDelimiterMap() {
- const delimiters: string[] = [];
- delimiters[ListFormat.None] = "";
- delimiters[ListFormat.CommaDelimited] = ",";
- delimiters[ListFormat.BarDelimited] = " |";
- delimiters[ListFormat.AmpersandDelimited] = " &";
- return delimiters;
- }
+ function createBracketsMap() {
+ const brackets: string[][] = [];
+ brackets[ListFormat.Braces] = ["{", "}"];
+ brackets[ListFormat.Parenthesis] = ["(", ")"];
+ brackets[ListFormat.AngleBrackets] = ["<", ">"];
+ brackets[ListFormat.SquareBrackets] = ["[", "]"];
+ return brackets;
+ }
- function getDelimiter(format: ListFormat) {
- return delimiters[format & ListFormat.DelimitersMask];
- }
+ function getOpeningBracket(format: ListFormat) {
+ return brackets[format & ListFormat.BracketsMask][0];
+ }
- function createBracketsMap() {
- const brackets: string[][] = [];
- brackets[ListFormat.Braces] = ["{", "}"];
- brackets[ListFormat.Parenthesis] = ["(", ")"];
- brackets[ListFormat.AngleBrackets] = ["<", ">"];
- brackets[ListFormat.SquareBrackets] = ["[", "]"];
- return brackets;
- }
+ function getClosingBracket(format: ListFormat) {
+ return brackets[format & ListFormat.BracketsMask][1];
+ }
- function getOpeningBracket(format: ListFormat) {
- return brackets[format & ListFormat.BracketsMask][0];
- }
-
- function getClosingBracket(format: ListFormat) {
- return brackets[format & ListFormat.BracketsMask][1];
- }
+ // Flags enum to track count of temp variables and a few dedicated names
+ const enum TempFlags {
+ Auto = 0x00000000, // No preferred name
+ CountMask = 0x0FFFFFFF, // Temp variable counter
+ _i = 0x10000000, // Use/preference flag for '_i'
}
const enum ListFormat {
@@ -2757,7 +2827,7 @@ namespace ts {
SpaceBetweenSiblings = 1 << 8, // Inserts a space between each sibling node.
// Brackets/Braces
- Braces = 1 << 9, // The list is surrounded by "{" and "}".
+ Braces = 1 << 9, // The list is surrounded by "{" and "}".
Parenthesis = 1 << 10, // The list is surrounded by "(" and ")".
AngleBrackets = 1 << 11, // The list is surrounded by "<" and ">".
SquareBrackets = 1 << 12, // The list is surrounded by "[" and "]".
@@ -2808,4 +2878,4 @@ namespace ts {
Parameters = CommaDelimited | SpaceBetweenSiblings | SingleLine | Indented | Parenthesis,
IndexSignatureParameters = CommaDelimited | SpaceBetweenSiblings | SingleLine | Indented | SquareBrackets,
}
-}
+}
\ No newline at end of file
diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts
index f63094ae16d..f2a8de3a93b 100644
--- a/src/compiler/factory.ts
+++ b/src/compiler/factory.ts
@@ -1530,6 +1530,19 @@ namespace ts {
return node;
}
+ export function createBundle(sourceFiles: SourceFile[]) {
+ const node = createNode(SyntaxKind.Bundle);
+ node.sourceFiles = sourceFiles;
+ return node;
+ }
+
+ export function updateBundle(node: Bundle, sourceFiles: SourceFile[]) {
+ if (node.sourceFiles !== sourceFiles) {
+ return createBundle(sourceFiles);
+ }
+ return node;
+ }
+
// Compound nodes
export function createComma(left: Expression, right: Expression) {
diff --git a/src/compiler/sourcemap.ts b/src/compiler/sourcemap.ts
index 650b9b0ef02..d743f488e75 100644
--- a/src/compiler/sourcemap.ts
+++ b/src/compiler/sourcemap.ts
@@ -8,10 +8,9 @@ namespace ts {
*
* @param filePath The path to the generated output file.
* @param sourceMapFilePath The path to the output source map file.
- * @param sourceFiles The input source files for the program.
- * @param isBundledEmit A value indicating whether the generated output file is a bundle.
+ * @param sourceFileOrBundle The input source file or bundle for the program.
*/
- initialize(filePath: string, sourceMapFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean): void;
+ initialize(filePath: string, sourceMapFilePath: string, sourceFileOrBundle: SourceFile | Bundle): void;
/**
* Reset the SourceMapWriter to an empty state.
@@ -38,11 +37,11 @@ namespace ts {
/**
* Emits a node with possible leading and trailing source maps.
*
- * @param emitContext The current emit context
+ * @param hint The current emit context
* @param node The node to emit.
* @param emitCallback The callback used to emit the node.
*/
- emitNodeWithSourceMap(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void;
+ emitNodeWithSourceMap(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void;
/**
* Emits a token of a node node with possible leading and trailing source maps.
@@ -115,10 +114,9 @@ namespace ts {
*
* @param filePath The path to the generated output file.
* @param sourceMapFilePath The path to the output source map file.
- * @param sourceFiles The input source files for the program.
- * @param isBundledEmit A value indicating whether the generated output file is a bundle.
+ * @param sourceFileOrBundle The input source file or bundle for the program.
*/
- function initialize(filePath: string, sourceMapFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean) {
+ function initialize(filePath: string, sourceMapFilePath: string, sourceFileOrBundle: SourceFile | Bundle) {
if (disabled) {
return;
}
@@ -161,11 +159,10 @@ namespace ts {
if (compilerOptions.mapRoot) {
sourceMapDir = normalizeSlashes(compilerOptions.mapRoot);
- if (!isBundledEmit) { // emitting single module file
- Debug.assert(sourceFiles.length === 1);
+ if (sourceFileOrBundle.kind === SyntaxKind.SourceFile) { // emitting single module file
// 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(sourceFiles[0], host, sourceMapDir));
+ sourceMapDir = getDirectoryPath(getSourceFilePathInNewDir(sourceFileOrBundle, host, sourceMapDir));
}
if (!isRootedDiskPath(sourceMapDir) && !isUrl(sourceMapDir)) {
@@ -311,12 +308,13 @@ namespace ts {
/**
* Emits a node with possible leading and trailing source maps.
*
+ * @param hint A hint as to the intended usage of the node.
* @param node The node to emit.
* @param emitCallback The callback used to emit the node.
*/
- function emitNodeWithSourceMap(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void) {
+ function emitNodeWithSourceMap(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) {
if (disabled) {
- return emitCallback(emitContext, node);
+ return emitCallback(hint, node);
}
if (node) {
@@ -332,11 +330,11 @@ namespace ts {
if (emitFlags & EmitFlags.NoNestedSourceMaps) {
disabled = true;
- emitCallback(emitContext, node);
+ emitCallback(hint, node);
disabled = false;
}
else {
- emitCallback(emitContext, node);
+ emitCallback(hint, node);
}
if (node.kind !== SyntaxKind.NotEmittedStatement
diff --git a/src/compiler/transformer.ts b/src/compiler/transformer.ts
index cef8c701455..62469df79c1 100644
--- a/src/compiler/transformer.ts
+++ b/src/compiler/transformer.ts
@@ -105,14 +105,16 @@ namespace ts {
hoistFunctionDeclaration,
requestEmitHelper,
readEmitHelpers,
- onSubstituteNode: (_emitContext, node) => node,
+ onSubstituteNode: (_, node) => node,
enableSubstitution,
isSubstitutionEnabled,
- onEmitNode: (node, emitContext, emitCallback) => emitCallback(node, emitContext),
+ onEmitNode: (hint, node, callback) => callback(hint, node),
enableEmitNotification,
isEmitNotificationEnabled
};
+ performance.mark("beforeTransform");
+
// Chain together and initialize each transformer.
const transformation = chain(...transformers)(context);
@@ -122,6 +124,9 @@ namespace ts {
// Disable modification of the lexical environment.
lexicalEnvironmentDisabled = true;
+ performance.mark("afterTransform");
+ performance.measure("transformTime", "beforeTransform", "afterTransform");
+
return {
transformed,
emitNodeWithSubstitution,
@@ -159,21 +164,16 @@ namespace ts {
/**
* Emits a node with possible substitution.
*
- * @param emitContext The current emit context.
+ * @param hint A hint as to the intended usage of the node.
* @param node The node to emit.
* @param emitCallback The callback used to emit the node or its substitute.
*/
- function emitNodeWithSubstitution(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void) {
+ function emitNodeWithSubstitution(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) {
if (node) {
if (isSubstitutionEnabled(node)) {
- const substitute = context.onSubstituteNode(emitContext, node);
- if (substitute && substitute !== node) {
- emitCallback(emitContext, substitute);
- return;
- }
+ node = context.onSubstituteNode(hint, node) || node;
}
-
- emitCallback(emitContext, node);
+ emitCallback(hint, node);
}
}
@@ -196,17 +196,17 @@ namespace ts {
/**
* Emits a node with possible emit notification.
*
- * @param emitContext The current emit context.
+ * @param hint A hint as to the intended usage of the node.
* @param node The node to emit.
* @param emitCallback The callback used to emit the node.
*/
- function emitNodeWithNotification(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void) {
+ function emitNodeWithNotification(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) {
if (node) {
if (isEmitNotificationEnabled(node)) {
- context.onEmitNode(emitContext, node, emitCallback);
+ context.onEmitNode(hint, node, emitCallback);
}
else {
- emitCallback(emitContext, node);
+ emitCallback(hint, node);
}
}
}
diff --git a/src/compiler/transformers/es2015.ts b/src/compiler/transformers/es2015.ts
index 3b8b4524aab..10ebe648fe2 100644
--- a/src/compiler/transformers/es2015.ts
+++ b/src/compiler/transformers/es2015.ts
@@ -3435,9 +3435,11 @@ namespace ts {
/**
* Called by the printer just before a node is printed.
*
+ * @param hint A hint as to the intended usage of the node.
* @param node The node to be printed.
+ * @param emitCallback The callback used to emit the node.
*/
- function onEmitNode(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void) {
+ function onEmitNode(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) {
if (enabledSubstitutions & ES2015SubstitutionFlags.CapturedThis && isFunctionLike(node)) {
// If we are tracking a captured `this`, keep track of the enclosing function.
const ancestorFacts = enterSubtree(
@@ -3445,11 +3447,11 @@ namespace ts {
getEmitFlags(node) & EmitFlags.CapturesThis
? HierarchyFacts.FunctionIncludes | HierarchyFacts.CapturesThis
: HierarchyFacts.FunctionIncludes);
- previousOnEmitNode(emitContext, node, emitCallback);
+ previousOnEmitNode(hint, node, emitCallback);
exitSubtree(ancestorFacts, HierarchyFacts.None, HierarchyFacts.None);
return;
}
- previousOnEmitNode(emitContext, node, emitCallback);
+ previousOnEmitNode(hint, node, emitCallback);
}
/**
@@ -3484,13 +3486,13 @@ namespace ts {
/**
* Hooks node substitutions.
*
- * @param emitContext The context for the emitter.
+ * @param hint The context for the emitter.
* @param node The node to substitute.
*/
- function onSubstituteNode(emitContext: EmitContext, node: Node) {
- node = previousOnSubstituteNode(emitContext, node);
+ function onSubstituteNode(hint: EmitHint, node: Node) {
+ node = previousOnSubstituteNode(hint, node);
- if (emitContext === EmitContext.Expression) {
+ if (hint === EmitHint.Expression) {
return substituteExpression(node);
}
diff --git a/src/compiler/transformers/es2017.ts b/src/compiler/transformers/es2017.ts
index c87dbe32518..a7f7b0da0ea 100644
--- a/src/compiler/transformers/es2017.ts
+++ b/src/compiler/transformers/es2017.ts
@@ -396,33 +396,33 @@ namespace ts {
/**
* Hook for node emit.
*
+ * @param hint A hint as to the intended usage of the node.
* @param node The node to emit.
* @param emit A callback used to emit the node in the printer.
*/
- function onEmitNode(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void {
+ function onEmitNode(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void {
// If we need to support substitutions for `super` in an async method,
// we should track it here.
if (enabledSubstitutions & ES2017SubstitutionFlags.AsyncMethodsWithSuper && isSuperContainer(node)) {
const savedCurrentSuperContainer = currentSuperContainer;
currentSuperContainer = node;
- previousOnEmitNode(emitContext, node, emitCallback);
+ previousOnEmitNode(hint, node, emitCallback);
currentSuperContainer = savedCurrentSuperContainer;
}
else {
- previousOnEmitNode(emitContext, node, emitCallback);
+ previousOnEmitNode(hint, node, emitCallback);
}
}
/**
* Hooks node substitutions.
*
+ * @param hint A hint as to the intended usage of the node.
* @param node The node to substitute.
- * @param isExpression A value indicating whether the node is to be used in an expression
- * position.
*/
- function onSubstituteNode(emitContext: EmitContext, node: Node) {
- node = previousOnSubstituteNode(emitContext, node);
- if (emitContext === EmitContext.Expression) {
+ function onSubstituteNode(hint: EmitHint, node: Node) {
+ node = previousOnSubstituteNode(hint, node);
+ if (hint === EmitHint.Expression) {
return substituteExpression(node);
}
diff --git a/src/compiler/transformers/es5.ts b/src/compiler/transformers/es5.ts
index c3b011fe36d..8b030642a89 100644
--- a/src/compiler/transformers/es5.ts
+++ b/src/compiler/transformers/es5.ts
@@ -12,7 +12,7 @@ namespace ts {
const compilerOptions = context.getCompilerOptions();
// enable emit notification only if using --jsx preserve or react-native
- let previousOnEmitNode: (emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void) => void;
+ let previousOnEmitNode: (hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) => void;
let noSubstitution: boolean[];
if (compilerOptions.jsx === JsxEmit.Preserve || compilerOptions.jsx === JsxEmit.ReactNative) {
previousOnEmitNode = context.onEmitNode;
@@ -41,9 +41,11 @@ namespace ts {
/**
* Called by the printer just before a node is printed.
*
- * @param node The node to be printed.
+ * @param hint A hint as to the intended usage of the node.
+ * @param node The node to emit.
+ * @param emitCallback A callback used to emit the node.
*/
- function onEmitNode(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void) {
+ function onEmitNode(hint: EmitHint, node: Node, emitCallback: (emitContext: EmitHint, node: Node) => void) {
switch (node.kind) {
case SyntaxKind.JsxOpeningElement:
case SyntaxKind.JsxClosingElement:
@@ -53,21 +55,21 @@ namespace ts {
break;
}
- previousOnEmitNode(emitContext, node, emitCallback);
+ previousOnEmitNode(hint, node, emitCallback);
}
/**
* Hooks node substitutions.
*
- * @param emitContext The context for the emitter.
+ * @param hint A hint as to the intended usage of the node.
* @param node The node to substitute.
*/
- function onSubstituteNode(emitContext: EmitContext, node: Node) {
+ function onSubstituteNode(hint: EmitHint, node: Node) {
if (node.id && noSubstitution && noSubstitution[node.id]) {
- return previousOnSubstituteNode(emitContext, node);
+ return previousOnSubstituteNode(hint, node);
}
- node = previousOnSubstituteNode(emitContext, node);
+ node = previousOnSubstituteNode(hint, node);
if (isPropertyAccessExpression(node)) {
return substitutePropertyAccessExpression(node);
}
diff --git a/src/compiler/transformers/generators.ts b/src/compiler/transformers/generators.ts
index 7dcfe76a427..ee17bdacbaf 100644
--- a/src/compiler/transformers/generators.ts
+++ b/src/compiler/transformers/generators.ts
@@ -1909,9 +1909,9 @@ namespace ts {
return -1;
}
- function onSubstituteNode(emitContext: EmitContext, node: Node): Node {
- node = previousOnSubstituteNode(emitContext, node);
- if (emitContext === EmitContext.Expression) {
+ function onSubstituteNode(hint: EmitHint, node: Node): Node {
+ node = previousOnSubstituteNode(hint, node);
+ if (hint === EmitHint.Expression) {
return substituteExpression(node);
}
return node;
diff --git a/src/compiler/transformers/module/es2015.ts b/src/compiler/transformers/module/es2015.ts
index 5611f890164..86fa1295f7b 100644
--- a/src/compiler/transformers/module/es2015.ts
+++ b/src/compiler/transformers/module/es2015.ts
@@ -71,18 +71,18 @@ namespace ts {
/**
* Hook for node emit.
*
- * @param emitContext A context hint for the emitter.
+ * @param hint A hint as to the intended usage of the node.
* @param node The node to emit.
* @param emit A callback used to emit the node in the printer.
*/
- function onEmitNode(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void {
+ function onEmitNode(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void {
if (isSourceFile(node)) {
currentSourceFile = node;
- previousOnEmitNode(emitContext, node, emitCallback);
+ previousOnEmitNode(hint, node, emitCallback);
currentSourceFile = undefined;
}
else {
- previousOnEmitNode(emitContext, node, emitCallback);
+ previousOnEmitNode(hint, node, emitCallback);
}
}
@@ -93,12 +93,12 @@ namespace ts {
/**
* Hooks node substitutions.
*
- * @param emitContext A context hint for the emitter.
+ * @param hint A hint as to the intended usage of the node.
* @param node The node to substitute.
*/
- function onSubstituteNode(emitContext: EmitContext, node: Node) {
- node = previousOnSubstituteNode(emitContext, node);
- if (isIdentifier(node) && emitContext === EmitContext.Expression) {
+ function onSubstituteNode(hint: EmitHint, node: Node) {
+ node = previousOnSubstituteNode(hint, node);
+ if (isIdentifier(node) && hint === EmitHint.Expression) {
return substituteExpressionIdentifier(node);
}
return node;
diff --git a/src/compiler/transformers/module/module.ts b/src/compiler/transformers/module/module.ts
index 0adea4fd540..e072429292a 100644
--- a/src/compiler/transformers/module/module.ts
+++ b/src/compiler/transformers/module/module.ts
@@ -1207,24 +1207,24 @@ namespace ts {
/**
* Hook for node emit notifications.
*
- * @param emitContext A context hint for the emitter.
+ * @param hint A hint as to the intended usage of the node.
* @param node The node to emit.
* @param emit A callback used to emit the node in the printer.
*/
- function onEmitNode(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void {
+ function onEmitNode(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void {
if (node.kind === SyntaxKind.SourceFile) {
currentSourceFile = node;
currentModuleInfo = moduleInfoMap[getOriginalNodeId(currentSourceFile)];
noSubstitution = [];
- previousOnEmitNode(emitContext, node, emitCallback);
+ previousOnEmitNode(hint, node, emitCallback);
currentSourceFile = undefined;
currentModuleInfo = undefined;
noSubstitution = undefined;
}
else {
- previousOnEmitNode(emitContext, node, emitCallback);
+ previousOnEmitNode(hint, node, emitCallback);
}
}
@@ -1235,16 +1235,16 @@ namespace ts {
/**
* Hooks node substitutions.
*
- * @param emitContext A context hint for the emitter.
+ * @param hint A hint as to the intended usage of the node.
* @param node The node to substitute.
*/
- function onSubstituteNode(emitContext: EmitContext, node: Node) {
- node = previousOnSubstituteNode(emitContext, node);
+ function onSubstituteNode(hint: EmitHint, node: Node) {
+ node = previousOnSubstituteNode(hint, node);
if (node.id && noSubstitution[node.id]) {
return node;
}
- if (emitContext === EmitContext.Expression) {
+ if (hint === EmitHint.Expression) {
return substituteExpression(node);
}
else if (isShorthandPropertyAssignment(node)) {
diff --git a/src/compiler/transformers/module/system.ts b/src/compiler/transformers/module/system.ts
index 3f1488b649d..6c59ea194cb 100644
--- a/src/compiler/transformers/module/system.ts
+++ b/src/compiler/transformers/module/system.ts
@@ -1548,11 +1548,11 @@ namespace ts {
/**
* Hook for node emit notifications.
*
- * @param emitContext A context hint for the emitter.
+ * @param hint A hint as to the intended usage of the node.
* @param node The node to emit.
- * @param emit A callback used to emit the node in the printer.
+ * @param emitCallback A callback used to emit the node in the printer.
*/
- function onEmitNode(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void {
+ function onEmitNode(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void {
if (node.kind === SyntaxKind.SourceFile) {
const id = getOriginalNodeId(node);
currentSourceFile = node;
@@ -1564,7 +1564,7 @@ namespace ts {
delete noSubstitutionMap[id];
}
- previousOnEmitNode(emitContext, node, emitCallback);
+ previousOnEmitNode(hint, node, emitCallback);
currentSourceFile = undefined;
moduleInfo = undefined;
@@ -1572,7 +1572,7 @@ namespace ts {
noSubstitution = undefined;
}
else {
- previousOnEmitNode(emitContext, node, emitCallback);
+ previousOnEmitNode(hint, node, emitCallback);
}
}
@@ -1583,16 +1583,16 @@ namespace ts {
/**
* Hooks node substitutions.
*
- * @param emitContext A context hint for the emitter.
+ * @param hint A hint as to the intended usage of the node.
* @param node The node to substitute.
*/
- function onSubstituteNode(emitContext: EmitContext, node: Node) {
- node = previousOnSubstituteNode(emitContext, node);
+ function onSubstituteNode(hint: EmitHint, node: Node) {
+ node = previousOnSubstituteNode(hint, node);
if (isSubstitutionPrevented(node)) {
return node;
}
- if (emitContext === EmitContext.Expression) {
+ if (hint === EmitHint.Expression) {
return substituteExpression(node);
}
diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts
index e2b8b8fe2ec..a61998e8af9 100644
--- a/src/compiler/transformers/ts.ts
+++ b/src/compiler/transformers/ts.ts
@@ -3154,11 +3154,11 @@ namespace ts {
/**
* Hook for node emit.
*
- * @param emitContext A context hint for the emitter.
+ * @param hint A hint as to the intended usage of the node.
* @param node The node to emit.
* @param emit A callback used to emit the node in the printer.
*/
- function onEmitNode(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void {
+ function onEmitNode(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void {
const savedApplicableSubstitutions = applicableSubstitutions;
if (enabledSubstitutions & TypeScriptSubstitutionFlags.NamespaceExports && isTransformedModuleDeclaration(node)) {
@@ -3169,7 +3169,7 @@ namespace ts {
applicableSubstitutions |= TypeScriptSubstitutionFlags.NonQualifiedEnumMembers;
}
- previousOnEmitNode(emitContext, node, emitCallback);
+ previousOnEmitNode(hint, node, emitCallback);
applicableSubstitutions = savedApplicableSubstitutions;
}
@@ -3177,12 +3177,12 @@ namespace ts {
/**
* Hooks node substitutions.
*
- * @param emitContext A context hint for the emitter.
+ * @param hint A hint as to the intended usage of the node.
* @param node The node to substitute.
*/
- function onSubstituteNode(emitContext: EmitContext, node: Node) {
- node = previousOnSubstituteNode(emitContext, node);
- if (emitContext === EmitContext.Expression) {
+ function onSubstituteNode(hint: EmitHint, node: Node) {
+ node = previousOnSubstituteNode(hint, node);
+ if (hint === EmitHint.Expression) {
return substituteExpression(node);
}
else if (isShorthandPropertyAssignment(node)) {
diff --git a/src/compiler/types.ts b/src/compiler/types.ts
index 8b49af402cf..8660e6138cd 100644
--- a/src/compiler/types.ts
+++ b/src/compiler/types.ts
@@ -348,6 +348,7 @@
EnumMember,
// Top-level nodes
SourceFile,
+ Bundle,
// JSDoc nodes
JSDocTypeExpression,
@@ -2201,6 +2202,11 @@
/* @internal */ ambientModuleNames: string[];
}
+ export interface Bundle extends Node {
+ kind: SyntaxKind.Bundle;
+ sourceFiles: SourceFile[];
+ }
+
export interface ScriptReferenceHost {
getCompilerOptions(): CompilerOptions;
getSourceFile(fileName: string): SourceFile;
@@ -3780,8 +3786,7 @@
LastEmitHelper = Generator
}
- /* @internal */
- export const enum EmitContext {
+ export const enum EmitHint {
SourceFile, // Emitting a SourceFile
Expression, // Emitting an Expression
IdentifierName, // Emitting an IdentifierName
@@ -3856,7 +3861,7 @@
* Hook used by transformers to substitute expressions just before they
* are emitted by the pretty printer.
*/
- onSubstituteNode?: (emitContext: EmitContext, node: Node) => Node;
+ onSubstituteNode?: (hint: EmitHint, node: Node) => Node;
/**
* Enables before/after emit notifications in the pretty printer for the provided
@@ -3874,7 +3879,7 @@
* Hook used to allow transformers to capture state before or after
* the printer emits a node.
*/
- onEmitNode?: (emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void) => void;
+ onEmitNode?: (hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) => void;
}
/* @internal */
@@ -3887,25 +3892,132 @@
/**
* Emits the substitute for a node, if one is available; otherwise, emits the node.
*
- * @param emitContext The current emit context.
+ * @param hint A hint as to the intended usage of the node.
* @param node The node to substitute.
* @param emitCallback A callback used to emit the node or its substitute.
*/
- emitNodeWithSubstitution(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void;
+ emitNodeWithSubstitution(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void;
/**
* Emits a node with possible notification.
*
- * @param emitContext The current emit context.
+ * @param hint A hint as to the intended usage of the node.
* @param node The node to emit.
* @param emitCallback A callback used to emit the node.
*/
- emitNodeWithNotification(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void;
+ emitNodeWithNotification(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void;
}
/* @internal */
export type Transformer = (context: TransformationContext) => (node: SourceFile) => SourceFile;
+ export interface Printer {
+ /**
+ * Print a node and its subtree as-is, without any emit transformations.
+ * @param hint A value indicating the purpose of a node. This is primarily used to
+ * distinguish between an `Identifier` used in an expression position, versus an
+ * `Identifier` used as an `IdentifierName` as part of a declaration. For most nodes you
+ * should just pass `Unspecified`.
+ * @param node The node to print. The node and its subtree are printed as-is, without any
+ * emit transformations.
+ * @param sourceFile A source file that provides context for the node. The source text of
+ * the file is used to emit the original source content for literals and identifiers, while
+ * the identifiers of the source file are used when generating unique names to avoid
+ * collisions.
+ */
+ printNode(hint: EmitHint, node: Node, sourceFile: SourceFile): string;
+ /**
+ * Prints a source file as-is, without any emit transformations.
+ */
+ printFile(sourceFile: SourceFile): string;
+ /**
+ * Prints a bundle of source files as-is, without any emit transformations.
+ */
+ printBundle(bundle: Bundle): string;
+ /*@internal*/ writeNode(hint: EmitHint, node: Node, sourceFile: SourceFile, writer: EmitTextWriter): void;
+ /*@internal*/ writeFile(sourceFile: SourceFile, writer: EmitTextWriter): void;
+ /*@internal*/ writeBundle(bundle: Bundle, writer: EmitTextWriter): void;
+ }
+
+ export interface PrintHandlers {
+ /**
+ * A hook used by the Printer when generating unique names to avoid collisions with
+ * globally defined names that exist outside of the current source file.
+ */
+ hasGlobalName?(name: string): boolean;
+ /**
+ * A hook used by the Printer to provide notifications prior to emitting a node. A
+ * compatible implementation **must** invoke `emitCallback` with the provided `hint` and
+ * `node` values.
+ * @param hint A hint indicating the intended purpose of the node.
+ * @param node The node to emit.
+ * @param emitCallback A callback that, when invoked, will emit the node.
+ * @example
+ * ```ts
+ * var printer = createPrinter(printerOptions, {
+ * onEmitNode(hint, node, emitCallback) {
+ * // set up or track state prior to emitting the node...
+ * emitCallback(hint, node);
+ * // restore state after emitting the node...
+ * }
+ * });
+ * ```
+ */
+ onEmitNode?(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void;
+ /**
+ * A hook used by the Printer to perform just-in-time substitution of a node. This is
+ * primarily used by node transformations that need to substitute one node for another,
+ * such as replacing `myExportedVar` with `exports.myExportedVar`. A compatible
+ * implementation **must** invoke `emitCallback` eith the provided `hint` and either
+ * the provided `node`, or its substitute.
+ * @param hint A hint indicating the intended purpose of the node.
+ * @param node The node to emit.
+ * @param emitCallback A callback that, when invoked, will emit the node.
+ * @example
+ * ```ts
+ * var printer = createPrinter(printerOptions, {
+ * onSubstituteNode(hint, node, emitCallback) {
+ * // perform substitution if necessary...
+ * emitCallback(hint, node);
+ * }
+ * });
+ * ```
+ */
+ onSubstituteNode?(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void;
+ /*@internal*/ onEmitSourceMapOfNode?: (hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) => void;
+ /*@internal*/ onEmitSourceMapOfToken?: (node: Node, token: SyntaxKind, pos: number, emitCallback: (token: SyntaxKind, pos: number) => number) => number;
+ /*@internal*/ onEmitSourceMapOfPosition?: (pos: number) => void;
+ /*@internal*/ onEmitHelpers?: (node: Node, writeLines: (text: string) => void) => void;
+ /*@internal*/ onSetSourceFile?: (node: SourceFile) => void;
+ }
+
+ export interface PrinterOptions {
+ target?: ScriptTarget;
+ removeComments?: boolean;
+ newLine?: NewLineKind;
+ /*@internal*/ sourceMap?: boolean;
+ /*@internal*/ inlineSourceMap?: boolean;
+ /*@internal*/ extendedDiagnostics?: boolean;
+ }
+
+ /*@internal*/
+ export interface EmitTextWriter {
+ write(s: string): void;
+ writeTextOfNode(text: string, node: Node): void;
+ writeLine(): void;
+ increaseIndent(): void;
+ decreaseIndent(): void;
+ getText(): string;
+ rawWrite(s: string): void;
+ writeLiteral(s: string): void;
+ getTextPos(): number;
+ getLine(): number;
+ getColumn(): number;
+ getIndent(): number;
+ isAtStartOfLine(): boolean;
+ reset(): void;
+ }
+
export interface TextSpan {
start: number;
length: number;
diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts
index 985564e0d94..01cfcb08047 100644
--- a/src/compiler/utilities.ts
+++ b/src/compiler/utilities.ts
@@ -2093,16 +2093,19 @@ namespace ts {
return undefined;
}
- export function getOriginalSourceFiles(sourceFiles: SourceFile[]) {
- const originalSourceFiles: SourceFile[] = [];
- for (const sourceFile of sourceFiles) {
- const originalSourceFile = getParseTreeNode(sourceFile, isSourceFile);
- if (originalSourceFile) {
- originalSourceFiles.push(originalSourceFile);
- }
+ export function getOriginalSourceFileOrBundle(sourceFileOrBundle: SourceFile | Bundle) {
+ if (sourceFileOrBundle.kind === SyntaxKind.Bundle) {
+ return updateBundle(sourceFileOrBundle, sameMap(sourceFileOrBundle.sourceFiles, getOriginalSourceFile));
}
+ return getOriginalSourceFile(sourceFileOrBundle);
+ }
- return originalSourceFiles;
+ function getOriginalSourceFile(sourceFile: SourceFile) {
+ return getParseTreeNode(sourceFile, isSourceFile) || sourceFile;
+ }
+
+ export function getOriginalSourceFiles(sourceFiles: SourceFile[]) {
+ return sameMap(sourceFiles, getOriginalSourceFile);
}
export function getOriginalNodeId(node: Node) {
@@ -2441,23 +2444,6 @@ namespace ts {
s;
}
- export interface EmitTextWriter {
- write(s: string): void;
- writeTextOfNode(text: string, node: Node): void;
- writeLine(): void;
- increaseIndent(): void;
- decreaseIndent(): void;
- getText(): string;
- rawWrite(s: string): void;
- writeLiteral(s: string): void;
- getTextPos(): number;
- getLine(): number;
- getColumn(): number;
- getIndent(): number;
- isAtStartOfLine(): boolean;
- reset(): void;
- }
-
const indentStrings: string[] = ["", " "];
export function getIndentString(level: number) {
if (indentStrings[level] === undefined) {
@@ -2640,7 +2626,7 @@ namespace ts {
* Else, calls `getSourceFilesToEmit` with the (optional) target source file to determine the list of source files to emit.
*/
export function forEachEmittedFile(
- host: EmitHost, action: (emitFileNames: EmitFileNames, sourceFiles: SourceFile[], isBundledEmit: boolean, emitOnlyDtsFiles: boolean) => void,
+ host: EmitHost, action: (emitFileNames: EmitFileNames, sourceFileOrBundle: SourceFile | Bundle, emitOnlyDtsFiles: boolean) => void,
sourceFilesOrTargetSourceFile?: SourceFile[] | SourceFile,
emitOnlyDtsFiles?: boolean) {
@@ -2651,7 +2637,7 @@ namespace ts {
const jsFilePath = options.outFile || options.out;
const sourceMapFilePath = getSourceMapFilePath(jsFilePath, options);
const declarationFilePath = options.declaration ? removeFileExtension(jsFilePath) + ".d.ts" : undefined;
- action({ jsFilePath, sourceMapFilePath, declarationFilePath }, sourceFiles, /*isBundledEmit*/true, emitOnlyDtsFiles);
+ action({ jsFilePath, sourceMapFilePath, declarationFilePath }, createBundle(sourceFiles), emitOnlyDtsFiles);
}
}
else {
@@ -2659,7 +2645,7 @@ namespace ts {
const jsFilePath = getOwnEmitOutputFilePath(sourceFile, host, getOutputExtension(sourceFile, options));
const sourceMapFilePath = getSourceMapFilePath(jsFilePath, options);
const declarationFilePath = !isSourceFileJavaScript(sourceFile) && (emitOnlyDtsFiles || options.declaration) ? getDeclarationEmitOutputFilePath(sourceFile, host) : undefined;
- action({ jsFilePath, sourceMapFilePath, declarationFilePath }, [sourceFile], /*isBundledEmit*/false, emitOnlyDtsFiles);
+ action({ jsFilePath, sourceMapFilePath, declarationFilePath }, sourceFile, emitOnlyDtsFiles);
}
}
}
@@ -3234,7 +3220,7 @@ namespace ts {
const carriageReturnLineFeed = "\r\n";
const lineFeed = "\n";
- export function getNewLineCharacter(options: CompilerOptions): string {
+ export function getNewLineCharacter(options: CompilerOptions | PrinterOptions): string {
if (options.newLine === NewLineKind.CarriageReturnLineFeed) {
return carriageReturnLineFeed;
}
diff --git a/src/harness/tsconfig.json b/src/harness/tsconfig.json
index ebd07118cc5..bea688d358b 100644
--- a/src/harness/tsconfig.json
+++ b/src/harness/tsconfig.json
@@ -118,6 +118,7 @@
"./unittests/initializeTSConfig.ts",
"./unittests/compileOnSave.ts",
"./unittests/typingsInstaller.ts",
- "./unittests/projectErrors.ts"
+ "./unittests/projectErrors.ts",
+ "./unittests/printer.ts"
]
}
diff --git a/src/harness/unittests/printer.ts b/src/harness/unittests/printer.ts
new file mode 100644
index 00000000000..2496a880bc5
--- /dev/null
+++ b/src/harness/unittests/printer.ts
@@ -0,0 +1,97 @@
+///
+///
+
+namespace ts {
+ describe("PrinterAPI", () => {
+ function makePrintsCorrectly(prefix: string) {
+ return function printsCorrectly(name: string, options: PrinterOptions, printCallback: (printer: Printer) => string) {
+ it(name, () => {
+ Harness.Baseline.runBaseline(`printerApi/${prefix}.${name}.js`, () =>
+ printCallback(createPrinter({ newLine: NewLineKind.CarriageReturnLineFeed, ...options })));
+ });
+ }
+ }
+
+ describe("printFile", () => {
+ const printsCorrectly = makePrintsCorrectly("printsFileCorrectly");
+ const sourceFile = createSourceFile("source.ts", `
+ interface A {
+ // comment1
+ readonly prop?: T;
+
+ // comment2
+ method(): void;
+
+ // comment3
+ new (): A;
+
+ // comment4
+ (): A;
+ }
+
+ // comment5
+ type B = number | string | object;
+ type C = A & { x: string; }; // comment6
+
+ // comment7
+ enum E1 {
+ // comment8
+ first
+ }
+
+ const enum E2 {
+ second
+ }
+
+ // comment9
+ console.log(1 + 2);
+ `, ScriptTarget.ES2015);
+
+ printsCorrectly("default", {}, printer => printer.printFile(sourceFile));
+ printsCorrectly("removeComments", { removeComments: true }, printer => printer.printFile(sourceFile));
+ });
+
+ describe("printBundle", () => {
+ const printsCorrectly = makePrintsCorrectly("printsBundleCorrectly");
+ const bundle = createBundle([
+ createSourceFile("a.ts", `
+ /*! [a.ts] */
+
+ // comment0
+ const a = 1;
+ `, ScriptTarget.ES2015),
+ createSourceFile("b.ts", `
+ /*! [b.ts] */
+
+ // comment1
+ const b = 2;
+ `, ScriptTarget.ES2015)
+ ]);
+ printsCorrectly("default", {}, printer => printer.printBundle(bundle));
+ printsCorrectly("removeComments", { removeComments: true }, printer => printer.printBundle(bundle));
+ });
+
+ describe("printNode", () => {
+ const printsCorrectly = makePrintsCorrectly("printsNodeCorrectly");
+ const sourceFile = createSourceFile("source.ts", "", ScriptTarget.ES2015);
+ const syntheticNode = createClassDeclaration(
+ undefined,
+ undefined,
+ /*name*/ createIdentifier("C"),
+ undefined,
+ undefined,
+ createNodeArray([
+ createProperty(
+ undefined,
+ createNodeArray([createToken(SyntaxKind.PublicKeyword)]),
+ createIdentifier("prop"),
+ undefined,
+ undefined,
+ undefined
+ )
+ ])
+ );
+ printsCorrectly("class", {}, printer => printer.printNode(EmitHint.Unspecified, syntheticNode, sourceFile));
+ });
+ });
+}
diff --git a/tests/baselines/reference/printerApi/printsBundleCorrectly.default.js b/tests/baselines/reference/printerApi/printsBundleCorrectly.default.js
new file mode 100644
index 00000000000..2d86cc0c09c
--- /dev/null
+++ b/tests/baselines/reference/printerApi/printsBundleCorrectly.default.js
@@ -0,0 +1,6 @@
+/*! [a.ts] */
+// comment0
+const a = 1;
+/*! [b.ts] */
+// comment1
+const b = 2;
diff --git a/tests/baselines/reference/printerApi/printsBundleCorrectly.removeComments.js b/tests/baselines/reference/printerApi/printsBundleCorrectly.removeComments.js
new file mode 100644
index 00000000000..22b03719cf3
--- /dev/null
+++ b/tests/baselines/reference/printerApi/printsBundleCorrectly.removeComments.js
@@ -0,0 +1,4 @@
+/*! [a.ts] */
+const a = 1;
+/*! [b.ts] */
+const b = 2;
diff --git a/tests/baselines/reference/printerApi/printsFileCorrectly.default.js b/tests/baselines/reference/printerApi/printsFileCorrectly.default.js
new file mode 100644
index 00000000000..9bff9d656b3
--- /dev/null
+++ b/tests/baselines/reference/printerApi/printsFileCorrectly.default.js
@@ -0,0 +1,25 @@
+interface A {
+ // comment1
+ readonly prop?: T;
+ // comment2
+ method(): void;
+ // comment3
+ new (): A;
+ // comment4
+ (): A;
+}
+// comment5
+type B = number | string | object;
+type C = A & {
+ x: string;
+}; // comment6
+// comment7
+enum E1 {
+ // comment8
+ first
+}
+const enum E2 {
+ second
+}
+// comment9
+console.log(1 + 2);
diff --git a/tests/baselines/reference/printerApi/printsFileCorrectly.removeComments.js b/tests/baselines/reference/printerApi/printsFileCorrectly.removeComments.js
new file mode 100644
index 00000000000..b511aff5e78
--- /dev/null
+++ b/tests/baselines/reference/printerApi/printsFileCorrectly.removeComments.js
@@ -0,0 +1,17 @@
+interface A {
+ readonly prop?: T;
+ method(): void;
+ new (): A;
+ (): A;
+}
+type B = number | string | object;
+type C = A & {
+ x: string;
+};
+enum E1 {
+ first
+}
+const enum E2 {
+ second
+}
+console.log(1 + 2);
diff --git a/tests/baselines/reference/printerApi/printsNodeCorrectly.class.js b/tests/baselines/reference/printerApi/printsNodeCorrectly.class.js
new file mode 100644
index 00000000000..fa19e836fed
--- /dev/null
+++ b/tests/baselines/reference/printerApi/printsNodeCorrectly.class.js
@@ -0,0 +1,3 @@
+class C {
+ public prop;
+}
\ No newline at end of file