From 028e4e2b13b4a65d5418865cc22d3b37c935c955 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Thu, 26 Jan 2017 15:46:26 -0800 Subject: [PATCH 1/6] Extract printer from emitter for reusability. --- Jakefile.js | 2 + src/compiler/comments.ts | 20 +- src/compiler/declarationEmitter.ts | 16 +- src/compiler/emitter.ts | 2791 +------------------- src/compiler/factory.ts | 13 + src/compiler/printer.ts | 2659 +++++++++++++++++++ src/compiler/sourcemap.ts | 28 +- src/compiler/transformer.ts | 30 +- src/compiler/transformers/es2015.ts | 16 +- src/compiler/transformers/es2017.ts | 16 +- src/compiler/transformers/es5.ts | 18 +- src/compiler/transformers/generators.ts | 6 +- src/compiler/transformers/module/es2015.ts | 16 +- src/compiler/transformers/module/module.ts | 16 +- src/compiler/transformers/module/system.ts | 18 +- src/compiler/transformers/ts.ts | 14 +- src/compiler/tsconfig.json | 1 + src/compiler/types.ts | 75 +- src/compiler/utilities.ts | 42 +- src/harness/tsconfig.json | 1 + src/services/tsconfig.json | 1 + 21 files changed, 2944 insertions(+), 2855 deletions(-) create mode 100644 src/compiler/printer.ts diff --git a/Jakefile.js b/Jakefile.js index 3ee8476c842d2..a23b9cbe03c64 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -84,6 +84,7 @@ var compilerSources = [ "sourcemap.ts", "comments.ts", "declarationEmitter.ts", + "printer.ts", "emitter.ts", "program.ts", "commandLineParser.ts", @@ -121,6 +122,7 @@ var servicesSources = [ "sourcemap.ts", "comments.ts", "declarationEmitter.ts", + "printer.ts", "emitter.ts", "program.ts", "commandLineParser.ts", diff --git a/src/compiler/comments.ts b/src/compiler/comments.ts index bd9190dbec7c3..3106d3ccfec48 100644 --- a/src/compiler/comments.ts +++ b/src/compiler/comments.ts @@ -5,17 +5,13 @@ namespace ts { export interface CommentWriter { reset(): void; setSourceFile(sourceFile: SourceFile): void; - emitNodeWithComments(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): 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(); + export function createCommentWriter(writer: EmitTextWriter, compilerOptions: CompilerOptions, newLine: string, emitPos: (pos: number) => void): CommentWriter { const extendedDiagnostics = compilerOptions.extendedDiagnostics; - const newLine = host.getNewLine(); - const { emitPos } = sourceMap; - let containerPos = -1; let containerEnd = -1; let declarationListContainerEnd = -1; @@ -34,9 +30,9 @@ namespace ts { 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 +43,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 +90,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) { diff --git a/src/compiler/declarationEmitter.ts b/src/compiler/declarationEmitter.ts index 46df4fed6896d..4b878adaff578 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 27285a4c2dc80..dd823cf79942d 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1,61 +1,28 @@ -/// +/// /// -/// -/// +/// +/// /// +/// /* @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]; // 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 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; + const comments = createCommentWriter(writer, compilerOptions, newLine, sourceMap.emitPos); - 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 +30,34 @@ 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(writer, compilerOptions, { + // resolver hooks + hasGlobalName: resolver.hasGlobalName, + + // transform hooks + onEmitNode: transform.emitNodeWithNotification, + onSubstituteNode: transform.emitNodeWithSubstitution, + + // sourcemap hooks + onEmitSourceMapOfNode: sourceMap.emitNodeWithSourceMap, + onEmitSourceMapOfToken: sourceMap.emitTokenWithSourceMap, + + // comment hooks + onEmitCommentsOfNode: comments.emitNodeWithComments, + onEmitDetachedCommentsOfNode: comments.emitBodyWithDetachedComments, + onEmitTrailingCommentsOfPosition: comments.emitTrailingCommentsOfPosition, + + // 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 +72,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 +84,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 +100,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.printBundle(bundle); + } + else { + isOwnFileEmit = true; + printer.printFile(sourceFile); } - // 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. @@ -159,2654 +141,57 @@ namespace ts { 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); - } - - /** - * Emits an IdentifierName. - */ - function emitIdentifierName(node: Identifier) { - pipelineEmitWithNotification(EmitContext.IdentifierName, node); - } - - /** - * Emits an expression node. - */ - function emitExpression(node: Expression) { - pipelineEmitWithNotification(EmitContext.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); - } - - /** - * 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); - 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: - case SyntaxKind.TemplateMiddle: - case SyntaxKind.TemplateTail: - return emitLiteral(node); - - // Identifiers - 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 - 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 emitThisType(); - case SyntaxKind.TypeOperator: - return emitTypeOperator(node); - case SyntaxKind.IndexedAccessType: - return emitIndexedAccessType(node); - case SyntaxKind.MappedType: - return emitMappedType(node); - case SyntaxKind.LiteralType: - return emitLiteralType(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 emitSemicolonClassElement(); - - // Statements - case SyntaxKind.Block: - return emitBlock(node); - case SyntaxKind.VariableStatement: - return emitVariableStatement(node); - case SyntaxKind.EmptyStatement: - return emitEmptyStatement(); - 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.JsxOpeningElement: - return emitJsxOpeningElement(node); - case SyntaxKind.JsxClosingElement: - return emitJsxClosingElement(node); - case SyntaxKind.JsxAttribute: - return emitJsxAttribute(node); - case SyntaxKind.JsxSpreadAttribute: - return emitJsxSpreadAttribute(node); - case SyntaxKind.JsxExpression: - return emitJsxExpression(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); - case SyntaxKind.SpreadAssignment: - return emitSpreadAssignment(node as SpreadAssignment); - - // Enum - case SyntaxKind.EnumMember: - return emitEnumMember(node); - - // JSDoc nodes (ignored) - // Transformation nodes (ignored) - } - - // If the node is an expression, try to emit it as an expression with - // substitution. - if (isExpression(node)) { - return pipelineEmitWithSubstitution(EmitContext.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 { - const kind = node.kind; - switch (kind) { - // Literals - case SyntaxKind.NumericLiteral: - return emitNumericLiteral(node); - - case SyntaxKind.StringLiteral: - case SyntaxKind.RegularExpressionLiteral: - case SyntaxKind.NoSubstitutionTemplateLiteral: - return emitLiteral(node); - - // Identifiers - case SyntaxKind.Identifier: - return emitIdentifier(node); - - // Reserved words - case SyntaxKind.FalseKeyword: - case SyntaxKind.NullKeyword: - case SyntaxKind.SuperKeyword: - case SyntaxKind.TrueKeyword: - case SyntaxKind.ThisKeyword: - writeTokenText(kind); - return; - - // 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.SpreadElement: - return emitSpreadExpression(node); - case SyntaxKind.ClassExpression: - return emitClassExpression(node); - case SyntaxKind.OmittedExpression: - return; - case SyntaxKind.AsExpression: - return emitAsExpression(node); - case SyntaxKind.NonNullExpression: - return emitNonNullExpression(node); - case SyntaxKind.MetaProperty: - return emitMetaProperty(node); - - // JSX - case SyntaxKind.JsxElement: - return emitJsxElement(node); - case SyntaxKind.JsxSelfClosingElement: - return emitJsxSelfClosingElement(node); - - // Transformation nodes - case SyntaxKind.PartiallyEmittedExpression: - return emitPartiallyEmittedExpression(node); - } - } - - // - // Literals/Pseudo-literals - // - - // SyntaxKind.NumericLiteral - function emitNumericLiteral(node: NumericLiteral) { - emitLiteral(node); - if (node.trailingComment) { - write(` /*${node.trailingComment}*/`); - } - } - - // SyntaxKind.StringLiteral - // SyntaxKind.RegularExpressionLiteral - // SyntaxKind.NoSubstitutionTemplateLiteral - // SyntaxKind.TemplateHead - // SyntaxKind.TemplateMiddle - // SyntaxKind.TemplateTail - function emitLiteral(node: LiteralLikeNode) { - const text = getLiteralTextOfNode(node); - if ((compilerOptions.sourceMap || compilerOptions.inlineSourceMap) - && (node.kind === SyntaxKind.StringLiteral || isTemplateLiteralKind(node.kind))) { - writer.writeLiteral(text); - } - else { - write(text); - } - } - - // - // Identifiers - // - - function emitIdentifier(node: Identifier) { - write(getTextOfNode(node, /*includeTrivia*/ false)); - } - - // - // 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, emitSignatureHead); - } - - function emitConstructor(node: ConstructorDeclaration) { - emitModifiers(node, node.modifiers); - write("constructor"); - emitSignatureAndBody(node, emitSignatureHead); - } - - function emitAccessorDeclaration(node: AccessorDeclaration) { - emitDecorators(node, node.decorators); - emitModifiers(node, node.modifiers); - write(node.kind === SyntaxKind.GetAccessor ? "get " : "set "); - emit(node.name); - emitSignatureAndBody(node, emitSignatureHead); - } - - 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(";"); - } - - function emitSemicolonClassElement() { - 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(")"); - } - - function emitThisType() { - write("this"); - } - - function emitTypeOperator(node: TypeOperatorNode) { - writeTokenText(node.operator); - write(" "); - emit(node.type); - } - - function emitIndexedAccessType(node: IndexedAccessTypeNode) { - emit(node.objectType); - write("["); - emit(node.indexType); - write("]"); - } - - function emitMappedType(node: MappedTypeNode) { - write("{"); - writeLine(); - increaseIndent(); - if (node.readonlyToken) { - write("readonly "); - } - write("["); - emit(node.typeParameter.name); - write(" in "); - emit(node.typeParameter.constraint); - write("]"); - if (node.questionToken) { - write("?"); - } - write(": "); - emit(node.type); - write(";"); - writeLine(); - decreaseIndent(); - write("}"); - } - - function emitLiteralType(node: LiteralTypeNode) { - emitExpression(node.literal); - } - - // - // 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 = node.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 indentedFlag = getEmitFlags(node) & EmitFlags.Indented; - if (indentedFlag) { - increaseIndent(); - } - - const preferNewLine = node.multiLine ? ListFormat.PreferNewLine : ListFormat.None; - const allowTrailingComma = languageVersion >= ScriptTarget.ES5 ? ListFormat.AllowTrailingComma : ListFormat.None; - emitList(node, properties, ListFormat.ObjectLiteralExpressionProperties | allowTrailingComma | preferNewLine); + function emitHelpers(node: Node, writeLines: (text: string) => void) { + let helpersEmitted = false; + const bundle = node.kind === SyntaxKind.Bundle ? node : undefined; + 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; + } - if (indentedFlag) { - decreaseIndent(); + writeLines(helper.text); + helpersEmitted = true; + } } } - } - - function emitPropertyAccessExpression(node: PropertyAccessExpression) { - let indentBeforeDot = false; - let indentAfterDot = false; - if (!(getEmitFlags(node) & EmitFlags.NoIndentation)) { - const dotRangeStart = node.expression.end; - const dotRangeEnd = skipTrivia(currentText, 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); - } - - emitExpression(node.expression); - increaseIndentIf(indentBeforeDot); - - const shouldEmitDotDot = !indentBeforeDot && needsDotDotForPropertyAccess(node.expression); - 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 is a decimal literal that was originally written with a dot - const text = getLiteralTextOfNode(expression); - return getNumericLiteralFlags(text, /*hint*/ NumericLiteralFlags.All) === NumericLiteralFlags.None - && !(expression).isOctalLiteral - && text.indexOf(tokenToString(SyntaxKind.DotToken)) < 0; - } - else if (isPropertyAccessExpression(expression) || isElementAccessExpression(expression)) { - // check if constant enum value is integer - const constantValue = getConstantValue(expression); - // isFinite handles cases when constantValue is undefined - return isFinite(constantValue) - && Math.floor(constantValue) === constantValue - && compilerOptions.removeComments; - } - } - - function emitElementAccessExpression(node: ElementAccessExpression) { - emitExpression(node.expression); - write("["); - emitExpression(node.argumentExpression); - write("]"); - } - - function emitCallExpression(node: CallExpression) { - emitExpression(node.expression); - emitTypeArguments(node, node.typeArguments); - emitExpressionList(node, node.arguments, ListFormat.CallExpressionArguments); - } - - function emitNewExpression(node: NewExpression) { - write("new "); - emitExpression(node.expression); - emitTypeArguments(node, node.typeArguments); - emitExpressionList(node, node.arguments, ListFormat.NewExpressionArguments); - } - - function emitTaggedTemplateExpression(node: TaggedTemplateExpression) { - emitExpression(node.tag); - write(" "); - 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); - emitSignatureAndBody(node, emitArrowFunctionHead); - } - - 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) { - writeTokenText(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) { - emitExpression(node.operand); - writeTokenText(node.operator); - } - - function emitBinaryExpression(node: BinaryExpression) { - const isCommaOperator = node.operatorToken.kind !== SyntaxKind.CommaToken; - const indentBeforeOperator = needsIndentation(node, node.left, node.operatorToken); - const indentAfterOperator = needsIndentation(node, node.operatorToken, node.right); - - emitExpression(node.left); - increaseIndentIf(indentBeforeOperator, isCommaOperator ? " " : undefined); - writeTokenText(node.operatorToken.kind); - 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 emitSpreadExpression(node: SpreadElement) { - write("..."); - emitExpression(node.expression); - } - - function emitClassExpression(node: ClassExpression) { - emitClassDeclarationOrExpression(node); - } - - function emitExpressionWithTypeArguments(node: ExpressionWithTypeArguments) { - emitExpression(node.expression); - emitTypeArguments(node, node.typeArguments); - } - - function emitAsExpression(node: AsExpression) { - emitExpression(node.expression); - if (node.type) { - write(" as "); - emit(node.type); - } - } - function emitNonNullExpression(node: NonNullExpression) { - emitExpression(node.expression); - write("!"); - } - - function emitMetaProperty(node: MetaProperty) { - writeToken(node.keywordToken, node.pos); - write("."); - emit(node.name); + return helpersEmitted; } - - // - // Misc - // - - function emitTemplateSpan(node: TemplateSpan) { - emitExpression(node.expression); - emit(node.literal); - } - - // - // Statements - // - - function emitBlock(node: Block) { - if (isSingleLineEmptyBlock(node)) { - writeToken(SyntaxKind.OpenBraceToken, node.pos, /*contextNode*/ node); - write(" "); - writeToken(SyntaxKind.CloseBraceToken, node.statements.end, /*contextNode*/ node); - } - else { - writeToken(SyntaxKind.OpenBraceToken, node.pos, /*contextNode*/ node); - emitBlockStatements(node); - writeToken(SyntaxKind.CloseBraceToken, node.statements.end, /*contextNode*/ node); - } - } - - function emitBlockStatements(node: BlockLike) { - if (getEmitFlags(node) & EmitFlags.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 emitEmptyStatement() { - write(";"); - } - - function emitExpressionStatement(node: ExpressionStatement) { - emitExpression(node.expression); - write(";"); - } - - function emitIfStatement(node: IfStatement) { - const openParenPos = writeToken(SyntaxKind.IfKeyword, node.pos, node); - write(" "); - writeToken(SyntaxKind.OpenParenToken, openParenPos, node); - emitExpression(node.expression); - writeToken(SyntaxKind.CloseParenToken, node.expression.end, node); - emitEmbeddedStatement(node, node.thenStatement); - if (node.elseStatement) { - writeLineOrSpace(node); - writeToken(SyntaxKind.ElseKeyword, node.thenStatement.end, node); - if (node.elseStatement.kind === SyntaxKind.IfStatement) { - write(" "); - emit(node.elseStatement); - } - else { - emitEmbeddedStatement(node, node.elseStatement); - } - } - } - - function emitDoStatement(node: DoStatement) { - write("do"); - emitEmbeddedStatement(node, node.statement); - if (isBlock(node.statement)) { - write(" "); - } - else { - writeLineOrSpace(node); - } - - write("while ("); - emitExpression(node.expression); - write(");"); - } - - function emitWhileStatement(node: WhileStatement) { - write("while ("); - emitExpression(node.expression); - write(")"); - emitEmbeddedStatement(node, node.statement); - } - - function emitForStatement(node: ForStatement) { - const openParenPos = writeToken(SyntaxKind.ForKeyword, node.pos); - write(" "); - writeToken(SyntaxKind.OpenParenToken, openParenPos, /*contextNode*/ node); - emitForBinding(node.initializer); - write(";"); - emitExpressionWithPrefix(" ", node.condition); - write(";"); - emitExpressionWithPrefix(" ", node.incrementor); - write(")"); - emitEmbeddedStatement(node, node.statement); - } - - function emitForInStatement(node: ForInStatement) { - const openParenPos = writeToken(SyntaxKind.ForKeyword, node.pos); - write(" "); - writeToken(SyntaxKind.OpenParenToken, openParenPos); - emitForBinding(node.initializer); - write(" in "); - emitExpression(node.expression); - writeToken(SyntaxKind.CloseParenToken, node.expression.end); - emitEmbeddedStatement(node, node.statement); - } - - function emitForOfStatement(node: ForOfStatement) { - const openParenPos = writeToken(SyntaxKind.ForKeyword, node.pos); - write(" "); - writeToken(SyntaxKind.OpenParenToken, openParenPos); - emitForBinding(node.initializer); - write(" of "); - emitExpression(node.expression); - writeToken(SyntaxKind.CloseParenToken, node.expression.end); - emitEmbeddedStatement(node, node.statement); - } - - function emitForBinding(node: VariableDeclarationList | Expression) { - if (node !== undefined) { - if (node.kind === SyntaxKind.VariableDeclarationList) { - emit(node); - } - else { - emitExpression(node); - } - } - } - - function emitContinueStatement(node: ContinueStatement) { - writeToken(SyntaxKind.ContinueKeyword, node.pos); - emitWithPrefix(" ", node.label); - write(";"); - } - - function emitBreakStatement(node: BreakStatement) { - writeToken(SyntaxKind.BreakKeyword, node.pos); - emitWithPrefix(" ", node.label); - write(";"); - } - - function emitReturnStatement(node: ReturnStatement) { - writeToken(SyntaxKind.ReturnKeyword, node.pos, /*contextNode*/ node); - emitExpressionWithPrefix(" ", node.expression); - write(";"); - } - - function emitWithStatement(node: WithStatement) { - write("with ("); - emitExpression(node.expression); - write(")"); - emitEmbeddedStatement(node, node.statement); - } - - function emitSwitchStatement(node: SwitchStatement) { - const openParenPos = writeToken(SyntaxKind.SwitchKeyword, node.pos); - write(" "); - writeToken(SyntaxKind.OpenParenToken, openParenPos); - emitExpression(node.expression); - writeToken(SyntaxKind.CloseParenToken, node.expression.end); - 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); - if (node.catchClause) { - writeLineOrSpace(node); - emit(node.catchClause); - } - if (node.finallyBlock) { - writeLineOrSpace(node); - write("finally "); - emit(node.finallyBlock); - } - } - - function emitDebuggerStatement(node: DebuggerStatement) { - writeToken(SyntaxKind.DebuggerKeyword, node.pos); - write(";"); - } - - // - // Declarations - // - - function emitVariableDeclaration(node: VariableDeclaration) { - emit(node.name); - emitWithPrefix(": ", node.type); - 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 "); - emitIdentifierName(node.name); - emitSignatureAndBody(node, emitSignatureHead); - } - - function emitSignatureAndBody(node: FunctionLikeDeclaration, emitSignatureHead: (node: SignatureDeclaration) => void) { - const body = node.body; - if (body) { - if (isBlock(body)) { - const indentedFlag = getEmitFlags(node) & EmitFlags.Indented; - if (indentedFlag) { - increaseIndent(); - } - - if (getEmitFlags(node) & EmitFlags.ReuseTempVariableScope) { - emitSignatureHead(node); - emitBlockFunctionBody(body); - } - else { - const savedTempFlags = tempFlags; - tempFlags = 0; - emitSignatureHead(node); - emitBlockFunctionBody(body); - tempFlags = savedTempFlags; - } - - if (indentedFlag) { - decreaseIndent(); - } - } - else { - emitSignatureHead(node); - write(" "); - emitExpression(body); - } - } - 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(body: Block) { - // We must emit a function body as a single-line body in the following case: - // * The body has NodeEmitFlags.SingleLine specified. - - // We must emit a function body as a multi-line body in the following cases: - // * The body is explicitly marked as multi-line. - // * A non-synthesized body's start and end position are on different lines. - // * Any statement in the body starts on a new line. - - if (getEmitFlags(body) & EmitFlags.SingleLine) { - return true; - } - - if (body.multiLine) { - return false; - } - - if (!nodeIsSynthesized(body) && !rangeIsOnSingleLine(body, currentSourceFile)) { - return false; - } - - if (shouldWriteLeadingLineTerminator(body, body.statements, ListFormat.PreserveLines) - || shouldWriteClosingLineTerminator(body, body.statements, ListFormat.PreserveLines)) { - return false; - } - - let previousStatement: Statement; - for (const statement of body.statements) { - if (shouldWriteSeparatingLineTerminator(previousStatement, statement, ListFormat.PreserveLines)) { - return false; - } - - previousStatement = statement; - } - - return true; - } - - function emitBlockFunctionBody(body: Block) { - write(" {"); - increaseIndent(); - - emitBodyWithDetachedComments(body, body.statements, - shouldEmitBlockFunctionBodyOnSingleLine(body) - ? emitBlockFunctionBodyOnSingleLine - : emitBlockFunctionBodyWorker); - - decreaseIndent(); - writeToken(SyntaxKind.CloseBraceToken, body.statements.end, body); - } - - function emitBlockFunctionBodyOnSingleLine(body: Block) { - emitBlockFunctionBodyWorker(body, /*emitBlockFunctionBodyOnSingleLine*/ true); - } - - function emitBlockFunctionBodyWorker(body: Block, emitBlockFunctionBodyOnSingleLine?: boolean) { - // Emit all the prologue directives (like "use strict"). - const statementOffset = emitPrologueDirectives(body.statements, /*startWithNewLine*/ true); - const helpersEmitted = emitHelpers(body); - - if (statementOffset === 0 && !helpersEmitted && emitBlockFunctionBodyOnSingleLine) { - decreaseIndent(); - emitList(body, body.statements, ListFormat.SingleLineFunctionBodyStatements); - increaseIndent(); - } - else { - emitList(body, body.statements, ListFormat.MultiLineFunctionBodyStatements, statementOffset); - } - } - - function emitClassDeclaration(node: ClassDeclaration) { - emitClassDeclarationOrExpression(node); - } - - function emitClassDeclarationOrExpression(node: ClassDeclaration | ClassExpression) { - emitDecorators(node, node.decorators); - emitModifiers(node, node.modifiers); - write("class"); - emitNodeWithPrefix(" ", node.name, emitIdentifierName); - - const indentedFlag = getEmitFlags(node) & EmitFlags.Indented; - if (indentedFlag) { - increaseIndent(); - } - - emitTypeParameters(node, node.typeParameters); - emitList(node, node.heritageClauses, ListFormat.ClassHeritageClauses); - - const savedTempFlags = tempFlags; - tempFlags = 0; - - write(" {"); - emitList(node, node.members, ListFormat.ClassMembers); - write("}"); - - if (indentedFlag) { - decreaseIndent(); - } - - 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.HeritageClauses); - 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 (isEmptyBlock(node)) { - write("{ }"); - } - else { - const savedTempFlags = tempFlags; - tempFlags = 0; - write("{"); - increaseIndent(); - emitBlockStatements(node); - write("}"); - tempFlags = savedTempFlags; - } - } - - function emitCaseBlock(node: CaseBlock) { - writeToken(SyntaxKind.OpenBraceToken, node.pos); - emitList(node, node.clauses, ListFormat.CaseBlockClauses); - writeToken(SyntaxKind.CloseBraceToken, node.clauses.end); - } - - 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 "); - if (node.importClause) { - emit(node.importClause); - write(" from "); - } - emitExpression(node.moduleSpecifier); - write(";"); - } - - function emitImportClause(node: ImportClause) { - emit(node.name); - if (node.name && node.namedBindings) { - write(", "); - } - emit(node.namedBindings); - } - - function emitNamespaceImport(node: NamespaceImport) { - write("* as "); - emit(node.name); - } - - 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); - } - else { - write("*"); - } - if (node.moduleSpecifier) { - 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("<"); - emitJsxTagName(node.tagName); - write(" "); - emitList(node, node.attributes, ListFormat.JsxElementAttributes); - write("/>"); - } - - function emitJsxOpeningElement(node: JsxOpeningElement) { - write("<"); - emitJsxTagName(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(""); - } - - function emitJsxAttribute(node: JsxAttribute) { - emit(node.name); - emitWithPrefix("=", node.initializer); - } - - function emitJsxSpreadAttribute(node: JsxSpreadAttribute) { - write("{..."); - emitExpression(node.expression); - write("}"); - } - - function emitJsxExpression(node: JsxExpression) { - if (node.expression) { - write("{"); - if (node.dotDotDotToken) { - write("..."); - } - emitExpression(node.expression); - write("}"); - } - } - - function emitJsxTagName(node: JsxTagNameExpression) { - if (node.kind === SyntaxKind.Identifier) { - emitExpression(node); - } - else { - emit(node); - } - } - - // - // 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) { - const emitAsSingleStatement = - statements.length === 1 && - ( - // treat synthesized nodes as located on the same line for emit purposes - nodeIsSynthesized(parentNode) || - nodeIsSynthesized(statements[0]) || - rangeStartPositionsAreOnSameLine(parentNode, statements[0], currentSourceFile) - ); - - if (emitAsSingleStatement) { - write(" "); - emit(statements[0]); - } - else { - emitList(parentNode, statements, ListFormat.CaseOrDefaultClauseStatements); - } - } - - function emitHeritageClause(node: HeritageClause) { - write(" "); - writeTokenText(node.token); - write(" "); - emitList(node, node.types, ListFormat.HeritageClauseTypes); - } - - function emitCatchClause(node: CatchClause) { - writeLine(); - const openParenPos = writeToken(SyntaxKind.CatchKeyword, node.pos); - write(" "); - writeToken(SyntaxKind.OpenParenToken, openParenPos); - emit(node.variableDeclaration); - writeToken(SyntaxKind.CloseParenToken, node.variableDeclaration ? node.variableDeclaration.end : openParenPos); - 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. - const initializer = node.initializer; - if ((getEmitFlags(initializer) & EmitFlags.NoLeadingComments) === 0) { - const commentRange = getCommentRange(initializer); - emitTrailingCommentsOfPosition(commentRange.pos); - } - - emitExpression(initializer); - } - - function emitShorthandPropertyAssignment(node: ShorthandPropertyAssignment) { - emit(node.name); - if (node.objectAssignmentInitializer) { - write(" = "); - emitExpression(node.objectAssignmentInitializer); - } - } - - function emitSpreadAssignment(node: SpreadAssignment) { - if (node.expression) { - write("..."); - emitExpression(node.expression); - } - } - - // - // Enum - // - - function emitEnumMember(node: EnumMember) { - emit(node.name); - emitExpressionWithPrefix(" = ", node.initializer); - } - - // - // Top-level nodes - // - - function emitSourceFile(node: SourceFile) { - writeLine(); - emitShebang(); - emitBodyWithDetachedComments(node, node.statements, emitSourceFileWorker); - } - - function emitSourceFileWorker(node: SourceFile) { - const statements = node.statements; - const statementOffset = emitPrologueDirectives(statements); - const savedTempFlags = tempFlags; - tempFlags = 0; - emitHelpers(node); - emitList(node, statements, ListFormat.MultiLine, statementOffset); - tempFlags = savedTempFlags; - } - - // Transformation nodes - - function emitPartiallyEmittedExpression(node: PartiallyEmittedExpression) { - emitExpression(node.expression); - } - - /** - * Emits any prologue directives at the start of a Statement list, returning the - * number of prologue directives written to the output. - */ - function emitPrologueDirectives(statements: Node[], startWithNewLine?: boolean): number { - 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 - return i; - } - } - - 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); - if (shebang) { - write(shebang); - writeLine(); - } - } - - function emitModifiers(node: Node, modifiers: NodeArray) { - if (modifiers && modifiers.length) { - emitList(node, modifiers, ListFormat.Modifiers); - 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 emitEmbeddedStatement(parent: Node, node: Statement) { - if (isBlock(node) || getEmitFlags(parent) & EmitFlags.SingleLine) { - 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. - const mayEmitInterveningComments = (format & ListFormat.NoInterveningComments) === 0; - let shouldEmitInterveningComments = mayEmitInterveningComments; - if (shouldWriteLeadingLineTerminator(parentNode, children, format)) { - writeLine(); - shouldEmitInterveningComments = false; - } - else if (format & ListFormat.SpaceBetweenBraces) { - write(" "); - } - - // Increase the indent, if requested. - if (format & ListFormat.Indented) { - increaseIndent(); - } - - // Emit each child. - let previousSibling: Node; - let shouldDecreaseIndentAfterEmit: boolean; - 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)) { - // If a synthesized node in a single-line list starts on a new - // line, we should increase the indent. - if ((format & (ListFormat.LinesMask | ListFormat.Indented)) === ListFormat.SingleLine) { - increaseIndent(); - shouldDecreaseIndentAfterEmit = true; - } - - writeLine(); - shouldEmitInterveningComments = false; - } - else if (previousSibling && format & ListFormat.SpaceBetweenSiblings) { - write(" "); - } - } - - if (shouldEmitInterveningComments) { - const commentRange = getCommentRange(child); - emitTrailingCommentsOfPosition(commentRange.pos); - } - else { - shouldEmitInterveningComments = mayEmitInterveningComments; - } - - // Emit this child. - emit(child); - - if (shouldDecreaseIndentAfterEmit) { - decreaseIndent(); - shouldDecreaseIndentAfterEmit = false; - } - - 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 writeLineOrSpace(node: Node) { - if (getEmitFlags(node) & EmitFlags.SingleLine) { - write(" "); - } - else { - writeLine(); - } - } - - 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, 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; - } - - 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; - } - - if (format & ListFormat.PreserveLines) { - if (format & ListFormat.PreferNewLine) { - return true; - } - - const firstChild = children[0]; - if (firstChild === undefined) { - return !rangeIsOnSingleLine(parentNode, currentSourceFile); - } - else if (positionIsSynthesized(parentNode.pos) || nodeIsSynthesized(firstChild)) { - return synthesizedNodeStartsOnNewLine(firstChild, format); - } - else { - return !rangeStartPositionsAreOnSameLine(parentNode, firstChild, currentSourceFile); - } - } - 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, currentSourceFile); - } - } - else { - return nextNode.startsOnNewLine; - } - } - - function shouldWriteClosingLineTerminator(parentNode: Node, children: NodeArray, format: ListFormat) { - if (format & ListFormat.MultiLine) { - return (format & ListFormat.NoTrailingNewLine) === 0; - } - else if (format & ListFormat.PreserveLines) { - if (format & ListFormat.PreferNewLine) { - return true; - } - - const lastChild = lastOrUndefined(children); - if (lastChild === undefined) { - return !rangeIsOnSingleLine(parentNode, currentSourceFile); - } - else if (positionIsSynthesized(parentNode.pos) || nodeIsSynthesized(lastChild)) { - return synthesizedNodeStartsOnNewLine(lastChild, format); - } - else { - return !rangeEndPositionsAreOnSameLine(parentNode, lastChild, currentSourceFile); - } - } - 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 needsIndentation(parent: Node, node1: Node, node2: Node): boolean { - parent = skipSynthesizedParentheses(parent); - node1 = skipSynthesizedParentheses(node1); - node2 = skipSynthesizedParentheses(node2); - - // Always use a newline for synthesized code if the synthesizer desires it. - if (node2.startsOnNewLine) { - return true; - } - - return !nodeIsSynthesized(parent) - && !nodeIsSynthesized(node1) - && !nodeIsSynthesized(node2) - && !rangeEndIsOnSameLineAsRangeStart(node1, node2, currentSourceFile); - } - - function skipSynthesizedParentheses(node: Node) { - while (node.kind === SyntaxKind.ParenthesizedExpression && nodeIsSynthesized(node)) { - node = (node).expression; - } - - return node; - } - - function getTextOfNode(node: Node, includeTrivia?: boolean): string { - if (isGeneratedIdentifier(node)) { - return getGeneratedIdentifier(node); - } - else if (isIdentifier(node) && (nodeIsSynthesized(node) || !node.parent)) { - return unescapeIdentifier(node.text); - } - else if (node.kind === SyntaxKind.StringLiteral && (node).textSourceNode) { - return getTextOfNode((node).textSourceNode, includeTrivia); - } - else if (isLiteralExpression(node) && (nodeIsSynthesized(node) || !node.parent)) { - return node.text; - } - - return getSourceTextOfNodeFromSourceFile(currentSourceFile, node, includeTrivia); - } - - function getLiteralTextOfNode(node: LiteralLikeNode): string { - if (node.kind === SyntaxKind.StringLiteral && (node).textSourceNode) { - const textSourceNode = (node).textSourceNode; - if (isIdentifier(textSourceNode)) { - return "\"" + escapeNonAsciiCharacters(escapeString(getTextOfNode(textSourceNode))) + "\""; - } - else { - return getLiteralTextOfNode(textSourceNode); - } - } - - return getLiteralText(node, currentSourceFile, languageVersion); - } - - function isSingleLineEmptyBlock(block: Block) { - return !block.multiLine - && isEmptyBlock(block); - } - - function isEmptyBlock(block: BlockLike) { - return block.statements.length === 0 - && rangeEndIsOnSameLineAsRangeStart(block, block, currentSourceFile); - } - - function isUniqueName(name: string): boolean { - return !resolver.hasGlobalName(name) && - !currentFileIdentifiers.has(name) && - !generatedNameSet.has(name); - } - - function isUniqueLocalName(name: string, container: Node): boolean { - for (let node = container; isNodeDescendantOf(node, container); node = node.nextContainer) { - if (node.locals) { - const local = node.locals.get(name); - // We conservatively include alias symbols to cover cases where they're emitted as locals - if (local && local.flags & (SymbolFlags.Value | SymbolFlags.ExportValue | SymbolFlags.Alias)) { - return false; - } - } - } - return true; - } - - /** - * 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; - } - } - } - } - - // 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._) { - baseName += "_"; - } - let i = 1; - while (true) { - const generatedName = baseName + i; - if (isUniqueName(generatedName)) { - generatedNameSet.set(generatedName, generatedName); - return generatedName; - } - i++; - } - } - - function generateNameForModuleOrEnum(node: ModuleDeclaration | EnumDeclaration) { - 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); - } - - function generateNameForImportOrExportDeclaration(node: ImportDeclaration | ExportDeclaration) { - const expr = getExternalModuleName(node); - const baseName = expr.kind === SyntaxKind.StringLiteral ? - escapeIdentifier(makeIdentifierFromModuleName((expr).text)) : "module"; - return makeUniqueName(baseName); - } - - function generateNameForExportDefault() { - return makeUniqueName("default"); - } - - function generateNameForClassExpression() { - return makeUniqueName("class"); - } - - function generateNameForMethodOrAccessor(node: MethodDeclaration | AccessorDeclaration) { - if (isIdentifier(node.name)) { - return generateNameForNodeCached(node.name); - } - return makeTempVariableName(TempFlags.Auto); - } - - /** - * Generates a unique name from a node. - * - * @param node A node. - */ - function generateNameForNode(node: Node): string { - switch (node.kind) { - case SyntaxKind.Identifier: - return makeUniqueName(getTextOfNode(node)); - case SyntaxKind.ModuleDeclaration: - case SyntaxKind.EnumDeclaration: - return generateNameForModuleOrEnum(node); - case SyntaxKind.ImportDeclaration: - case SyntaxKind.ExportDeclaration: - return generateNameForImportOrExportDeclaration(node); - case SyntaxKind.FunctionDeclaration: - case SyntaxKind.ClassDeclaration: - case SyntaxKind.ExportAssignment: - return generateNameForExportDefault(); - case SyntaxKind.ClassExpression: - return generateNameForClassExpression(); - case SyntaxKind.MethodDeclaration: - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - return generateNameForMethodOrAccessor(node); - default: - return makeTempVariableName(TempFlags.Auto); - } - } - - /** - * Generates a unique identifier for a node. - * - * @param name A generated name. - */ - function generateName(name: Identifier) { - switch (name.autoGenerateKind) { - case GeneratedIdentifierKind.Auto: - return makeTempVariableName(TempFlags.Auto); - case GeneratedIdentifierKind.Loop: - return makeTempVariableName(TempFlags._i); - case GeneratedIdentifierKind.Unique: - return makeUniqueName(name.text); - } - - Debug.fail("Unsupported GeneratedIdentifierKind."); - } - - /** - * Gets the node from which a name should be generated. - * - * @param name A generated name wrapper. - */ - function getNodeForGeneratedName(name: Identifier) { - const autoGenerateId = name.autoGenerateId; - let node = name as Node; - let original = node.original; - while (original) { - node = original; - - // if "node" is a different generated name (having a different - // "autoGenerateId"), use it and stop traversing. - if (isIdentifier(node) - && node.autoGenerateKind === GeneratedIdentifierKind.Node - && node.autoGenerateId !== autoGenerateId) { - break; - } - - original = node.original; - } - - // 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))); - } - - /** - * 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 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 = 0, // Prints the list on a single line (default). - MultiLine = 1 << 0, // Prints the list on multiple lines. - PreserveLines = 1 << 1, // Prints the list using line preservation if possible. - LinesMask = SingleLine | MultiLine | PreserveLines, - - // Delimiters - NotDelimited = 0, // There is no delimiter between list items (default). - BarDelimited = 1 << 2, // Each list item is space-and-bar (" |") delimited. - AmpersandDelimited = 1 << 3, // Each list item is space-and-ampersand (" &") delimited. - CommaDelimited = 1 << 4, // Each list item is comma (",") delimited. - DelimitersMask = BarDelimited | AmpersandDelimited | CommaDelimited, - - AllowTrailingComma = 1 << 5, // Write a trailing comma (",") if present. - - // Whitespace - Indented = 1 << 6, // The list should be indented. - SpaceBetweenBraces = 1 << 7, // Inserts a space after the opening brace and before the closing brace. - SpaceBetweenSiblings = 1 << 8, // Inserts a space between each sibling node. - - // 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. - NoTrailingNewLine = 1 << 16, // Do not emit a trailing NewLine for a MultiLine list. - NoInterveningComments = 1 << 17, // Do not emit comments between each node - - // Precomputed Formats - Modifiers = SingleLine | SpaceBetweenSiblings, - HeritageClauses = SingleLine | SpaceBetweenSiblings, - TypeLiteralMembers = MultiLine | Indented, - TupleTypeElements = CommaDelimited | SpaceBetweenSiblings | SingleLine | Indented, - UnionTypeConstituents = BarDelimited | SpaceBetweenSiblings | SingleLine, - IntersectionTypeConstituents = AmpersandDelimited | SpaceBetweenSiblings | SingleLine, - ObjectBindingPatternElements = SingleLine | AllowTrailingComma | SpaceBetweenBraces | CommaDelimited | SpaceBetweenSiblings, - ArrayBindingPatternElements = SingleLine | AllowTrailingComma | CommaDelimited | SpaceBetweenSiblings, - ObjectLiteralExpressionProperties = PreserveLines | CommaDelimited | SpaceBetweenSiblings | SpaceBetweenBraces | Indented | Braces, - ArrayLiteralExpressionElements = PreserveLines | CommaDelimited | SpaceBetweenSiblings | AllowTrailingComma | Indented | SquareBrackets, - CallExpressionArguments = CommaDelimited | SpaceBetweenSiblings | SingleLine | Parenthesis, - NewExpressionArguments = CommaDelimited | SpaceBetweenSiblings | SingleLine | Parenthesis | OptionalIfUndefined, - TemplateExpressionSpans = SingleLine | NoInterveningComments, - SingleLineBlockStatements = SpaceBetweenBraces | SpaceBetweenSiblings | SingleLine, - MultiLineBlockStatements = Indented | MultiLine, - VariableDeclarationList = CommaDelimited | SpaceBetweenSiblings | SingleLine, - SingleLineFunctionBodyStatements = SingleLine | SpaceBetweenSiblings | SpaceBetweenBraces, - MultiLineFunctionBodyStatements = MultiLine, - ClassHeritageClauses = SingleLine | SpaceBetweenSiblings, - ClassMembers = Indented | MultiLine, - InterfaceMembers = Indented | MultiLine, - EnumMembers = CommaDelimited | Indented | MultiLine, - CaseBlockClauses = Indented | MultiLine, - NamedImportsOrExportsElements = CommaDelimited | SpaceBetweenSiblings | AllowTrailingComma | SingleLine | SpaceBetweenBraces, - JsxElementChildren = SingleLine | NoInterveningComments, - JsxElementAttributes = SingleLine | SpaceBetweenSiblings | NoInterveningComments, - CaseOrDefaultClauseStatements = Indented | MultiLine | NoTrailingNewLine | OptionalIfEmpty, - HeritageClauseTypes = CommaDelimited | SpaceBetweenSiblings | SingleLine, - SourceFileStatements = MultiLine | NoTrailingNewLine, - Decorators = MultiLine | Optional, - TypeArguments = CommaDelimited | SpaceBetweenSiblings | SingleLine | Indented | AngleBrackets | Optional, - TypeParameters = CommaDelimited | SpaceBetweenSiblings | SingleLine | Indented | AngleBrackets | Optional, - 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 f63094ae16d7e..f2a8de3a93b4d 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/printer.ts b/src/compiler/printer.ts new file mode 100644 index 0000000000000..9ae824dac6700 --- /dev/null +++ b/src/compiler/printer.ts @@ -0,0 +1,2659 @@ +/// + +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 delimiters = createDelimiterMap(); + const brackets = createBracketsMap(); + + export function createPrinter(writer: EmitTextWriter, compilerOptions: CompilerOptions, printerOptions: PrinterOptions = {}): Printer { + const languageVersion = getEmitScriptTarget(compilerOptions); + const moduleKind = getEmitModuleKind(compilerOptions); + const { + hasGlobalName, + onEmitSourceMapOfNode, + onEmitSourceMapOfToken, + onEmitCommentsOfNode, + onEmitDetachedCommentsOfNode, + onEmitTrailingCommentsOfPosition, + onEmitNode, + onEmitHelpers, + onSetSourceFile, + onSubstituteNode, + } = printerOptions; + const { + write, + writeLine, + increaseIndent, + decreaseIndent + } = writer; + + 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. + + return { + printNode, + printFile, + printBundle + }; + + function printNode(hint: EmitHint, node: Node, sourceFile: SourceFile) { + print(hint, node, sourceFile); + resetGeneratedNames(); + } + + function printBundle(bundle: Bundle) { + if (moduleKind) { + emitHelpersIndirect(bundle); + } + for (const sourceFile of bundle.sourceFiles) { + print(EmitHint.SourceFile, sourceFile, sourceFile); + } + resetGeneratedNames(); + } + + function printFile(sourceFile: SourceFile) { + print(EmitHint.SourceFile, sourceFile, sourceFile); + resetGeneratedNames(); + } + + function print(hint: EmitHint, node: Node, sourceFile: SourceFile) { + setSourceFile(sourceFile); + pipelineEmitWithNotification(hint, node); + } + + function setSourceFile(sourceFile: SourceFile) { + currentSourceFile = sourceFile; + + if (onSetSourceFile) { + onSetSourceFile(sourceFile); + } + } + + function emit(node: Node, hint = EmitHint.Unspecified) { + pipelineEmitWithNotification(hint, node); + } + + function emitIdentifierName(node: Identifier) { + pipelineEmitWithNotification(EmitHint.IdentifierName, node); + } + + function emitExpression(node: Expression) { + pipelineEmitWithNotification(EmitHint.Expression, node); + } + + function pipelineEmitWithNotification(hint: EmitHint, node: Node) { + if (onEmitNode) { + onEmitNode(hint, node, pipelineEmitWithComments); + } + else { + pipelineEmitWithComments(hint, node); + } + } + + function pipelineEmitWithComments(hint: EmitHint, node: Node) { + if (onEmitCommentsOfNode && hint !== EmitHint.SourceFile) { + onEmitCommentsOfNode(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; + switch (kind) { + // Pseudo-literals + case SyntaxKind.TemplateHead: + case SyntaxKind.TemplateMiddle: + case SyntaxKind.TemplateTail: + return emitLiteral(node); + + // Identifiers + 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 + 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 emitThisType(); + case SyntaxKind.TypeOperator: + return emitTypeOperator(node); + case SyntaxKind.IndexedAccessType: + return emitIndexedAccessType(node); + case SyntaxKind.MappedType: + return emitMappedType(node); + case SyntaxKind.LiteralType: + return emitLiteralType(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 emitSemicolonClassElement(); + + // Statements + case SyntaxKind.Block: + return emitBlock(node); + case SyntaxKind.VariableStatement: + return emitVariableStatement(node); + case SyntaxKind.EmptyStatement: + return emitEmptyStatement(); + 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.JsxOpeningElement: + return emitJsxOpeningElement(node); + case SyntaxKind.JsxClosingElement: + return emitJsxClosingElement(node); + case SyntaxKind.JsxAttribute: + return emitJsxAttribute(node); + case SyntaxKind.JsxSpreadAttribute: + return emitJsxSpreadAttribute(node); + case SyntaxKind.JsxExpression: + return emitJsxExpression(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); + case SyntaxKind.SpreadAssignment: + return emitSpreadAssignment(node as SpreadAssignment); + + // Enum + case SyntaxKind.EnumMember: + return emitEnumMember(node); + + // JSDoc nodes (ignored) + // Transformation nodes (ignored) + } + + // If the node is an expression, try to emit it as an expression with + // substitution. + if (isExpression(node)) { + return pipelineEmitWithSubstitution(EmitHint.Expression, node); + } + } + + function pipelineEmitExpression(node: Node): void { + const kind = node.kind; + switch (kind) { + // Literals + case SyntaxKind.NumericLiteral: + return emitNumericLiteral(node); + + case SyntaxKind.StringLiteral: + case SyntaxKind.RegularExpressionLiteral: + case SyntaxKind.NoSubstitutionTemplateLiteral: + return emitLiteral(node); + + // Identifiers + case SyntaxKind.Identifier: + return emitIdentifier(node); + + // Reserved words + case SyntaxKind.FalseKeyword: + case SyntaxKind.NullKeyword: + case SyntaxKind.SuperKeyword: + case SyntaxKind.TrueKeyword: + case SyntaxKind.ThisKeyword: + writeTokenText(kind); + return; + + // 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.SpreadElement: + return emitSpreadExpression(node); + case SyntaxKind.ClassExpression: + return emitClassExpression(node); + case SyntaxKind.OmittedExpression: + return; + case SyntaxKind.AsExpression: + return emitAsExpression(node); + case SyntaxKind.NonNullExpression: + return emitNonNullExpression(node); + case SyntaxKind.MetaProperty: + return emitMetaProperty(node); + + // JSX + case SyntaxKind.JsxElement: + return emitJsxElement(node); + case SyntaxKind.JsxSelfClosingElement: + return emitJsxSelfClosingElement(node); + + // Transformation nodes + case SyntaxKind.PartiallyEmittedExpression: + return emitPartiallyEmittedExpression(node); + } + } + + function emitBodyIndirect(node: Node, elements: NodeArray, emitCallback: (node: Node) => void): void { + if (onEmitDetachedCommentsOfNode) { + onEmitDetachedCommentsOfNode(node, elements, emitCallback); + } + else { + emitCallback(node); + } + } + + function emitHelpersIndirect(node: Node) { + if (onEmitHelpers) { + onEmitHelpers(node, writeLines); + } + } + + // + // Literals/Pseudo-literals + // + + // SyntaxKind.NumericLiteral + function emitNumericLiteral(node: NumericLiteral) { + emitLiteral(node); + if (node.trailingComment) { + write(` /*${node.trailingComment}*/`); + } + } + + // SyntaxKind.StringLiteral + // SyntaxKind.RegularExpressionLiteral + // SyntaxKind.NoSubstitutionTemplateLiteral + // SyntaxKind.TemplateHead + // SyntaxKind.TemplateMiddle + // SyntaxKind.TemplateTail + function emitLiteral(node: LiteralLikeNode) { + const text = getLiteralTextOfNode(node); + if ((compilerOptions.sourceMap || compilerOptions.inlineSourceMap) + && (node.kind === SyntaxKind.StringLiteral || isTemplateLiteralKind(node.kind))) { + writer.writeLiteral(text); + } + else { + write(text); + } + } + + // + // Identifiers + // + + function emitIdentifier(node: Identifier) { + write(getTextOfNode(node, /*includeTrivia*/ false)); + } + + // + // 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, emitSignatureHead); + } + + function emitConstructor(node: ConstructorDeclaration) { + emitModifiers(node, node.modifiers); + write("constructor"); + emitSignatureAndBody(node, emitSignatureHead); + } + + function emitAccessorDeclaration(node: AccessorDeclaration) { + emitDecorators(node, node.decorators); + emitModifiers(node, node.modifiers); + write(node.kind === SyntaxKind.GetAccessor ? "get " : "set "); + emit(node.name); + emitSignatureAndBody(node, emitSignatureHead); + } + + 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(";"); + } + + function emitSemicolonClassElement() { + 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(")"); + } + + function emitThisType() { + write("this"); + } + + function emitTypeOperator(node: TypeOperatorNode) { + writeTokenText(node.operator); + write(" "); + emit(node.type); + } + + function emitIndexedAccessType(node: IndexedAccessTypeNode) { + emit(node.objectType); + write("["); + emit(node.indexType); + write("]"); + } + + function emitMappedType(node: MappedTypeNode) { + write("{"); + writeLine(); + increaseIndent(); + writeIfPresent(node.readonlyToken, "readonly "); + write("["); + emit(node.typeParameter.name); + write(" in "); + emit(node.typeParameter.constraint); + write("]"); + writeIfPresent(node.questionToken, "?"); + write(": "); + emit(node.type); + write(";"); + writeLine(); + decreaseIndent(); + write("}"); + } + + function emitLiteralType(node: LiteralTypeNode) { + emitExpression(node.literal); + } + + // + // 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 = node.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 indentedFlag = getEmitFlags(node) & EmitFlags.Indented; + if (indentedFlag) { + increaseIndent(); + } + + const preferNewLine = node.multiLine ? ListFormat.PreferNewLine : ListFormat.None; + const allowTrailingComma = languageVersion >= ScriptTarget.ES5 ? ListFormat.AllowTrailingComma : ListFormat.None; + emitList(node, properties, ListFormat.ObjectLiteralExpressionProperties | allowTrailingComma | preferNewLine); + + if (indentedFlag) { + decreaseIndent(); + } + } + } + + function emitPropertyAccessExpression(node: PropertyAccessExpression) { + let indentBeforeDot = false; + let indentAfterDot = false; + if (!(getEmitFlags(node) & EmitFlags.NoIndentation)) { + const dotRangeStart = node.expression.end; + 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); + } + + emitExpression(node.expression); + increaseIndentIf(indentBeforeDot); + + const shouldEmitDotDot = !indentBeforeDot && needsDotDotForPropertyAccess(node.expression); + 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 is a decimal literal that was originally written with a dot + const text = getLiteralTextOfNode(expression); + return getNumericLiteralFlags(text, /*hint*/ NumericLiteralFlags.All) === NumericLiteralFlags.None + && !(expression).isOctalLiteral + && text.indexOf(tokenToString(SyntaxKind.DotToken)) < 0; + } + else if (isPropertyAccessExpression(expression) || isElementAccessExpression(expression)) { + // check if constant enum value is integer + const constantValue = getConstantValue(expression); + // isFinite handles cases when constantValue is undefined + return isFinite(constantValue) + && Math.floor(constantValue) === constantValue + && compilerOptions.removeComments; + } + } + + function emitElementAccessExpression(node: ElementAccessExpression) { + emitExpression(node.expression); + write("["); + emitExpression(node.argumentExpression); + write("]"); + } + + function emitCallExpression(node: CallExpression) { + emitExpression(node.expression); + emitTypeArguments(node, node.typeArguments); + emitExpressionList(node, node.arguments, ListFormat.CallExpressionArguments); + } + + function emitNewExpression(node: NewExpression) { + write("new "); + emitExpression(node.expression); + emitTypeArguments(node, node.typeArguments); + emitExpressionList(node, node.arguments, ListFormat.NewExpressionArguments); + } + + function emitTaggedTemplateExpression(node: TaggedTemplateExpression) { + emitExpression(node.tag); + write(" "); + emitExpression(node.template); + } + + function emitTypeAssertionExpression(node: TypeAssertion) { + 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); + emitSignatureAndBody(node, emitArrowFunctionHead); + } + + 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) { + writeTokenText(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) { + emitExpression(node.operand); + writeTokenText(node.operator); + } + + function emitBinaryExpression(node: BinaryExpression) { + const isCommaOperator = node.operatorToken.kind !== SyntaxKind.CommaToken; + const indentBeforeOperator = needsIndentation(node, node.left, node.operatorToken); + const indentAfterOperator = needsIndentation(node, node.operatorToken, node.right); + + emitExpression(node.left); + increaseIndentIf(indentBeforeOperator, isCommaOperator ? " " : undefined); + writeTokenText(node.operatorToken.kind); + 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 emitSpreadExpression(node: SpreadElement) { + write("..."); + emitExpression(node.expression); + } + + function emitClassExpression(node: ClassExpression) { + emitClassDeclarationOrExpression(node); + } + + function emitExpressionWithTypeArguments(node: ExpressionWithTypeArguments) { + emitExpression(node.expression); + emitTypeArguments(node, node.typeArguments); + } + + function emitAsExpression(node: AsExpression) { + emitExpression(node.expression); + if (node.type) { + write(" as "); + emit(node.type); + } + } + + function emitNonNullExpression(node: NonNullExpression) { + emitExpression(node.expression); + write("!"); + } + + function emitMetaProperty(node: MetaProperty) { + writeToken(node.keywordToken, node.pos); + write("."); + emit(node.name); + } + + // + // Misc + // + + function emitTemplateSpan(node: TemplateSpan) { + emitExpression(node.expression); + emit(node.literal); + } + + // + // Statements + // + + function emitBlock(node: Block) { + if (isSingleLineEmptyBlock(node)) { + writeToken(SyntaxKind.OpenBraceToken, node.pos, /*contextNode*/ node); + write(" "); + writeToken(SyntaxKind.CloseBraceToken, node.statements.end, /*contextNode*/ node); + } + else { + writeToken(SyntaxKind.OpenBraceToken, node.pos, /*contextNode*/ node); + emitBlockStatements(node); + writeToken(SyntaxKind.CloseBraceToken, node.statements.end, /*contextNode*/ node); + } + } + + function emitBlockStatements(node: BlockLike) { + if (getEmitFlags(node) & EmitFlags.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 emitEmptyStatement() { + write(";"); + } + + function emitExpressionStatement(node: ExpressionStatement) { + emitExpression(node.expression); + write(";"); + } + + function emitIfStatement(node: IfStatement) { + const openParenPos = writeToken(SyntaxKind.IfKeyword, node.pos, node); + write(" "); + writeToken(SyntaxKind.OpenParenToken, openParenPos, node); + emitExpression(node.expression); + writeToken(SyntaxKind.CloseParenToken, node.expression.end, node); + emitEmbeddedStatement(node, node.thenStatement); + if (node.elseStatement) { + writeLineOrSpace(node); + writeToken(SyntaxKind.ElseKeyword, node.thenStatement.end, node); + if (node.elseStatement.kind === SyntaxKind.IfStatement) { + write(" "); + emit(node.elseStatement); + } + else { + emitEmbeddedStatement(node, node.elseStatement); + } + } + } + + function emitDoStatement(node: DoStatement) { + write("do"); + emitEmbeddedStatement(node, node.statement); + if (isBlock(node.statement)) { + write(" "); + } + else { + writeLineOrSpace(node); + } + + write("while ("); + emitExpression(node.expression); + write(");"); + } + + function emitWhileStatement(node: WhileStatement) { + write("while ("); + emitExpression(node.expression); + write(")"); + emitEmbeddedStatement(node, node.statement); + } + + function emitForStatement(node: ForStatement) { + const openParenPos = writeToken(SyntaxKind.ForKeyword, node.pos); + write(" "); + writeToken(SyntaxKind.OpenParenToken, openParenPos, /*contextNode*/ node); + emitForBinding(node.initializer); + write(";"); + emitExpressionWithPrefix(" ", node.condition); + write(";"); + emitExpressionWithPrefix(" ", node.incrementor); + write(")"); + emitEmbeddedStatement(node, node.statement); + } + + function emitForInStatement(node: ForInStatement) { + const openParenPos = writeToken(SyntaxKind.ForKeyword, node.pos); + write(" "); + writeToken(SyntaxKind.OpenParenToken, openParenPos); + emitForBinding(node.initializer); + write(" in "); + emitExpression(node.expression); + writeToken(SyntaxKind.CloseParenToken, node.expression.end); + emitEmbeddedStatement(node, node.statement); + } + + function emitForOfStatement(node: ForOfStatement) { + const openParenPos = writeToken(SyntaxKind.ForKeyword, node.pos); + write(" "); + writeToken(SyntaxKind.OpenParenToken, openParenPos); + emitForBinding(node.initializer); + write(" of "); + emitExpression(node.expression); + writeToken(SyntaxKind.CloseParenToken, node.expression.end); + emitEmbeddedStatement(node, node.statement); + } + + function emitForBinding(node: VariableDeclarationList | Expression) { + if (node !== undefined) { + if (node.kind === SyntaxKind.VariableDeclarationList) { + emit(node); + } + else { + emitExpression(node); + } + } + } + + function emitContinueStatement(node: ContinueStatement) { + writeToken(SyntaxKind.ContinueKeyword, node.pos); + emitWithPrefix(" ", node.label); + write(";"); + } + + function emitBreakStatement(node: BreakStatement) { + writeToken(SyntaxKind.BreakKeyword, node.pos); + emitWithPrefix(" ", node.label); + write(";"); + } + + function emitReturnStatement(node: ReturnStatement) { + writeToken(SyntaxKind.ReturnKeyword, node.pos, /*contextNode*/ node); + emitExpressionWithPrefix(" ", node.expression); + write(";"); + } + + function emitWithStatement(node: WithStatement) { + write("with ("); + emitExpression(node.expression); + write(")"); + emitEmbeddedStatement(node, node.statement); + } + + function emitSwitchStatement(node: SwitchStatement) { + const openParenPos = writeToken(SyntaxKind.SwitchKeyword, node.pos); + write(" "); + writeToken(SyntaxKind.OpenParenToken, openParenPos); + emitExpression(node.expression); + writeToken(SyntaxKind.CloseParenToken, node.expression.end); + 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); + if (node.catchClause) { + writeLineOrSpace(node); + emit(node.catchClause); + } + if (node.finallyBlock) { + writeLineOrSpace(node); + write("finally "); + emit(node.finallyBlock); + } + } + + function emitDebuggerStatement(node: DebuggerStatement) { + writeToken(SyntaxKind.DebuggerKeyword, node.pos); + write(";"); + } + + // + // Declarations + // + + function emitVariableDeclaration(node: VariableDeclaration) { + emit(node.name); + emitWithPrefix(": ", node.type); + 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 "); + emitIdentifierName(node.name); + emitSignatureAndBody(node, emitSignatureHead); + } + + function emitSignatureAndBody(node: FunctionLikeDeclaration, emitSignatureHead: (node: SignatureDeclaration) => void) { + const body = node.body; + if (body) { + if (isBlock(body)) { + const indentedFlag = getEmitFlags(node) & EmitFlags.Indented; + if (indentedFlag) { + increaseIndent(); + } + + if (getEmitFlags(node) & EmitFlags.ReuseTempVariableScope) { + emitSignatureHead(node); + emitBlockFunctionBody(body); + } + else { + pushNameGenerationScope(); + emitSignatureHead(node); + emitBlockFunctionBody(body); + popNameGenerationScope(); + } + + if (indentedFlag) { + decreaseIndent(); + } + } + else { + emitSignatureHead(node); + write(" "); + emitExpression(body); + } + } + 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(body: Block) { + // We must emit a function body as a single-line body in the following case: + // * The body has NodeEmitFlags.SingleLine specified. + + // We must emit a function body as a multi-line body in the following cases: + // * The body is explicitly marked as multi-line. + // * A non-synthesized body's start and end position are on different lines. + // * Any statement in the body starts on a new line. + + if (getEmitFlags(body) & EmitFlags.SingleLine) { + return true; + } + + if (body.multiLine) { + return false; + } + + if (!nodeIsSynthesized(body) && !rangeIsOnSingleLine(body, currentSourceFile)) { + return false; + } + + if (shouldWriteLeadingLineTerminator(body, body.statements, ListFormat.PreserveLines) + || shouldWriteClosingLineTerminator(body, body.statements, ListFormat.PreserveLines)) { + return false; + } + + let previousStatement: Statement; + for (const statement of body.statements) { + if (shouldWriteSeparatingLineTerminator(previousStatement, statement, ListFormat.PreserveLines)) { + return false; + } + + previousStatement = statement; + } + + return true; + } + + function emitBlockFunctionBody(body: Block) { + write(" {"); + increaseIndent(); + + const emitBlockFunctionBody = shouldEmitBlockFunctionBodyOnSingleLine(body) + ? emitBlockFunctionBodyOnSingleLine + : emitBlockFunctionBodyWorker; + + emitBodyIndirect(body, body.statements, emitBlockFunctionBody); + + decreaseIndent(); + writeToken(SyntaxKind.CloseBraceToken, body.statements.end, body); + } + + function emitBlockFunctionBodyOnSingleLine(body: Block) { + emitBlockFunctionBodyWorker(body, /*emitBlockFunctionBodyOnSingleLine*/ true); + } + + function emitBlockFunctionBodyWorker(body: Block, emitBlockFunctionBodyOnSingleLine?: boolean) { + // Emit all the prologue directives (like "use strict"). + const statementOffset = emitPrologueDirectives(body.statements, /*startWithNewLine*/ true); + const pos = writer.getTextPos(); + emitHelpersIndirect(body); + if (statementOffset === 0 && pos === writer.getTextPos() && emitBlockFunctionBodyOnSingleLine) { + decreaseIndent(); + emitList(body, body.statements, ListFormat.SingleLineFunctionBodyStatements); + increaseIndent(); + } + else { + emitList(body, body.statements, ListFormat.MultiLineFunctionBodyStatements, statementOffset); + } + } + + function emitClassDeclaration(node: ClassDeclaration) { + emitClassDeclarationOrExpression(node); + } + + function emitClassDeclarationOrExpression(node: ClassDeclaration | ClassExpression) { + emitDecorators(node, node.decorators); + emitModifiers(node, node.modifiers); + write("class"); + emitNodeWithPrefix(" ", node.name, emitIdentifierName); + + const indentedFlag = getEmitFlags(node) & EmitFlags.Indented; + if (indentedFlag) { + increaseIndent(); + } + + emitTypeParameters(node, node.typeParameters); + emitList(node, node.heritageClauses, ListFormat.ClassHeritageClauses); + + pushNameGenerationScope(); + write(" {"); + emitList(node, node.members, ListFormat.ClassMembers); + write("}"); + popNameGenerationScope(); + + if (indentedFlag) { + decreaseIndent(); + } + } + + 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.HeritageClauses); + 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); + pushNameGenerationScope(); + write(" {"); + emitList(node, node.members, ListFormat.EnumMembers); + write("}"); + popNameGenerationScope(); + } + + 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 (isEmptyBlock(node)) { + write("{ }"); + } + else { + pushNameGenerationScope(); + write("{"); + increaseIndent(); + emitBlockStatements(node); + write("}"); + popNameGenerationScope(); + } + } + + function emitCaseBlock(node: CaseBlock) { + writeToken(SyntaxKind.OpenBraceToken, node.pos); + emitList(node, node.clauses, ListFormat.CaseBlockClauses); + writeToken(SyntaxKind.CloseBraceToken, node.clauses.end); + } + + 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 "); + if (node.importClause) { + emit(node.importClause); + write(" from "); + } + emitExpression(node.moduleSpecifier); + write(";"); + } + + function emitImportClause(node: ImportClause) { + emit(node.name); + if (node.name && node.namedBindings) { + write(", "); + } + emit(node.namedBindings); + } + + function emitNamespaceImport(node: NamespaceImport) { + write("* as "); + emit(node.name); + } + + 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); + } + else { + write("*"); + } + if (node.moduleSpecifier) { + 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("<"); + emitJsxTagName(node.tagName); + write(" "); + emitList(node, node.attributes, ListFormat.JsxElementAttributes); + write("/>"); + } + + function emitJsxOpeningElement(node: JsxOpeningElement) { + write("<"); + emitJsxTagName(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(""); + } + + function emitJsxAttribute(node: JsxAttribute) { + emit(node.name); + emitWithPrefix("=", node.initializer); + } + + function emitJsxSpreadAttribute(node: JsxSpreadAttribute) { + write("{..."); + emitExpression(node.expression); + write("}"); + } + + function emitJsxExpression(node: JsxExpression) { + if (node.expression) { + write("{"); + if (node.dotDotDotToken) { + write("..."); + } + emitExpression(node.expression); + write("}"); + } + } + + function emitJsxTagName(node: JsxTagNameExpression) { + if (node.kind === SyntaxKind.Identifier) { + emitExpression(node); + } + else { + emit(node); + } + } + + // + // 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) { + const emitAsSingleStatement = + statements.length === 1 && + ( + // treat synthesized nodes as located on the same line for emit purposes + nodeIsSynthesized(parentNode) || + nodeIsSynthesized(statements[0]) || + rangeStartPositionsAreOnSameLine(parentNode, statements[0], currentSourceFile) + ); + + if (emitAsSingleStatement) { + write(" "); + emit(statements[0]); + } + else { + emitList(parentNode, statements, ListFormat.CaseOrDefaultClauseStatements); + } + } + + function emitHeritageClause(node: HeritageClause) { + write(" "); + writeTokenText(node.token); + write(" "); + emitList(node, node.types, ListFormat.HeritageClauseTypes); + } + + function emitCatchClause(node: CatchClause) { + writeLine(); + const openParenPos = writeToken(SyntaxKind.CatchKeyword, node.pos); + write(" "); + writeToken(SyntaxKind.OpenParenToken, openParenPos); + emit(node.variableDeclaration); + writeToken(SyntaxKind.CloseParenToken, node.variableDeclaration ? node.variableDeclaration.end : openParenPos); + 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. + const initializer = node.initializer; + if (onEmitTrailingCommentsOfPosition && (getEmitFlags(initializer) & EmitFlags.NoLeadingComments) === 0) { + const commentRange = getCommentRange(initializer); + onEmitTrailingCommentsOfPosition(commentRange.pos); + } + emitExpression(initializer); + } + + function emitShorthandPropertyAssignment(node: ShorthandPropertyAssignment) { + emit(node.name); + if (node.objectAssignmentInitializer) { + write(" = "); + emitExpression(node.objectAssignmentInitializer); + } + } + + function emitSpreadAssignment(node: SpreadAssignment) { + if (node.expression) { + write("..."); + emitExpression(node.expression); + } + } + + // + // Enum + // + + function emitEnumMember(node: EnumMember) { + emit(node.name); + emitExpressionWithPrefix(" = ", node.initializer); + } + + // + // Top-level nodes + // + + function emitSourceFile(node: SourceFile) { + writeLine(); + emitShebang(); + emitBodyIndirect(node, node.statements, emitSourceFileWorker); + } + + function emitSourceFileWorker(node: SourceFile) { + const statements = node.statements; + const statementOffset = emitPrologueDirectives(statements); + pushNameGenerationScope(); + emitHelpersIndirect(node); + emitList(node, statements, ListFormat.MultiLine, statementOffset); + popNameGenerationScope(); + } + + // Transformation nodes + + function emitPartiallyEmittedExpression(node: PartiallyEmittedExpression) { + emitExpression(node.expression); + } + + /** + * Emits any prologue directives at the start of a Statement list, returning the + * number of prologue directives written to the output. + */ + function emitPrologueDirectives(statements: Node[], startWithNewLine?: boolean): number { + 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 + return i; + } + } + + return statements.length; + } + + // + // Helpers + // + + function emitShebang() { + const shebang = getShebang(currentSourceFile.text); + if (shebang) { + write(shebang); + writeLine(); + } + } + + function emitModifiers(node: Node, modifiers: NodeArray) { + if (modifiers && modifiers.length) { + emitList(node, modifiers, ListFormat.Modifiers); + 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 emitEmbeddedStatement(parent: Node, node: Statement) { + if (isBlock(node) || getEmitFlags(parent) & EmitFlags.SingleLine) { + 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. + const mayEmitInterveningComments = (format & ListFormat.NoInterveningComments) === 0; + let shouldEmitInterveningComments = mayEmitInterveningComments; + if (shouldWriteLeadingLineTerminator(parentNode, children, format)) { + writeLine(); + shouldEmitInterveningComments = false; + } + else if (format & ListFormat.SpaceBetweenBraces) { + write(" "); + } + + // Increase the indent, if requested. + if (format & ListFormat.Indented) { + increaseIndent(); + } + + // Emit each child. + let previousSibling: Node; + let shouldDecreaseIndentAfterEmit: boolean; + 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)) { + // If a synthesized node in a single-line list starts on a new + // line, we should increase the indent. + if ((format & (ListFormat.LinesMask | ListFormat.Indented)) === ListFormat.SingleLine) { + increaseIndent(); + shouldDecreaseIndentAfterEmit = true; + } + + writeLine(); + shouldEmitInterveningComments = false; + } + else if (previousSibling && format & ListFormat.SpaceBetweenSiblings) { + write(" "); + } + } + + // Emit this child. + if (shouldEmitInterveningComments) { + if (onEmitTrailingCommentsOfPosition) { + const commentRange = getCommentRange(child); + onEmitTrailingCommentsOfPosition(commentRange.pos); + } + } + else { + shouldEmitInterveningComments = mayEmitInterveningComments; + } + + emit(child); + + if (shouldDecreaseIndentAfterEmit) { + decreaseIndent(); + shouldDecreaseIndentAfterEmit = false; + } + + 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 (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(" "); + } + else { + writeLine(); + } + } + + 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 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; + } + + 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; + } + + if (format & ListFormat.PreserveLines) { + if (format & ListFormat.PreferNewLine) { + return true; + } + + const firstChild = children[0]; + if (firstChild === undefined) { + return !rangeIsOnSingleLine(parentNode, currentSourceFile); + } + else if (positionIsSynthesized(parentNode.pos) || nodeIsSynthesized(firstChild)) { + return synthesizedNodeStartsOnNewLine(firstChild, format); + } + else { + return !rangeStartPositionsAreOnSameLine(parentNode, firstChild, currentSourceFile); + } + } + 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, currentSourceFile); + } + } + else { + return nextNode.startsOnNewLine; + } + } + + function shouldWriteClosingLineTerminator(parentNode: Node, children: NodeArray, format: ListFormat) { + if (format & ListFormat.MultiLine) { + return (format & ListFormat.NoTrailingNewLine) === 0; + } + else if (format & ListFormat.PreserveLines) { + if (format & ListFormat.PreferNewLine) { + return true; + } + + const lastChild = lastOrUndefined(children); + if (lastChild === undefined) { + return !rangeIsOnSingleLine(parentNode, currentSourceFile); + } + else if (positionIsSynthesized(parentNode.pos) || nodeIsSynthesized(lastChild)) { + return synthesizedNodeStartsOnNewLine(lastChild, format); + } + else { + return !rangeEndPositionsAreOnSameLine(parentNode, lastChild, currentSourceFile); + } + } + 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 needsIndentation(parent: Node, node1: Node, node2: Node): boolean { + parent = skipSynthesizedParentheses(parent); + node1 = skipSynthesizedParentheses(node1); + node2 = skipSynthesizedParentheses(node2); + + // Always use a newline for synthesized code if the synthesizer desires it. + if (node2.startsOnNewLine) { + return true; + } + + return !nodeIsSynthesized(parent) + && !nodeIsSynthesized(node1) + && !nodeIsSynthesized(node2) + && !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; + } + + return node; + } + + function getTextOfNode(node: Node, includeTrivia?: boolean): string { + if (isGeneratedIdentifier(node)) { + return generateName(node); + } + else if (isIdentifier(node) && (nodeIsSynthesized(node) || !node.parent)) { + return unescapeIdentifier(node.text); + } + else if (node.kind === SyntaxKind.StringLiteral && (node).textSourceNode) { + return getTextOfNode((node).textSourceNode, includeTrivia); + } + else if (isLiteralExpression(node) && (nodeIsSynthesized(node) || !node.parent)) { + return node.text; + } + + return getSourceTextOfNodeFromSourceFile(currentSourceFile, node, includeTrivia); + } + + function getLiteralTextOfNode(node: LiteralLikeNode): string { + if (node.kind === SyntaxKind.StringLiteral && (node).textSourceNode) { + const textSourceNode = (node).textSourceNode; + if (isIdentifier(textSourceNode)) { + return "\"" + escapeNonAsciiCharacters(escapeString(getTextOfNode(textSourceNode))) + "\""; + } + else { + return getLiteralTextOfNode(textSourceNode); + } + } + + return getLiteralText(node, currentSourceFile, languageVersion); + } + + function resetGeneratedNames() { + nodeIdToGeneratedName = undefined; + autoGeneratedIdToGeneratedName = undefined; + generatedNames = undefined; + tempFlagsStack = undefined; + tempFlags = TempFlags.Auto; + } + + function ensureNameGeneration() { + if (generatedNames === undefined) { + nodeIdToGeneratedName = []; + autoGeneratedIdToGeneratedName = []; + generatedNames = createMap(); + tempFlagsStack = []; + } + } + + /** + * Push a new name generation scope. + */ + function pushNameGenerationScope() { + ensureNameGeneration(); + tempFlagsStack.push(tempFlags); + tempFlags = 0; + } + + /** + * Pop the current name generation scope. + */ + function popNameGenerationScope() { + ensureNameGeneration(); + tempFlags = tempFlagsStack.pop(); + } + + /** + * Generate the text for a generated identifier. + */ + function generateName(name: GeneratedIdentifier) { + ensureNameGeneration(); + 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 !(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) { + const local = node.locals.get(name); + // We conservatively include alias symbols to cover cases where they're emitted as locals + if (local && local.flags & (SymbolFlags.Value | SymbolFlags.ExportValue | SymbolFlags.Alias)) { + return false; + } + } + } + return true; + } + + /** + * 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; + } + } + } + } + + /** + * 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._) { + baseName += "_"; + } + let i = 1; + while (true) { + const generatedName = baseName + i; + if (isUniqueName(generatedName)) { + generatedNames.set(generatedName, generatedName); + return generatedName; + } + i++; + } + } + + /** + * 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 ? + escapeIdentifier(makeIdentifierFromModuleName((expr).text)) : "module"; + 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, getTextOfNode: (node: Node, includeTrivia?: boolean) => string) { + if (isIdentifier(node.name)) { + return generateNameCached(node.name, getTextOfNode); + } + return makeTempVariableName(TempFlags.Auto); + } + + /** + * Generates a unique name from a node. + */ + 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, getTextOfNode); + case SyntaxKind.ImportDeclaration: + case SyntaxKind.ExportDeclaration: + return generateNameForImportOrExportDeclaration(node); + case SyntaxKind.FunctionDeclaration: + case SyntaxKind.ClassDeclaration: + case SyntaxKind.ExportAssignment: + return generateNameForExportDefault(); + case SyntaxKind.ClassExpression: + return generateNameForClassExpression(); + case SyntaxKind.MethodDeclaration: + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: + return generateNameForMethodOrAccessor(node, getTextOfNode); + default: + return makeTempVariableName(TempFlags.Auto); + } + } + + /** + * Generates a unique identifier for a node. + */ + function makeName(name: GeneratedIdentifier) { + switch (name.autoGenerateKind) { + case GeneratedIdentifierKind.Auto: + return makeTempVariableName(TempFlags.Auto); + case GeneratedIdentifierKind.Loop: + return makeTempVariableName(TempFlags._i); + case GeneratedIdentifierKind.Unique: + return makeUniqueName(name.text); + } + + Debug.fail("Unsupported GeneratedIdentifierKind."); + } + + /** + * Gets the node from which a name should be generated. + */ + function getNodeForGeneratedName(name: GeneratedIdentifier) { + const autoGenerateId = name.autoGenerateId; + let node = name as Node; + let original = node.original; + while (original) { + node = original; + + // if "node" is a different generated name (having a different + // "autoGenerateId"), use it and stop traversing. + if (isIdentifier(node) + && node.autoGenerateKind === GeneratedIdentifierKind.Node + && node.autoGenerateId !== autoGenerateId) { + break; + } + + original = node.original; + } + + // otherwise, return the original node for the source; + return node; + } + } + + 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 = 0, // Prints the list on a single line (default). + MultiLine = 1 << 0, // Prints the list on multiple lines. + PreserveLines = 1 << 1, // Prints the list using line preservation if possible. + LinesMask = SingleLine | MultiLine | PreserveLines, + + // Delimiters + NotDelimited = 0, // There is no delimiter between list items (default). + BarDelimited = 1 << 2, // Each list item is space-and-bar (" |") delimited. + AmpersandDelimited = 1 << 3, // Each list item is space-and-ampersand (" &") delimited. + CommaDelimited = 1 << 4, // Each list item is comma (",") delimited. + DelimitersMask = BarDelimited | AmpersandDelimited | CommaDelimited, + + AllowTrailingComma = 1 << 5, // Write a trailing comma (",") if present. + + // Whitespace + Indented = 1 << 6, // The list should be indented. + SpaceBetweenBraces = 1 << 7, // Inserts a space after the opening brace and before the closing brace. + SpaceBetweenSiblings = 1 << 8, // Inserts a space between each sibling node. + + // 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. + NoTrailingNewLine = 1 << 16, // Do not emit a trailing NewLine for a MultiLine list. + NoInterveningComments = 1 << 17, // Do not emit comments between each node + + // Precomputed Formats + Modifiers = SingleLine | SpaceBetweenSiblings, + HeritageClauses = SingleLine | SpaceBetweenSiblings, + TypeLiteralMembers = MultiLine | Indented, + TupleTypeElements = CommaDelimited | SpaceBetweenSiblings | SingleLine | Indented, + UnionTypeConstituents = BarDelimited | SpaceBetweenSiblings | SingleLine, + IntersectionTypeConstituents = AmpersandDelimited | SpaceBetweenSiblings | SingleLine, + ObjectBindingPatternElements = SingleLine | AllowTrailingComma | SpaceBetweenBraces | CommaDelimited | SpaceBetweenSiblings, + ArrayBindingPatternElements = SingleLine | AllowTrailingComma | CommaDelimited | SpaceBetweenSiblings, + ObjectLiteralExpressionProperties = PreserveLines | CommaDelimited | SpaceBetweenSiblings | SpaceBetweenBraces | Indented | Braces, + ArrayLiteralExpressionElements = PreserveLines | CommaDelimited | SpaceBetweenSiblings | AllowTrailingComma | Indented | SquareBrackets, + CallExpressionArguments = CommaDelimited | SpaceBetweenSiblings | SingleLine | Parenthesis, + NewExpressionArguments = CommaDelimited | SpaceBetweenSiblings | SingleLine | Parenthesis | OptionalIfUndefined, + TemplateExpressionSpans = SingleLine | NoInterveningComments, + SingleLineBlockStatements = SpaceBetweenBraces | SpaceBetweenSiblings | SingleLine, + MultiLineBlockStatements = Indented | MultiLine, + VariableDeclarationList = CommaDelimited | SpaceBetweenSiblings | SingleLine, + SingleLineFunctionBodyStatements = SingleLine | SpaceBetweenSiblings | SpaceBetweenBraces, + MultiLineFunctionBodyStatements = MultiLine, + ClassHeritageClauses = SingleLine | SpaceBetweenSiblings, + ClassMembers = Indented | MultiLine, + InterfaceMembers = Indented | MultiLine, + EnumMembers = CommaDelimited | Indented | MultiLine, + CaseBlockClauses = Indented | MultiLine, + NamedImportsOrExportsElements = CommaDelimited | SpaceBetweenSiblings | AllowTrailingComma | SingleLine | SpaceBetweenBraces, + JsxElementChildren = SingleLine | NoInterveningComments, + JsxElementAttributes = SingleLine | SpaceBetweenSiblings | NoInterveningComments, + CaseOrDefaultClauseStatements = Indented | MultiLine | NoTrailingNewLine | OptionalIfEmpty, + HeritageClauseTypes = CommaDelimited | SpaceBetweenSiblings | SingleLine, + SourceFileStatements = MultiLine | NoTrailingNewLine, + Decorators = MultiLine | Optional, + TypeArguments = CommaDelimited | SpaceBetweenSiblings | SingleLine | Indented | AngleBrackets | Optional, + TypeParameters = CommaDelimited | SpaceBetweenSiblings | SingleLine | Indented | AngleBrackets | Optional, + Parameters = CommaDelimited | SpaceBetweenSiblings | SingleLine | Indented | Parenthesis, + IndexSignatureParameters = CommaDelimited | SpaceBetweenSiblings | SingleLine | Indented | SquareBrackets, + } +} \ No newline at end of file diff --git a/src/compiler/sourcemap.ts b/src/compiler/sourcemap.ts index 650b9b0ef02b2..d743f488e75d7 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 cef8c701455a6..62469df79c110 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 3b8b4524aabc9..10ebe648fe2ed 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 c87dbe32518f0..a7f7b0da0ea64 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 c3b011fe36d47..8b030642a89c6 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 7dcfe76a42774..ee17bdacbaf5b 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 5611f89016435..86fa1295f7b7d 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 0adea4fd5407b..e072429292aab 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 3f1488b649d19..6c59ea194cb0d 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 e2b8b8fe2ec40..a61998e8af984 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/tsconfig.json b/src/compiler/tsconfig.json index 52bb92ee1f003..54d5eb77e10f8 100644 --- a/src/compiler/tsconfig.json +++ b/src/compiler/tsconfig.json @@ -34,6 +34,7 @@ "comments.ts", "sourcemap.ts", "declarationEmitter.ts", + "printer.ts", "emitter.ts", "program.ts", "commandLineParser.ts", diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 8b49af402cf6c..682cd9e617b56 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -348,6 +348,7 @@ EnumMember, // Top-level nodes SourceFile, + Bundle, // JSDoc nodes JSDocTypeExpression, @@ -557,15 +558,6 @@ export type ModifiersArray = NodeArray; - /*@internal*/ - export const enum GeneratedIdentifierKind { - None, // Not automatically generated. - Auto, // Automatically generated identifier. - Loop, // Automatically generated identifier with a preference for '_i'. - Unique, // Unique name based on the 'text' property. - Node, // Unique name based on the node in the 'original' property. - } - export interface Identifier extends PrimaryExpression { kind: SyntaxKind.Identifier; text: string; // Text of identifier (with escapes converted to characters) @@ -580,7 +572,14 @@ resolvedSymbol: Symbol; } - /*@internal*/ + export const enum GeneratedIdentifierKind { + None, // Not automatically generated. + Auto, // Automatically generated identifier. + Loop, // Automatically generated identifier with a preference for '_i'. + Unique, // Unique name based on the 'text' property. + Node, // Unique name based on the node in the 'original' property. + } + export interface GeneratedIdentifier extends Identifier { autoGenerateKind: GeneratedIdentifierKind.Auto | GeneratedIdentifierKind.Loop @@ -2201,6 +2200,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 +3784,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 +3859,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 +3877,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 +3890,61 @@ /** * 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 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 Printer { + printNode(hint: EmitHint, node: Node, sourceFile: SourceFile): void; + printFile(sourceFile: SourceFile): void; + printBundle(bundle: Bundle): void; + } + + export interface PrinterOptions { + hasGlobalName?: (name: string) => boolean; + onEmitSourceMapOfNode?: (hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) => void; + onEmitSourceMapOfToken?: (node: Node, token: SyntaxKind, pos: number, emitCallback: (token: SyntaxKind, pos: number) => number) => number; + onEmitCommentsOfNode?: (hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) => void; + onEmitDetachedCommentsOfNode?: (node: Node, detachedRange: TextRange, emitCallback: (node: Node) => void) => void; + onEmitTrailingCommentsOfPosition?: (pos: number) => void; + onEmitNode?: (hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) => void; + onEmitHelpers?: (node: Node, writeLines: (text: string) => void) => void; + onSetSourceFile?: (node: SourceFile) => void; + onSubstituteNode?: (hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) => void; + } + export interface TextSpan { start: number; length: number; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 0a16875f8c0b3..5c92bfa65cce4 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2088,16 +2088,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); + } + + function getOriginalSourceFile(sourceFile: SourceFile) { + return getParseTreeNode(sourceFile, isSourceFile) || sourceFile; + } - return originalSourceFiles; + export function getOriginalSourceFiles(sourceFiles: SourceFile[]) { + return sameMap(sourceFiles, getOriginalSourceFile); } export function getOriginalNodeId(node: Node) { @@ -2436,23 +2439,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) { @@ -2635,7 +2621,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) { @@ -2646,7 +2632,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 { @@ -2654,7 +2640,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); } } } diff --git a/src/harness/tsconfig.json b/src/harness/tsconfig.json index ebd07118cc531..d7bb935db897e 100644 --- a/src/harness/tsconfig.json +++ b/src/harness/tsconfig.json @@ -37,6 +37,7 @@ "../compiler/comments.ts", "../compiler/sourcemap.ts", "../compiler/declarationEmitter.ts", + "../compiler/printer.ts", "../compiler/emitter.ts", "../compiler/program.ts", "../compiler/commandLineParser.ts", diff --git a/src/services/tsconfig.json b/src/services/tsconfig.json index b60e00292f51f..493f3b45f9398 100644 --- a/src/services/tsconfig.json +++ b/src/services/tsconfig.json @@ -34,6 +34,7 @@ "../compiler/comments.ts", "../compiler/sourcemap.ts", "../compiler/declarationEmitter.ts", + "../compiler/printer.ts", "../compiler/emitter.ts", "../compiler/program.ts", "../compiler/commandLineParser.ts", From 0165b71549533e3e6d810ffbe4dfb781b8794abc Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Mon, 30 Jan 2017 12:27:24 -0800 Subject: [PATCH 2/6] Embed comment emit inside of printer --- Jakefile.js | 2 - src/compiler/comments.ts | 30 +- src/compiler/core.ts | 4 +- src/compiler/emitter.ts | 2734 +++++++++++++++++++++++++++++++++++- src/compiler/printer.ts | 2659 ----------------------------------- src/compiler/tsconfig.json | 1 - src/compiler/types.ts | 68 +- src/compiler/utilities.ts | 19 +- src/harness/tsconfig.json | 1 - src/services/tsconfig.json | 1 - 10 files changed, 2791 insertions(+), 2728 deletions(-) delete mode 100644 src/compiler/printer.ts diff --git a/Jakefile.js b/Jakefile.js index a23b9cbe03c64..3ee8476c842d2 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -84,7 +84,6 @@ var compilerSources = [ "sourcemap.ts", "comments.ts", "declarationEmitter.ts", - "printer.ts", "emitter.ts", "program.ts", "commandLineParser.ts", @@ -122,7 +121,6 @@ var servicesSources = [ "sourcemap.ts", "comments.ts", "declarationEmitter.ts", - "printer.ts", "emitter.ts", "program.ts", "commandLineParser.ts", diff --git a/src/compiler/comments.ts b/src/compiler/comments.ts index 3106d3ccfec48..f69b4c546a0d6 100644 --- a/src/compiler/comments.ts +++ b/src/compiler/comments.ts @@ -5,13 +5,16 @@ namespace ts { export interface CommentWriter { reset(): void; setSourceFile(sourceFile: SourceFile): 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(writer: EmitTextWriter, compilerOptions: CompilerOptions, newLine: string, emitPos: (pos: number) => void): CommentWriter { - const extendedDiagnostics = compilerOptions.extendedDiagnostics; + export function createCommentWriter(printerOptions: PrinterOptions, emitPos: (pos: number) => void): CommentWriter { + const extendedDiagnostics = printerOptions.extendedDiagnostics; + const newLine = getNewLineCharacter(printerOptions); + let writer: EmitTextWriter; let containerPos = -1; let containerEnd = -1; let declarationListContainerEnd = -1; @@ -20,10 +23,11 @@ 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, @@ -194,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(); @@ -216,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(); @@ -244,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(); @@ -282,6 +286,10 @@ namespace ts { detachedCommentsInfo = undefined; } + function setWriter(output: EmitTextWriter): void { + writer = output; + } + function setSourceFile(sourceFile: SourceFile) { currentSourceFile = sourceFile; currentText = currentSourceFile.text; @@ -319,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 de3472afd8845..b8097acaa7113 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -1447,11 +1447,11 @@ namespace ts { return /^\.\.?($|[\\/])/.test(moduleName); } - export function getEmitScriptTarget(compilerOptions: CompilerOptions) { + export function getEmitScriptTarget(compilerOptions: CompilerOptions | PrinterOptions) { return compilerOptions.target || ScriptTarget.ES3; } - export function getEmitModuleKind(compilerOptions: CompilerOptions) { + export function getEmitModuleKind(compilerOptions: CompilerOptions | PrinterOptions) { return typeof compilerOptions.module === "number" ? compilerOptions.module : getEmitScriptTarget(compilerOptions) >= ScriptTarget.ES2015 ? ModuleKind.ES2015 : ModuleKind.CommonJS; diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index dd823cf79942d..b3bd594ae8862 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -3,13 +3,12 @@ /// /// /// -/// -/* @internal */ namespace ts { - 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 compilerOptions = host.getCompilerOptions(); @@ -17,10 +16,9 @@ namespace ts { 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 sourceMap = createSourceMapWriter(host, writer); - const comments = createCommentWriter(writer, compilerOptions, newLine, sourceMap.emitPos); let currentSourceFile: SourceFile; let bundledHelpers: Map; @@ -33,7 +31,7 @@ namespace ts { const transform = transformFiles(resolver, host, sourceFiles, transformers); // Create a printer to print the nodes - const printer = createPrinter(writer, compilerOptions, { + const printer = createPrinter(compilerOptions, { // resolver hooks hasGlobalName: resolver.hasGlobalName, @@ -44,11 +42,7 @@ namespace ts { // sourcemap hooks onEmitSourceMapOfNode: sourceMap.emitNodeWithSourceMap, onEmitSourceMapOfToken: sourceMap.emitTokenWithSourceMap, - - // comment hooks - onEmitCommentsOfNode: comments.emitNodeWithComments, - onEmitDetachedCommentsOfNode: comments.emitBodyWithDetachedComments, - onEmitTrailingCommentsOfPosition: comments.emitTrailingCommentsOfPosition, + onEmitSourceMapOfPosition: sourceMap.emitPos, // emitter hooks onEmitHelpers: emitHelpers, @@ -109,11 +103,11 @@ namespace ts { if (bundle) { bundledHelpers = createMap(); isOwnFileEmit = false; - printer.printBundle(bundle); + printer.writeBundle(bundle, writer); } else { isOwnFileEmit = true; - printer.printFile(sourceFile); + printer.writeFile(sourceFile, writer); } writer.writeLine(); @@ -138,7 +132,6 @@ namespace ts { // Reset state sourceMap.reset(); - comments.reset(); writer.reset(); currentSourceFile = undefined; @@ -149,7 +142,6 @@ namespace ts { function setSourceFile(node: SourceFile) { currentSourceFile = node; sourceMap.setSourceFile(node); - comments.setSourceFile(node); } function emitHelpers(node: Node, writeLines: (text: string) => void) { @@ -194,4 +186,2714 @@ namespace ts { 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 moduleKind = getEmitModuleKind(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 { + 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); + if (moduleKind) { + 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 = writer.getText(); + if (writer === ownWriter) { + writer.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); + } + + function emitIdentifierName(node: Identifier) { + pipelineEmitWithNotification(EmitHint.IdentifierName, node); + } + + function emitExpression(node: Expression) { + pipelineEmitWithNotification(EmitHint.Expression, node); + } + + function pipelineEmitWithNotification(hint: EmitHint, node: Node) { + if (onEmitNode) { + onEmitNode(hint, node, pipelineEmitWithComments); + } + else { + pipelineEmitWithComments(hint, 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; + switch (kind) { + // Pseudo-literals + case SyntaxKind.TemplateHead: + case SyntaxKind.TemplateMiddle: + case SyntaxKind.TemplateTail: + return emitLiteral(node); + + // Identifiers + 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 + 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 emitThisType(); + case SyntaxKind.TypeOperator: + return emitTypeOperator(node); + case SyntaxKind.IndexedAccessType: + return emitIndexedAccessType(node); + case SyntaxKind.MappedType: + return emitMappedType(node); + case SyntaxKind.LiteralType: + return emitLiteralType(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 emitSemicolonClassElement(); + + // Statements + case SyntaxKind.Block: + return emitBlock(node); + case SyntaxKind.VariableStatement: + return emitVariableStatement(node); + case SyntaxKind.EmptyStatement: + return emitEmptyStatement(); + 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.JsxOpeningElement: + return emitJsxOpeningElement(node); + case SyntaxKind.JsxClosingElement: + return emitJsxClosingElement(node); + case SyntaxKind.JsxAttribute: + return emitJsxAttribute(node); + case SyntaxKind.JsxSpreadAttribute: + return emitJsxSpreadAttribute(node); + case SyntaxKind.JsxExpression: + return emitJsxExpression(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); + case SyntaxKind.SpreadAssignment: + return emitSpreadAssignment(node as SpreadAssignment); + + // Enum + case SyntaxKind.EnumMember: + return emitEnumMember(node); + + // JSDoc nodes (ignored) + // Transformation nodes (ignored) + } + + // If the node is an expression, try to emit it as an expression with + // substitution. + if (isExpression(node)) { + return pipelineEmitWithSubstitution(EmitHint.Expression, node); + } + } + + function pipelineEmitExpression(node: Node): void { + const kind = node.kind; + switch (kind) { + // Literals + case SyntaxKind.NumericLiteral: + return emitNumericLiteral(node); + + case SyntaxKind.StringLiteral: + case SyntaxKind.RegularExpressionLiteral: + case SyntaxKind.NoSubstitutionTemplateLiteral: + return emitLiteral(node); + + // Identifiers + case SyntaxKind.Identifier: + return emitIdentifier(node); + + // Reserved words + case SyntaxKind.FalseKeyword: + case SyntaxKind.NullKeyword: + case SyntaxKind.SuperKeyword: + case SyntaxKind.TrueKeyword: + case SyntaxKind.ThisKeyword: + writeTokenText(kind); + return; + + // 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.SpreadElement: + return emitSpreadExpression(node); + case SyntaxKind.ClassExpression: + return emitClassExpression(node); + case SyntaxKind.OmittedExpression: + return; + case SyntaxKind.AsExpression: + return emitAsExpression(node); + case SyntaxKind.NonNullExpression: + return emitNonNullExpression(node); + case SyntaxKind.MetaProperty: + return emitMetaProperty(node); + + // JSX + case SyntaxKind.JsxElement: + return emitJsxElement(node); + case SyntaxKind.JsxSelfClosingElement: + return emitJsxSelfClosingElement(node); + + // Transformation nodes + case SyntaxKind.PartiallyEmittedExpression: + return emitPartiallyEmittedExpression(node); + } + } + + 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 + // + + // SyntaxKind.NumericLiteral + function emitNumericLiteral(node: NumericLiteral) { + emitLiteral(node); + if (node.trailingComment) { + write(` /*${node.trailingComment}*/`); + } + } + + // SyntaxKind.StringLiteral + // SyntaxKind.RegularExpressionLiteral + // SyntaxKind.NoSubstitutionTemplateLiteral + // SyntaxKind.TemplateHead + // SyntaxKind.TemplateMiddle + // SyntaxKind.TemplateTail + function emitLiteral(node: LiteralLikeNode) { + const text = getLiteralTextOfNode(node); + if ((printerOptions.sourceMap || printerOptions.inlineSourceMap) + && (node.kind === SyntaxKind.StringLiteral || isTemplateLiteralKind(node.kind))) { + writer.writeLiteral(text); + } + else { + write(text); + } + } + + // + // Identifiers + // + + function emitIdentifier(node: Identifier) { + write(getTextOfNode(node, /*includeTrivia*/ false)); + } + + // + // 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, emitSignatureHead); + } + + function emitConstructor(node: ConstructorDeclaration) { + emitModifiers(node, node.modifiers); + write("constructor"); + emitSignatureAndBody(node, emitSignatureHead); + } + + function emitAccessorDeclaration(node: AccessorDeclaration) { + emitDecorators(node, node.decorators); + emitModifiers(node, node.modifiers); + write(node.kind === SyntaxKind.GetAccessor ? "get " : "set "); + emit(node.name); + emitSignatureAndBody(node, emitSignatureHead); + } + + 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(";"); + } + + function emitSemicolonClassElement() { + 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(")"); + } + + function emitThisType() { + write("this"); + } + + function emitTypeOperator(node: TypeOperatorNode) { + writeTokenText(node.operator); + write(" "); + emit(node.type); + } + + function emitIndexedAccessType(node: IndexedAccessTypeNode) { + emit(node.objectType); + write("["); + emit(node.indexType); + write("]"); + } + + function emitMappedType(node: MappedTypeNode) { + write("{"); + writeLine(); + increaseIndent(); + writeIfPresent(node.readonlyToken, "readonly "); + write("["); + emit(node.typeParameter.name); + write(" in "); + emit(node.typeParameter.constraint); + write("]"); + writeIfPresent(node.questionToken, "?"); + write(": "); + emit(node.type); + write(";"); + writeLine(); + decreaseIndent(); + write("}"); + } + + function emitLiteralType(node: LiteralTypeNode) { + emitExpression(node.literal); + } + + // + // 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 = node.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 indentedFlag = getEmitFlags(node) & EmitFlags.Indented; + if (indentedFlag) { + increaseIndent(); + } + + const preferNewLine = node.multiLine ? ListFormat.PreferNewLine : ListFormat.None; + const allowTrailingComma = languageVersion >= ScriptTarget.ES5 ? ListFormat.AllowTrailingComma : ListFormat.None; + emitList(node, properties, ListFormat.ObjectLiteralExpressionProperties | allowTrailingComma | preferNewLine); + + if (indentedFlag) { + decreaseIndent(); + } + } + } + + function emitPropertyAccessExpression(node: PropertyAccessExpression) { + let indentBeforeDot = false; + let indentAfterDot = false; + if (!(getEmitFlags(node) & EmitFlags.NoIndentation)) { + const dotRangeStart = node.expression.end; + 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); + } + + emitExpression(node.expression); + increaseIndentIf(indentBeforeDot); + + const shouldEmitDotDot = !indentBeforeDot && needsDotDotForPropertyAccess(node.expression); + 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 is a decimal literal that was originally written with a dot + const text = getLiteralTextOfNode(expression); + return getNumericLiteralFlags(text, /*hint*/ NumericLiteralFlags.All) === NumericLiteralFlags.None + && !(expression).isOctalLiteral + && text.indexOf(tokenToString(SyntaxKind.DotToken)) < 0; + } + else if (isPropertyAccessExpression(expression) || isElementAccessExpression(expression)) { + // check if constant enum value is integer + const constantValue = getConstantValue(expression); + // isFinite handles cases when constantValue is undefined + return isFinite(constantValue) + && Math.floor(constantValue) === constantValue + && printerOptions.removeComments; + } + } + + function emitElementAccessExpression(node: ElementAccessExpression) { + emitExpression(node.expression); + write("["); + emitExpression(node.argumentExpression); + write("]"); + } + + function emitCallExpression(node: CallExpression) { + emitExpression(node.expression); + emitTypeArguments(node, node.typeArguments); + emitExpressionList(node, node.arguments, ListFormat.CallExpressionArguments); + } + + function emitNewExpression(node: NewExpression) { + write("new "); + emitExpression(node.expression); + emitTypeArguments(node, node.typeArguments); + emitExpressionList(node, node.arguments, ListFormat.NewExpressionArguments); + } + + function emitTaggedTemplateExpression(node: TaggedTemplateExpression) { + emitExpression(node.tag); + write(" "); + emitExpression(node.template); + } + + function emitTypeAssertionExpression(node: TypeAssertion) { + 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); + emitSignatureAndBody(node, emitArrowFunctionHead); + } + + 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) { + writeTokenText(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) { + emitExpression(node.operand); + writeTokenText(node.operator); + } + + function emitBinaryExpression(node: BinaryExpression) { + const isCommaOperator = node.operatorToken.kind !== SyntaxKind.CommaToken; + const indentBeforeOperator = needsIndentation(node, node.left, node.operatorToken); + const indentAfterOperator = needsIndentation(node, node.operatorToken, node.right); + + emitExpression(node.left); + increaseIndentIf(indentBeforeOperator, isCommaOperator ? " " : undefined); + writeTokenText(node.operatorToken.kind); + 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 emitSpreadExpression(node: SpreadElement) { + write("..."); + emitExpression(node.expression); + } + + function emitClassExpression(node: ClassExpression) { + emitClassDeclarationOrExpression(node); + } + + function emitExpressionWithTypeArguments(node: ExpressionWithTypeArguments) { + emitExpression(node.expression); + emitTypeArguments(node, node.typeArguments); + } + + function emitAsExpression(node: AsExpression) { + emitExpression(node.expression); + if (node.type) { + write(" as "); + emit(node.type); + } + } + + function emitNonNullExpression(node: NonNullExpression) { + emitExpression(node.expression); + write("!"); + } + + function emitMetaProperty(node: MetaProperty) { + writeToken(node.keywordToken, node.pos); + write("."); + emit(node.name); + } + + // + // Misc + // + + function emitTemplateSpan(node: TemplateSpan) { + emitExpression(node.expression); + emit(node.literal); + } + + // + // Statements + // + + function emitBlock(node: Block) { + if (isSingleLineEmptyBlock(node)) { + writeToken(SyntaxKind.OpenBraceToken, node.pos, /*contextNode*/ node); + write(" "); + writeToken(SyntaxKind.CloseBraceToken, node.statements.end, /*contextNode*/ node); + } + else { + writeToken(SyntaxKind.OpenBraceToken, node.pos, /*contextNode*/ node); + emitBlockStatements(node); + writeToken(SyntaxKind.CloseBraceToken, node.statements.end, /*contextNode*/ node); + } + } + + function emitBlockStatements(node: BlockLike) { + if (getEmitFlags(node) & EmitFlags.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 emitEmptyStatement() { + write(";"); + } + + function emitExpressionStatement(node: ExpressionStatement) { + emitExpression(node.expression); + write(";"); + } + + function emitIfStatement(node: IfStatement) { + const openParenPos = writeToken(SyntaxKind.IfKeyword, node.pos, node); + write(" "); + writeToken(SyntaxKind.OpenParenToken, openParenPos, node); + emitExpression(node.expression); + writeToken(SyntaxKind.CloseParenToken, node.expression.end, node); + emitEmbeddedStatement(node, node.thenStatement); + if (node.elseStatement) { + writeLineOrSpace(node); + writeToken(SyntaxKind.ElseKeyword, node.thenStatement.end, node); + if (node.elseStatement.kind === SyntaxKind.IfStatement) { + write(" "); + emit(node.elseStatement); + } + else { + emitEmbeddedStatement(node, node.elseStatement); + } + } + } + + function emitDoStatement(node: DoStatement) { + write("do"); + emitEmbeddedStatement(node, node.statement); + if (isBlock(node.statement)) { + write(" "); + } + else { + writeLineOrSpace(node); + } + + write("while ("); + emitExpression(node.expression); + write(");"); + } + + function emitWhileStatement(node: WhileStatement) { + write("while ("); + emitExpression(node.expression); + write(")"); + emitEmbeddedStatement(node, node.statement); + } + + function emitForStatement(node: ForStatement) { + const openParenPos = writeToken(SyntaxKind.ForKeyword, node.pos); + write(" "); + writeToken(SyntaxKind.OpenParenToken, openParenPos, /*contextNode*/ node); + emitForBinding(node.initializer); + write(";"); + emitExpressionWithPrefix(" ", node.condition); + write(";"); + emitExpressionWithPrefix(" ", node.incrementor); + write(")"); + emitEmbeddedStatement(node, node.statement); + } + + function emitForInStatement(node: ForInStatement) { + const openParenPos = writeToken(SyntaxKind.ForKeyword, node.pos); + write(" "); + writeToken(SyntaxKind.OpenParenToken, openParenPos); + emitForBinding(node.initializer); + write(" in "); + emitExpression(node.expression); + writeToken(SyntaxKind.CloseParenToken, node.expression.end); + emitEmbeddedStatement(node, node.statement); + } + + function emitForOfStatement(node: ForOfStatement) { + const openParenPos = writeToken(SyntaxKind.ForKeyword, node.pos); + write(" "); + writeToken(SyntaxKind.OpenParenToken, openParenPos); + emitForBinding(node.initializer); + write(" of "); + emitExpression(node.expression); + writeToken(SyntaxKind.CloseParenToken, node.expression.end); + emitEmbeddedStatement(node, node.statement); + } + + function emitForBinding(node: VariableDeclarationList | Expression) { + if (node !== undefined) { + if (node.kind === SyntaxKind.VariableDeclarationList) { + emit(node); + } + else { + emitExpression(node); + } + } + } + + function emitContinueStatement(node: ContinueStatement) { + writeToken(SyntaxKind.ContinueKeyword, node.pos); + emitWithPrefix(" ", node.label); + write(";"); + } + + function emitBreakStatement(node: BreakStatement) { + writeToken(SyntaxKind.BreakKeyword, node.pos); + emitWithPrefix(" ", node.label); + write(";"); + } + + function emitReturnStatement(node: ReturnStatement) { + writeToken(SyntaxKind.ReturnKeyword, node.pos, /*contextNode*/ node); + emitExpressionWithPrefix(" ", node.expression); + write(";"); + } + + function emitWithStatement(node: WithStatement) { + write("with ("); + emitExpression(node.expression); + write(")"); + emitEmbeddedStatement(node, node.statement); + } + + function emitSwitchStatement(node: SwitchStatement) { + const openParenPos = writeToken(SyntaxKind.SwitchKeyword, node.pos); + write(" "); + writeToken(SyntaxKind.OpenParenToken, openParenPos); + emitExpression(node.expression); + writeToken(SyntaxKind.CloseParenToken, node.expression.end); + 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); + if (node.catchClause) { + writeLineOrSpace(node); + emit(node.catchClause); + } + if (node.finallyBlock) { + writeLineOrSpace(node); + write("finally "); + emit(node.finallyBlock); + } + } + + function emitDebuggerStatement(node: DebuggerStatement) { + writeToken(SyntaxKind.DebuggerKeyword, node.pos); + write(";"); + } + + // + // Declarations + // + + function emitVariableDeclaration(node: VariableDeclaration) { + emit(node.name); + emitWithPrefix(": ", node.type); + 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 "); + emitIdentifierName(node.name); + emitSignatureAndBody(node, emitSignatureHead); + } + + function emitSignatureAndBody(node: FunctionLikeDeclaration, emitSignatureHead: (node: SignatureDeclaration) => void) { + const body = node.body; + if (body) { + if (isBlock(body)) { + const indentedFlag = getEmitFlags(node) & EmitFlags.Indented; + if (indentedFlag) { + increaseIndent(); + } + + if (getEmitFlags(node) & EmitFlags.ReuseTempVariableScope) { + emitSignatureHead(node); + emitBlockFunctionBody(body); + } + else { + pushNameGenerationScope(); + emitSignatureHead(node); + emitBlockFunctionBody(body); + popNameGenerationScope(); + } + + if (indentedFlag) { + decreaseIndent(); + } + } + else { + emitSignatureHead(node); + write(" "); + emitExpression(body); + } + } + 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(body: Block) { + // We must emit a function body as a single-line body in the following case: + // * The body has NodeEmitFlags.SingleLine specified. + + // We must emit a function body as a multi-line body in the following cases: + // * The body is explicitly marked as multi-line. + // * A non-synthesized body's start and end position are on different lines. + // * Any statement in the body starts on a new line. + + if (getEmitFlags(body) & EmitFlags.SingleLine) { + return true; + } + + if (body.multiLine) { + return false; + } + + if (!nodeIsSynthesized(body) && !rangeIsOnSingleLine(body, currentSourceFile)) { + return false; + } + + if (shouldWriteLeadingLineTerminator(body, body.statements, ListFormat.PreserveLines) + || shouldWriteClosingLineTerminator(body, body.statements, ListFormat.PreserveLines)) { + return false; + } + + let previousStatement: Statement; + for (const statement of body.statements) { + if (shouldWriteSeparatingLineTerminator(previousStatement, statement, ListFormat.PreserveLines)) { + return false; + } + + previousStatement = statement; + } + + return true; + } + + function emitBlockFunctionBody(body: Block) { + write(" {"); + increaseIndent(); + + const emitBlockFunctionBody = shouldEmitBlockFunctionBodyOnSingleLine(body) + ? emitBlockFunctionBodyOnSingleLine + : emitBlockFunctionBodyWorker; + + emitBodyIndirect(body, body.statements, emitBlockFunctionBody); + + decreaseIndent(); + writeToken(SyntaxKind.CloseBraceToken, body.statements.end, body); + } + + function emitBlockFunctionBodyOnSingleLine(body: Block) { + emitBlockFunctionBodyWorker(body, /*emitBlockFunctionBodyOnSingleLine*/ true); + } + + function emitBlockFunctionBodyWorker(body: Block, emitBlockFunctionBodyOnSingleLine?: boolean) { + // Emit all the prologue directives (like "use strict"). + const statementOffset = emitPrologueDirectives(body.statements, /*startWithNewLine*/ true); + const pos = writer.getTextPos(); + emitHelpersIndirect(body); + if (statementOffset === 0 && pos === writer.getTextPos() && emitBlockFunctionBodyOnSingleLine) { + decreaseIndent(); + emitList(body, body.statements, ListFormat.SingleLineFunctionBodyStatements); + increaseIndent(); + } + else { + emitList(body, body.statements, ListFormat.MultiLineFunctionBodyStatements, statementOffset); + } + } + + function emitClassDeclaration(node: ClassDeclaration) { + emitClassDeclarationOrExpression(node); + } + + function emitClassDeclarationOrExpression(node: ClassDeclaration | ClassExpression) { + emitDecorators(node, node.decorators); + emitModifiers(node, node.modifiers); + write("class"); + emitNodeWithPrefix(" ", node.name, emitIdentifierName); + + const indentedFlag = getEmitFlags(node) & EmitFlags.Indented; + if (indentedFlag) { + increaseIndent(); + } + + emitTypeParameters(node, node.typeParameters); + emitList(node, node.heritageClauses, ListFormat.ClassHeritageClauses); + + pushNameGenerationScope(); + write(" {"); + emitList(node, node.members, ListFormat.ClassMembers); + write("}"); + popNameGenerationScope(); + + if (indentedFlag) { + decreaseIndent(); + } + } + + 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.HeritageClauses); + 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); + pushNameGenerationScope(); + write(" {"); + emitList(node, node.members, ListFormat.EnumMembers); + write("}"); + popNameGenerationScope(); + } + + 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 (isEmptyBlock(node)) { + write("{ }"); + } + else { + pushNameGenerationScope(); + write("{"); + increaseIndent(); + emitBlockStatements(node); + write("}"); + popNameGenerationScope(); + } + } + + function emitCaseBlock(node: CaseBlock) { + writeToken(SyntaxKind.OpenBraceToken, node.pos); + emitList(node, node.clauses, ListFormat.CaseBlockClauses); + writeToken(SyntaxKind.CloseBraceToken, node.clauses.end); + } + + 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 "); + if (node.importClause) { + emit(node.importClause); + write(" from "); + } + emitExpression(node.moduleSpecifier); + write(";"); + } + + function emitImportClause(node: ImportClause) { + emit(node.name); + if (node.name && node.namedBindings) { + write(", "); + } + emit(node.namedBindings); + } + + function emitNamespaceImport(node: NamespaceImport) { + write("* as "); + emit(node.name); + } + + 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); + } + else { + write("*"); + } + if (node.moduleSpecifier) { + 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("<"); + emitJsxTagName(node.tagName); + write(" "); + emitList(node, node.attributes, ListFormat.JsxElementAttributes); + write("/>"); + } + + function emitJsxOpeningElement(node: JsxOpeningElement) { + write("<"); + emitJsxTagName(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(""); + } + + function emitJsxAttribute(node: JsxAttribute) { + emit(node.name); + emitWithPrefix("=", node.initializer); + } + + function emitJsxSpreadAttribute(node: JsxSpreadAttribute) { + write("{..."); + emitExpression(node.expression); + write("}"); + } + + function emitJsxExpression(node: JsxExpression) { + if (node.expression) { + write("{"); + if (node.dotDotDotToken) { + write("..."); + } + emitExpression(node.expression); + write("}"); + } + } + + function emitJsxTagName(node: JsxTagNameExpression) { + if (node.kind === SyntaxKind.Identifier) { + emitExpression(node); + } + else { + emit(node); + } + } + + // + // 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) { + const emitAsSingleStatement = + statements.length === 1 && + ( + // treat synthesized nodes as located on the same line for emit purposes + nodeIsSynthesized(parentNode) || + nodeIsSynthesized(statements[0]) || + rangeStartPositionsAreOnSameLine(parentNode, statements[0], currentSourceFile) + ); + + if (emitAsSingleStatement) { + write(" "); + emit(statements[0]); + } + else { + emitList(parentNode, statements, ListFormat.CaseOrDefaultClauseStatements); + } + } + + function emitHeritageClause(node: HeritageClause) { + write(" "); + writeTokenText(node.token); + write(" "); + emitList(node, node.types, ListFormat.HeritageClauseTypes); + } + + function emitCatchClause(node: CatchClause) { + writeLine(); + const openParenPos = writeToken(SyntaxKind.CatchKeyword, node.pos); + write(" "); + writeToken(SyntaxKind.OpenParenToken, openParenPos); + emit(node.variableDeclaration); + writeToken(SyntaxKind.CloseParenToken, node.variableDeclaration ? node.variableDeclaration.end : openParenPos); + 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. + const initializer = node.initializer; + if (emitTrailingCommentsOfPosition && (getEmitFlags(initializer) & EmitFlags.NoLeadingComments) === 0) { + const commentRange = getCommentRange(initializer); + emitTrailingCommentsOfPosition(commentRange.pos); + } + emitExpression(initializer); + } + + function emitShorthandPropertyAssignment(node: ShorthandPropertyAssignment) { + emit(node.name); + if (node.objectAssignmentInitializer) { + write(" = "); + emitExpression(node.objectAssignmentInitializer); + } + } + + function emitSpreadAssignment(node: SpreadAssignment) { + if (node.expression) { + write("..."); + emitExpression(node.expression); + } + } + + // + // Enum + // + + function emitEnumMember(node: EnumMember) { + emit(node.name); + emitExpressionWithPrefix(" = ", node.initializer); + } + + // + // Top-level nodes + // + + function emitSourceFile(node: SourceFile) { + writeLine(); + emitShebang(); + emitBodyIndirect(node, node.statements, emitSourceFileWorker); + } + + function emitSourceFileWorker(node: SourceFile) { + const statements = node.statements; + const statementOffset = emitPrologueDirectives(statements); + pushNameGenerationScope(); + emitHelpersIndirect(node); + emitList(node, statements, ListFormat.MultiLine, statementOffset); + popNameGenerationScope(); + } + + // Transformation nodes + + function emitPartiallyEmittedExpression(node: PartiallyEmittedExpression) { + emitExpression(node.expression); + } + + /** + * Emits any prologue directives at the start of a Statement list, returning the + * number of prologue directives written to the output. + */ + function emitPrologueDirectives(statements: Node[], startWithNewLine?: boolean): number { + 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 + return i; + } + } + + return statements.length; + } + + // + // Helpers + // + + function emitShebang() { + const shebang = getShebang(currentSourceFile.text); + if (shebang) { + write(shebang); + writeLine(); + } + } + + function emitModifiers(node: Node, modifiers: NodeArray) { + if (modifiers && modifiers.length) { + emitList(node, modifiers, ListFormat.Modifiers); + 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 emitEmbeddedStatement(parent: Node, node: Statement) { + if (isBlock(node) || getEmitFlags(parent) & EmitFlags.SingleLine) { + 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. + const mayEmitInterveningComments = (format & ListFormat.NoInterveningComments) === 0; + let shouldEmitInterveningComments = mayEmitInterveningComments; + if (shouldWriteLeadingLineTerminator(parentNode, children, format)) { + writeLine(); + shouldEmitInterveningComments = false; + } + else if (format & ListFormat.SpaceBetweenBraces) { + write(" "); + } + + // Increase the indent, if requested. + if (format & ListFormat.Indented) { + increaseIndent(); + } + + // Emit each child. + let previousSibling: Node; + let shouldDecreaseIndentAfterEmit: boolean; + 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)) { + // If a synthesized node in a single-line list starts on a new + // line, we should increase the indent. + if ((format & (ListFormat.LinesMask | ListFormat.Indented)) === ListFormat.SingleLine) { + increaseIndent(); + shouldDecreaseIndentAfterEmit = true; + } + + writeLine(); + shouldEmitInterveningComments = false; + } + else if (previousSibling && format & ListFormat.SpaceBetweenSiblings) { + write(" "); + } + } + + // Emit this child. + if (shouldEmitInterveningComments) { + if (emitTrailingCommentsOfPosition) { + const commentRange = getCommentRange(child); + emitTrailingCommentsOfPosition(commentRange.pos); + } + } + else { + shouldEmitInterveningComments = mayEmitInterveningComments; + } + + emit(child); + + if (shouldDecreaseIndentAfterEmit) { + decreaseIndent(); + shouldDecreaseIndentAfterEmit = false; + } + + 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 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(" "); + } + else { + writeLine(); + } + } + + 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 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; + } + + 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; + } + + if (format & ListFormat.PreserveLines) { + if (format & ListFormat.PreferNewLine) { + return true; + } + + const firstChild = children[0]; + if (firstChild === undefined) { + return !rangeIsOnSingleLine(parentNode, currentSourceFile); + } + else if (positionIsSynthesized(parentNode.pos) || nodeIsSynthesized(firstChild)) { + return synthesizedNodeStartsOnNewLine(firstChild, format); + } + else { + return !rangeStartPositionsAreOnSameLine(parentNode, firstChild, currentSourceFile); + } + } + 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, currentSourceFile); + } + } + else { + return nextNode.startsOnNewLine; + } + } + + function shouldWriteClosingLineTerminator(parentNode: Node, children: NodeArray, format: ListFormat) { + if (format & ListFormat.MultiLine) { + return (format & ListFormat.NoTrailingNewLine) === 0; + } + else if (format & ListFormat.PreserveLines) { + if (format & ListFormat.PreferNewLine) { + return true; + } + + const lastChild = lastOrUndefined(children); + if (lastChild === undefined) { + return !rangeIsOnSingleLine(parentNode, currentSourceFile); + } + else if (positionIsSynthesized(parentNode.pos) || nodeIsSynthesized(lastChild)) { + return synthesizedNodeStartsOnNewLine(lastChild, format); + } + else { + return !rangeEndPositionsAreOnSameLine(parentNode, lastChild, currentSourceFile); + } + } + 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 needsIndentation(parent: Node, node1: Node, node2: Node): boolean { + parent = skipSynthesizedParentheses(parent); + node1 = skipSynthesizedParentheses(node1); + node2 = skipSynthesizedParentheses(node2); + + // Always use a newline for synthesized code if the synthesizer desires it. + if (node2.startsOnNewLine) { + return true; + } + + return !nodeIsSynthesized(parent) + && !nodeIsSynthesized(node1) + && !nodeIsSynthesized(node2) + && !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; + } + + return node; + } + + function getTextOfNode(node: Node, includeTrivia?: boolean): string { + if (isGeneratedIdentifier(node)) { + return generateName(node); + } + else if (isIdentifier(node) && (nodeIsSynthesized(node) || !node.parent)) { + return unescapeIdentifier(node.text); + } + else if (node.kind === SyntaxKind.StringLiteral && (node).textSourceNode) { + return getTextOfNode((node).textSourceNode, includeTrivia); + } + else if (isLiteralExpression(node) && (nodeIsSynthesized(node) || !node.parent)) { + return node.text; + } + + return getSourceTextOfNodeFromSourceFile(currentSourceFile, node, includeTrivia); + } + + function getLiteralTextOfNode(node: LiteralLikeNode): string { + if (node.kind === SyntaxKind.StringLiteral && (node).textSourceNode) { + const textSourceNode = (node).textSourceNode; + if (isIdentifier(textSourceNode)) { + return "\"" + escapeNonAsciiCharacters(escapeString(getTextOfNode(textSourceNode))) + "\""; + } + else { + return getLiteralTextOfNode(textSourceNode); + } + } + + return getLiteralText(node, currentSourceFile, languageVersion); + } + + /** + * Push a new name generation scope. + */ + function pushNameGenerationScope() { + tempFlagsStack.push(tempFlags); + tempFlags = 0; + } + + /** + * 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 !(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) { + const local = node.locals.get(name); + // We conservatively include alias symbols to cover cases where they're emitted as locals + if (local && local.flags & (SymbolFlags.Value | SymbolFlags.ExportValue | SymbolFlags.Alias)) { + return false; + } + } + } + return true; + } + + /** + * 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; + } + } + } + } + + /** + * 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._) { + baseName += "_"; + } + let i = 1; + while (true) { + const generatedName = baseName + i; + if (isUniqueName(generatedName)) { + generatedNames.set(generatedName, generatedName); + return generatedName; + } + i++; + } + } + + /** + * 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 ? + escapeIdentifier(makeIdentifierFromModuleName((expr).text)) : "module"; + 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, getTextOfNode: (node: Node, includeTrivia?: boolean) => string) { + if (isIdentifier(node.name)) { + return generateNameCached(node.name, getTextOfNode); + } + return makeTempVariableName(TempFlags.Auto); + } + + /** + * Generates a unique name from a node. + */ + 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, getTextOfNode); + case SyntaxKind.ImportDeclaration: + case SyntaxKind.ExportDeclaration: + return generateNameForImportOrExportDeclaration(node); + case SyntaxKind.FunctionDeclaration: + case SyntaxKind.ClassDeclaration: + case SyntaxKind.ExportAssignment: + return generateNameForExportDefault(); + case SyntaxKind.ClassExpression: + return generateNameForClassExpression(); + case SyntaxKind.MethodDeclaration: + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: + return generateNameForMethodOrAccessor(node, getTextOfNode); + default: + return makeTempVariableName(TempFlags.Auto); + } + } + + /** + * Generates a unique identifier for a node. + */ + function makeName(name: GeneratedIdentifier) { + switch (name.autoGenerateKind) { + case GeneratedIdentifierKind.Auto: + return makeTempVariableName(TempFlags.Auto); + case GeneratedIdentifierKind.Loop: + return makeTempVariableName(TempFlags._i); + case GeneratedIdentifierKind.Unique: + return makeUniqueName(name.text); + } + + Debug.fail("Unsupported GeneratedIdentifierKind."); + } + + /** + * Gets the node from which a name should be generated. + */ + function getNodeForGeneratedName(name: GeneratedIdentifier) { + const autoGenerateId = name.autoGenerateId; + let node = name as Node; + let original = node.original; + while (original) { + node = original; + + // if "node" is a different generated name (having a different + // "autoGenerateId"), use it and stop traversing. + if (isIdentifier(node) + && node.autoGenerateKind === GeneratedIdentifierKind.Node + && node.autoGenerateId !== autoGenerateId) { + break; + } + + original = node.original; + } + + // otherwise, return the original node for the source; + return node; + } + } + + 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]; + } + + // 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 { + None = 0, + + // Line separators + SingleLine = 0, // Prints the list on a single line (default). + MultiLine = 1 << 0, // Prints the list on multiple lines. + PreserveLines = 1 << 1, // Prints the list using line preservation if possible. + LinesMask = SingleLine | MultiLine | PreserveLines, + + // Delimiters + NotDelimited = 0, // There is no delimiter between list items (default). + BarDelimited = 1 << 2, // Each list item is space-and-bar (" |") delimited. + AmpersandDelimited = 1 << 3, // Each list item is space-and-ampersand (" &") delimited. + CommaDelimited = 1 << 4, // Each list item is comma (",") delimited. + DelimitersMask = BarDelimited | AmpersandDelimited | CommaDelimited, + + AllowTrailingComma = 1 << 5, // Write a trailing comma (",") if present. + + // Whitespace + Indented = 1 << 6, // The list should be indented. + SpaceBetweenBraces = 1 << 7, // Inserts a space after the opening brace and before the closing brace. + SpaceBetweenSiblings = 1 << 8, // Inserts a space between each sibling node. + + // 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. + NoTrailingNewLine = 1 << 16, // Do not emit a trailing NewLine for a MultiLine list. + NoInterveningComments = 1 << 17, // Do not emit comments between each node + + // Precomputed Formats + Modifiers = SingleLine | SpaceBetweenSiblings, + HeritageClauses = SingleLine | SpaceBetweenSiblings, + TypeLiteralMembers = MultiLine | Indented, + TupleTypeElements = CommaDelimited | SpaceBetweenSiblings | SingleLine | Indented, + UnionTypeConstituents = BarDelimited | SpaceBetweenSiblings | SingleLine, + IntersectionTypeConstituents = AmpersandDelimited | SpaceBetweenSiblings | SingleLine, + ObjectBindingPatternElements = SingleLine | AllowTrailingComma | SpaceBetweenBraces | CommaDelimited | SpaceBetweenSiblings, + ArrayBindingPatternElements = SingleLine | AllowTrailingComma | CommaDelimited | SpaceBetweenSiblings, + ObjectLiteralExpressionProperties = PreserveLines | CommaDelimited | SpaceBetweenSiblings | SpaceBetweenBraces | Indented | Braces, + ArrayLiteralExpressionElements = PreserveLines | CommaDelimited | SpaceBetweenSiblings | AllowTrailingComma | Indented | SquareBrackets, + CallExpressionArguments = CommaDelimited | SpaceBetweenSiblings | SingleLine | Parenthesis, + NewExpressionArguments = CommaDelimited | SpaceBetweenSiblings | SingleLine | Parenthesis | OptionalIfUndefined, + TemplateExpressionSpans = SingleLine | NoInterveningComments, + SingleLineBlockStatements = SpaceBetweenBraces | SpaceBetweenSiblings | SingleLine, + MultiLineBlockStatements = Indented | MultiLine, + VariableDeclarationList = CommaDelimited | SpaceBetweenSiblings | SingleLine, + SingleLineFunctionBodyStatements = SingleLine | SpaceBetweenSiblings | SpaceBetweenBraces, + MultiLineFunctionBodyStatements = MultiLine, + ClassHeritageClauses = SingleLine | SpaceBetweenSiblings, + ClassMembers = Indented | MultiLine, + InterfaceMembers = Indented | MultiLine, + EnumMembers = CommaDelimited | Indented | MultiLine, + CaseBlockClauses = Indented | MultiLine, + NamedImportsOrExportsElements = CommaDelimited | SpaceBetweenSiblings | AllowTrailingComma | SingleLine | SpaceBetweenBraces, + JsxElementChildren = SingleLine | NoInterveningComments, + JsxElementAttributes = SingleLine | SpaceBetweenSiblings | NoInterveningComments, + CaseOrDefaultClauseStatements = Indented | MultiLine | NoTrailingNewLine | OptionalIfEmpty, + HeritageClauseTypes = CommaDelimited | SpaceBetweenSiblings | SingleLine, + SourceFileStatements = MultiLine | NoTrailingNewLine, + Decorators = MultiLine | Optional, + TypeArguments = CommaDelimited | SpaceBetweenSiblings | SingleLine | Indented | AngleBrackets | Optional, + TypeParameters = CommaDelimited | SpaceBetweenSiblings | SingleLine | Indented | AngleBrackets | Optional, + Parameters = CommaDelimited | SpaceBetweenSiblings | SingleLine | Indented | Parenthesis, + IndexSignatureParameters = CommaDelimited | SpaceBetweenSiblings | SingleLine | Indented | SquareBrackets, + } } \ No newline at end of file diff --git a/src/compiler/printer.ts b/src/compiler/printer.ts deleted file mode 100644 index 9ae824dac6700..0000000000000 --- a/src/compiler/printer.ts +++ /dev/null @@ -1,2659 +0,0 @@ -/// - -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 delimiters = createDelimiterMap(); - const brackets = createBracketsMap(); - - export function createPrinter(writer: EmitTextWriter, compilerOptions: CompilerOptions, printerOptions: PrinterOptions = {}): Printer { - const languageVersion = getEmitScriptTarget(compilerOptions); - const moduleKind = getEmitModuleKind(compilerOptions); - const { - hasGlobalName, - onEmitSourceMapOfNode, - onEmitSourceMapOfToken, - onEmitCommentsOfNode, - onEmitDetachedCommentsOfNode, - onEmitTrailingCommentsOfPosition, - onEmitNode, - onEmitHelpers, - onSetSourceFile, - onSubstituteNode, - } = printerOptions; - const { - write, - writeLine, - increaseIndent, - decreaseIndent - } = writer; - - 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. - - return { - printNode, - printFile, - printBundle - }; - - function printNode(hint: EmitHint, node: Node, sourceFile: SourceFile) { - print(hint, node, sourceFile); - resetGeneratedNames(); - } - - function printBundle(bundle: Bundle) { - if (moduleKind) { - emitHelpersIndirect(bundle); - } - for (const sourceFile of bundle.sourceFiles) { - print(EmitHint.SourceFile, sourceFile, sourceFile); - } - resetGeneratedNames(); - } - - function printFile(sourceFile: SourceFile) { - print(EmitHint.SourceFile, sourceFile, sourceFile); - resetGeneratedNames(); - } - - function print(hint: EmitHint, node: Node, sourceFile: SourceFile) { - setSourceFile(sourceFile); - pipelineEmitWithNotification(hint, node); - } - - function setSourceFile(sourceFile: SourceFile) { - currentSourceFile = sourceFile; - - if (onSetSourceFile) { - onSetSourceFile(sourceFile); - } - } - - function emit(node: Node, hint = EmitHint.Unspecified) { - pipelineEmitWithNotification(hint, node); - } - - function emitIdentifierName(node: Identifier) { - pipelineEmitWithNotification(EmitHint.IdentifierName, node); - } - - function emitExpression(node: Expression) { - pipelineEmitWithNotification(EmitHint.Expression, node); - } - - function pipelineEmitWithNotification(hint: EmitHint, node: Node) { - if (onEmitNode) { - onEmitNode(hint, node, pipelineEmitWithComments); - } - else { - pipelineEmitWithComments(hint, node); - } - } - - function pipelineEmitWithComments(hint: EmitHint, node: Node) { - if (onEmitCommentsOfNode && hint !== EmitHint.SourceFile) { - onEmitCommentsOfNode(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; - switch (kind) { - // Pseudo-literals - case SyntaxKind.TemplateHead: - case SyntaxKind.TemplateMiddle: - case SyntaxKind.TemplateTail: - return emitLiteral(node); - - // Identifiers - 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 - 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 emitThisType(); - case SyntaxKind.TypeOperator: - return emitTypeOperator(node); - case SyntaxKind.IndexedAccessType: - return emitIndexedAccessType(node); - case SyntaxKind.MappedType: - return emitMappedType(node); - case SyntaxKind.LiteralType: - return emitLiteralType(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 emitSemicolonClassElement(); - - // Statements - case SyntaxKind.Block: - return emitBlock(node); - case SyntaxKind.VariableStatement: - return emitVariableStatement(node); - case SyntaxKind.EmptyStatement: - return emitEmptyStatement(); - 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.JsxOpeningElement: - return emitJsxOpeningElement(node); - case SyntaxKind.JsxClosingElement: - return emitJsxClosingElement(node); - case SyntaxKind.JsxAttribute: - return emitJsxAttribute(node); - case SyntaxKind.JsxSpreadAttribute: - return emitJsxSpreadAttribute(node); - case SyntaxKind.JsxExpression: - return emitJsxExpression(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); - case SyntaxKind.SpreadAssignment: - return emitSpreadAssignment(node as SpreadAssignment); - - // Enum - case SyntaxKind.EnumMember: - return emitEnumMember(node); - - // JSDoc nodes (ignored) - // Transformation nodes (ignored) - } - - // If the node is an expression, try to emit it as an expression with - // substitution. - if (isExpression(node)) { - return pipelineEmitWithSubstitution(EmitHint.Expression, node); - } - } - - function pipelineEmitExpression(node: Node): void { - const kind = node.kind; - switch (kind) { - // Literals - case SyntaxKind.NumericLiteral: - return emitNumericLiteral(node); - - case SyntaxKind.StringLiteral: - case SyntaxKind.RegularExpressionLiteral: - case SyntaxKind.NoSubstitutionTemplateLiteral: - return emitLiteral(node); - - // Identifiers - case SyntaxKind.Identifier: - return emitIdentifier(node); - - // Reserved words - case SyntaxKind.FalseKeyword: - case SyntaxKind.NullKeyword: - case SyntaxKind.SuperKeyword: - case SyntaxKind.TrueKeyword: - case SyntaxKind.ThisKeyword: - writeTokenText(kind); - return; - - // 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.SpreadElement: - return emitSpreadExpression(node); - case SyntaxKind.ClassExpression: - return emitClassExpression(node); - case SyntaxKind.OmittedExpression: - return; - case SyntaxKind.AsExpression: - return emitAsExpression(node); - case SyntaxKind.NonNullExpression: - return emitNonNullExpression(node); - case SyntaxKind.MetaProperty: - return emitMetaProperty(node); - - // JSX - case SyntaxKind.JsxElement: - return emitJsxElement(node); - case SyntaxKind.JsxSelfClosingElement: - return emitJsxSelfClosingElement(node); - - // Transformation nodes - case SyntaxKind.PartiallyEmittedExpression: - return emitPartiallyEmittedExpression(node); - } - } - - function emitBodyIndirect(node: Node, elements: NodeArray, emitCallback: (node: Node) => void): void { - if (onEmitDetachedCommentsOfNode) { - onEmitDetachedCommentsOfNode(node, elements, emitCallback); - } - else { - emitCallback(node); - } - } - - function emitHelpersIndirect(node: Node) { - if (onEmitHelpers) { - onEmitHelpers(node, writeLines); - } - } - - // - // Literals/Pseudo-literals - // - - // SyntaxKind.NumericLiteral - function emitNumericLiteral(node: NumericLiteral) { - emitLiteral(node); - if (node.trailingComment) { - write(` /*${node.trailingComment}*/`); - } - } - - // SyntaxKind.StringLiteral - // SyntaxKind.RegularExpressionLiteral - // SyntaxKind.NoSubstitutionTemplateLiteral - // SyntaxKind.TemplateHead - // SyntaxKind.TemplateMiddle - // SyntaxKind.TemplateTail - function emitLiteral(node: LiteralLikeNode) { - const text = getLiteralTextOfNode(node); - if ((compilerOptions.sourceMap || compilerOptions.inlineSourceMap) - && (node.kind === SyntaxKind.StringLiteral || isTemplateLiteralKind(node.kind))) { - writer.writeLiteral(text); - } - else { - write(text); - } - } - - // - // Identifiers - // - - function emitIdentifier(node: Identifier) { - write(getTextOfNode(node, /*includeTrivia*/ false)); - } - - // - // 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, emitSignatureHead); - } - - function emitConstructor(node: ConstructorDeclaration) { - emitModifiers(node, node.modifiers); - write("constructor"); - emitSignatureAndBody(node, emitSignatureHead); - } - - function emitAccessorDeclaration(node: AccessorDeclaration) { - emitDecorators(node, node.decorators); - emitModifiers(node, node.modifiers); - write(node.kind === SyntaxKind.GetAccessor ? "get " : "set "); - emit(node.name); - emitSignatureAndBody(node, emitSignatureHead); - } - - 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(";"); - } - - function emitSemicolonClassElement() { - 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(")"); - } - - function emitThisType() { - write("this"); - } - - function emitTypeOperator(node: TypeOperatorNode) { - writeTokenText(node.operator); - write(" "); - emit(node.type); - } - - function emitIndexedAccessType(node: IndexedAccessTypeNode) { - emit(node.objectType); - write("["); - emit(node.indexType); - write("]"); - } - - function emitMappedType(node: MappedTypeNode) { - write("{"); - writeLine(); - increaseIndent(); - writeIfPresent(node.readonlyToken, "readonly "); - write("["); - emit(node.typeParameter.name); - write(" in "); - emit(node.typeParameter.constraint); - write("]"); - writeIfPresent(node.questionToken, "?"); - write(": "); - emit(node.type); - write(";"); - writeLine(); - decreaseIndent(); - write("}"); - } - - function emitLiteralType(node: LiteralTypeNode) { - emitExpression(node.literal); - } - - // - // 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 = node.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 indentedFlag = getEmitFlags(node) & EmitFlags.Indented; - if (indentedFlag) { - increaseIndent(); - } - - const preferNewLine = node.multiLine ? ListFormat.PreferNewLine : ListFormat.None; - const allowTrailingComma = languageVersion >= ScriptTarget.ES5 ? ListFormat.AllowTrailingComma : ListFormat.None; - emitList(node, properties, ListFormat.ObjectLiteralExpressionProperties | allowTrailingComma | preferNewLine); - - if (indentedFlag) { - decreaseIndent(); - } - } - } - - function emitPropertyAccessExpression(node: PropertyAccessExpression) { - let indentBeforeDot = false; - let indentAfterDot = false; - if (!(getEmitFlags(node) & EmitFlags.NoIndentation)) { - const dotRangeStart = node.expression.end; - 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); - } - - emitExpression(node.expression); - increaseIndentIf(indentBeforeDot); - - const shouldEmitDotDot = !indentBeforeDot && needsDotDotForPropertyAccess(node.expression); - 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 is a decimal literal that was originally written with a dot - const text = getLiteralTextOfNode(expression); - return getNumericLiteralFlags(text, /*hint*/ NumericLiteralFlags.All) === NumericLiteralFlags.None - && !(expression).isOctalLiteral - && text.indexOf(tokenToString(SyntaxKind.DotToken)) < 0; - } - else if (isPropertyAccessExpression(expression) || isElementAccessExpression(expression)) { - // check if constant enum value is integer - const constantValue = getConstantValue(expression); - // isFinite handles cases when constantValue is undefined - return isFinite(constantValue) - && Math.floor(constantValue) === constantValue - && compilerOptions.removeComments; - } - } - - function emitElementAccessExpression(node: ElementAccessExpression) { - emitExpression(node.expression); - write("["); - emitExpression(node.argumentExpression); - write("]"); - } - - function emitCallExpression(node: CallExpression) { - emitExpression(node.expression); - emitTypeArguments(node, node.typeArguments); - emitExpressionList(node, node.arguments, ListFormat.CallExpressionArguments); - } - - function emitNewExpression(node: NewExpression) { - write("new "); - emitExpression(node.expression); - emitTypeArguments(node, node.typeArguments); - emitExpressionList(node, node.arguments, ListFormat.NewExpressionArguments); - } - - function emitTaggedTemplateExpression(node: TaggedTemplateExpression) { - emitExpression(node.tag); - write(" "); - emitExpression(node.template); - } - - function emitTypeAssertionExpression(node: TypeAssertion) { - 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); - emitSignatureAndBody(node, emitArrowFunctionHead); - } - - 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) { - writeTokenText(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) { - emitExpression(node.operand); - writeTokenText(node.operator); - } - - function emitBinaryExpression(node: BinaryExpression) { - const isCommaOperator = node.operatorToken.kind !== SyntaxKind.CommaToken; - const indentBeforeOperator = needsIndentation(node, node.left, node.operatorToken); - const indentAfterOperator = needsIndentation(node, node.operatorToken, node.right); - - emitExpression(node.left); - increaseIndentIf(indentBeforeOperator, isCommaOperator ? " " : undefined); - writeTokenText(node.operatorToken.kind); - 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 emitSpreadExpression(node: SpreadElement) { - write("..."); - emitExpression(node.expression); - } - - function emitClassExpression(node: ClassExpression) { - emitClassDeclarationOrExpression(node); - } - - function emitExpressionWithTypeArguments(node: ExpressionWithTypeArguments) { - emitExpression(node.expression); - emitTypeArguments(node, node.typeArguments); - } - - function emitAsExpression(node: AsExpression) { - emitExpression(node.expression); - if (node.type) { - write(" as "); - emit(node.type); - } - } - - function emitNonNullExpression(node: NonNullExpression) { - emitExpression(node.expression); - write("!"); - } - - function emitMetaProperty(node: MetaProperty) { - writeToken(node.keywordToken, node.pos); - write("."); - emit(node.name); - } - - // - // Misc - // - - function emitTemplateSpan(node: TemplateSpan) { - emitExpression(node.expression); - emit(node.literal); - } - - // - // Statements - // - - function emitBlock(node: Block) { - if (isSingleLineEmptyBlock(node)) { - writeToken(SyntaxKind.OpenBraceToken, node.pos, /*contextNode*/ node); - write(" "); - writeToken(SyntaxKind.CloseBraceToken, node.statements.end, /*contextNode*/ node); - } - else { - writeToken(SyntaxKind.OpenBraceToken, node.pos, /*contextNode*/ node); - emitBlockStatements(node); - writeToken(SyntaxKind.CloseBraceToken, node.statements.end, /*contextNode*/ node); - } - } - - function emitBlockStatements(node: BlockLike) { - if (getEmitFlags(node) & EmitFlags.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 emitEmptyStatement() { - write(";"); - } - - function emitExpressionStatement(node: ExpressionStatement) { - emitExpression(node.expression); - write(";"); - } - - function emitIfStatement(node: IfStatement) { - const openParenPos = writeToken(SyntaxKind.IfKeyword, node.pos, node); - write(" "); - writeToken(SyntaxKind.OpenParenToken, openParenPos, node); - emitExpression(node.expression); - writeToken(SyntaxKind.CloseParenToken, node.expression.end, node); - emitEmbeddedStatement(node, node.thenStatement); - if (node.elseStatement) { - writeLineOrSpace(node); - writeToken(SyntaxKind.ElseKeyword, node.thenStatement.end, node); - if (node.elseStatement.kind === SyntaxKind.IfStatement) { - write(" "); - emit(node.elseStatement); - } - else { - emitEmbeddedStatement(node, node.elseStatement); - } - } - } - - function emitDoStatement(node: DoStatement) { - write("do"); - emitEmbeddedStatement(node, node.statement); - if (isBlock(node.statement)) { - write(" "); - } - else { - writeLineOrSpace(node); - } - - write("while ("); - emitExpression(node.expression); - write(");"); - } - - function emitWhileStatement(node: WhileStatement) { - write("while ("); - emitExpression(node.expression); - write(")"); - emitEmbeddedStatement(node, node.statement); - } - - function emitForStatement(node: ForStatement) { - const openParenPos = writeToken(SyntaxKind.ForKeyword, node.pos); - write(" "); - writeToken(SyntaxKind.OpenParenToken, openParenPos, /*contextNode*/ node); - emitForBinding(node.initializer); - write(";"); - emitExpressionWithPrefix(" ", node.condition); - write(";"); - emitExpressionWithPrefix(" ", node.incrementor); - write(")"); - emitEmbeddedStatement(node, node.statement); - } - - function emitForInStatement(node: ForInStatement) { - const openParenPos = writeToken(SyntaxKind.ForKeyword, node.pos); - write(" "); - writeToken(SyntaxKind.OpenParenToken, openParenPos); - emitForBinding(node.initializer); - write(" in "); - emitExpression(node.expression); - writeToken(SyntaxKind.CloseParenToken, node.expression.end); - emitEmbeddedStatement(node, node.statement); - } - - function emitForOfStatement(node: ForOfStatement) { - const openParenPos = writeToken(SyntaxKind.ForKeyword, node.pos); - write(" "); - writeToken(SyntaxKind.OpenParenToken, openParenPos); - emitForBinding(node.initializer); - write(" of "); - emitExpression(node.expression); - writeToken(SyntaxKind.CloseParenToken, node.expression.end); - emitEmbeddedStatement(node, node.statement); - } - - function emitForBinding(node: VariableDeclarationList | Expression) { - if (node !== undefined) { - if (node.kind === SyntaxKind.VariableDeclarationList) { - emit(node); - } - else { - emitExpression(node); - } - } - } - - function emitContinueStatement(node: ContinueStatement) { - writeToken(SyntaxKind.ContinueKeyword, node.pos); - emitWithPrefix(" ", node.label); - write(";"); - } - - function emitBreakStatement(node: BreakStatement) { - writeToken(SyntaxKind.BreakKeyword, node.pos); - emitWithPrefix(" ", node.label); - write(";"); - } - - function emitReturnStatement(node: ReturnStatement) { - writeToken(SyntaxKind.ReturnKeyword, node.pos, /*contextNode*/ node); - emitExpressionWithPrefix(" ", node.expression); - write(";"); - } - - function emitWithStatement(node: WithStatement) { - write("with ("); - emitExpression(node.expression); - write(")"); - emitEmbeddedStatement(node, node.statement); - } - - function emitSwitchStatement(node: SwitchStatement) { - const openParenPos = writeToken(SyntaxKind.SwitchKeyword, node.pos); - write(" "); - writeToken(SyntaxKind.OpenParenToken, openParenPos); - emitExpression(node.expression); - writeToken(SyntaxKind.CloseParenToken, node.expression.end); - 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); - if (node.catchClause) { - writeLineOrSpace(node); - emit(node.catchClause); - } - if (node.finallyBlock) { - writeLineOrSpace(node); - write("finally "); - emit(node.finallyBlock); - } - } - - function emitDebuggerStatement(node: DebuggerStatement) { - writeToken(SyntaxKind.DebuggerKeyword, node.pos); - write(";"); - } - - // - // Declarations - // - - function emitVariableDeclaration(node: VariableDeclaration) { - emit(node.name); - emitWithPrefix(": ", node.type); - 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 "); - emitIdentifierName(node.name); - emitSignatureAndBody(node, emitSignatureHead); - } - - function emitSignatureAndBody(node: FunctionLikeDeclaration, emitSignatureHead: (node: SignatureDeclaration) => void) { - const body = node.body; - if (body) { - if (isBlock(body)) { - const indentedFlag = getEmitFlags(node) & EmitFlags.Indented; - if (indentedFlag) { - increaseIndent(); - } - - if (getEmitFlags(node) & EmitFlags.ReuseTempVariableScope) { - emitSignatureHead(node); - emitBlockFunctionBody(body); - } - else { - pushNameGenerationScope(); - emitSignatureHead(node); - emitBlockFunctionBody(body); - popNameGenerationScope(); - } - - if (indentedFlag) { - decreaseIndent(); - } - } - else { - emitSignatureHead(node); - write(" "); - emitExpression(body); - } - } - 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(body: Block) { - // We must emit a function body as a single-line body in the following case: - // * The body has NodeEmitFlags.SingleLine specified. - - // We must emit a function body as a multi-line body in the following cases: - // * The body is explicitly marked as multi-line. - // * A non-synthesized body's start and end position are on different lines. - // * Any statement in the body starts on a new line. - - if (getEmitFlags(body) & EmitFlags.SingleLine) { - return true; - } - - if (body.multiLine) { - return false; - } - - if (!nodeIsSynthesized(body) && !rangeIsOnSingleLine(body, currentSourceFile)) { - return false; - } - - if (shouldWriteLeadingLineTerminator(body, body.statements, ListFormat.PreserveLines) - || shouldWriteClosingLineTerminator(body, body.statements, ListFormat.PreserveLines)) { - return false; - } - - let previousStatement: Statement; - for (const statement of body.statements) { - if (shouldWriteSeparatingLineTerminator(previousStatement, statement, ListFormat.PreserveLines)) { - return false; - } - - previousStatement = statement; - } - - return true; - } - - function emitBlockFunctionBody(body: Block) { - write(" {"); - increaseIndent(); - - const emitBlockFunctionBody = shouldEmitBlockFunctionBodyOnSingleLine(body) - ? emitBlockFunctionBodyOnSingleLine - : emitBlockFunctionBodyWorker; - - emitBodyIndirect(body, body.statements, emitBlockFunctionBody); - - decreaseIndent(); - writeToken(SyntaxKind.CloseBraceToken, body.statements.end, body); - } - - function emitBlockFunctionBodyOnSingleLine(body: Block) { - emitBlockFunctionBodyWorker(body, /*emitBlockFunctionBodyOnSingleLine*/ true); - } - - function emitBlockFunctionBodyWorker(body: Block, emitBlockFunctionBodyOnSingleLine?: boolean) { - // Emit all the prologue directives (like "use strict"). - const statementOffset = emitPrologueDirectives(body.statements, /*startWithNewLine*/ true); - const pos = writer.getTextPos(); - emitHelpersIndirect(body); - if (statementOffset === 0 && pos === writer.getTextPos() && emitBlockFunctionBodyOnSingleLine) { - decreaseIndent(); - emitList(body, body.statements, ListFormat.SingleLineFunctionBodyStatements); - increaseIndent(); - } - else { - emitList(body, body.statements, ListFormat.MultiLineFunctionBodyStatements, statementOffset); - } - } - - function emitClassDeclaration(node: ClassDeclaration) { - emitClassDeclarationOrExpression(node); - } - - function emitClassDeclarationOrExpression(node: ClassDeclaration | ClassExpression) { - emitDecorators(node, node.decorators); - emitModifiers(node, node.modifiers); - write("class"); - emitNodeWithPrefix(" ", node.name, emitIdentifierName); - - const indentedFlag = getEmitFlags(node) & EmitFlags.Indented; - if (indentedFlag) { - increaseIndent(); - } - - emitTypeParameters(node, node.typeParameters); - emitList(node, node.heritageClauses, ListFormat.ClassHeritageClauses); - - pushNameGenerationScope(); - write(" {"); - emitList(node, node.members, ListFormat.ClassMembers); - write("}"); - popNameGenerationScope(); - - if (indentedFlag) { - decreaseIndent(); - } - } - - 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.HeritageClauses); - 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); - pushNameGenerationScope(); - write(" {"); - emitList(node, node.members, ListFormat.EnumMembers); - write("}"); - popNameGenerationScope(); - } - - 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 (isEmptyBlock(node)) { - write("{ }"); - } - else { - pushNameGenerationScope(); - write("{"); - increaseIndent(); - emitBlockStatements(node); - write("}"); - popNameGenerationScope(); - } - } - - function emitCaseBlock(node: CaseBlock) { - writeToken(SyntaxKind.OpenBraceToken, node.pos); - emitList(node, node.clauses, ListFormat.CaseBlockClauses); - writeToken(SyntaxKind.CloseBraceToken, node.clauses.end); - } - - 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 "); - if (node.importClause) { - emit(node.importClause); - write(" from "); - } - emitExpression(node.moduleSpecifier); - write(";"); - } - - function emitImportClause(node: ImportClause) { - emit(node.name); - if (node.name && node.namedBindings) { - write(", "); - } - emit(node.namedBindings); - } - - function emitNamespaceImport(node: NamespaceImport) { - write("* as "); - emit(node.name); - } - - 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); - } - else { - write("*"); - } - if (node.moduleSpecifier) { - 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("<"); - emitJsxTagName(node.tagName); - write(" "); - emitList(node, node.attributes, ListFormat.JsxElementAttributes); - write("/>"); - } - - function emitJsxOpeningElement(node: JsxOpeningElement) { - write("<"); - emitJsxTagName(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(""); - } - - function emitJsxAttribute(node: JsxAttribute) { - emit(node.name); - emitWithPrefix("=", node.initializer); - } - - function emitJsxSpreadAttribute(node: JsxSpreadAttribute) { - write("{..."); - emitExpression(node.expression); - write("}"); - } - - function emitJsxExpression(node: JsxExpression) { - if (node.expression) { - write("{"); - if (node.dotDotDotToken) { - write("..."); - } - emitExpression(node.expression); - write("}"); - } - } - - function emitJsxTagName(node: JsxTagNameExpression) { - if (node.kind === SyntaxKind.Identifier) { - emitExpression(node); - } - else { - emit(node); - } - } - - // - // 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) { - const emitAsSingleStatement = - statements.length === 1 && - ( - // treat synthesized nodes as located on the same line for emit purposes - nodeIsSynthesized(parentNode) || - nodeIsSynthesized(statements[0]) || - rangeStartPositionsAreOnSameLine(parentNode, statements[0], currentSourceFile) - ); - - if (emitAsSingleStatement) { - write(" "); - emit(statements[0]); - } - else { - emitList(parentNode, statements, ListFormat.CaseOrDefaultClauseStatements); - } - } - - function emitHeritageClause(node: HeritageClause) { - write(" "); - writeTokenText(node.token); - write(" "); - emitList(node, node.types, ListFormat.HeritageClauseTypes); - } - - function emitCatchClause(node: CatchClause) { - writeLine(); - const openParenPos = writeToken(SyntaxKind.CatchKeyword, node.pos); - write(" "); - writeToken(SyntaxKind.OpenParenToken, openParenPos); - emit(node.variableDeclaration); - writeToken(SyntaxKind.CloseParenToken, node.variableDeclaration ? node.variableDeclaration.end : openParenPos); - 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. - const initializer = node.initializer; - if (onEmitTrailingCommentsOfPosition && (getEmitFlags(initializer) & EmitFlags.NoLeadingComments) === 0) { - const commentRange = getCommentRange(initializer); - onEmitTrailingCommentsOfPosition(commentRange.pos); - } - emitExpression(initializer); - } - - function emitShorthandPropertyAssignment(node: ShorthandPropertyAssignment) { - emit(node.name); - if (node.objectAssignmentInitializer) { - write(" = "); - emitExpression(node.objectAssignmentInitializer); - } - } - - function emitSpreadAssignment(node: SpreadAssignment) { - if (node.expression) { - write("..."); - emitExpression(node.expression); - } - } - - // - // Enum - // - - function emitEnumMember(node: EnumMember) { - emit(node.name); - emitExpressionWithPrefix(" = ", node.initializer); - } - - // - // Top-level nodes - // - - function emitSourceFile(node: SourceFile) { - writeLine(); - emitShebang(); - emitBodyIndirect(node, node.statements, emitSourceFileWorker); - } - - function emitSourceFileWorker(node: SourceFile) { - const statements = node.statements; - const statementOffset = emitPrologueDirectives(statements); - pushNameGenerationScope(); - emitHelpersIndirect(node); - emitList(node, statements, ListFormat.MultiLine, statementOffset); - popNameGenerationScope(); - } - - // Transformation nodes - - function emitPartiallyEmittedExpression(node: PartiallyEmittedExpression) { - emitExpression(node.expression); - } - - /** - * Emits any prologue directives at the start of a Statement list, returning the - * number of prologue directives written to the output. - */ - function emitPrologueDirectives(statements: Node[], startWithNewLine?: boolean): number { - 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 - return i; - } - } - - return statements.length; - } - - // - // Helpers - // - - function emitShebang() { - const shebang = getShebang(currentSourceFile.text); - if (shebang) { - write(shebang); - writeLine(); - } - } - - function emitModifiers(node: Node, modifiers: NodeArray) { - if (modifiers && modifiers.length) { - emitList(node, modifiers, ListFormat.Modifiers); - 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 emitEmbeddedStatement(parent: Node, node: Statement) { - if (isBlock(node) || getEmitFlags(parent) & EmitFlags.SingleLine) { - 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. - const mayEmitInterveningComments = (format & ListFormat.NoInterveningComments) === 0; - let shouldEmitInterveningComments = mayEmitInterveningComments; - if (shouldWriteLeadingLineTerminator(parentNode, children, format)) { - writeLine(); - shouldEmitInterveningComments = false; - } - else if (format & ListFormat.SpaceBetweenBraces) { - write(" "); - } - - // Increase the indent, if requested. - if (format & ListFormat.Indented) { - increaseIndent(); - } - - // Emit each child. - let previousSibling: Node; - let shouldDecreaseIndentAfterEmit: boolean; - 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)) { - // If a synthesized node in a single-line list starts on a new - // line, we should increase the indent. - if ((format & (ListFormat.LinesMask | ListFormat.Indented)) === ListFormat.SingleLine) { - increaseIndent(); - shouldDecreaseIndentAfterEmit = true; - } - - writeLine(); - shouldEmitInterveningComments = false; - } - else if (previousSibling && format & ListFormat.SpaceBetweenSiblings) { - write(" "); - } - } - - // Emit this child. - if (shouldEmitInterveningComments) { - if (onEmitTrailingCommentsOfPosition) { - const commentRange = getCommentRange(child); - onEmitTrailingCommentsOfPosition(commentRange.pos); - } - } - else { - shouldEmitInterveningComments = mayEmitInterveningComments; - } - - emit(child); - - if (shouldDecreaseIndentAfterEmit) { - decreaseIndent(); - shouldDecreaseIndentAfterEmit = false; - } - - 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 (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(" "); - } - else { - writeLine(); - } - } - - 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 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; - } - - 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; - } - - if (format & ListFormat.PreserveLines) { - if (format & ListFormat.PreferNewLine) { - return true; - } - - const firstChild = children[0]; - if (firstChild === undefined) { - return !rangeIsOnSingleLine(parentNode, currentSourceFile); - } - else if (positionIsSynthesized(parentNode.pos) || nodeIsSynthesized(firstChild)) { - return synthesizedNodeStartsOnNewLine(firstChild, format); - } - else { - return !rangeStartPositionsAreOnSameLine(parentNode, firstChild, currentSourceFile); - } - } - 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, currentSourceFile); - } - } - else { - return nextNode.startsOnNewLine; - } - } - - function shouldWriteClosingLineTerminator(parentNode: Node, children: NodeArray, format: ListFormat) { - if (format & ListFormat.MultiLine) { - return (format & ListFormat.NoTrailingNewLine) === 0; - } - else if (format & ListFormat.PreserveLines) { - if (format & ListFormat.PreferNewLine) { - return true; - } - - const lastChild = lastOrUndefined(children); - if (lastChild === undefined) { - return !rangeIsOnSingleLine(parentNode, currentSourceFile); - } - else if (positionIsSynthesized(parentNode.pos) || nodeIsSynthesized(lastChild)) { - return synthesizedNodeStartsOnNewLine(lastChild, format); - } - else { - return !rangeEndPositionsAreOnSameLine(parentNode, lastChild, currentSourceFile); - } - } - 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 needsIndentation(parent: Node, node1: Node, node2: Node): boolean { - parent = skipSynthesizedParentheses(parent); - node1 = skipSynthesizedParentheses(node1); - node2 = skipSynthesizedParentheses(node2); - - // Always use a newline for synthesized code if the synthesizer desires it. - if (node2.startsOnNewLine) { - return true; - } - - return !nodeIsSynthesized(parent) - && !nodeIsSynthesized(node1) - && !nodeIsSynthesized(node2) - && !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; - } - - return node; - } - - function getTextOfNode(node: Node, includeTrivia?: boolean): string { - if (isGeneratedIdentifier(node)) { - return generateName(node); - } - else if (isIdentifier(node) && (nodeIsSynthesized(node) || !node.parent)) { - return unescapeIdentifier(node.text); - } - else if (node.kind === SyntaxKind.StringLiteral && (node).textSourceNode) { - return getTextOfNode((node).textSourceNode, includeTrivia); - } - else if (isLiteralExpression(node) && (nodeIsSynthesized(node) || !node.parent)) { - return node.text; - } - - return getSourceTextOfNodeFromSourceFile(currentSourceFile, node, includeTrivia); - } - - function getLiteralTextOfNode(node: LiteralLikeNode): string { - if (node.kind === SyntaxKind.StringLiteral && (node).textSourceNode) { - const textSourceNode = (node).textSourceNode; - if (isIdentifier(textSourceNode)) { - return "\"" + escapeNonAsciiCharacters(escapeString(getTextOfNode(textSourceNode))) + "\""; - } - else { - return getLiteralTextOfNode(textSourceNode); - } - } - - return getLiteralText(node, currentSourceFile, languageVersion); - } - - function resetGeneratedNames() { - nodeIdToGeneratedName = undefined; - autoGeneratedIdToGeneratedName = undefined; - generatedNames = undefined; - tempFlagsStack = undefined; - tempFlags = TempFlags.Auto; - } - - function ensureNameGeneration() { - if (generatedNames === undefined) { - nodeIdToGeneratedName = []; - autoGeneratedIdToGeneratedName = []; - generatedNames = createMap(); - tempFlagsStack = []; - } - } - - /** - * Push a new name generation scope. - */ - function pushNameGenerationScope() { - ensureNameGeneration(); - tempFlagsStack.push(tempFlags); - tempFlags = 0; - } - - /** - * Pop the current name generation scope. - */ - function popNameGenerationScope() { - ensureNameGeneration(); - tempFlags = tempFlagsStack.pop(); - } - - /** - * Generate the text for a generated identifier. - */ - function generateName(name: GeneratedIdentifier) { - ensureNameGeneration(); - 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 !(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) { - const local = node.locals.get(name); - // We conservatively include alias symbols to cover cases where they're emitted as locals - if (local && local.flags & (SymbolFlags.Value | SymbolFlags.ExportValue | SymbolFlags.Alias)) { - return false; - } - } - } - return true; - } - - /** - * 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; - } - } - } - } - - /** - * 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._) { - baseName += "_"; - } - let i = 1; - while (true) { - const generatedName = baseName + i; - if (isUniqueName(generatedName)) { - generatedNames.set(generatedName, generatedName); - return generatedName; - } - i++; - } - } - - /** - * 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 ? - escapeIdentifier(makeIdentifierFromModuleName((expr).text)) : "module"; - 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, getTextOfNode: (node: Node, includeTrivia?: boolean) => string) { - if (isIdentifier(node.name)) { - return generateNameCached(node.name, getTextOfNode); - } - return makeTempVariableName(TempFlags.Auto); - } - - /** - * Generates a unique name from a node. - */ - 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, getTextOfNode); - case SyntaxKind.ImportDeclaration: - case SyntaxKind.ExportDeclaration: - return generateNameForImportOrExportDeclaration(node); - case SyntaxKind.FunctionDeclaration: - case SyntaxKind.ClassDeclaration: - case SyntaxKind.ExportAssignment: - return generateNameForExportDefault(); - case SyntaxKind.ClassExpression: - return generateNameForClassExpression(); - case SyntaxKind.MethodDeclaration: - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - return generateNameForMethodOrAccessor(node, getTextOfNode); - default: - return makeTempVariableName(TempFlags.Auto); - } - } - - /** - * Generates a unique identifier for a node. - */ - function makeName(name: GeneratedIdentifier) { - switch (name.autoGenerateKind) { - case GeneratedIdentifierKind.Auto: - return makeTempVariableName(TempFlags.Auto); - case GeneratedIdentifierKind.Loop: - return makeTempVariableName(TempFlags._i); - case GeneratedIdentifierKind.Unique: - return makeUniqueName(name.text); - } - - Debug.fail("Unsupported GeneratedIdentifierKind."); - } - - /** - * Gets the node from which a name should be generated. - */ - function getNodeForGeneratedName(name: GeneratedIdentifier) { - const autoGenerateId = name.autoGenerateId; - let node = name as Node; - let original = node.original; - while (original) { - node = original; - - // if "node" is a different generated name (having a different - // "autoGenerateId"), use it and stop traversing. - if (isIdentifier(node) - && node.autoGenerateKind === GeneratedIdentifierKind.Node - && node.autoGenerateId !== autoGenerateId) { - break; - } - - original = node.original; - } - - // otherwise, return the original node for the source; - return node; - } - } - - 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 = 0, // Prints the list on a single line (default). - MultiLine = 1 << 0, // Prints the list on multiple lines. - PreserveLines = 1 << 1, // Prints the list using line preservation if possible. - LinesMask = SingleLine | MultiLine | PreserveLines, - - // Delimiters - NotDelimited = 0, // There is no delimiter between list items (default). - BarDelimited = 1 << 2, // Each list item is space-and-bar (" |") delimited. - AmpersandDelimited = 1 << 3, // Each list item is space-and-ampersand (" &") delimited. - CommaDelimited = 1 << 4, // Each list item is comma (",") delimited. - DelimitersMask = BarDelimited | AmpersandDelimited | CommaDelimited, - - AllowTrailingComma = 1 << 5, // Write a trailing comma (",") if present. - - // Whitespace - Indented = 1 << 6, // The list should be indented. - SpaceBetweenBraces = 1 << 7, // Inserts a space after the opening brace and before the closing brace. - SpaceBetweenSiblings = 1 << 8, // Inserts a space between each sibling node. - - // 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. - NoTrailingNewLine = 1 << 16, // Do not emit a trailing NewLine for a MultiLine list. - NoInterveningComments = 1 << 17, // Do not emit comments between each node - - // Precomputed Formats - Modifiers = SingleLine | SpaceBetweenSiblings, - HeritageClauses = SingleLine | SpaceBetweenSiblings, - TypeLiteralMembers = MultiLine | Indented, - TupleTypeElements = CommaDelimited | SpaceBetweenSiblings | SingleLine | Indented, - UnionTypeConstituents = BarDelimited | SpaceBetweenSiblings | SingleLine, - IntersectionTypeConstituents = AmpersandDelimited | SpaceBetweenSiblings | SingleLine, - ObjectBindingPatternElements = SingleLine | AllowTrailingComma | SpaceBetweenBraces | CommaDelimited | SpaceBetweenSiblings, - ArrayBindingPatternElements = SingleLine | AllowTrailingComma | CommaDelimited | SpaceBetweenSiblings, - ObjectLiteralExpressionProperties = PreserveLines | CommaDelimited | SpaceBetweenSiblings | SpaceBetweenBraces | Indented | Braces, - ArrayLiteralExpressionElements = PreserveLines | CommaDelimited | SpaceBetweenSiblings | AllowTrailingComma | Indented | SquareBrackets, - CallExpressionArguments = CommaDelimited | SpaceBetweenSiblings | SingleLine | Parenthesis, - NewExpressionArguments = CommaDelimited | SpaceBetweenSiblings | SingleLine | Parenthesis | OptionalIfUndefined, - TemplateExpressionSpans = SingleLine | NoInterveningComments, - SingleLineBlockStatements = SpaceBetweenBraces | SpaceBetweenSiblings | SingleLine, - MultiLineBlockStatements = Indented | MultiLine, - VariableDeclarationList = CommaDelimited | SpaceBetweenSiblings | SingleLine, - SingleLineFunctionBodyStatements = SingleLine | SpaceBetweenSiblings | SpaceBetweenBraces, - MultiLineFunctionBodyStatements = MultiLine, - ClassHeritageClauses = SingleLine | SpaceBetweenSiblings, - ClassMembers = Indented | MultiLine, - InterfaceMembers = Indented | MultiLine, - EnumMembers = CommaDelimited | Indented | MultiLine, - CaseBlockClauses = Indented | MultiLine, - NamedImportsOrExportsElements = CommaDelimited | SpaceBetweenSiblings | AllowTrailingComma | SingleLine | SpaceBetweenBraces, - JsxElementChildren = SingleLine | NoInterveningComments, - JsxElementAttributes = SingleLine | SpaceBetweenSiblings | NoInterveningComments, - CaseOrDefaultClauseStatements = Indented | MultiLine | NoTrailingNewLine | OptionalIfEmpty, - HeritageClauseTypes = CommaDelimited | SpaceBetweenSiblings | SingleLine, - SourceFileStatements = MultiLine | NoTrailingNewLine, - Decorators = MultiLine | Optional, - TypeArguments = CommaDelimited | SpaceBetweenSiblings | SingleLine | Indented | AngleBrackets | Optional, - TypeParameters = CommaDelimited | SpaceBetweenSiblings | SingleLine | Indented | AngleBrackets | Optional, - Parameters = CommaDelimited | SpaceBetweenSiblings | SingleLine | Indented | Parenthesis, - IndexSignatureParameters = CommaDelimited | SpaceBetweenSiblings | SingleLine | Indented | SquareBrackets, - } -} \ No newline at end of file diff --git a/src/compiler/tsconfig.json b/src/compiler/tsconfig.json index 54d5eb77e10f8..52bb92ee1f003 100644 --- a/src/compiler/tsconfig.json +++ b/src/compiler/tsconfig.json @@ -34,7 +34,6 @@ "comments.ts", "sourcemap.ts", "declarationEmitter.ts", - "printer.ts", "emitter.ts", "program.ts", "commandLineParser.ts", diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 682cd9e617b56..99cdca407b68d 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -558,6 +558,15 @@ export type ModifiersArray = NodeArray; + /*@internal*/ + export const enum GeneratedIdentifierKind { + None, // Not automatically generated. + Auto, // Automatically generated identifier. + Loop, // Automatically generated identifier with a preference for '_i'. + Unique, // Unique name based on the 'text' property. + Node, // Unique name based on the node in the 'original' property. + } + export interface Identifier extends PrimaryExpression { kind: SyntaxKind.Identifier; text: string; // Text of identifier (with escapes converted to characters) @@ -572,14 +581,7 @@ resolvedSymbol: Symbol; } - export const enum GeneratedIdentifierKind { - None, // Not automatically generated. - Auto, // Automatically generated identifier. - Loop, // Automatically generated identifier with a preference for '_i'. - Unique, // Unique name based on the 'text' property. - Node, // Unique name based on the node in the 'original' property. - } - + /*@internal*/ export interface GeneratedIdentifier extends Identifier { autoGenerateKind: GeneratedIdentifierKind.Auto | GeneratedIdentifierKind.Loop @@ -3909,40 +3911,38 @@ /* @internal */ export type Transformer = (context: TransformationContext) => (node: SourceFile) => SourceFile; - 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 Printer { + printNode(hint: EmitHint, node: Node, sourceFile: SourceFile): string; + printFile(sourceFile: SourceFile): string; + printBundle(bundle: Bundle): string; } + /*@internal*/ export interface Printer { - printNode(hint: EmitHint, node: Node, sourceFile: SourceFile): void; - printFile(sourceFile: SourceFile): void; - printBundle(bundle: Bundle): void; + writeNode(hint: EmitHint, node: Node, sourceFile: SourceFile, writer: EmitTextWriter): void; + writeFile(sourceFile: SourceFile, writer: EmitTextWriter): void; + writeBundle(bundle: Bundle, writer: EmitTextWriter): void; } - export interface PrinterOptions { + export interface PrintHandlers { hasGlobalName?: (name: string) => boolean; - onEmitSourceMapOfNode?: (hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) => void; - onEmitSourceMapOfToken?: (node: Node, token: SyntaxKind, pos: number, emitCallback: (token: SyntaxKind, pos: number) => number) => number; - onEmitCommentsOfNode?: (hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) => void; - onEmitDetachedCommentsOfNode?: (node: Node, detachedRange: TextRange, emitCallback: (node: Node) => void) => void; - onEmitTrailingCommentsOfPosition?: (pos: number) => void; onEmitNode?: (hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) => void; - onEmitHelpers?: (node: Node, writeLines: (text: string) => void) => void; - onSetSourceFile?: (node: SourceFile) => void; 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; + module?: ModuleKind; + removeComments?: boolean; + newLine?: NewLineKind; + /*@internal*/ sourceMap?: boolean; + /*@internal*/ inlineSourceMap?: boolean; + /*@internal*/ extendedDiagnostics?: boolean; } export interface TextSpan { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 5c92bfa65cce4..8d85ebeca9926 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2439,6 +2439,23 @@ 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) { @@ -3215,7 +3232,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 d7bb935db897e..ebd07118cc531 100644 --- a/src/harness/tsconfig.json +++ b/src/harness/tsconfig.json @@ -37,7 +37,6 @@ "../compiler/comments.ts", "../compiler/sourcemap.ts", "../compiler/declarationEmitter.ts", - "../compiler/printer.ts", "../compiler/emitter.ts", "../compiler/program.ts", "../compiler/commandLineParser.ts", diff --git a/src/services/tsconfig.json b/src/services/tsconfig.json index 493f3b45f9398..b60e00292f51f 100644 --- a/src/services/tsconfig.json +++ b/src/services/tsconfig.json @@ -34,7 +34,6 @@ "../compiler/comments.ts", "../compiler/sourcemap.ts", "../compiler/declarationEmitter.ts", - "../compiler/printer.ts", "../compiler/emitter.ts", "../compiler/program.ts", "../compiler/commandLineParser.ts", From f0563ac2874190ef79549f17fdcf1da883add655 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Mon, 30 Jan 2017 13:40:42 -0800 Subject: [PATCH 3/6] Printer API unit tests --- Jakefile.js | 1 + src/compiler/core.ts | 2 +- src/compiler/emitter.ts | 66 ++++--------- src/compiler/types.ts | 1 - src/harness/tsconfig.json | 3 +- src/harness/unittests/printer.ts | 97 +++++++++++++++++++ .../printsBundleCorrectly.default.js | 6 ++ .../printsBundleCorrectly.removeComments.js | 4 + .../printerApi/printsFileCorrectly.default.js | 25 +++++ .../printsFileCorrectly.removeComments.js | 17 ++++ .../printerApi/printsNodeCorrectly.class.js | 3 + 11 files changed, 173 insertions(+), 52 deletions(-) create mode 100644 src/harness/unittests/printer.ts create mode 100644 tests/baselines/reference/printerApi/printsBundleCorrectly.default.js create mode 100644 tests/baselines/reference/printerApi/printsBundleCorrectly.removeComments.js create mode 100644 tests/baselines/reference/printerApi/printsFileCorrectly.default.js create mode 100644 tests/baselines/reference/printerApi/printsFileCorrectly.removeComments.js create mode 100644 tests/baselines/reference/printerApi/printsNodeCorrectly.class.js diff --git a/Jakefile.js b/Jakefile.js index 3ee8476c842d2..0b3a26dbb771e 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/core.ts b/src/compiler/core.ts index b8097acaa7113..304fba69b2677 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -1451,7 +1451,7 @@ namespace ts { return compilerOptions.target || ScriptTarget.ES3; } - export function getEmitModuleKind(compilerOptions: CompilerOptions | PrinterOptions) { + export function getEmitModuleKind(compilerOptions: CompilerOptions) { return typeof compilerOptions.module === "number" ? compilerOptions.module : getEmitScriptTarget(compilerOptions) >= ScriptTarget.ES2015 ? ModuleKind.ES2015 : ModuleKind.CommonJS; diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index b3bd594ae8862..f20a7944a0f66 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -12,6 +12,7 @@ namespace ts { // 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 compilerOptions = host.getCompilerOptions(); + const moduleKind = getEmitModuleKind(compilerOptions); const sourceMapDataList: SourceMapData[] = compilerOptions.sourceMap || compilerOptions.inlineSourceMap ? [] : undefined; const emittedFilesList: string[] = compilerOptions.listEmittedFiles ? [] : undefined; const emitterDiagnostics = createDiagnosticCollection(); @@ -147,6 +148,10 @@ namespace ts { 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; @@ -201,7 +206,6 @@ namespace ts { const newLine = getNewLineCharacter(printerOptions); const languageVersion = getEmitScriptTarget(printerOptions); - const moduleKind = getEmitModuleKind(printerOptions); const comments = createCommentWriter(printerOptions, onEmitSourceMapOfPosition); const { emitNodeWithComments, @@ -257,9 +261,7 @@ namespace ts { function writeBundle(bundle: Bundle, output: EmitTextWriter) { const previousWriter = writer; setWriter(output); - if (moduleKind) { - emitHelpersIndirect(bundle); - } + emitHelpersIndirect(bundle); for (const sourceFile of bundle.sourceFiles) { print(EmitHint.SourceFile, sourceFile, sourceFile); } @@ -280,11 +282,8 @@ namespace ts { } function endPrint() { - const text = writer.getText(); - if (writer === ownWriter) { - writer.reset(); - } - + const text = ownWriter.getText(); + ownWriter.reset(); return text; } @@ -385,6 +384,15 @@ namespace ts { function pipelineEmitUnspecified(node: Node): void { const kind = node.kind; + + // Reserved words + // Strict mode reserved words + // Contextual keywords + if (isKeyword(kind)) { + writeTokenText(kind); + return; + } + switch (kind) { // Pseudo-literals case SyntaxKind.TemplateHead: @@ -396,46 +404,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 diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 99cdca407b68d..7e097ef40b106 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3937,7 +3937,6 @@ export interface PrinterOptions { target?: ScriptTarget; - module?: ModuleKind; removeComments?: boolean; newLine?: NewLineKind; /*@internal*/ sourceMap?: boolean; diff --git a/src/harness/tsconfig.json b/src/harness/tsconfig.json index ebd07118cc531..bea688d358b17 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 0000000000000..2496a880bc55d --- /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 0000000000000..2d86cc0c09ce5 --- /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 0000000000000..22b03719cf350 --- /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 0000000000000..9bff9d656b3e6 --- /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 0000000000000..b511aff5e7888 --- /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 0000000000000..fa19e836fed79 --- /dev/null +++ b/tests/baselines/reference/printerApi/printsNodeCorrectly.class.js @@ -0,0 +1,3 @@ +class C { + public prop; +} \ No newline at end of file From 64ff691a2e9c2cf4f5eb651bffc2a19d82adccd3 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Mon, 30 Jan 2017 14:21:52 -0800 Subject: [PATCH 4/6] Move EmitTextWriter to types.ts --- src/compiler/types.ts | 28 +++++++++++++++++++++------- src/compiler/utilities.ts | 17 ----------------- 2 files changed, 21 insertions(+), 24 deletions(-) diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 7e097ef40b106..03098fd41290b 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3915,13 +3915,9 @@ printNode(hint: EmitHint, node: Node, sourceFile: SourceFile): string; printFile(sourceFile: SourceFile): string; printBundle(bundle: Bundle): string; - } - - /*@internal*/ - export interface Printer { - writeNode(hint: EmitHint, node: Node, sourceFile: SourceFile, writer: EmitTextWriter): void; - writeFile(sourceFile: SourceFile, writer: EmitTextWriter): void; - writeBundle(bundle: Bundle, writer: EmitTextWriter): void; + /*@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 { @@ -3944,6 +3940,24 @@ /*@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 8d85ebeca9926..a2c5cbd15a380 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2439,23 +2439,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) { From f02ce1f6a02661da69aade37d9d167274c23dad4 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Wed, 1 Feb 2017 15:22:35 -0800 Subject: [PATCH 5/6] PR Feedback --- src/compiler/comments.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/comments.ts b/src/compiler/comments.ts index f69b4c546a0d6..200158c2f5ce7 100644 --- a/src/compiler/comments.ts +++ b/src/compiler/comments.ts @@ -11,7 +11,7 @@ namespace ts { emitTrailingCommentsOfPosition(pos: number): void; } - export function createCommentWriter(printerOptions: PrinterOptions, emitPos: (pos: number) => void): CommentWriter { + export function createCommentWriter(printerOptions: PrinterOptions, emitPos: ((pos: number) => void) | undefined): CommentWriter { const extendedDiagnostics = printerOptions.extendedDiagnostics; const newLine = getNewLineCharacter(printerOptions); let writer: EmitTextWriter; From 984541316ac586bc9fdd385400b8858122a16baf Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Wed, 1 Feb 2017 16:16:04 -0800 Subject: [PATCH 6/6] Added comments and a few Debug assertions --- src/compiler/emitter.ts | 15 ++++++++++ src/compiler/types.ts | 66 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 78 insertions(+), 3 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index f20a7944a0f66..8c6868555f2d1 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -236,6 +236,21 @@ namespace ts { }; 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(); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 03098fd41290b..8660e6138cdad 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3912,8 +3912,27 @@ 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; @@ -3921,9 +3940,50 @@ } export interface PrintHandlers { - hasGlobalName?: (name: string) => boolean; - onEmitNode?: (hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) => void; - onSubstituteNode?: (hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) => void; + /** + * 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;