diff --git a/Jakefile.js b/Jakefile.js
index 3dd9999aa8a..44cb1a61dbe 100644
--- a/Jakefile.js
+++ b/Jakefile.js
@@ -44,6 +44,8 @@ var compilerSources = [
"visitor.ts",
"transformer.ts",
"sourcemap.ts",
+ "comments.ts",
+ "printer.ts",
"declarationEmitter.ts",
"emitter.ts",
"program.ts",
@@ -67,6 +69,8 @@ var servicesSources = [
"visitor.ts",
"transformer.ts",
"sourcemap.ts",
+ "comments.ts",
+ "printer.ts",
"declarationEmitter.ts",
"emitter.ts",
"program.ts",
diff --git a/src/compiler/comments.ts b/src/compiler/comments.ts
new file mode 100644
index 00000000000..2b51e937fad
--- /dev/null
+++ b/src/compiler/comments.ts
@@ -0,0 +1,214 @@
+///
+
+/* @internal */
+namespace ts {
+ export interface CommentWriter {
+ reset(): void;
+ setSourceFile(sourceFile: SourceFile): void;
+ getLeadingCommentsToEmit(node: TextRange): CommentRange[];
+ getTrailingCommentsToEmit(node: TextRange): CommentRange[];
+ emitDetachedComments(node: TextRange): void;
+ emitLeadingComments(node: TextRange, comments?: CommentRange[]): void;
+ emitTrailingComments(node: TextRange, comments?: CommentRange[]): void;
+ }
+
+ export function createCommentWriter(host: EmitHost, writer: EmitTextWriter, sourceMap: SourceMapWriter): CommentWriter {
+ const compilerOptions = host.getCompilerOptions();
+ const newLine = host.getNewLine();
+ const { emitPos } = sourceMap;
+
+ let currentSourceFile: SourceFile;
+ let currentText: string;
+ let currentLineMap: number[];
+ let detachedCommentsInfo: { nodePos: number, detachedCommentEndPos: number}[];
+
+ // This maps start->end for a comment range. See `hasConsumedCommentRange` and
+ // `consumeCommentRange` for usage.
+ let consumedCommentRanges: number[];
+ let leadingCommentRangeNodeStarts: boolean[];
+ let trailingCommentRangeNodeEnds: boolean[];
+
+ return compilerOptions.removeComments
+ ? createCommentRemovingWriter()
+ : createCommentPreservingWriter();
+
+ function createCommentRemovingWriter(): CommentWriter {
+ return {
+ reset,
+ setSourceFile,
+ getLeadingCommentsToEmit(node: TextRange): CommentRange[] { return undefined; },
+ getTrailingCommentsToEmit(node: TextRange): CommentRange[] { return undefined; },
+ emitDetachedComments,
+ emitLeadingComments(node: TextRange, comments?: CommentRange[]): void { },
+ emitTrailingComments(node: TextRange, comments?: CommentRange[]): void { },
+ };
+
+ function emitDetachedComments(node: TextRange): void {
+ emitDetachedCommentsAndUpdateCommentsInfo(node, /*removeComments*/ true);
+ }
+ }
+
+ function createCommentPreservingWriter(): CommentWriter {
+ const noComments: CommentRange[] = [];
+ return {
+ reset,
+ setSourceFile,
+ getLeadingCommentsToEmit,
+ getTrailingCommentsToEmit,
+ emitDetachedComments,
+ emitLeadingComments,
+ emitTrailingComments,
+ };
+
+ function getLeadingCommentsToEmit(node: TextRange) {
+ if (nodeIsSynthesized(node)) {
+ return;
+ }
+
+ if (!leadingCommentRangeNodeStarts[node.pos]) {
+ leadingCommentRangeNodeStarts[node.pos] = true;
+ const comments = hasDetachedComments(node.pos)
+ ? getLeadingCommentsWithoutDetachedComments()
+ : getLeadingCommentRanges(currentText, node.pos);
+ return consumeCommentRanges(comments);
+ }
+
+ return noComments;
+ }
+
+ function getTrailingCommentsToEmit(node: TextRange) {
+ if (nodeIsSynthesized(node)) {
+ return;
+ }
+
+ if (!trailingCommentRangeNodeEnds[node.end]) {
+ trailingCommentRangeNodeEnds[node.end] = true;
+ const comments = getTrailingCommentRanges(currentText, node.end);
+ return consumeCommentRanges(comments);
+ }
+
+ return noComments;
+ }
+
+ function hasConsumedCommentRange(comment: CommentRange) {
+ return comment.end === consumedCommentRanges[comment.pos];
+ }
+
+ function consumeCommentRange(comment: CommentRange) {
+ if (!hasConsumedCommentRange(comment)) {
+ consumedCommentRanges[comment.pos] = comment.end;
+ return true;
+ }
+
+ return false;
+ }
+
+ function consumeCommentRanges(comments: CommentRange[]) {
+ let consumed: CommentRange[];
+ if (comments) {
+ let commentsSkipped = 0;
+ let commentsConsumed = 0;
+ for (let i = 0; i < comments.length; i++) {
+ const comment = comments[i];
+ if (consumeCommentRange(comment)) {
+ commentsConsumed++;
+ if (commentsSkipped !== 0) {
+ if (consumed === undefined) {
+ consumed = [comment];
+ }
+ else {
+ consumed.push(comment);
+ }
+ }
+ }
+ else {
+ commentsSkipped++;
+ if (commentsConsumed !== 0 && consumed === undefined) {
+ consumed = comments.slice(0, i);
+ }
+ }
+ }
+
+ if (commentsConsumed) {
+ return consumed || comments;
+ }
+ }
+
+ return noComments;
+ }
+
+ function emitLeadingComments(range: TextRange, leadingComments: CommentRange[] = getLeadingCommentsToEmit(range)) {
+ emitNewLineBeforeLeadingComments(currentLineMap, writer, range, leadingComments);
+
+ // Leading comments are emitted at /*leading comment1 */space/*leading comment*/space
+ emitComments(currentText, currentLineMap, writer, leadingComments, /*trailingSeparator*/ true, newLine, writeComment);
+ }
+
+ function emitTrailingComments(range: TextRange, trailingComments = getTrailingCommentsToEmit(range)) {
+ // trailing comments are emitted at space/*trailing comment1 */space/*trailing comment*/
+ emitComments(currentText, currentLineMap, writer, trailingComments, /*trailingSeparator*/ false, newLine, writeComment);
+ }
+
+ function emitDetachedComments(range: TextRange) {
+ emitDetachedCommentsAndUpdateCommentsInfo(range, /*removeComments*/ false);
+ }
+ }
+
+ function reset() {
+ currentSourceFile = undefined;
+ currentText = undefined;
+ currentLineMap = undefined;
+ detachedCommentsInfo = undefined;
+ consumedCommentRanges = undefined;
+ trailingCommentRangeNodeEnds = undefined;
+ leadingCommentRangeNodeStarts = undefined;
+ }
+
+ function setSourceFile(sourceFile: SourceFile) {
+ currentSourceFile = sourceFile;
+ currentText = sourceFile.text;
+ currentLineMap = getLineStarts(sourceFile);
+ detachedCommentsInfo = undefined;
+ consumedCommentRanges = [];
+ leadingCommentRangeNodeStarts = [];
+ trailingCommentRangeNodeEnds = [];
+ }
+
+ function hasDetachedComments(pos: number) {
+ return detachedCommentsInfo !== undefined && lastOrUndefined(detachedCommentsInfo).nodePos === pos;
+ }
+
+ function getLeadingCommentsWithoutDetachedComments() {
+ // get the leading comments from detachedPos
+ const pos = lastOrUndefined(detachedCommentsInfo).detachedCommentEndPos;
+ const leadingComments = getLeadingCommentRanges(currentText, pos);
+ if (detachedCommentsInfo.length - 1) {
+ detachedCommentsInfo.pop();
+ }
+ else {
+ detachedCommentsInfo = undefined;
+ }
+
+ return leadingComments;
+ }
+
+ function emitDetachedCommentsAndUpdateCommentsInfo(node: TextRange, removeComments: boolean) {
+ const currentDetachedCommentInfo = emitDetachedComments(currentText, currentLineMap, writer, writeComment, node, newLine, removeComments);
+
+ if (currentDetachedCommentInfo) {
+ if (detachedCommentsInfo) {
+ detachedCommentsInfo.push(currentDetachedCommentInfo);
+ }
+ else {
+ detachedCommentsInfo = [currentDetachedCommentInfo];
+ }
+ }
+ }
+
+ function writeComment(text: string, lineMap: number[], writer: EmitTextWriter, comment: CommentRange, newLine: string) {
+ emitPos(comment.pos);
+ writeCommentRange(text, lineMap, writer, comment, newLine);
+ emitPos(comment.end);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts
index f0e32bdf96c..4b1886fa848 100644
--- a/src/compiler/emitter.ts
+++ b/src/compiler/emitter.ts
@@ -895,7 +895,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
}
function emitLiteral(node: LiteralExpression | TemplateLiteralFragment) {
- const text = getLiteralText(node);
+ const text = getLiteralText(node, currentSourceFile, languageVersion);
if ((compilerOptions.sourceMap || compilerOptions.inlineSourceMap) && (node.kind === SyntaxKind.StringLiteral || isTemplateLiteralKind(node.kind))) {
writer.writeLiteral(text);
@@ -909,43 +909,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
}
}
- function getLiteralText(node: LiteralExpression | TemplateLiteralFragment) {
- // Any template literal or string literal with an extended escape
- // (e.g. "\u{0067}") will need to be downleveled as a escaped string literal.
- if (languageVersion < ScriptTarget.ES6 && (isTemplateLiteralKind(node.kind) || node.hasExtendedUnicodeEscape)) {
- return getQuotedEscapedLiteralText("\"", node.text, "\"");
- }
-
- // If we don't need to downlevel and we can reach the original source text using
- // the node's parent reference, then simply get the text as it was originally written.
- if (node.parent) {
- return getTextOfNodeFromSourceText(currentText, node);
- }
-
- // If we can't reach the original source text, use the canonical form if it's a number,
- // or an escaped quoted form of the original text if it's string-like.
- switch (node.kind) {
- case SyntaxKind.StringLiteral:
- return getQuotedEscapedLiteralText("\"", node.text, "\"");
- case SyntaxKind.NoSubstitutionTemplateLiteral:
- return getQuotedEscapedLiteralText("`", node.text, "`");
- case SyntaxKind.TemplateHead:
- return getQuotedEscapedLiteralText("`", node.text, "${");
- case SyntaxKind.TemplateMiddle:
- return getQuotedEscapedLiteralText("}", node.text, "${");
- case SyntaxKind.TemplateTail:
- return getQuotedEscapedLiteralText("}", node.text, "`");
- case SyntaxKind.NumericLiteral:
- return node.text;
- }
-
- Debug.fail(`Literal kind '${node.kind}' not accounted for.`);
- }
-
- function getQuotedEscapedLiteralText(leftQuote: string, text: string, rightQuote: string) {
- return leftQuote + escapeNonAsciiCharacters(escapeString(text)) + rightQuote;
- }
-
function emitDownlevelRawTemplateLiteral(node: LiteralExpression) {
// Find original source text, since we need to emit the raw strings of the tagged template.
// The raw strings contain the (escaped) strings of what the user wrote.
@@ -6590,7 +6553,8 @@ const _super = (function (geti, seti) {
}
const moduleName = getExternalModuleName(importNode);
if (moduleName.kind === SyntaxKind.StringLiteral) {
- return tryRenameExternalModule(moduleName) || getLiteralText(moduleName);
+ return tryRenameExternalModule(moduleName)
+ || getLiteralText(moduleName, currentSourceFile, languageVersion);
}
return undefined;
diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts
index ff051ce477e..0d225184a4b 100644
--- a/src/compiler/factory.ts
+++ b/src/compiler/factory.ts
@@ -144,14 +144,9 @@ namespace ts {
export function createLiteral(value: string): StringLiteral;
export function createLiteral(value: number): LiteralExpression;
- export function createLiteral(value: string | number | boolean | void): PrimaryExpression;
- export function createLiteral(value: string | number | boolean | void): T {
- if (typeof value === "string") {
- const node = createNode(SyntaxKind.StringLiteral);
- node.text = value;
- return node;
- }
- else if (typeof value === "number") {
+ export function createLiteral(value: string | number | boolean): PrimaryExpression;
+ export function createLiteral(value: string | number | boolean): T {
+ if (typeof value === "number") {
const node = createNode(SyntaxKind.NumericLiteral);
node.text = value.toString();
return node;
@@ -159,8 +154,10 @@ namespace ts {
else if (typeof value === "boolean") {
return createNode(value ? SyntaxKind.TrueKeyword : SyntaxKind.FalseKeyword);
}
- else if (value === null) {
- return createNode(SyntaxKind.NullKeyword);
+ else {
+ const node = createNode(SyntaxKind.StringLiteral);
+ node.text = String(value);
+ return node;
}
}
@@ -254,9 +251,6 @@ namespace ts {
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
return createLiteral(value);
}
- else if (value === null) {
- return createLiteral(null);
- }
else {
return value;
}
diff --git a/src/compiler/printer.ts b/src/compiler/printer.ts
new file mode 100644
index 00000000000..150ddb6a918
--- /dev/null
+++ b/src/compiler/printer.ts
@@ -0,0 +1,2421 @@
+///
+///
+///
+///
+///
+
+/* @internal */
+namespace ts {
+ const delimiters = createDelimiterMap();
+ const brackets = createBracketsMap();
+
+ // 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'
+ }
+
+ // targetSourceFile is when users only want one file in entire project to be emitted. This is used in compileOnSave feature
+ export function printFiles(resolver: EmitResolver, host: EmitHost, targetSourceFile: SourceFile): EmitResult {
+ // emit output for the __extends helper function
+ const extendsHelper = `
+var __extends = (this && this.__extends) || function (d, b) {
+ for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
+ function __() { this.constructor = d; }
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+};`;
+
+ // emit output for the __decorate helper function
+ const decorateHelper = `
+var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
+};`;
+
+ // emit output for the __metadata helper function
+ const metadataHelper = `
+var __metadata = (this && this.__metadata) || function (k, v) {
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
+};`;
+
+ // emit output for the __param helper function
+ const paramHelper = `
+var __param = (this && this.__param) || function (paramIndex, decorator) {
+ return function (target, key) { decorator(target, key, paramIndex); }
+};`;
+
+ // emit output for the __awaiter helper function
+ const awaiterHelper = `
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator.throw(value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments)).next());
+ });
+};`;
+
+ // emit output for the __export helper function
+ const exportStarHelper = `
+function __export(m) {
+ for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
+}`;
+
+ // emit output for the UMD helper function.
+ const umdHelper = `
+(function (dependencies, factory) {
+ if (typeof module === 'object' && typeof module.exports === 'object') {
+ var v = factory(require, exports); if (v !== undefined) module.exports = v;
+ }
+ else if (typeof define === 'function' && define.amd) {
+ define(dependencies, factory);
+ }
+})`;
+
+ const compilerOptions = host.getCompilerOptions();
+ const languageVersion = getLanguageVersion(compilerOptions);
+ const moduleKind = getModuleKind(compilerOptions);
+ const sourceMapDataList: SourceMapData[] = compilerOptions.sourceMap || compilerOptions.inlineSourceMap ? [] : undefined;
+ const emitterDiagnostics = createDiagnosticCollection();
+
+ let emitSkipped = false;
+ const newLine = host.getNewLine();
+ const printFile = createFilePrinter();
+ forEachExpectedEmitFile(host, emitFile, targetSourceFile);
+
+ return {
+ emitSkipped,
+ diagnostics: emitterDiagnostics.getDiagnostics(),
+ sourceMaps: sourceMapDataList
+ };
+
+ function emitFile({ jsFilePath, sourceMapFilePath, declarationFilePath}: EmitFileNames, sourceFiles: SourceFile[], isBundledEmit: boolean) {
+ // Make sure not to write js file and source map file if any of them cannot be written
+ if (!host.isEmitBlocked(jsFilePath) && !compilerOptions.noEmit) {
+ printFile(jsFilePath, sourceMapFilePath, sourceFiles, isBundledEmit);
+ }
+ else {
+ emitSkipped = true;
+ }
+
+ if (declarationFilePath) {
+ emitSkipped = writeDeclarationFile(declarationFilePath, sourceFiles, isBundledEmit, host, resolver, emitterDiagnostics) || emitSkipped;
+ }
+ }
+
+ function createFilePrinter() {
+ const transformers = getTransformers(compilerOptions).concat(initializePrinter);
+
+ const writer = createTextWriter(newLine);
+ const { write, writeTextOfNode, writeLine, increaseIndent, decreaseIndent } = writer;
+
+ const sourceMap = compilerOptions.sourceMap || compilerOptions.inlineSourceMap ? createSourceMapWriter(host, writer) : getNullSourceMapWriter();
+ const { emitStart, emitEnd, emitPos } = sourceMap;
+
+ const comments = createCommentWriter(host, writer, sourceMap);
+ const { emitDetachedComments, emitLeadingComments, emitTrailingComments, getLeadingCommentsToEmit, getTrailingCommentsToEmit } = comments;
+
+ let context: TransformationContext;
+ let startLexicalEnvironment: () => void;
+ let endLexicalEnvironment: () => Statement[];
+ let getNodeEmitFlags: (node: Node) => NodeEmitFlags;
+ let expressionSubstitution: (node: Expression) => Expression;
+ let identifierSubstitution: (node: Identifier) => Identifier;
+ let isUniqueName: (name: string) => boolean;
+ let temporaryVariables: string[] = [];
+ let tempFlags: TempFlags;
+ let currentSourceFile: SourceFile;
+ let currentText: string;
+ let extendsEmitted: boolean;
+ let decorateEmitted: boolean;
+ let paramEmitted: boolean;
+ let awaiterEmitted: boolean;
+ let isOwnFileEmit: boolean;
+
+ return doPrint;
+
+ function doPrint(jsFilePath: string, sourceMapFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean) {
+ sourceMap.initialize(jsFilePath, sourceMapFilePath, sourceFiles, isBundledEmit);
+ isOwnFileEmit = !isBundledEmit;
+
+ // Emit helpers from all the files
+ if (isBundledEmit && moduleKind) {
+ forEach(sourceFiles, emitEmitHelpers);
+ }
+
+ // Transform and print the source files
+ transformFiles(resolver, host, sourceFiles, transformers);
+
+ writeLine();
+
+ const sourceMappingURL = sourceMap.getSourceMappingURL();
+ if (sourceMappingURL) {
+ write(`//# sourceMappingURL=${sourceMappingURL}`);
+ }
+
+ // Write the source map
+ if (compilerOptions.sourceMap && !compilerOptions.inlineSourceMap) {
+ writeFile(host, emitterDiagnostics, sourceMapFilePath, sourceMap.getText(), compilerOptions.emitBOM);
+ }
+
+ // Record source map data for the test harness.
+ if (sourceMapDataList) {
+ sourceMapDataList.push(sourceMap.getSourceMapData());
+ }
+
+ // Write the output file
+ writeFile(host, emitterDiagnostics, jsFilePath, writer.getText(), compilerOptions.emitBOM);
+
+ // Reset state
+ sourceMap.reset();
+ comments.reset();
+ writer.reset();
+
+ startLexicalEnvironment = undefined;
+ endLexicalEnvironment = undefined;
+ getNodeEmitFlags = undefined;
+ expressionSubstitution = undefined;
+ identifierSubstitution = undefined;
+ isUniqueName = undefined;
+ temporaryVariables = undefined;
+ tempFlags = 0;
+ currentSourceFile = undefined;
+ currentText = undefined;
+ extendsEmitted = false;
+ decorateEmitted = false;
+ paramEmitted = false;
+ awaiterEmitted = false;
+ isOwnFileEmit = false;
+ }
+
+ function initializePrinter(_context: TransformationContext) {
+ context = _context;
+ startLexicalEnvironment = context.startLexicalEnvironment;
+ endLexicalEnvironment = context.endLexicalEnvironment;
+ getNodeEmitFlags = context.getNodeEmitFlags;
+ expressionSubstitution = context.expressionSubstitution;
+ identifierSubstitution = context.identifierSubstitution;
+ isUniqueName = context.isUniqueName;
+ return printSourceFile;
+ }
+
+ function printSourceFile(node: SourceFile) {
+ currentSourceFile = node;
+ currentText = node.text;
+ sourceMap.setSourceFile(node);
+ comments.setSourceFile(node);
+ emitWorker(node);
+ return node;
+ }
+
+ function emit(node: Node) {
+ if (node) {
+ const leadingComments = getLeadingCommentsToEmit(node);
+ const trailingComments = getTrailingCommentsToEmit(node);
+ emitLeadingComments(node, leadingComments);
+ emitStart(node);
+ emitWorker(node);
+ emitEnd(node);
+ emitTrailingComments(node, trailingComments);
+ }
+ }
+
+ function emitWorker(node: Node) {
+ const kind = node.kind;
+ switch (kind) {
+ // Pseudo-literals
+ case SyntaxKind.TemplateHead:
+ case SyntaxKind.TemplateMiddle:
+ case SyntaxKind.TemplateTail:
+ return emitLiteral(node);
+
+ // Identifiers
+ case SyntaxKind.Identifier:
+ return emitIdentifier(node, identifierSubstitution);
+
+ // 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.AnyKeyword:
+ case SyntaxKind.AsyncKeyword:
+ case SyntaxKind.BooleanKeyword:
+ case SyntaxKind.DeclareKeyword:
+ case SyntaxKind.NumberKeyword:
+ case SyntaxKind.ReadonlyKeyword:
+ case SyntaxKind.StringKeyword:
+ case SyntaxKind.SymbolKeyword:
+ case SyntaxKind.GlobalKeyword:
+ return writeTokenNode(node);
+
+ // Parse tree nodes
+
+ // Names
+ case SyntaxKind.QualifiedName:
+ return emitQualifiedName(node);
+ case SyntaxKind.ComputedPropertyName:
+ return emitComputedPropertyName(node);
+
+ // Signature elements
+ case SyntaxKind.TypeParameter:
+ return emitTypeParameter(node);
+ case SyntaxKind.Parameter:
+ return emitParameter(node);
+ case SyntaxKind.Decorator:
+ return emitDecorator(node);
+
+ // Type members
+ case SyntaxKind.PropertySignature:
+ return emitPropertySignature(node);
+ case SyntaxKind.PropertyDeclaration:
+ return emitPropertyDeclaration(node);
+ case SyntaxKind.MethodSignature:
+ return emitMethodSignature(node);
+ case SyntaxKind.MethodDeclaration:
+ return emitMethodDeclaration(node);
+ case SyntaxKind.Constructor:
+ return emitConstructor(node);
+ case SyntaxKind.GetAccessor:
+ case SyntaxKind.SetAccessor:
+ return emitAccessorDeclaration(node);
+ case SyntaxKind.CallSignature:
+ return emitCallSignature(node);
+ case SyntaxKind.ConstructSignature:
+ return emitConstructSignature(node);
+ case SyntaxKind.IndexSignature:
+ return emitIndexSignature(node);
+
+ // Types
+ case SyntaxKind.TypePredicate:
+ return emitTypePredicate(node);
+ case SyntaxKind.TypeReference:
+ return emitTypeReference(node);
+ case SyntaxKind.FunctionType:
+ return emitFunctionType(node);
+ case SyntaxKind.ConstructorType:
+ return emitConstructorType(node);
+ case SyntaxKind.TypeQuery:
+ return emitTypeQuery(node);
+ case SyntaxKind.TypeLiteral:
+ return emitTypeLiteral(node);
+ case SyntaxKind.ArrayType:
+ return emitArrayType(node);
+ case SyntaxKind.TupleType:
+ return emitTupleType(node);
+ case SyntaxKind.UnionType:
+ return emitUnionType(node);
+ case SyntaxKind.IntersectionType:
+ return emitIntersectionType(node);
+ case SyntaxKind.ParenthesizedType:
+ return emitParenthesizedType(node);
+ case SyntaxKind.ExpressionWithTypeArguments:
+ return emitExpressionWithTypeArguments(node);
+ case SyntaxKind.ThisType:
+ return write("this");
+ case SyntaxKind.StringLiteralType:
+ return emitLiteral(node);
+
+ // Binding patterns
+ case SyntaxKind.ObjectBindingPattern:
+ return emitObjectBindingPattern(node);
+ case SyntaxKind.ArrayBindingPattern:
+ return emitArrayBindingPattern(node);
+ case SyntaxKind.BindingElement:
+ return emitBindingElement(node);
+
+ // Misc
+ case SyntaxKind.TemplateSpan:
+ return emitTemplateSpan(node);
+ case SyntaxKind.SemicolonClassElement:
+ return write(";");
+
+ // Statements
+ case SyntaxKind.Block:
+ return emitBlock(node);
+ case SyntaxKind.VariableStatement:
+ return emitVariableStatement(node);
+ case SyntaxKind.EmptyStatement:
+ return write(";");
+ case SyntaxKind.ExpressionStatement:
+ return emitExpressionStatement(node);
+ case SyntaxKind.IfStatement:
+ return emitIfStatement(node);
+ case SyntaxKind.DoStatement:
+ return emitDoStatement(node);
+ case SyntaxKind.WhileStatement:
+ return emitWhileStatement(node);
+ case SyntaxKind.ForStatement:
+ return emitForStatement(node);
+ case SyntaxKind.ForInStatement:
+ return emitForInStatement(node);
+ case SyntaxKind.ForOfStatement:
+ return emitForOfStatement(node);
+ case SyntaxKind.ContinueStatement:
+ return emitContinueStatement(node);
+ case SyntaxKind.BreakStatement:
+ return emitBreakStatement(node);
+ case SyntaxKind.ReturnStatement:
+ return emitReturnStatement(node);
+ case SyntaxKind.WithStatement:
+ return emitWithStatement(node);
+ case SyntaxKind.SwitchStatement:
+ return emitSwitchStatement(node);
+ case SyntaxKind.LabeledStatement:
+ return emitLabeledStatement(node);
+ case SyntaxKind.ThrowStatement:
+ return emitThrowStatement(node);
+ case SyntaxKind.TryStatement:
+ return emitTryStatement(node);
+ case SyntaxKind.DebuggerStatement:
+ return emitDebuggerStatement(node);
+
+ // Declarations
+ case SyntaxKind.VariableDeclaration:
+ return emitVariableDeclaration(node);
+ case SyntaxKind.VariableDeclarationList:
+ return emitVariableDeclarationList(node);
+ case SyntaxKind.FunctionDeclaration:
+ return emitFunctionDeclaration(node);
+ case SyntaxKind.ClassDeclaration:
+ return emitClassDeclaration(node);
+ case SyntaxKind.InterfaceDeclaration:
+ return emitInterfaceDeclaration(node);
+ case SyntaxKind.TypeAliasDeclaration:
+ return emitTypeAliasDeclaration(node);
+ case SyntaxKind.EnumDeclaration:
+ return emitEnumDeclaration(node);
+ case SyntaxKind.ModuleDeclaration:
+ return emitModuleDeclaration(node);
+ case SyntaxKind.ModuleBlock:
+ return emitModuleBlock(node);
+ case SyntaxKind.CaseBlock:
+ return emitCaseBlock(node);
+ case SyntaxKind.ImportEqualsDeclaration:
+ return emitImportEqualsDeclaration(node);
+ case SyntaxKind.ImportDeclaration:
+ return emitImportDeclaration(node);
+ case SyntaxKind.ImportClause:
+ return emitImportClause(node);
+ case SyntaxKind.NamespaceImport:
+ return emitNamespaceImport(node);
+ case SyntaxKind.NamedImports:
+ return emitNamedImports(node);
+ case SyntaxKind.ImportSpecifier:
+ return emitImportSpecifier(node);
+ case SyntaxKind.ExportAssignment:
+ return emitExportAssignment(node);
+ case SyntaxKind.ExportDeclaration:
+ return emitExportDeclaration(node);
+ case SyntaxKind.NamedExports:
+ return emitNamedExports(node);
+ case SyntaxKind.ExportSpecifier:
+ return emitExportSpecifier(node);
+ case SyntaxKind.MissingDeclaration:
+ return;
+
+ // Module references
+ case SyntaxKind.ExternalModuleReference:
+ return emitExternalModuleReference(node);
+
+ // JSX (non-expression)
+ case SyntaxKind.JsxText:
+ return emitJsxText(node);
+ case SyntaxKind.JsxClosingElement:
+ return emitJsxClosingElement(node);
+ case SyntaxKind.JsxAttribute:
+ return emitJsxAttribute(node);
+ case SyntaxKind.JsxSpreadAttribute:
+ return emitJsxSpreadAttribute(node);
+
+ // Clauses
+ case SyntaxKind.CaseClause:
+ return emitCaseClause(node);
+ case SyntaxKind.DefaultClause:
+ return emitDefaultClause(node);
+ case SyntaxKind.HeritageClause:
+ return emitHeritageClause(node);
+ case SyntaxKind.CatchClause:
+ return emitCatchClause(node);
+
+ // Property assignments
+ case SyntaxKind.PropertyAssignment:
+ return emitPropertyAssignment(node);
+ case SyntaxKind.ShorthandPropertyAssignment:
+ return emitShorthandPropertyAssignment(node);
+
+ // Enum
+ case SyntaxKind.EnumMember:
+ return emitEnumMember(node);
+
+ // Top-level nodes
+ case SyntaxKind.SourceFile:
+ return emitSourceFile(node);
+
+ // JSDoc nodes (ignored)
+ }
+
+ if (isExpressionKind(kind)) {
+ return emitExpressionWorker(node);
+ }
+ }
+
+ function emitExpression(node: Expression) {
+ if (node) {
+ const leadingComments = getLeadingCommentsToEmit(node);
+ const trailingComments = getTrailingCommentsToEmit(node);
+ emitLeadingComments(node, leadingComments);
+ emitStart(node);
+ emitExpressionWorker(node);
+ emitEnd(node);
+ emitTrailingComments(node, trailingComments);
+ }
+ }
+
+ function emitExpressionWorker(node: Node) {
+ const kind = node.kind;
+ switch (kind) {
+ // Literals
+ case SyntaxKind.NumericLiteral:
+ case SyntaxKind.StringLiteral:
+ case SyntaxKind.RegularExpressionLiteral:
+ case SyntaxKind.NoSubstitutionTemplateLiteral:
+ return emitLiteral(node);
+
+ // Identifiers
+ case SyntaxKind.Identifier:
+ return emitIdentifier(node, expressionSubstitution);
+
+ // Reserved words
+ case SyntaxKind.FalseKeyword:
+ case SyntaxKind.NullKeyword:
+ case SyntaxKind.SuperKeyword:
+ case SyntaxKind.TrueKeyword:
+ return writeTokenNode(node);
+ case SyntaxKind.ThisKeyword:
+ return emitThisKeyword(node);
+
+ // Expressions
+ case SyntaxKind.ArrayLiteralExpression:
+ return emitArrayLiteralExpression(node);
+ case SyntaxKind.ObjectLiteralExpression:
+ return emitObjectLiteralExpression(node);
+ case SyntaxKind.PropertyAccessExpression:
+ return emitPropertyAccessExpression(node);
+ case SyntaxKind.ElementAccessExpression:
+ return emitElementAccessExpression(node);
+ case SyntaxKind.CallExpression:
+ return emitCallExpression(node);
+ case SyntaxKind.NewExpression:
+ return emitNewExpression(node);
+ case SyntaxKind.TaggedTemplateExpression:
+ return emitTaggedTemplateExpression(node);
+ case SyntaxKind.TypeAssertionExpression:
+ return emitTypeAssertionExpression(node);
+ case SyntaxKind.ParenthesizedExpression:
+ return emitParenthesizedExpression(node);
+ case SyntaxKind.FunctionExpression:
+ return emitFunctionExpression(node);
+ case SyntaxKind.ArrowFunction:
+ return emitArrowFunction(node);
+ case SyntaxKind.DeleteExpression:
+ return emitDeleteExpression(node);
+ case SyntaxKind.TypeOfExpression:
+ return emitTypeOfExpression(node);
+ case SyntaxKind.VoidExpression:
+ return emitVoidExpression(node);
+ case SyntaxKind.AwaitExpression:
+ return emitAwaitExpression(node);
+ case SyntaxKind.PrefixUnaryExpression:
+ return emitPrefixUnaryExpression(node);
+ case SyntaxKind.PostfixUnaryExpression:
+ return emitPostfixUnaryExpression(node);
+ case SyntaxKind.BinaryExpression:
+ return emitBinaryExpression(node);
+ case SyntaxKind.ConditionalExpression:
+ return emitConditionalExpression(node);
+ case SyntaxKind.TemplateExpression:
+ return emitTemplateExpression(node);
+ case SyntaxKind.YieldExpression:
+ return emitYieldExpression(node);
+ case SyntaxKind.SpreadElementExpression:
+ return emitSpreadElementExpression(node);
+ case SyntaxKind.ClassExpression:
+ return emitClassExpression(node);
+ case SyntaxKind.OmittedExpression:
+ return;
+ case SyntaxKind.AsExpression:
+ return emitAsExpression(node);
+
+ // JSX
+ case SyntaxKind.JsxElement:
+ return emitJsxElement(node);
+ case SyntaxKind.JsxSelfClosingElement:
+ return emitJsxSelfClosingElement(node);
+ case SyntaxKind.JsxOpeningElement:
+ return emitJsxOpeningElement(node);
+ case SyntaxKind.JsxExpression:
+ return emitJsxExpression(node);
+ }
+ }
+
+ //
+ // Literals/Pseudo-literals
+ //
+
+ // SyntaxKind.NumericLiteral
+ // SyntaxKind.StringLiteral
+ // SyntaxKind.RegularExpressionLiteral
+ // SyntaxKind.NoSubstitutionTemplateLiteral
+ // SyntaxKind.TemplateHead
+ // SyntaxKind.TemplateMiddle
+ // SyntaxKind.TemplateTail
+ function emitLiteral(node: LiteralLikeNode) {
+ const text = getLiteralText(node, currentSourceFile, languageVersion);
+ if ((compilerOptions.sourceMap || compilerOptions.inlineSourceMap)
+ && (node.kind === SyntaxKind.StringLiteral || isTemplateLiteralKind(node.kind))) {
+ writer.writeLiteral(text);
+ }
+ else {
+ write(text);
+ }
+ }
+
+ //
+ // Identifiers
+ //
+
+ function emitIdentifier(node: Identifier, substitution: (node: Node) => Node) {
+ if (tryEmitSubstitute(node, substitution)) {
+ return;
+ }
+ else if (node.text === undefined) {
+ // Emit a temporary variable name for this node.
+ const nodeId = getOriginalNodeId(node);
+ const text = temporaryVariables[nodeId] || (temporaryVariables[nodeId] = makeTempVariableName(tempKindToFlags(node.tempKind)));
+ write(text);
+ }
+ else if (nodeIsSynthesized(node)) {
+ if (getNodeEmitFlags(node) & NodeEmitFlags.UMDDefine) {
+ writeLines(umdHelper);
+ }
+ else {
+ write(node.text);
+ }
+ }
+ else {
+ writeTextOfNode(currentText, node);
+ }
+ }
+
+ function emitThisKeyword(node: PrimaryExpression) {
+ if (tryEmitSubstitute(node, expressionSubstitution)) {
+ return;
+ }
+ else {
+ writeTokenNode(node);
+ }
+ }
+
+ //
+ // Names
+ //
+
+ function emitQualifiedName(node: QualifiedName) {
+ emitEntityName(node.left);
+ write(".");
+ emit(node.right);
+ }
+
+ function emitEntityName(node: EntityName) {
+ if (node.kind === SyntaxKind.Identifier) {
+ emitExpression(node);
+ }
+ else {
+ emit(node);
+ }
+ }
+
+ function emitComputedPropertyName(node: ComputedPropertyName) {
+ write("[");
+ emitExpression(node.expression);
+ write("]");
+ }
+
+ //
+ // Signature elements
+ //
+
+ function emitTypeParameter(node: TypeParameterDeclaration) {
+ emit(node.name);
+ emitWithPrefix(" extends ", node.constraint);
+ }
+
+ function emitParameter(node: ParameterDeclaration) {
+ emitDecorators(node, node.decorators);
+ emitModifiers(node, node.modifiers);
+ writeIfPresent(node.dotDotDotToken, "...");
+ emit(node.name);
+ writeIfPresent(node.questionToken, "?");
+ emitExpressionWithPrefix(" = ", node.initializer);
+ emitWithPrefix(": ", node.type);
+ }
+
+ function emitDecorator(decorator: Decorator) {
+ write("@");
+ emitExpression(decorator.expression);
+ }
+
+ //
+ // Type members
+ //
+
+ function emitPropertySignature(node: PropertySignature) {
+ emitDecorators(node, node.decorators);
+ emitModifiers(node, node.modifiers);
+ emit(node.name);
+ writeIfPresent(node.questionToken, "?");
+ emitWithPrefix(": ", node.type);
+ write(";");
+ }
+
+ function emitPropertyDeclaration(node: PropertyDeclaration) {
+ emitDecorators(node, node.decorators);
+ emitModifiers(node, node.modifiers);
+ emit(node.name);
+ emitWithPrefix(": ", node.type);
+ emitExpressionWithPrefix(" = ", node.initializer);
+ write(";");
+ }
+
+ function emitMethodSignature(node: MethodSignature) {
+ emitDecorators(node, node.decorators);
+ emitModifiers(node, node.modifiers);
+ emit(node.name);
+ writeIfPresent(node.questionToken, "?");
+ emitTypeParameters(node, node.typeParameters);
+ emitParameters(node, node.parameters);
+ emitWithPrefix(": ", node.type);
+ write(";");
+ }
+
+ function emitMethodDeclaration(node: MethodDeclaration) {
+ emitDecorators(node, node.decorators);
+ emitModifiers(node, node.modifiers);
+ writeIfPresent(node.asteriskToken, "*");
+ emit(node.name);
+ emitSignatureAndBody(node);
+ }
+
+ function emitConstructor(node: ConstructorDeclaration) {
+ emitModifiers(node, node.modifiers);
+ write("constructor");
+ emitSignatureAndBody(node);
+ }
+
+ function emitAccessorDeclaration(node: AccessorDeclaration) {
+ emitDecorators(node, node.decorators);
+ emitModifiers(node, node.modifiers);
+ write(node.kind === SyntaxKind.GetAccessor ? "get " : "set ");
+ emit(node.name);
+ emitSignatureAndBody(node);
+ }
+
+ function emitCallSignature(node: CallSignatureDeclaration) {
+ emitDecorators(node, node.decorators);
+ emitModifiers(node, node.modifiers);
+ emitTypeParameters(node, node.typeParameters);
+ emitParameters(node, node.parameters);
+ emitWithPrefix(": ", node.type);
+ write(";");
+ }
+
+ function emitConstructSignature(node: ConstructSignatureDeclaration) {
+ emitDecorators(node, node.decorators);
+ emitModifiers(node, node.modifiers);
+ write("new ");
+ emitTypeParameters(node, node.typeParameters);
+ emitParameters(node, node.parameters);
+ emitWithPrefix(": ", node.type);
+ write(";");
+ }
+
+ function emitIndexSignature(node: IndexSignatureDeclaration) {
+ emitDecorators(node, node.decorators);
+ emitModifiers(node, node.modifiers);
+ emitParametersForIndexSignature(node, node.parameters);
+ emitWithPrefix(": ", node.type);
+ write(";");
+ }
+
+ //
+ // Types
+ //
+
+ function emitTypePredicate(node: TypePredicateNode) {
+ emit(node.parameterName);
+ write(" is ");
+ emit(node.type);
+ }
+
+ function emitTypeReference(node: TypeReferenceNode) {
+ emit(node.typeName);
+ emitTypeArguments(node, node.typeArguments);
+ }
+
+ function emitFunctionType(node: FunctionTypeNode) {
+ emitTypeParameters(node, node.typeParameters);
+ emitParametersForArrow(node, node.parameters);
+ write(" => ");
+ emit(node.type);
+ }
+
+ function emitConstructorType(node: ConstructorTypeNode) {
+ write("new ");
+ emitTypeParameters(node, node.typeParameters);
+ emitParametersForArrow(node, node.parameters);
+ write(" => ");
+ emit(node.type);
+ }
+
+ function emitTypeQuery(node: TypeQueryNode) {
+ write("typeof ");
+ emit(node.exprName);
+ }
+
+ function emitTypeLiteral(node: TypeLiteralNode) {
+ write("{");
+ emitList(node, node.members, ListFormat.TypeLiteralMembers);
+ write("}");
+ }
+
+ function emitArrayType(node: ArrayTypeNode) {
+ emit(node.elementType);
+ write("[]");
+ }
+
+ function emitTupleType(node: TupleTypeNode) {
+ write("[");
+ emitList(node, node.elementTypes, ListFormat.TupleTypeElements);
+ write("]");
+ }
+
+ function emitUnionType(node: UnionTypeNode) {
+ emitList(node, node.types, ListFormat.UnionTypeConstituents);
+ }
+
+ function emitIntersectionType(node: IntersectionTypeNode) {
+ emitList(node, node.types, ListFormat.IntersectionTypeConstituents);
+ }
+
+ function emitParenthesizedType(node: ParenthesizedTypeNode) {
+ write("(");
+ emit(node.type);
+ write(")");
+ }
+
+ //
+ // Binding patterns
+ //
+
+ function emitObjectBindingPattern(node: ObjectBindingPattern) {
+ const elements = node.elements;
+ if (elements.length === 0) {
+ write("{}");
+ }
+ else {
+ write("{");
+ emitList(node, elements, ListFormat.ObjectBindingPatternElements);
+ write("}");
+ }
+ }
+
+ function emitArrayBindingPattern(node: ArrayBindingPattern) {
+ const elements = node.elements;
+ if (elements.length === 0) {
+ write("[]");
+ }
+ else {
+ write("[");
+ emitList(node, node.elements, ListFormat.ArrayBindingPatternElements);
+ write("]");
+ }
+ }
+
+ function emitBindingElement(node: BindingElement) {
+ emitWithSuffix(node.propertyName, ": ");
+ writeIfPresent(node.dotDotDotToken, "...");
+ emit(node.name);
+ emitExpressionWithPrefix(" = ", node.initializer);
+ }
+
+ //
+ // Expressions
+ //
+
+ function emitArrayLiteralExpression(node: ArrayLiteralExpression) {
+ const elements = node.elements;
+ if (elements.length === 0) {
+ write("[]");
+ }
+ else {
+ const preferNewLine = getNodeEmitFlags(node) & NodeEmitFlags.MultiLine ? ListFormat.PreferNewLine : ListFormat.None;
+ emitExpressionList(node, elements, ListFormat.ArrayLiteralExpressionElements | preferNewLine);
+ }
+ }
+
+ function emitObjectLiteralExpression(node: ObjectLiteralExpression) {
+ const properties = node.properties;
+ if (properties.length === 0) {
+ write("{}");
+ }
+ else {
+ const preferNewLine = getNodeEmitFlags(node) & NodeEmitFlags.MultiLine ? ListFormat.PreferNewLine : ListFormat.None;
+ const allowTrailingComma = languageVersion >= ScriptTarget.ES5 ? ListFormat.AllowTrailingComma : ListFormat.None;
+ emitList(node, properties, ListFormat.ObjectLiteralExpressionProperties | allowTrailingComma | preferNewLine);
+ }
+ }
+
+ function emitPropertyAccessExpression(node: PropertyAccessExpression) {
+ if (tryEmitConstantValue(node)) {
+ return;
+ }
+
+ const indentBeforeDot = needsIndentation(node, node.expression, node.dotToken);
+ const indentAfterDot = needsIndentation(node, node.dotToken, node.name);
+ const shouldEmitDotDot = !indentBeforeDot && needsDotDotForPropertyAccess(node.expression);
+
+ emitExpression(node.expression);
+ increaseIndentIf(indentBeforeDot);
+ write(shouldEmitDotDot ? ".." : ".");
+ increaseIndentIf(indentAfterDot);
+ 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 needsDotDotForPropertyAccess(expression: Expression) {
+ if (expression.kind === SyntaxKind.NumericLiteral) {
+ // check if numeric literal was originally written with a dot
+ const text = getLiteralText(expression, currentSourceFile, languageVersion);
+ return text.indexOf(tokenToString(SyntaxKind.DotToken)) < 0;
+ }
+ else {
+ // check if constant enum value is integer
+ const constantValue = tryGetConstEnumValue(expression);
+ // isFinite handles cases when constantValue is undefined
+ return isFinite(constantValue) && Math.floor(constantValue) === constantValue;
+ }
+ }
+
+ function emitElementAccessExpression(node: ElementAccessExpression) {
+ if (tryEmitConstantValue(node)) {
+ return;
+ }
+
+ emitExpression(node.expression);
+ write("[");
+ emitExpression(node.argumentExpression);
+ write("]");
+ }
+
+ function emitCallExpression(node: CallExpression) {
+ emitExpression(node.expression);
+ emitExpressionList(node, node.arguments, ListFormat.CallExpressionArguments);
+ }
+
+ function emitNewExpression(node: NewExpression) {
+ write("new ");
+ emitExpression(node.expression);
+ if (node.arguments) {
+ emitExpressionList(node, node.arguments, ListFormat.NewExpressionArguments);
+ }
+ }
+
+ function emitTaggedTemplateExpression(node: TaggedTemplateExpression) {
+ emitExpression(node.tag);
+ emitExpression(node.template);
+ }
+
+ function emitTypeAssertionExpression(node: TypeAssertion) {
+ if (node.type) {
+ write("<");
+ emit(node.type);
+ write(">");
+ }
+
+ emitExpression(node.expression);
+ }
+
+ function emitParenthesizedExpression(node: ParenthesizedExpression) {
+ write("(");
+ emitExpression(node.expression);
+ write(")");
+ }
+
+ function emitFunctionExpression(node: FunctionExpression) {
+ emitFunctionDeclarationOrExpression(node);
+ }
+
+ function emitArrowFunction(node: ArrowFunction) {
+ emitDecorators(node, node.decorators);
+ emitModifiers(node, node.modifiers);
+ const body = node.body;
+ if (isBlock(body)) {
+ const savedTempFlags = tempFlags;
+ tempFlags = 0;
+ startLexicalEnvironment();
+ emitArrowFunctionHead(node);
+ write(" {");
+
+ const startingLine = writer.getLine();
+ emitBlockFunctionBody(node, body);
+
+ const endingLine = writer.getLine();
+ emitLexicalEnvironment(endLexicalEnvironment(), /*newLine*/ startingLine !== endingLine);
+ tempFlags = savedTempFlags;
+ write("}");
+ }
+ else {
+ emitArrowFunctionHead(node);
+ write(" ");
+ emitExpression(body);
+ }
+ }
+
+ function emitArrowFunctionHead(node: ArrowFunction) {
+ emitTypeParameters(node, node.typeParameters);
+ emitParametersForArrow(node, node.parameters);
+ emitWithPrefix(": ", node.type);
+ write(" =>");
+ }
+
+ function emitDeleteExpression(node: DeleteExpression) {
+ write("delete ");
+ emitExpression(node.expression);
+ }
+
+ function emitTypeOfExpression(node: TypeOfExpression) {
+ write("typeof ");
+ emitExpression(node.expression);
+ }
+
+ function emitVoidExpression(node: VoidExpression) {
+ write("void ");
+ emitExpression(node.expression);
+ }
+
+ function emitAwaitExpression(node: AwaitExpression) {
+ write("await ");
+ emitExpression(node.expression);
+ }
+
+ function emitPrefixUnaryExpression(node: PrefixUnaryExpression) {
+ writeToken(node.operator);
+ if (shouldEmitWhitespaceBeforeOperand(node)) {
+ write(" ");
+ }
+ 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 && ((operand).operator === SyntaxKind.PlusToken || (operand).operator === SyntaxKind.PlusPlusToken))
+ || (node.operator === SyntaxKind.MinusToken && ((operand).operator === SyntaxKind.MinusToken || (operand).operator === SyntaxKind.MinusMinusToken)));
+ }
+
+ function emitPostfixUnaryExpression(node: PostfixUnaryExpression) {
+ if (tryEmitSubstitute(node, expressionSubstitution)) {
+ return;
+ }
+
+ emitExpression(node.operand);
+ writeToken(node.operator);
+ }
+
+ function emitBinaryExpression(node: BinaryExpression) {
+ if (tryEmitSubstitute(node, expressionSubstitution)) {
+ return;
+ }
+
+ 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 ? " " : undefined);
+ writeTokenNode(node.operatorToken);
+ increaseIndentIf(indentAfterOperator, " ");
+ 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, " ");
+ write("?");
+ increaseIndentIf(indentAfterQuestion, " ");
+ emitExpression(node.whenTrue);
+ decreaseIndentIf(indentBeforeQuestion, indentAfterQuestion);
+
+ increaseIndentIf(indentBeforeColon, " ");
+ write(":");
+ increaseIndentIf(indentAfterColon, " ");
+ emitExpression(node.whenFalse);
+ decreaseIndentIf(indentBeforeColon, indentAfterColon);
+ }
+
+ function emitTemplateExpression(node: TemplateExpression) {
+ emit(node.head);
+ emitList(node, node.templateSpans, ListFormat.TemplateExpressionSpans);
+ }
+
+ function emitYieldExpression(node: YieldExpression) {
+ write(node.asteriskToken ? "yield*" : "yield");
+ emitExpressionWithPrefix(" ", node.expression);
+ }
+
+ function emitSpreadElementExpression(node: SpreadElementExpression) {
+ write("...");
+ emitExpression(node.expression);
+ }
+
+ function emitClassExpression(node: ClassExpression) {
+ emitClassDeclarationOrExpression(node);
+ }
+
+ function emitExpressionWithTypeArguments(node: ExpressionWithTypeArguments) {
+ emitStart(node);
+ emitExpression(node.expression);
+ emitTypeArguments(node, node.typeArguments);
+ emitEnd(node);
+ }
+
+ function emitAsExpression(node: AsExpression) {
+ emitExpression(node.expression);
+ if (node.type) {
+ write(" as ");
+ emit(node.type);
+ }
+ }
+
+ //
+ // Misc
+ //
+
+ function emitTemplateSpan(node: TemplateSpan) {
+ emitExpression(node.expression);
+ emit(node.literal);
+ }
+
+ //
+ // Statements
+ //
+
+ function emitBlock(node: Block, format?: ListFormat) {
+ if (isSingleLineEmptyBlock(node)) {
+ write("{ }");
+ }
+ else {
+ write("{");
+ emitBlockStatements(node);
+ write("}");
+ }
+ }
+
+ function emitBlockStatements(node: Block) {
+ if (getNodeEmitFlags(node) & NodeEmitFlags.SingleLine) {
+ emitList(node, node.statements, ListFormat.SingleLineBlockStatements);
+ }
+ else {
+ emitList(node, node.statements, ListFormat.MultiLineBlockStatements);
+ }
+ }
+
+ function emitVariableStatement(node: VariableStatement) {
+ emitModifiers(node, node.modifiers);
+ emit(node.declarationList);
+ write(";");
+ }
+
+ function emitExpressionStatement(node: ExpressionStatement) {
+ emitExpression(node.expression);
+ write(";");
+ }
+
+ function emitIfStatement(node: IfStatement) {
+ write("if (");
+ emitExpression(node.expression);
+ write(")");
+ emitEmbeddedStatement(node.thenStatement);
+ if (node.elseStatement) {
+ writeLine();
+ write("else");
+ if (node.elseStatement.kind === SyntaxKind.IfStatement) {
+ write(" ");
+ emit(node.elseStatement);
+ }
+ else {
+ emitEmbeddedStatement(node.elseStatement);
+ }
+ }
+ }
+
+ function emitDoStatement(node: DoStatement) {
+ write("do");
+ emitEmbeddedStatement(node.statement);
+ if (isBlock(node.statement)) {
+ write(" ");
+ }
+ else {
+ writeLine();
+ }
+
+ write("while (");
+ emitExpression(node.expression);
+ write(");");
+ }
+
+ function emitWhileStatement(node: WhileStatement) {
+ write("while (");
+ emitExpression(node.expression);
+ write(")");
+ emitEmbeddedStatement(node.statement);
+ }
+
+ function emitForStatement(node: ForStatement) {
+ write("for (");
+ emitForBinding(node.initializer);
+ write(";");
+ emitExpressionWithPrefix(" ", node.condition);
+ write(";");
+ emitExpressionWithPrefix(" ", node.incrementor);
+ write(")");
+ emitEmbeddedStatement(node.statement);
+ }
+
+ function emitForInStatement(node: ForInStatement) {
+ write("for (");
+ emitForBinding(node.initializer);
+ write(" in ");
+ emitExpression(node.expression);
+ write(")");
+ emitEmbeddedStatement(node.statement);
+ }
+
+ function emitForOfStatement(node: ForOfStatement) {
+ write("for (");
+ emitForBinding(node.initializer);
+ write(" of ");
+ emitExpression(node.expression);
+ write(")");
+ emitEmbeddedStatement(node.statement);
+ }
+
+ function emitForBinding(node: VariableDeclarationList | Expression) {
+ if (node !== undefined) {
+ if (node.kind === SyntaxKind.VariableDeclarationList) {
+ emit(node);
+ }
+ else {
+ emitExpression(node);
+ }
+ }
+ }
+
+ function emitContinueStatement(node: ContinueStatement) {
+ write("continue");
+ emitWithPrefix(" ", node.label);
+ write(";");
+ }
+
+ function emitBreakStatement(node: BreakStatement) {
+ write("break");
+ emitWithPrefix(" ", node.label);
+ write(";");
+ }
+
+ function emitReturnStatement(node: ReturnStatement) {
+ write("return");
+ emitExpressionWithPrefix(" ", node.expression);
+ write(";");
+ }
+
+ function emitWithStatement(node: WithStatement) {
+ write("with (");
+ emitExpression(node.expression);
+ write(")");
+ emitEmbeddedStatement(node.statement);
+ }
+
+ function emitSwitchStatement(node: SwitchStatement) {
+ write("switch (");
+ emitExpression(node.expression);
+ write(") ");
+ emit(node.caseBlock);
+ }
+
+ function emitLabeledStatement(node: LabeledStatement) {
+ emit(node.label);
+ write(": ");
+ emit(node.statement);
+ }
+
+ function emitThrowStatement(node: ThrowStatement) {
+ write("throw");
+ emitExpressionWithPrefix(" ", node.expression);
+ write(";");
+ }
+
+ function emitTryStatement(node: TryStatement) {
+ write("try ");
+ emit(node.tryBlock);
+ emit(node.catchClause);
+ if (node.finallyBlock) {
+ writeLine();
+ write("finally ");
+ emit(node.finallyBlock);
+ }
+ }
+
+ function emitDebuggerStatement(node: DebuggerStatement) {
+ write("debugger;");
+ }
+
+ //
+ // Declarations
+ //
+
+ function emitVariableDeclaration(node: VariableDeclaration) {
+ emit(node.name);
+ emitExpressionWithPrefix(" = ", node.initializer);
+ }
+
+ function emitVariableDeclarationList(node: VariableDeclarationList) {
+ write(isLet(node) ? "let " : isConst(node) ? "const " : "var ");
+ 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);
+ write(node.asteriskToken ? "function* " : "function ");
+ emit(node.name);
+ emitSignatureAndBody(node);
+ }
+
+ function emitSignatureAndBody(node: FunctionDeclaration | FunctionExpression | MethodDeclaration | AccessorDeclaration | ConstructorDeclaration) {
+ const body = node.body;
+ if (body) {
+ const savedTempFlags = tempFlags;
+ tempFlags = 0;
+ startLexicalEnvironment();
+ emitSignatureHead(node);
+ write(" {");
+
+ const startingLine = writer.getLine();
+ emitBlockFunctionBody(node, body);
+
+ const endingLine = writer.getLine();
+ emitLexicalEnvironment(endLexicalEnvironment(), /*newLine*/ startingLine !== endingLine);
+ write("}");
+ tempFlags = savedTempFlags;
+ }
+ else {
+ emitSignatureHead(node);
+ write(";");
+ }
+
+ }
+
+ function emitSignatureHead(node: FunctionDeclaration | FunctionExpression | MethodDeclaration | AccessorDeclaration | ConstructorDeclaration) {
+ emitTypeParameters(node, node.typeParameters);
+ emitParameters(node, node.parameters);
+ emitWithPrefix(": ", node.type);
+ }
+
+ function shouldEmitBlockFunctionBodyOnSingleLine(parentNode: Node, body: Block) {
+ const originalNode = getOriginalNode(parentNode);
+ if (isFunctionLike(originalNode) && !nodeIsSynthesized(originalNode) && rangeEndIsOnSameLineAsRangeStart(originalNode.body, originalNode.body)) {
+ for (const statement of body.statements) {
+ if (synthesizedNodeStartsOnNewLine(statement)) {
+ return false;
+ }
+ }
+
+ if (originalNode.kind === SyntaxKind.ArrowFunction && !rangeEndIsOnSameLineAsRangeStart((originalNode).equalsGreaterThanToken, originalNode.body)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ function emitBlockFunctionBody(parentNode: Node, body: Block) {
+ // Emit all the prologue directives (like "use strict").
+ const statements = body.statements;
+ const statementOffset = emitPrologueDirectives(statements, /*startWithNewLine*/ true, /*indented*/ true);
+ const helpersEmitted = emitHelpers(body);
+ if (statementOffset === 0 && !helpersEmitted && shouldEmitBlockFunctionBodyOnSingleLine(parentNode, body)) {
+ emitList(body, statements, ListFormat.SingleLineFunctionBodyStatements);
+ }
+ else {
+ emitList(body, statements, ListFormat.MultiLineFunctionBodyStatements, statementOffset);
+ }
+ }
+
+ function emitClassDeclaration(node: ClassDeclaration) {
+ emitClassDeclarationOrExpression(node);
+ }
+
+ function emitClassDeclarationOrExpression(node: ClassDeclaration | ClassExpression) {
+ emitDecorators(node, node.decorators);
+ emitModifiers(node, node.modifiers);
+ write("class");
+ emitWithPrefix(" ", node.name);
+ emitTypeParameters(node, node.typeParameters);
+ emitList(node, node.heritageClauses, ListFormat.ClassHeritageClauses);
+
+ const savedTempFlags = tempFlags;
+ tempFlags = 0;
+
+ write(" {");
+ emitList(node, node.members, ListFormat.ClassMembers);
+ write("}");
+
+ tempFlags = savedTempFlags;
+ }
+
+ function emitInterfaceDeclaration(node: InterfaceDeclaration) {
+ emitDecorators(node, node.decorators);
+ emitModifiers(node, node.modifiers);
+ write("interface ");
+ emit(node.name);
+ emitTypeParameters(node, node.typeParameters);
+ emitList(node, node.heritageClauses, ListFormat.SingleLine);
+ write(" {");
+ emitList(node, node.members, ListFormat.InterfaceMembers);
+ write("}");
+ }
+
+ function emitTypeAliasDeclaration(node: TypeAliasDeclaration) {
+ emitDecorators(node, node.decorators);
+ emitModifiers(node, node.modifiers);
+ write("type ");
+ emit(node.name);
+ emitTypeParameters(node, node.typeParameters);
+ write(" = ");
+ emit(node.type);
+ write(";");
+ }
+
+ function emitEnumDeclaration(node: EnumDeclaration) {
+ emitModifiers(node, node.modifiers);
+ write("enum ");
+ emit(node.name);
+
+ const savedTempFlags = tempFlags;
+ tempFlags = 0;
+
+ write(" {");
+ emitList(node, node.members, ListFormat.EnumMembers);
+ write("}");
+ tempFlags = savedTempFlags;
+ }
+
+ function emitModuleDeclaration(node: ModuleDeclaration) {
+ emitModifiers(node, node.modifiers);
+ write(node.flags & NodeFlags.Namespace ? "namespace " : "module ");
+ emit(node.name);
+
+ let body = node.body;
+ while (body.kind === SyntaxKind.ModuleDeclaration) {
+ write(".");
+ emit((body).name);
+ body = (body).body;
+ }
+
+ write(" ");
+ emit(body);
+ }
+
+ function emitModuleBlock(node: ModuleBlock) {
+ if (isSingleLineEmptyBlock(node)) {
+ write("{ }");
+ }
+ else {
+ const savedTempFlags = tempFlags;
+ tempFlags = 0;
+ startLexicalEnvironment();
+ write("{");
+ increaseIndent();
+
+ const startingLine = writer.getLine();
+ emitBlockStatements(node);
+
+ const endingLine = writer.getLine();
+ emitLexicalEnvironment(endLexicalEnvironment(), /*newLine*/ startingLine !== endingLine);
+ write("}");
+ tempFlags = savedTempFlags;
+ }
+ }
+
+ function emitCaseBlock(node: CaseBlock) {
+ write("{");
+ emitList(node, node.clauses, ListFormat.CaseBlockClauses);
+ write("}");
+ }
+
+ function emitImportEqualsDeclaration(node: ImportEqualsDeclaration) {
+ emitModifiers(node, node.modifiers);
+ write("import ");
+ emit(node.name);
+ write(" = ");
+ emitModuleReference(node.moduleReference);
+ write(";");
+ }
+
+ function emitModuleReference(node: ModuleReference) {
+ if (node.kind === SyntaxKind.Identifier) {
+ emitExpression(node);
+ }
+ else {
+ emit(node);
+ }
+ }
+
+ function emitImportDeclaration(node: ImportDeclaration) {
+ emitModifiers(node, node.modifiers);
+ write("import ");
+ emit(node.importClause);
+ emitExpression(node.moduleSpecifier);
+ write(";");
+ }
+
+ function emitImportClause(node: ImportClause) {
+ emitStart(node);
+ emit(node.name);
+ if (node.name && node.namedBindings) {
+ write(", ");
+ }
+ emit(node.namedBindings);
+ emitEnd(node);
+ write(" from ");
+ }
+
+ function emitNamespaceImport(node: NamespaceImport) {
+ emitStart(node);
+ write("* as ");
+ emit(node.name);
+ emitEnd(node);
+ }
+
+ function emitNamedImports(node: NamedImports) {
+ emitNamedImportsOrExports(node);
+ }
+
+ function emitImportSpecifier(node: ImportSpecifier) {
+ emitImportOrExportSpecifier(node);
+ }
+
+ function emitExportAssignment(node: ExportAssignment) {
+ write(node.isExportEquals ? "export = " : "export default ");
+ emitExpression(node.expression);
+ write(";");
+ }
+
+ function emitExportDeclaration(node: ExportDeclaration) {
+ write("export ");
+ if (node.exportClause) {
+ emit(node.exportClause);
+ write(" from ");
+ }
+ else {
+ write("* from ");
+ }
+ emitExpression(node.moduleSpecifier);
+ write(";");
+ }
+
+ function emitNamedExports(node: NamedExports) {
+ emitNamedImportsOrExports(node);
+ }
+
+ function emitExportSpecifier(node: ExportSpecifier) {
+ emitImportOrExportSpecifier(node);
+ }
+
+ function emitNamedImportsOrExports(node: NamedImportsOrExports) {
+ write("{");
+ emitList(node, node.elements, ListFormat.NamedImportsOrExportsElements);
+ write("}");
+ }
+
+ function emitImportOrExportSpecifier(node: ImportOrExportSpecifier) {
+ if (node.propertyName) {
+ emit(node.propertyName);
+ write(" as ");
+ }
+
+ emit(node.name);
+ }
+
+ //
+ // Module references
+ //
+
+ function emitExternalModuleReference(node: ExternalModuleReference) {
+ write("require(");
+ emitExpression(node.expression);
+ write(")");
+ }
+
+ //
+ // JSX
+ //
+
+ function emitJsxElement(node: JsxElement) {
+ emit(node.openingElement);
+ emitList(node, node.children, ListFormat.JsxElementChildren);
+ emit(node.closingElement);
+ }
+
+ function emitJsxSelfClosingElement(node: JsxSelfClosingElement) {
+ write("<");
+ emit(node.tagName);
+ write(" ");
+ emitList(node, node.attributes, ListFormat.JsxElementAttributes);
+ write("/>");
+ }
+
+ function emitJsxOpeningElement(node: JsxOpeningElement) {
+ write("<");
+ emit(node.tagName);
+ writeIfAny(node.attributes, " ");
+ emitList(node, node.attributes, ListFormat.JsxElementAttributes);
+ write(">");
+ }
+
+ function emitJsxText(node: JsxText) {
+ writer.writeLiteral(getTextOfNode(node, /*includeTrivia*/ true));
+ }
+
+ function emitJsxClosingElement(node: JsxClosingElement) {
+ write("");
+ emit(node.tagName);
+ write(">");
+ }
+
+ function emitJsxAttribute(node: JsxAttribute) {
+ emit(node.name);
+ emitWithPrefix("=", node.initializer);
+ }
+
+ function emitJsxSpreadAttribute(node: JsxSpreadAttribute) {
+ write("{...");
+ emitExpression(node.expression);
+ write("}");
+ }
+
+ function emitJsxExpression(node: JsxExpression) {
+ write("{");
+ emitExpression(node.expression);
+ write("}");
+ }
+
+ //
+ // Clauses
+ //
+
+ function emitCaseClause(node: CaseClause) {
+ write("case ");
+ emitExpression(node.expression);
+ write(":");
+ emitCaseOrDefaultClauseStatements(node, node.statements);
+ }
+
+ function emitDefaultClause(node: DefaultClause) {
+ write("default:");
+ emitCaseOrDefaultClauseStatements(node, node.statements);
+ }
+
+ function emitCaseOrDefaultClauseStatements(parentNode: Node, statements: NodeArray) {
+ if (statements.length === 1 && rangeStartPositionsAreOnSameLine(parentNode, statements[0])) {
+ write(" ");
+ emit(statements[0]);
+ }
+ else {
+ emitList(parentNode, statements, ListFormat.CaseOrDefaultClauseStatements);
+ }
+ }
+
+ function emitHeritageClause(node: HeritageClause) {
+ emitStart(node);
+ writeToken(node.token);
+ write(" ");
+ emitList(node, node.types, ListFormat.HeritageClauseTypes);
+ emitEnd(node);
+ }
+
+ function emitCatchClause(node: CatchClause) {
+ writeLine();
+ write("catch (");
+ emit(node.variableDeclaration);
+ write(") ");
+ emit(node.block);
+ }
+
+ //
+ // Property assignments
+ //
+
+ function emitPropertyAssignment(node: PropertyAssignment) {
+ emit(node.name);
+ write(": ");
+ // // This is to ensure that we emit comment in the following case:
+ // // For example:
+ // // obj = {
+ // // id: /*comment1*/ ()=>void
+ // // }
+ // // "comment1" is not considered to be leading comment for node.initializer
+ // // but rather a trailing comment on the previous node.
+ // emitTrailingCommentsOfPosition(node.initializer.pos);
+ emitExpression(node.initializer);
+ }
+
+ function emitShorthandPropertyAssignment(node: ShorthandPropertyAssignment) {
+ emit(node.name);
+ }
+
+ //
+ // Enum
+ //
+
+ function emitEnumMember(node: EnumMember) {
+ emit(node.name);
+ emitExpressionWithPrefix(" = ", node.initializer);
+ }
+
+ //
+ // Top-level nodes
+ //
+
+ function emitSourceFile(node: SourceFile) {
+ writeLine();
+ emitShebang();
+ emitDetachedComments(node);
+
+ const statements = node.statements;
+ const statementOffset = emitPrologueDirectives(statements);
+ if (getNodeEmitFlags(node) & NodeEmitFlags.NoLexicalEnvironment) {
+ emitHelpers(node);
+ emitList(node, statements, ListFormat.MultiLine, statementOffset);
+ }
+ else {
+ const savedTempFlags = tempFlags;
+ tempFlags = 0;
+ startLexicalEnvironment();
+ emitHelpers(node);
+ emitList(node, statements, ListFormat.MultiLine, statementOffset);
+ emitLexicalEnvironment(endLexicalEnvironment(), /*newLine*/ true);
+ tempFlags = savedTempFlags;
+ }
+
+ emitLeadingComments(node.endOfFileToken);
+ }
+
+ function emitLexicalEnvironment(declarations: Statement[], newLine: boolean) {
+ if (declarations && declarations.length > 0) {
+ for (const node of declarations) {
+ if (newLine) {
+ writeLine();
+ }
+ else {
+ write(" ");
+ }
+
+ emit(node);
+ }
+
+ if (newLine) {
+ writeLine();
+ }
+ else {
+ write(" ");
+ }
+ }
+ }
+
+ function emitPrologueDirectives(statements: Node[], startWithNewLine?: boolean, indented?: boolean) {
+ increaseIndentIf(indented);
+ for (let i = 0; i < statements.length; i++) {
+ if (isPrologueDirective(statements[i])) {
+ if (startWithNewLine || i > 0) {
+ writeLine();
+ }
+ emit(statements[i]);
+ }
+ else {
+ // return index of the first non prologue directive
+ decreaseIndentIf(indented);
+ return i;
+ }
+ }
+
+ decreaseIndentIf(indented);
+ return statements.length;
+ }
+
+ function emitHelpers(node: Node) {
+ const emitFlags = getNodeEmitFlags(node);
+ let helpersEmitted = false;
+ if (emitFlags & NodeEmitFlags.EmitHelpers) {
+ helpersEmitted = emitEmitHelpers(currentSourceFile);
+ }
+
+ if (emitFlags & NodeEmitFlags.EmitExportStar) {
+ emitExportStarHelper();
+ helpersEmitted = true;
+ }
+
+ return helpersEmitted;
+ }
+
+ function emitEmitHelpers(node: SourceFile) {
+ let helpersEmitted = false;
+
+ // Only emit helpers if the user did not say otherwise.
+ if (!compilerOptions.noEmitHelpers) {
+ // Only Emit __extends function when target ES5.
+ // For target ES6 and above, we can emit classDeclaration as is.
+ if ((languageVersion < ScriptTarget.ES6) && (!extendsEmitted && node.flags & NodeFlags.HasClassExtends)) {
+ writeLines(extendsHelper);
+ extendsEmitted = true;
+ helpersEmitted = true;
+ }
+
+ if (!decorateEmitted && node.flags & NodeFlags.HasDecorators) {
+ writeLines(decorateHelper);
+ if (compilerOptions.emitDecoratorMetadata) {
+ writeLines(metadataHelper);
+ }
+
+ decorateEmitted = true;
+ helpersEmitted = true;
+ }
+
+ if (!paramEmitted && node.flags & NodeFlags.HasParamDecorators) {
+ writeLines(paramHelper);
+ paramEmitted = true;
+ helpersEmitted = true;
+ }
+
+ if (!awaiterEmitted && node.flags & NodeFlags.HasAsyncFunctions) {
+ writeLines(awaiterHelper);
+ awaiterEmitted = true;
+ helpersEmitted = true;
+ }
+
+ if (helpersEmitted) {
+ writeLine();
+ }
+ }
+
+ return helpersEmitted;
+ }
+
+ function emitExportStarHelper() {
+ writeLines(exportStarHelper);
+ }
+
+ function writeLines(text: string): void {
+ const lines = text.split(/\r\n|\r|\n/g);
+ for (let i = 0; i < lines.length; i++) {
+ const line = lines[i];
+ if (line.length) {
+ if (i > 0) {
+ writeLine();
+ }
+ write(line);
+ }
+ }
+ }
+
+ //
+ // Helpers
+ //
+
+ function emitShebang() {
+ const shebang = getShebang(currentText);
+ if (shebang) {
+ write(shebang);
+ }
+ }
+
+ function emitModifiers(node: Node, modifiers: ModifiersArray) {
+ const startingPos = writer.getTextPos();
+ emitList(node, modifiers, ListFormat.SingleLine);
+
+ const endingPos = writer.getTextPos();
+ if (startingPos !== endingPos) {
+ write(" ");
+ }
+ }
+
+ function emitWithPrefix(prefix: string, node: Node) {
+ emitNodeWithPrefix(prefix, node, emit);
+ }
+
+ function emitExpressionWithPrefix(prefix: string, node: Node) {
+ emitNodeWithPrefix(prefix, node, emitExpression);
+ }
+
+ function emitNodeWithPrefix(prefix: string, node: Node, emit: (node: Node) => void) {
+ if (node) {
+ write(prefix);
+ emit(node);
+ }
+ }
+
+ function emitWithSuffix(node: Node, suffix: string) {
+ if (node) {
+ emit(node);
+ write(suffix);
+ }
+ }
+
+ function tryEmitSubstitute(node: Node, substitution: (node: Node) => Node) {
+ const substitute = substitution ? substitution(node) : node;
+ if (substitute && substitute !== node) {
+ emitWorker(substitute);
+ return true;
+ }
+
+ return false;
+ }
+
+ function tryEmitConstantValue(node: PropertyAccessExpression | ElementAccessExpression): boolean {
+ const constantValue = tryGetConstEnumValue(node);
+ if (constantValue !== undefined) {
+ write(String(constantValue));
+ if (!compilerOptions.removeComments) {
+ const propertyName = isPropertyAccessExpression(node)
+ ? declarationNameToString(node.name)
+ : getTextOfNode(node.argumentExpression);
+ write(` /* ${propertyName} */`);
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ function emitEmbeddedStatement(node: Statement) {
+ if (isBlock(node)) {
+ write(" ");
+ emit(node);
+ }
+ else {
+ writeLine();
+ increaseIndent();
+ emit(node);
+ decreaseIndent();
+ }
+ }
+
+ function emitDecorators(parentNode: Node, decorators: NodeArray) {
+ emitList(parentNode, decorators, ListFormat.Decorators);
+ }
+
+ function emitTypeArguments(parentNode: Node, typeArguments: NodeArray) {
+ emitList(parentNode, typeArguments, ListFormat.TypeArguments);
+ }
+
+ function emitTypeParameters(parentNode: Node, typeParameters: NodeArray) {
+ emitList(parentNode, typeParameters, ListFormat.TypeParameters);
+ }
+
+ function emitParameters(parentNode: Node, parameters: NodeArray) {
+ emitList(parentNode, parameters, ListFormat.Parameters);
+ }
+
+ function emitParametersForArrow(parentNode: Node, parameters: NodeArray) {
+ if (parameters &&
+ parameters.length === 1 &&
+ parameters[0].type === undefined &&
+ parameters[0].pos === parentNode.pos) {
+ emit(parameters[0]);
+ }
+ else {
+ emitParameters(parentNode, parameters);
+ }
+ }
+
+ function emitParametersForIndexSignature(parentNode: Node, parameters: NodeArray) {
+ emitList(parentNode, parameters, ListFormat.IndexSignatureParameters);
+ }
+
+ function emitList(parentNode: Node, children: NodeArray, format: ListFormat, start?: number, count?: number) {
+ emitNodeList(emit, parentNode, children, format, start, count);
+ }
+
+ function emitExpressionList(parentNode: Node, children: NodeArray, format: ListFormat, start?: number, count?: number) {
+ emitNodeList(emitExpression, parentNode, children, format, start, count);
+ }
+
+ function emitNodeList(emit: (node: Node) => void, parentNode: Node, children: NodeArray, format: ListFormat, start = 0, count = children ? children.length - start : 0) {
+ const isUndefined = children === undefined;
+ if (isUndefined && format & ListFormat.OptionalIfUndefined) {
+ return;
+ }
+
+ const isEmpty = isUndefined || children.length === 0 || start >= children.length || count === 0;
+ if (isEmpty && format & ListFormat.OptionalIfEmpty) {
+ return;
+ }
+
+ if (format & ListFormat.BracketsMask) {
+ write(getOpeningBracket(format));
+ }
+
+ if (isEmpty) {
+ // Write a line terminator if the parent node was multi-line
+ if (format & ListFormat.MultiLine) {
+ writeLine();
+ }
+ else if (format & ListFormat.SpaceBetweenBraces) {
+ write(" ");
+ }
+ }
+ else {
+ // Write the opening line terminator or leading whitespace.
+ if (shouldWriteLeadingLineTerminator(parentNode, children, format)) {
+ writeLine();
+ }
+ else if (format & ListFormat.SpaceBetweenBraces) {
+ write(" ");
+ }
+
+ // Increase the indent, if requested.
+ if (format & ListFormat.Indented) {
+ increaseIndent();
+ }
+
+ // Emit each child.
+ let previousSibling: Node;
+ const delimiter = getDelimiter(format);
+ for (let i = 0; i < count; i++) {
+ const child = children[start + i];
+
+ // Write the delimiter if this is not the first node.
+ if (previousSibling) {
+ write(delimiter);
+
+ // Write either a line terminator or whitespace to separate the elements.
+ if (shouldWriteSeparatingLineTerminator(previousSibling, child, format)) {
+ writeLine();
+ }
+ else if (previousSibling) {
+ write(" ");
+ }
+ }
+
+ // Emit this child.
+ emit(child);
+
+ previousSibling = child;
+ }
+
+ // Write a trailing comma, if requested.
+ const hasTrailingComma = (format & ListFormat.AllowTrailingComma) && children.hasTrailingComma;
+ if (format & ListFormat.CommaDelimited && hasTrailingComma) {
+ write(",");
+ }
+
+ // Decrease the indent, if requested.
+ if (format & ListFormat.Indented) {
+ decreaseIndent();
+ }
+
+ // Write the closing line terminator or closing whitespace.
+ if (shouldWriteClosingLineTerminator(parentNode, children, format)) {
+ writeLine();
+ }
+ else if (format & ListFormat.SpaceBetweenBraces) {
+ write(" ");
+ }
+ }
+
+ if (format & ListFormat.BracketsMask) {
+ write(getClosingBracket(format));
+ }
+ }
+
+ function writeIfAny(nodes: NodeArray, text: string) {
+ if (nodes && nodes.length > 0) {
+ write(text);
+ }
+ }
+
+ function writeIfPresent(node: Node, text: string) {
+ if (node !== undefined) {
+ write(text);
+ }
+ }
+
+ function writeToken(token: SyntaxKind, pos?: number) {
+ const tokenStartPos = skipTrivia(currentText, pos);
+ emitPos(tokenStartPos);
+ const tokenEndPos = writeTokenText(token, pos);
+ emitPos(tokenEndPos);
+ return tokenEndPos;
+ }
+
+ function writeTokenText(token: SyntaxKind, pos?: number) {
+ const tokenString = tokenToString(token);
+ write(tokenString);
+ return positionIsSynthesized(pos) ? -1 : pos + tokenString.length;
+ }
+
+ function writeTokenNode(node: Node) {
+ if (node) {
+ emitStart(node);
+ writeTokenText(node.kind);
+ emitEnd(node);
+ }
+ }
+
+ function increaseIndentIf(value: boolean, valueToWriteWhenNotIndenting?: string) {
+ if (value) {
+ increaseIndent();
+ writeLine();
+ }
+ else if (valueToWriteWhenNotIndenting) {
+ write(valueToWriteWhenNotIndenting);
+ }
+ }
+
+ // Helper function to decrease the indent if we previously indented. Allows multiple
+ // previous indent values to be considered at a time. This also allows caller to just
+ // call this once, passing in all their appropriate indent values, instead of needing
+ // to call this helper function multiple times.
+ function decreaseIndentIf(value1: boolean, value2?: boolean) {
+ if (value1) {
+ decreaseIndent();
+ }
+ if (value2) {
+ decreaseIndent();
+ }
+ }
+
+ function shouldWriteLeadingLineTerminator(parentNode: Node, children: NodeArray, format: ListFormat) {
+ if (format & ListFormat.MultiLine) {
+ return true;
+ }
+ else if (format & ListFormat.PreserveLines) {
+ if (getNodeEmitFlags(parentNode) & NodeEmitFlags.MultiLine) {
+ return true;
+ }
+
+ const firstChild = children[0];
+ if (firstChild === undefined) {
+ return !positionsAreOnSameLine(getStartPos(parentNode), parentNode.end);
+ }
+ else if (positionIsSynthesized(parentNode.pos) || nodeIsSynthesized(firstChild)) {
+ return synthesizedNodeStartsOnNewLine(firstChild, format);
+ }
+ else {
+ return !rangeStartPositionsAreOnSameLine(parentNode, firstChild);
+ }
+ }
+ else {
+ return false;
+ }
+ }
+
+ function shouldWriteSeparatingLineTerminator(previousNode: Node, nextNode: Node, format: ListFormat) {
+ if (format & ListFormat.MultiLine) {
+ return true;
+ }
+ else if (format & ListFormat.PreserveLines) {
+ if (previousNode === undefined || nextNode === undefined) {
+ return false;
+ }
+ else if (nodeIsSynthesized(previousNode) || nodeIsSynthesized(nextNode)) {
+ return synthesizedNodeStartsOnNewLine(previousNode, format) || synthesizedNodeStartsOnNewLine(nextNode, format);
+ }
+ else {
+ return !rangeEndIsOnSameLineAsRangeStart(previousNode, nextNode);
+ }
+ }
+ else {
+ return false;
+ }
+ }
+
+ function shouldWriteClosingLineTerminator(parentNode: Node, children: NodeArray, format: ListFormat) {
+ if (format & ListFormat.MultiLine) {
+ return true;
+ }
+ else if (format & ListFormat.PreserveLines) {
+ if (getNodeEmitFlags(parentNode) & NodeEmitFlags.MultiLine) {
+ return true;
+ }
+
+ const lastChild = lastOrUndefined(children);
+ if (lastChild === undefined) {
+ return !positionsAreOnSameLine(getStartPos(parentNode), parentNode.end);
+ }
+ else if (positionIsSynthesized(parentNode.pos) || nodeIsSynthesized(lastChild)) {
+ return synthesizedNodeStartsOnNewLine(lastChild, format);
+ }
+ else {
+ return !rangeEndPositionsAreOnSameLine(parentNode, lastChild);
+ }
+ }
+ else {
+ return false;
+ }
+ }
+
+ function synthesizedNodeStartsOnNewLine(node: Node, format?: ListFormat) {
+ if (nodeIsSynthesized(node)) {
+ const startsOnNewLine = (node).startsOnNewLine;
+ if (startsOnNewLine === undefined) {
+ return (format & ListFormat.PreferNewLine) !== 0;
+ }
+
+ return startsOnNewLine;
+ }
+ return (format & ListFormat.PreferNewLine) !== 0;
+ }
+
+ function rangeStartPositionsAreOnSameLine(range1: TextRange, range2: TextRange) {
+ return positionsAreOnSameLine(getStartPos(range1), getStartPos(range2));
+ }
+
+ function rangeEndPositionsAreOnSameLine(range1: TextRange, range2: TextRange) {
+ return positionsAreOnSameLine(range1.end, range2.end);
+ }
+
+ function rangeEndIsOnSameLineAsRangeStart(range1: TextRange, range2: TextRange) {
+ return positionsAreOnSameLine(range1.end, getStartPos(range2));
+ }
+
+ function positionsAreOnSameLine(pos1: number, pos2: number) {
+ return pos1 === pos2 ||
+ getLineOfLocalPosition(currentSourceFile, pos1) === getLineOfLocalPosition(currentSourceFile, pos2);
+ }
+
+ function getStartPos(range: TextRange) {
+ return range.pos === -1 ? -1 : skipTrivia(currentText, range.pos);
+ }
+
+ function needsIndentation(parent: Node, node1: Node, node2: Node): boolean {
+ // Always use a newline for synthesized code if the synthesizer desires it.
+ if (synthesizedNodeStartsOnNewLine(node2)) {
+ return true;
+ }
+
+ return !nodeIsSynthesized(parent)
+ && !nodeIsSynthesized(node1)
+ && !nodeIsSynthesized(node2)
+ && !rangeEndIsOnSameLineAsRangeStart(node1, node2);
+ }
+
+ function getTextOfNode(node: Node, includeTrivia?: boolean) {
+ if (nodeIsSynthesized(node) && (isLiteralExpression(node) || isIdentifier(node))) {
+ return node.text;
+ }
+
+ return getSourceTextOfNodeFromSourceFile(currentSourceFile, node, includeTrivia);
+ }
+
+ function tryGetConstEnumValue(node: Node): number {
+ if (compilerOptions.isolatedModules) {
+ return undefined;
+ }
+
+ return isPropertyAccessExpression(node) || isElementAccessExpression(node)
+ ? resolver.getConstantValue(node)
+ : undefined;
+ }
+
+ function isSingleLineEmptyBlock(block: Block) {
+ return (getNodeEmitFlags(block) & NodeEmitFlags.MultiLine) === 0 &&
+ block.statements.length === 0 &&
+ rangeEndIsOnSameLineAsRangeStart(block, block);
+ }
+
+ function tempKindToFlags(kind: TempVariableKind) {
+ return kind === TempVariableKind.Loop
+ ? TempFlags._i
+ : TempFlags.Auto;
+ }
+
+ /**
+ * 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";
+ if (isUniqueName(name)) {
+ tempFlags |= flags;
+ return name;
+ }
+ }
+ while (true) {
+ const count = tempFlags & TempFlags.CountMask;
+ tempFlags++;
+ // Skip over 'i' and 'n'
+ if (count !== 8 && count !== 13) {
+ const name = count < 26
+ ? "_" + String.fromCharCode(CharacterCodes.a + count)
+ : "_" + (count - 26);
+ if (isUniqueName(name)) {
+ return name;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ function createDelimiterMap() {
+ const delimiters: string[] = [];
+ delimiters[ListFormat.None] = "";
+ delimiters[ListFormat.CommaDelimited] = ",";
+ delimiters[ListFormat.BarDelimited] = " |";
+ delimiters[ListFormat.AmpersandDelimited] = " &";
+ return delimiters;
+ }
+
+ function getDelimiter(format: ListFormat) {
+ return delimiters[format & ListFormat.DelimitersMask];
+ }
+
+ function createBracketsMap() {
+ const brackets: string[][] = [];
+ brackets[ListFormat.Braces] = ["{", "}"];
+ brackets[ListFormat.Parenthesis] = ["(", ")"];
+ brackets[ListFormat.AngleBrackets] = ["<", ">"];
+ brackets[ListFormat.SquareBrackets] = ["[", "]"];
+ return brackets;
+ }
+
+ function getOpeningBracket(format: ListFormat) {
+ return brackets[format & ListFormat.BracketsMask][0];
+ }
+
+ function getClosingBracket(format: ListFormat) {
+ return brackets[format & ListFormat.BracketsMask][1];
+ }
+
+ const enum ListFormat {
+ None = 0,
+
+ // Line separators
+ SingleLine = 1 << 0, // Prints the list on a single line (default).
+ MultiLine = 1 << 1, // Prints the list on multiple lines.
+ PreserveLines = 1 << 2, // Prints the list using line preservation if possible.
+
+ // Delimiters
+ NotDelimited = 0, // There is no delimiter between list items (default).
+ BarDelimited = 1 << 3, // Each list item is space-and-bar (" |") delimited.
+ AmpersandDelimited = 1 << 4, // Each list item is space-and-ampersand (" &") delimited.
+ CommaDelimited = 1 << 5, // Each list item is comma (",") delimited.
+ AllowTrailingComma = 1 << 6, // Write a trailing comma (",") if present.
+ DelimitersMask = BarDelimited | AmpersandDelimited | CommaDelimited,
+
+ // Whitespace
+ Indented = 1 << 7, // The list should be indented.
+ SpaceBetweenBraces = 1 << 8, // Inserts a space after the opening brace and before the closing brace.
+
+ // Brackets/Braces
+ 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 "]".
+ BracketsMask = Braces | Parenthesis | AngleBrackets | SquareBrackets,
+ OptionalIfUndefined = 1 << 13, // Do not emit brackets if the list is undefined.
+ OptionalIfEmpty = 1 << 14, // Do not emit brackets if the list is empty.
+ Optional = OptionalIfUndefined | OptionalIfEmpty,
+
+ // Other
+ PreferNewLine = 1 << 15, // Prefer adding a LineTerminator between synthesized nodes.
+
+ // Precomputed Formats
+ TypeLiteralMembers = MultiLine | Indented,
+ TupleTypeElements = CommaDelimited | SingleLine | Indented,
+ UnionTypeConstituents = BarDelimited | SingleLine,
+ IntersectionTypeConstituents = AmpersandDelimited | SingleLine,
+ ObjectBindingPatternElements = SingleLine | AllowTrailingComma | SpaceBetweenBraces,
+ ArrayBindingPatternElements = SingleLine | AllowTrailingComma,
+ ObjectLiteralExpressionProperties = PreserveLines | CommaDelimited | SpaceBetweenBraces | Indented | Braces,
+ ArrayLiteralExpressionElements = PreserveLines | CommaDelimited | AllowTrailingComma | Indented | SquareBrackets,
+ CallExpressionArguments = CommaDelimited | SingleLine | Parenthesis,
+ NewExpressionArguments = CommaDelimited | SingleLine | Parenthesis | OptionalIfUndefined,
+ TemplateExpressionSpans = SingleLine,
+ SingleLineBlockStatements = SpaceBetweenBraces | SingleLine,
+ MultiLineBlockStatements = Indented | MultiLine,
+ VariableDeclarationList = CommaDelimited | SingleLine,
+ SingleLineFunctionBodyStatements = SingleLine | SpaceBetweenBraces,
+ MultiLineFunctionBodyStatements = MultiLine | Indented,
+ ClassHeritageClauses = SingleLine,
+ ClassMembers = Indented | MultiLine,
+ InterfaceMembers = Indented | MultiLine,
+ EnumMembers = CommaDelimited | Indented | MultiLine,
+ CaseBlockClauses = Indented | MultiLine,
+ NamedImportsOrExportsElements = CommaDelimited | AllowTrailingComma | SingleLine | SpaceBetweenBraces,
+ JsxElementChildren = SingleLine,
+ JsxElementAttributes = SingleLine,
+ CaseOrDefaultClauseStatements = Indented | MultiLine,
+ HeritageClauseTypes = CommaDelimited | SingleLine,
+ SourceFileStatements = MultiLine,
+ Decorators = MultiLine | Optional,
+ TypeArguments = CommaDelimited | SingleLine | Indented | AngleBrackets | Optional,
+ TypeParameters = CommaDelimited | SingleLine | Indented | AngleBrackets | Optional,
+ Parameters = CommaDelimited | SingleLine | Indented | Parenthesis,
+ IndexSignatureParameters = CommaDelimited | SingleLine | Indented | SquareBrackets,
+ }
+}
\ No newline at end of file
diff --git a/src/compiler/transformer.ts b/src/compiler/transformer.ts
index d6d3dedd361..0507442642e 100644
--- a/src/compiler/transformer.ts
+++ b/src/compiler/transformer.ts
@@ -2,6 +2,12 @@
/* @internal */
namespace ts {
+ export function getTransformers(compilerOptions: CompilerOptions) {
+ const transformers: Transformer[] = [];
+ // TODO(rbuckton): Add transformers
+ return transformers;
+ }
+
/**
* Transforms an array of SourceFiles by passing them through each transformer.
*
diff --git a/src/compiler/tsconfig.json b/src/compiler/tsconfig.json
index 29f39b983f3..07b59ab67bd 100644
--- a/src/compiler/tsconfig.json
+++ b/src/compiler/tsconfig.json
@@ -20,6 +20,9 @@
"factory.ts",
"visitor.ts",
"transformer.ts",
+ "comments.ts",
+ "printer.ts",
+ "declarationEmitter.ts",
"emitter.ts",
"program.ts",
"commandLineParser.ts",
diff --git a/src/compiler/types.ts b/src/compiler/types.ts
index 772c90fb08c..70338d3d0f2 100644
--- a/src/compiler/types.ts
+++ b/src/compiler/types.ts
@@ -2795,6 +2795,7 @@ namespace ts {
UMDDefine = 1 << 2, // This node should be replaced with the UMD define helper.
NoLexicalEnvironment = 1 << 3, // A new LexicalEnvironment should *not* be introduced when emitting this node.
SingleLine = 1 << 4, // The contents of this node should be emit on a single line.
+ MultiLine = 1 << 5, // The contents of this node should be emit on multiple lines.
}
/** Additional context provided to `visitEachChild` */
diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts
index 0f1d27a28f2..2ab7f15549b 100644
--- a/src/compiler/utilities.ts
+++ b/src/compiler/utilities.ts
@@ -102,6 +102,22 @@ namespace ts {
return true;
}
+ export function getLanguageVersion(compilerOptions: CompilerOptions) {
+ return compilerOptions.target || ScriptTarget.ES3;
+ }
+
+ export function getModuleKind(compilerOptions: CompilerOptions) {
+ if (compilerOptions.module) {
+ return compilerOptions.module;
+ }
+
+ if (getLanguageVersion(compilerOptions) === ScriptTarget.ES6) {
+ return ModuleKind.ES6;
+ }
+
+ return ModuleKind.None;
+ }
+
export function hasResolvedModule(sourceFile: SourceFile, moduleNameText: string): boolean {
return sourceFile.resolvedModules && hasProperty(sourceFile.resolvedModules, moduleNameText);
}
@@ -242,6 +258,60 @@ namespace ts {
return getSourceTextOfNodeFromSourceFile(getSourceFileOfNode(node), node, includeTrivia);
}
+ export function getLiteralText(node: LiteralLikeNode, sourceFile: SourceFile, languageVersion: ScriptTarget) {
+ // Any template literal or string literal with an extended escape
+ // (e.g. "\u{0067}") will need to be downleveled as a escaped string literal.
+ if (languageVersion < ScriptTarget.ES6 && (isTemplateLiteralKind(node.kind) || node.hasExtendedUnicodeEscape)) {
+ return getQuotedEscapedLiteralText("\"", node.text, "\"");
+ }
+
+ // If we don't need to downlevel and we can reach the original source text using
+ // the node's parent reference, then simply get the text as it was originally written.
+ if (!nodeIsSynthesized(node) && node.parent) {
+ const text = getSourceTextOfNodeFromSourceFile(sourceFile, node);
+ if (languageVersion < ScriptTarget.ES6 && isBinaryOrOctalIntegerLiteral(node, text)) {
+ return node.text;
+ }
+ return text;
+ }
+
+ // If we can't reach the original source text, use the canonical form if it's a number,
+ // or an escaped quoted form of the original text if it's string-like.
+ switch (node.kind) {
+ case SyntaxKind.StringLiteral:
+ return getQuotedEscapedLiteralText("\"", node.text, "\"");
+ case SyntaxKind.NoSubstitutionTemplateLiteral:
+ return getQuotedEscapedLiteralText("`", node.text, "`");
+ case SyntaxKind.TemplateHead:
+ return getQuotedEscapedLiteralText("`", node.text, "${");
+ case SyntaxKind.TemplateMiddle:
+ return getQuotedEscapedLiteralText("}", node.text, "${");
+ case SyntaxKind.TemplateTail:
+ return getQuotedEscapedLiteralText("}", node.text, "`");
+ case SyntaxKind.NumericLiteral:
+ return node.text;
+ }
+
+ Debug.fail(`Literal kind '${node.kind}' not accounted for.`);
+ }
+
+ export function isBinaryOrOctalIntegerLiteral(node: LiteralLikeNode, text: string) {
+ if (node.kind === SyntaxKind.NumericLiteral && text.length > 1) {
+ switch (text.charCodeAt(1)) {
+ case CharacterCodes.b:
+ case CharacterCodes.B:
+ case CharacterCodes.o:
+ case CharacterCodes.O:
+ return true;
+ }
+ }
+ return false;
+ }
+
+ function getQuotedEscapedLiteralText(leftQuote: string, text: string, rightQuote: string) {
+ return leftQuote + escapeNonAsciiCharacters(escapeString(text)) + rightQuote;
+ }
+
// Add an extra underscore to identifiers that start with two underscores to avoid issues with magic names like '__proto__'
export function escapeIdentifier(identifier: string): string {
return identifier.length >= 2 && identifier.charCodeAt(0) === CharacterCodes._ && identifier.charCodeAt(1) === CharacterCodes._ ? "_" + identifier : identifier;
@@ -2765,7 +2835,7 @@ namespace ts {
return isUnaryExpressionKind(node.kind);
}
- function isExpressionKind(kind: SyntaxKind): boolean {
+ export function isExpressionKind(kind: SyntaxKind): boolean {
return kind === SyntaxKind.ConditionalExpression
|| kind === SyntaxKind.YieldExpression
|| kind === SyntaxKind.ArrowFunction