diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index cb24f8cefd488..b09c672b4516b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -17160,7 +17160,7 @@ namespace ts { function isArgumentsLocalBinding(node: Identifier): boolean { if (!isGeneratedIdentifier(node)) { - node = getSourceTreeNodeOfType(node, isIdentifier); + node = getParseTreeNode(node, isIdentifier); if (node) { return getReferencedValueSymbol(node) === argumentsSymbol; } @@ -17206,7 +17206,7 @@ namespace ts { // When resolved as an expression identifier, if the given node references an exported entity, return the declaration // node of the exported entity's container. Otherwise, return undefined. function getReferencedExportContainer(node: Identifier, prefixLocals?: boolean): SourceFile | ModuleDeclaration | EnumDeclaration { - node = getSourceTreeNodeOfType(node, isIdentifier); + node = getParseTreeNode(node, isIdentifier); if (node) { // When resolving the export container for the name of a module or enum // declaration, we need to start resolution at the declaration's container. @@ -17244,7 +17244,7 @@ namespace ts { // When resolved as an expression identifier, if the given node references an import, return the declaration of // that import. Otherwise, return undefined. function getReferencedImportDeclaration(node: Identifier): Declaration { - node = getSourceTreeNodeOfType(node, isIdentifier); + node = getParseTreeNode(node, isIdentifier); if (node) { const symbol = getReferencedValueSymbol(node); if (symbol && symbol.flags & SymbolFlags.Alias) { @@ -17303,7 +17303,7 @@ namespace ts { // return the declaration of that entity. Otherwise, return undefined. function getReferencedDeclarationWithCollidingName(node: Identifier): Declaration { if (!isGeneratedIdentifier(node)) { - node = getSourceTreeNodeOfType(node, isIdentifier); + node = getParseTreeNode(node, isIdentifier); if (node) { const symbol = getReferencedValueSymbol(node); if (symbol && isSymbolOfDeclarationWithCollidingName(symbol)) { @@ -17318,7 +17318,7 @@ namespace ts { // Return true if the given node is a declaration of a nested block scoped entity with a name that either hides an // existing name or might hide a name when compiled downlevel function isDeclarationWithCollidingName(node: Declaration): boolean { - node = getSourceTreeNodeOfType(node, isDeclaration); + node = getParseTreeNode(node, isDeclaration); if (node) { const symbol = getSymbolOfNode(node); if (symbol) { @@ -17330,7 +17330,7 @@ namespace ts { } function isValueAliasDeclaration(node: Node): boolean { - node = getSourceTreeNode(node); + node = getParseTreeNode(node); if (node === undefined) { // A synthesized node comes from an emit transformation and is always a value. return true; @@ -17356,7 +17356,7 @@ namespace ts { } function isTopLevelValueImportEqualsWithEntityName(node: ImportEqualsDeclaration): boolean { - node = getSourceTreeNodeOfType(node, isImportEqualsDeclaration); + node = getParseTreeNode(node, isImportEqualsDeclaration); if (node === undefined || node.parent.kind !== SyntaxKind.SourceFile || !isInternalModuleImportEqualsDeclaration(node)) { // parent is not source file or it is not reference to internal module return false; @@ -17382,7 +17382,7 @@ namespace ts { } function isReferencedAliasDeclaration(node: Node, checkChildren?: boolean): boolean { - node = getSourceTreeNode(node); + node = getParseTreeNode(node); if (isAliasSymbolDeclaration(node)) { const symbol = getSymbolOfNode(node); if (symbol && getSymbolLinks(symbol).referenced) { @@ -17418,7 +17418,7 @@ namespace ts { } function getNodeCheckFlags(node: Node): NodeCheckFlags { - node = getSourceTreeNode(node); + node = getParseTreeNode(node); return node ? getNodeLinks(node).flags : undefined; } @@ -17549,7 +17549,7 @@ namespace ts { function getReferencedValueDeclaration(reference: Identifier): Declaration { if (!isGeneratedIdentifier(reference)) { - reference = getSourceTreeNodeOfType(reference, isIdentifier); + reference = getParseTreeNode(reference, isIdentifier); if (reference) { const symbol = getReferencedValueSymbol(reference); if (symbol) { diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 93fe7bbdf8bfe..d3af358875ee1 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -1150,9 +1150,23 @@ namespace ts { /** Performance measurements for the compiler. */ /*@internal*/ export namespace performance { + declare const onProfilerEvent: { (markName: string): void; profiler: boolean; }; + let profilerEvent: (markName: string) => void; let counters: Map; let measures: Map; + /** + * Emit a performance event if ts-profiler is connected. This is primarily used + * to generate heap snapshots. + * + * @param eventName A name for the event. + */ + export function emit(eventName: string) { + if (profilerEvent) { + onProfilerEvent(eventName); + } + } + /** * Increments a counter with the specified name. * @@ -1188,7 +1202,7 @@ namespace ts { */ export function measure(measureName: string, marker: number) { if (measures) { - measures[measureName] = (getProperty(measures, measureName) || 0) + (mark() - marker); + measures[measureName] = (getProperty(measures, measureName) || 0) + (Date.now() - marker); } } @@ -1215,12 +1229,17 @@ namespace ts { commentTime: 0, sourceMapTime: 0 }; + + profilerEvent = typeof onProfilerEvent === "function" && onProfilerEvent.profiler === true + ? onProfilerEvent + : undefined; } /** Disables (and clears) performance measurements for the compiler. */ export function disable() { counters = undefined; measures = undefined; + profilerEvent = undefined; } } } diff --git a/src/compiler/printer.ts b/src/compiler/printer.ts index 4a8b1d6b1e964..dd66cef346598 100644 --- a/src/compiler/printer.ts +++ b/src/compiler/printer.ts @@ -103,11 +103,79 @@ const _super = (function (geti, seti) { const sourceMapDataList: SourceMapData[] = compilerOptions.sourceMap || compilerOptions.inlineSourceMap ? [] : undefined; const emittedFilesList: string[] = compilerOptions.listEmittedFiles ? [] : undefined; const emitterDiagnostics = createDiagnosticCollection(); - - let emitSkipped = false; const newLine = host.getNewLine(); - const printFile = createFilePrinter(); - forEachExpectedEmitFile(host, emitFile, targetSourceFile); + const transformers = getTransformers(compilerOptions); + const writer = createTextWriter(newLine); + const { + write, + writeLine, + increaseIndent, + decreaseIndent + } = writer; + + const sourceMap = createSourceMapWriter(host, writer); + const { + emitStart, + emitEnd, + emitTokenStart, + emitTokenEnd + } = sourceMap; + + const comments = createCommentWriter(host, writer, sourceMap); + const { + emitNodeWithComments, + emitBodyWithDetachedComments, + emitTrailingCommentsOfPosition + } = comments; + + let nodeIdToGeneratedName: string[]; + let autoGeneratedIdToGeneratedName: string[]; + let generatedNameSet: Map; + let tempFlags: TempFlags; + let currentSourceFile: SourceFile; + let currentText: string; + let currentFileIdentifiers: Map; + let extendsEmitted: boolean; + let assignEmitted: boolean; + let decorateEmitted: boolean; + let paramEmitted: boolean; + let awaiterEmitted: boolean; + let isOwnFileEmit: boolean; + let emitSkipped = false; + + performance.emit("beforeTransform"); + const transformStart = performance.mark(); + + // Transform the source files + const transformed = transformFiles( + resolver, + host, + getSourceFilesToEmit(host, targetSourceFile), + transformers); + + performance.measure("transformTime", transformStart); + performance.emit("afterTransform"); + + // Extract helpers from the result + const { + getTokenSourceMapRange, + isSubstitutionEnabled, + isEmitNotificationEnabled, + onSubstituteNode, + onEmitNode + } = transformed; + + performance.emit("beforePrint"); + const printStart = performance.mark(); + + // Emit each output file + forEachTransformedEmitFile(host, transformed.getSourceFiles(), emitFile); + + // Clean up after transformation + transformed.dispose(); + + performance.measure("printTime", printStart); + performance.emit("afterPrint"); return { emitSkipped, @@ -116,7 +184,7 @@ const _super = (function (geti, seti) { sourceMaps: sourceMapDataList }; - function emitFile({ jsFilePath, sourceMapFilePath, declarationFilePath}: EmitFileNames, sourceFiles: SourceFile[], isBundledEmit: boolean) { + function emitFile(jsFilePath: string, sourceMapFilePath: string, declarationFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean) { // Make sure not to write js file and source map file if any of them cannot be written if (!host.isEmitBlocked(jsFilePath) && !compilerOptions.noEmit) { printFile(jsFilePath, sourceMapFilePath, sourceFiles, isBundledEmit); @@ -126,7 +194,7 @@ const _super = (function (geti, seti) { } if (declarationFilePath) { - emitSkipped = writeDeclarationFile(declarationFilePath, sourceFiles, isBundledEmit, host, resolver, emitterDiagnostics) || emitSkipped; + emitSkipped = writeDeclarationFile(declarationFilePath, getOriginalSourceFiles(sourceFiles), isBundledEmit, host, resolver, emitterDiagnostics) || emitSkipped; } if (!emitSkipped && emittedFilesList) { @@ -140,2679 +208,2604 @@ const _super = (function (geti, seti) { } } - function createFilePrinter() { - const transformers = getTransformers(compilerOptions).concat(initializePrinter); - - const writer = createTextWriter(newLine); - const { - write, - writeLine, - increaseIndent, - decreaseIndent - } = writer; - - const sourceMap = createSourceMapWriter(host, writer); - const { - emitStart, - emitEnd, - emitTokenStart, - emitTokenEnd - } = sourceMap; - - const comments = createCommentWriter(host, writer, sourceMap); - const { - emitNodeWithComments, - emitBodyWithDetachedComments, - emitTrailingCommentsOfPosition - } = comments; - - let context: TransformationContext; - let getNodeEmitFlags: (node: Node) => NodeEmitFlags; - let setNodeEmitFlags: (node: Node, flags: NodeEmitFlags) => void; - let getSourceMapRange: (node: Node) => TextRange; - let getTokenSourceMapRange: (node: Node, token: SyntaxKind) => TextRange; - let isSubstitutionEnabled: (node: Node) => boolean; - let isEmitNotificationEnabled: (node: Node) => boolean; - let onSubstituteNode: (node: Node, isExpression: boolean) => Node; - let onEmitNode: (node: Node, emit: (node: Node) => void) => void; - let nodeIdToGeneratedName: string[]; - let autoGeneratedIdToGeneratedName: string[]; - let generatedNameSet: Map; - let tempFlags: TempFlags; - let currentSourceFile: SourceFile; - let currentText: string; - let currentFileIdentifiers: Map; - let extendsEmitted: boolean; - let assignEmitted: boolean; - let decorateEmitted: boolean; - let paramEmitted: boolean; - let awaiterEmitted: boolean; - let isOwnFileEmit: boolean; - - return doPrint; - - function doPrint(jsFilePath: string, sourceMapFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean) { - sourceMap.initialize(jsFilePath, sourceMapFilePath, sourceFiles, isBundledEmit); - nodeIdToGeneratedName = []; - autoGeneratedIdToGeneratedName = []; - generatedNameSet = {}; - isOwnFileEmit = !isBundledEmit; - - // Emit helpers from all the files - if (isBundledEmit && moduleKind) { - forEach(sourceFiles, emitEmitHelpers); - } - - // Transform and print the source files - transformFiles(resolver, host, sourceFiles, transformers); - - writeLine(); - - const sourceMappingURL = sourceMap.getSourceMappingURL(); - if (sourceMappingURL) { - write(`//# sourceMappingURL=${sourceMappingURL}`); - } + function printFile(jsFilePath: string, sourceMapFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean) { + sourceMap.initialize(jsFilePath, sourceMapFilePath, sourceFiles, isBundledEmit); + nodeIdToGeneratedName = []; + autoGeneratedIdToGeneratedName = []; + generatedNameSet = {}; + isOwnFileEmit = !isBundledEmit; - // Write the source map - if (compilerOptions.sourceMap && !compilerOptions.inlineSourceMap) { - writeFile(host, emitterDiagnostics, sourceMapFilePath, sourceMap.getText(), /*writeByteOrderMark*/ false); - } + // Emit helpers from all the files + if (isBundledEmit && moduleKind) { + forEach(sourceFiles, emitEmitHelpers); + } - // Record source map data for the test harness. - if (sourceMapDataList) { - sourceMapDataList.push(sourceMap.getSourceMapData()); - } + // Print each transformed source file. + forEach(sourceFiles, printSourceFile); - // Write the output file - writeFile(host, emitterDiagnostics, jsFilePath, writer.getText(), compilerOptions.emitBOM); - - // Reset state - sourceMap.reset(); - comments.reset(); - writer.reset(); - - getNodeEmitFlags = undefined; - setNodeEmitFlags = undefined; - getSourceMapRange = undefined; - getTokenSourceMapRange = undefined; - isSubstitutionEnabled = undefined; - isEmitNotificationEnabled = undefined; - onSubstituteNode = undefined; - onEmitNode = undefined; - tempFlags = TempFlags.Auto; - currentSourceFile = undefined; - currentText = undefined; - extendsEmitted = false; - assignEmitted = false; - decorateEmitted = false; - paramEmitted = false; - awaiterEmitted = false; - isOwnFileEmit = false; - } - - function initializePrinter(_context: TransformationContext) { - context = _context; - getNodeEmitFlags = context.getNodeEmitFlags; - setNodeEmitFlags = context.setNodeEmitFlags; - getSourceMapRange = context.getSourceMapRange; - getTokenSourceMapRange = context.getTokenSourceMapRange; - isSubstitutionEnabled = context.isSubstitutionEnabled; - isEmitNotificationEnabled = context.isEmitNotificationEnabled; - onSubstituteNode = context.onSubstituteNode; - onEmitNode = context.onEmitNode; - return compilerOptions.extendedDiagnostics ? printSourceFileWithExtendedDiagnostics : printSourceFile; - } - - function printSourceFile(node: SourceFile) { - currentSourceFile = node; - currentText = node.text; - currentFileIdentifiers = node.identifiers; - sourceMap.setSourceFile(node); - comments.setSourceFile(node); - emitNodeWithNotification(node, emitWorker); - return node; - } - - function printSourceFileWithExtendedDiagnostics(node: SourceFile) { - const printStart = performance.mark(); - printSourceFile(node); - performance.measure("printTime", printStart); - return node; - } - - /** - * Emits a node. - */ - function emit(node: Node) { - emitNodeWithNotification(node, emitWithComments); - } - - /** - * Emits a node with specialized emit flags. - */ - // TODO(rbuckton): This should be removed once source maps are aligned with the old - // emitter and new baselines are taken. This exists solely to - // align with the old emitter. - function emitSpecialized(node: Node, flags: NodeEmitFlags) { - if (node) { - const flagsToAdd = flags & ~getNodeEmitFlags(node); - if (flagsToAdd) { - setNodeEmitFlags(node, getNodeEmitFlags(node) | flagsToAdd); - emit(node); - setNodeEmitFlags(node, getNodeEmitFlags(node) & ~flagsToAdd); - return; - } + writeLine(); - emit(node); - } + const sourceMappingURL = sourceMap.getSourceMappingURL(); + if (sourceMappingURL) { + write(`//# sourceMappingURL=${sourceMappingURL}`); } - /** - * 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 emit. - */ - function emitWithComments(node: Node) { - emitNodeWithComments(node, emitWithSourceMap); - } - - /** - * 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 emitWithComments. - */ - function emitWithSourceMap(node: Node) { - emitNodeWithSourceMap(node, emitWorker); - } - - /** - * Emits an expression node. - */ - function emitExpression(node: Expression) { - emitNodeWithNotification(node, emitExpressionWithComments); - } - - /** - * Emits an expression with comments. - * - * NOTE: Do not call this method directly. It is part of the emitExpression pipeline - * and should only be called indirectly from emitExpression. - */ - function emitExpressionWithComments(node: Expression) { - emitNodeWithComments(node, emitExpressionWithSourceMap); - } - - /** - * Emits an expression with source maps. - * - * NOTE: Do not call this method directly. It is part of the emitExpression pipeline - * and should only be called indirectly from emitExpressionWithComments. - */ - function emitExpressionWithSourceMap(node: Expression) { - emitNodeWithSourceMap(node, emitExpressionWorker); - } - - /** - * Emits a node with emit notification if available. - */ - function emitNodeWithNotification(node: Node, emitCallback: (node: Node) => void) { - if (node) { - if (isEmitNotificationEnabled(node)) { - onEmitNode(node, emitCallback); - } - else { - emitCallback(node); - } - } + // Write the source map + if (compilerOptions.sourceMap && !compilerOptions.inlineSourceMap) { + writeFile(host, emitterDiagnostics, sourceMapFilePath, sourceMap.getText(), /*writeByteOrderMark*/ false); } - function emitNodeWithSourceMap(node: Node, emitCallback: (node: Node) => void) { - if (node) { - emitStart(/*range*/ node, /*contextNode*/ node, shouldSkipLeadingSourceMapForNode, shouldSkipSourceMapForChildren, getSourceMapRange); - emitCallback(node); - emitEnd(/*range*/ node, /*contextNode*/ node, shouldSkipTrailingSourceMapForNode, shouldSkipSourceMapForChildren, getSourceMapRange); - } + // Record source map data for the test harness. + if (sourceMapDataList) { + sourceMapDataList.push(sourceMap.getSourceMapData()); } - /** - * Determines whether to skip leading comment emit for a node. - * - * We do not emit comments for NotEmittedStatement nodes or any node that has - * NodeEmitFlags.NoLeadingComments. - * - * @param node A Node. - */ - function shouldSkipLeadingCommentsForNode(node: Node) { - return isNotEmittedStatement(node) - || (getNodeEmitFlags(node) & NodeEmitFlags.NoLeadingComments) !== 0; - } - - /** - * Determines whether to skip source map emit for the start position of a node. - * - * We do not emit source maps for NotEmittedStatement nodes or any node that - * has NodeEmitFlags.NoLeadingSourceMap. - * - * @param node A Node. - */ - function shouldSkipLeadingSourceMapForNode(node: Node) { - return isNotEmittedStatement(node) - || (getNodeEmitFlags(node) & NodeEmitFlags.NoLeadingSourceMap) !== 0; - } - - - /** - * Determines whether to skip source map emit for the end position of a node. - * - * We do not emit source maps for NotEmittedStatement nodes or any node that - * has NodeEmitFlags.NoTrailingSourceMap. - * - * @param node A Node. - */ - function shouldSkipTrailingSourceMapForNode(node: Node) { - return isNotEmittedStatement(node) - || (getNodeEmitFlags(node) & NodeEmitFlags.NoTrailingSourceMap) !== 0; - } - - /** - * Determines whether to skip source map emit for a node and its children. - * - * We do not emit source maps for a node that has NodeEmitFlags.NoNestedSourceMaps. - */ - function shouldSkipSourceMapForChildren(node: Node) { - return (getNodeEmitFlags(node) & NodeEmitFlags.NoNestedSourceMaps) !== 0; - } - - function emitWorker(node: Node): void { - if (tryEmitSubstitute(node, emitWorker, /*isExpression*/ false)) { - return; - } + // Write the output file + writeFile(host, emitterDiagnostics, jsFilePath, writer.getText(), compilerOptions.emitBOM); - 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.AnyKeyword: - case SyntaxKind.AsyncKeyword: - case SyntaxKind.BooleanKeyword: - case SyntaxKind.DeclareKeyword: - case SyntaxKind.NumberKeyword: - case SyntaxKind.ReadonlyKeyword: - case SyntaxKind.StringKeyword: - case SyntaxKind.SymbolKeyword: - case SyntaxKind.GlobalKeyword: - return writeTokenNode(node); - - // Parse tree nodes - - // Names - case SyntaxKind.QualifiedName: - return emitQualifiedName(node); - case SyntaxKind.ComputedPropertyName: - return emitComputedPropertyName(node); - - // Signature elements - case SyntaxKind.TypeParameter: - return emitTypeParameter(node); - case SyntaxKind.Parameter: - return emitParameter(node); - case SyntaxKind.Decorator: - return emitDecorator(node); - - // Type members - case SyntaxKind.PropertySignature: - return emitPropertySignature(node); - case SyntaxKind.PropertyDeclaration: - return emitPropertyDeclaration(node); - case SyntaxKind.MethodSignature: - return emitMethodSignature(node); - case SyntaxKind.MethodDeclaration: - return emitMethodDeclaration(node); - case SyntaxKind.Constructor: - return emitConstructor(node); - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - return emitAccessorDeclaration(node); - case SyntaxKind.CallSignature: - return emitCallSignature(node); - case SyntaxKind.ConstructSignature: - return emitConstructSignature(node); - case SyntaxKind.IndexSignature: - return emitIndexSignature(node); - - // Types - case SyntaxKind.TypePredicate: - return emitTypePredicate(node); - case SyntaxKind.TypeReference: - return emitTypeReference(node); - case SyntaxKind.FunctionType: - return emitFunctionType(node); - case SyntaxKind.ConstructorType: - return emitConstructorType(node); - case SyntaxKind.TypeQuery: - return emitTypeQuery(node); - case SyntaxKind.TypeLiteral: - return emitTypeLiteral(node); - case SyntaxKind.ArrayType: - return emitArrayType(node); - case SyntaxKind.TupleType: - return emitTupleType(node); - case SyntaxKind.UnionType: - return emitUnionType(node); - case SyntaxKind.IntersectionType: - return emitIntersectionType(node); - case SyntaxKind.ParenthesizedType: - return emitParenthesizedType(node); - case SyntaxKind.ExpressionWithTypeArguments: - return emitExpressionWithTypeArguments(node); - case SyntaxKind.ThisType: - return emitThisType(node); - case SyntaxKind.StringLiteralType: - return emitLiteral(node); - - // Binding patterns - case SyntaxKind.ObjectBindingPattern: - return emitObjectBindingPattern(node); - case SyntaxKind.ArrayBindingPattern: - return emitArrayBindingPattern(node); - case SyntaxKind.BindingElement: - return emitBindingElement(node); - - // Misc - case SyntaxKind.TemplateSpan: - return emitTemplateSpan(node); - case SyntaxKind.SemicolonClassElement: - return emitSemicolonClassElement(node); - - // Statements - case SyntaxKind.Block: - return emitBlock(node); - case SyntaxKind.VariableStatement: - return emitVariableStatement(node); - case SyntaxKind.EmptyStatement: - return emitEmptyStatement(node); - 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); - - // Enum - case SyntaxKind.EnumMember: - return emitEnumMember(node); - - // Top-level nodes - case SyntaxKind.SourceFile: - return emitSourceFile(node); - - // JSDoc nodes (ignored) - - // Transformation nodes (ignored) - } + // Reset state + sourceMap.reset(); + comments.reset(); + writer.reset(); - if (isExpression(node)) { - return emitExpressionWorker(node); - } - } + tempFlags = TempFlags.Auto; + currentSourceFile = undefined; + currentText = undefined; + extendsEmitted = false; + assignEmitted = false; + decorateEmitted = false; + paramEmitted = false; + awaiterEmitted = false; + isOwnFileEmit = false; + } + + function printSourceFile(node: SourceFile) { + currentSourceFile = node; + currentText = node.text; + currentFileIdentifiers = node.identifiers; + sourceMap.setSourceFile(node); + comments.setSourceFile(node); + emitNodeWithNotification(node, emitWorker); + } - function emitExpressionWorker(node: Node) { - if (tryEmitSubstitute(node, emitExpressionWorker, /*isExpression*/ true)) { + /** + * Emits a node. + */ + function emit(node: Node) { + emitNodeWithNotification(node, emitWithComments); + } + + /** + * Emits a node with specialized emit flags. + */ + // TODO(rbuckton): This should be removed once source maps are aligned with the old + // emitter and new baselines are taken. This exists solely to + // align with the old emitter. + function emitSpecialized(node: Node, flags: NodeEmitFlags) { + if (node) { + const flagsToAdd = flags & ~node.emitFlags; + if (flagsToAdd) { + node.emitFlags |= flagsToAdd; + emit(node); + node.emitFlags &= ~flagsToAdd; return; } - const kind = node.kind; - switch (kind) { - // Literals - case SyntaxKind.NumericLiteral: - case SyntaxKind.StringLiteral: - case SyntaxKind.RegularExpressionLiteral: - case SyntaxKind.NoSubstitutionTemplateLiteral: - return emitLiteral(node); - - // Identifiers - case SyntaxKind.Identifier: - return emitIdentifier(node); - - // Reserved words - case SyntaxKind.FalseKeyword: - case SyntaxKind.NullKeyword: - case SyntaxKind.SuperKeyword: - case SyntaxKind.TrueKeyword: - case SyntaxKind.ThisKeyword: - return writeTokenNode(node); - - // Expressions - case SyntaxKind.ArrayLiteralExpression: - return emitArrayLiteralExpression(node); - case SyntaxKind.ObjectLiteralExpression: - return emitObjectLiteralExpression(node); - case SyntaxKind.PropertyAccessExpression: - return emitPropertyAccessExpression(node); - case SyntaxKind.ElementAccessExpression: - return emitElementAccessExpression(node); - case SyntaxKind.CallExpression: - return emitCallExpression(node); - case SyntaxKind.NewExpression: - return emitNewExpression(node); - case SyntaxKind.TaggedTemplateExpression: - return emitTaggedTemplateExpression(node); - case SyntaxKind.TypeAssertionExpression: - return emitTypeAssertionExpression(node); - case SyntaxKind.ParenthesizedExpression: - return emitParenthesizedExpression(node); - case SyntaxKind.FunctionExpression: - return emitFunctionExpression(node); - case SyntaxKind.ArrowFunction: - return emitArrowFunction(node); - case SyntaxKind.DeleteExpression: - return emitDeleteExpression(node); - case SyntaxKind.TypeOfExpression: - return emitTypeOfExpression(node); - case SyntaxKind.VoidExpression: - return emitVoidExpression(node); - case SyntaxKind.AwaitExpression: - return emitAwaitExpression(node); - case SyntaxKind.PrefixUnaryExpression: - return emitPrefixUnaryExpression(node); - case SyntaxKind.PostfixUnaryExpression: - return emitPostfixUnaryExpression(node); - case SyntaxKind.BinaryExpression: - return emitBinaryExpression(node); - case SyntaxKind.ConditionalExpression: - return emitConditionalExpression(node); - case SyntaxKind.TemplateExpression: - return emitTemplateExpression(node); - case SyntaxKind.YieldExpression: - return emitYieldExpression(node); - case SyntaxKind.SpreadElementExpression: - return emitSpreadElementExpression(node); - case SyntaxKind.ClassExpression: - return emitClassExpression(node); - case SyntaxKind.OmittedExpression: - return; - case SyntaxKind.AsExpression: - return emitAsExpression(node); - case SyntaxKind.NonNullExpression: - return emitNonNullExpression(node); - - // JSX - case SyntaxKind.JsxElement: - return emitJsxElement(node); - case SyntaxKind.JsxSelfClosingElement: - return emitJsxSelfClosingElement(node); - - // Transformation nodes - case SyntaxKind.PartiallyEmittedExpression: - return emitPartiallyEmittedExpression(node); - } + emit(node); } + } - // - // Literals/Pseudo-literals - // - - // SyntaxKind.NumericLiteral - // 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); - } - } + /** + * 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 emit. + */ + function emitWithComments(node: Node) { + emitNodeWithComments(node, emitWithSourceMap); + } - // - // Identifiers - // + /** + * 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 emitWithComments. + */ + function emitWithSourceMap(node: Node) { + emitNodeWithSourceMap(node, emitWorker); + } - function emitIdentifier(node: Identifier) { - if (getNodeEmitFlags(node) & NodeEmitFlags.UMDDefine) { - writeLines(umdHelper); - } - else { - write(getTextOfNode(node, /*includeTrivia*/ false)); - } - } + /** + * Emits an expression node. + */ + function emitExpression(node: Expression) { + emitNodeWithNotification(node, emitExpressionWithComments); + } - // - // Names - // + /** + * Emits an expression with comments. + * + * NOTE: Do not call this method directly. It is part of the emitExpression pipeline + * and should only be called indirectly from emitExpression. + */ + function emitExpressionWithComments(node: Expression) { + emitNodeWithComments(node, emitExpressionWithSourceMap); + } - function emitQualifiedName(node: QualifiedName) { - emitEntityName(node.left); - write("."); - emit(node.right); - } + /** + * Emits an expression with source maps. + * + * NOTE: Do not call this method directly. It is part of the emitExpression pipeline + * and should only be called indirectly from emitExpressionWithComments. + */ + function emitExpressionWithSourceMap(node: Expression) { + emitNodeWithSourceMap(node, emitExpressionWorker); + } - function emitEntityName(node: EntityName) { - if (node.kind === SyntaxKind.Identifier) { - emitExpression(node); + /** + * Emits a node with emit notification if available. + */ + function emitNodeWithNotification(node: Node, emitCallback: (node: Node) => void) { + if (node) { + if (isEmitNotificationEnabled(node)) { + onEmitNode(node, emitCallback); } else { - emit(node); + emitCallback(node); } } + } - function emitComputedPropertyName(node: ComputedPropertyName) { - write("["); - emitExpression(node.expression); - write("]"); + function emitNodeWithSourceMap(node: Node, emitCallback: (node: Node) => void) { + if (node) { + emitStart(/*range*/ node, /*contextNode*/ node, shouldSkipLeadingSourceMapForNode, shouldSkipSourceMapForChildren, getSourceMapRange); + emitCallback(node); + emitEnd(/*range*/ node, /*contextNode*/ node, shouldSkipTrailingSourceMapForNode, shouldSkipSourceMapForChildren, getSourceMapRange); } + } - // - // Signature elements - // + function getSourceMapRange(node: Node) { + return node.sourceMapRange || node; + } - function emitTypeParameter(node: TypeParameterDeclaration) { - emit(node.name); - emitWithPrefix(" extends ", node.constraint); - } + /** + * Determines whether to skip leading comment emit for a node. + * + * We do not emit comments for NotEmittedStatement nodes or any node that has + * NodeEmitFlags.NoLeadingComments. + * + * @param node A Node. + */ + function shouldSkipLeadingCommentsForNode(node: Node) { + return isNotEmittedStatement(node) + || (node.emitFlags & NodeEmitFlags.NoLeadingComments) !== 0; + } - 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); - } + /** + * Determines whether to skip source map emit for the start position of a node. + * + * We do not emit source maps for NotEmittedStatement nodes or any node that + * has NodeEmitFlags.NoLeadingSourceMap. + * + * @param node A Node. + */ + function shouldSkipLeadingSourceMapForNode(node: Node) { + return isNotEmittedStatement(node) + || (node.emitFlags & NodeEmitFlags.NoLeadingSourceMap) !== 0; + } - function emitDecorator(decorator: Decorator) { - write("@"); - emitExpression(decorator.expression); - } - // - // Type members - // + /** + * Determines whether to skip source map emit for the end position of a node. + * + * We do not emit source maps for NotEmittedStatement nodes or any node that + * has NodeEmitFlags.NoTrailingSourceMap. + * + * @param node A Node. + */ + function shouldSkipTrailingSourceMapForNode(node: Node) { + return isNotEmittedStatement(node) + || (node.emitFlags & NodeEmitFlags.NoTrailingSourceMap) !== 0; + } - function emitPropertySignature(node: PropertySignature) { - emitDecorators(node, node.decorators); - emitModifiers(node, node.modifiers); - emit(node.name); - writeIfPresent(node.questionToken, "?"); - emitWithPrefix(": ", node.type); - write(";"); - } + /** + * Determines whether to skip source map emit for a node and its children. + * + * We do not emit source maps for a node that has NodeEmitFlags.NoNestedSourceMaps. + */ + function shouldSkipSourceMapForChildren(node: Node) { + return (node.emitFlags & NodeEmitFlags.NoNestedSourceMaps) !== 0; + } - function emitPropertyDeclaration(node: PropertyDeclaration) { - emitDecorators(node, node.decorators); - emitModifiers(node, node.modifiers); - emit(node.name); - emitWithPrefix(": ", node.type); - emitExpressionWithPrefix(" = ", node.initializer); - write(";"); - } + function emitWorker(node: Node): void { + if (tryEmitSubstitute(node, emitWorker, /*isExpression*/ false)) { + return; + } + + 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.AnyKeyword: + case SyntaxKind.AsyncKeyword: + case SyntaxKind.BooleanKeyword: + case SyntaxKind.DeclareKeyword: + case SyntaxKind.NumberKeyword: + case SyntaxKind.ReadonlyKeyword: + case SyntaxKind.StringKeyword: + case SyntaxKind.SymbolKeyword: + case SyntaxKind.GlobalKeyword: + return writeTokenNode(node); + + // Parse tree nodes + + // Names + case SyntaxKind.QualifiedName: + return emitQualifiedName(node); + case SyntaxKind.ComputedPropertyName: + return emitComputedPropertyName(node); + + // Signature elements + case SyntaxKind.TypeParameter: + return emitTypeParameter(node); + case SyntaxKind.Parameter: + return emitParameter(node); + case SyntaxKind.Decorator: + return emitDecorator(node); + + // Type members + case SyntaxKind.PropertySignature: + return emitPropertySignature(node); + case SyntaxKind.PropertyDeclaration: + return emitPropertyDeclaration(node); + case SyntaxKind.MethodSignature: + return emitMethodSignature(node); + case SyntaxKind.MethodDeclaration: + return emitMethodDeclaration(node); + case SyntaxKind.Constructor: + return emitConstructor(node); + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: + return emitAccessorDeclaration(node); + case SyntaxKind.CallSignature: + return emitCallSignature(node); + case SyntaxKind.ConstructSignature: + return emitConstructSignature(node); + case SyntaxKind.IndexSignature: + return emitIndexSignature(node); + + // Types + case SyntaxKind.TypePredicate: + return emitTypePredicate(node); + case SyntaxKind.TypeReference: + return emitTypeReference(node); + case SyntaxKind.FunctionType: + return emitFunctionType(node); + case SyntaxKind.ConstructorType: + return emitConstructorType(node); + case SyntaxKind.TypeQuery: + return emitTypeQuery(node); + case SyntaxKind.TypeLiteral: + return emitTypeLiteral(node); + case SyntaxKind.ArrayType: + return emitArrayType(node); + case SyntaxKind.TupleType: + return emitTupleType(node); + case SyntaxKind.UnionType: + return emitUnionType(node); + case SyntaxKind.IntersectionType: + return emitIntersectionType(node); + case SyntaxKind.ParenthesizedType: + return emitParenthesizedType(node); + case SyntaxKind.ExpressionWithTypeArguments: + return emitExpressionWithTypeArguments(node); + case SyntaxKind.ThisType: + return emitThisType(node); + case SyntaxKind.StringLiteralType: + return emitLiteral(node); + + // Binding patterns + case SyntaxKind.ObjectBindingPattern: + return emitObjectBindingPattern(node); + case SyntaxKind.ArrayBindingPattern: + return emitArrayBindingPattern(node); + case SyntaxKind.BindingElement: + return emitBindingElement(node); + + // Misc + case SyntaxKind.TemplateSpan: + return emitTemplateSpan(node); + case SyntaxKind.SemicolonClassElement: + return emitSemicolonClassElement(node); + + // Statements + case SyntaxKind.Block: + return emitBlock(node); + case SyntaxKind.VariableStatement: + return emitVariableStatement(node); + case SyntaxKind.EmptyStatement: + return emitEmptyStatement(node); + 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; - 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(";"); + // 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); + + // Enum + case SyntaxKind.EnumMember: + return emitEnumMember(node); + + // Top-level nodes + case SyntaxKind.SourceFile: + return emitSourceFile(node); + + // JSDoc nodes (ignored) + + // Transformation nodes (ignored) + } + + if (isExpression(node)) { + return emitExpressionWorker(node); } + } - function emitMethodDeclaration(node: MethodDeclaration) { - emitDecorators(node, node.decorators); - emitModifiers(node, node.modifiers); - writeIfPresent(node.asteriskToken, "*"); - emit(node.name); - emitSignatureAndBody(node, emitSignatureHead); - } + function emitExpressionWorker(node: Node) { + if (tryEmitSubstitute(node, emitExpressionWorker, /*isExpression*/ true)) { + return; + } + + const kind = node.kind; + switch (kind) { + // Literals + case SyntaxKind.NumericLiteral: + case SyntaxKind.StringLiteral: + case SyntaxKind.RegularExpressionLiteral: + case SyntaxKind.NoSubstitutionTemplateLiteral: + return emitLiteral(node); + + // Identifiers + case SyntaxKind.Identifier: + return emitIdentifier(node); + + // Reserved words + case SyntaxKind.FalseKeyword: + case SyntaxKind.NullKeyword: + case SyntaxKind.SuperKeyword: + case SyntaxKind.TrueKeyword: + case SyntaxKind.ThisKeyword: + return writeTokenNode(node); + + // Expressions + case SyntaxKind.ArrayLiteralExpression: + return emitArrayLiteralExpression(node); + case SyntaxKind.ObjectLiteralExpression: + return emitObjectLiteralExpression(node); + case SyntaxKind.PropertyAccessExpression: + return emitPropertyAccessExpression(node); + case SyntaxKind.ElementAccessExpression: + return emitElementAccessExpression(node); + case SyntaxKind.CallExpression: + return emitCallExpression(node); + case SyntaxKind.NewExpression: + return emitNewExpression(node); + case SyntaxKind.TaggedTemplateExpression: + return emitTaggedTemplateExpression(node); + case SyntaxKind.TypeAssertionExpression: + return emitTypeAssertionExpression(node); + case SyntaxKind.ParenthesizedExpression: + return emitParenthesizedExpression(node); + case SyntaxKind.FunctionExpression: + return emitFunctionExpression(node); + case SyntaxKind.ArrowFunction: + return emitArrowFunction(node); + case SyntaxKind.DeleteExpression: + return emitDeleteExpression(node); + case SyntaxKind.TypeOfExpression: + return emitTypeOfExpression(node); + case SyntaxKind.VoidExpression: + return emitVoidExpression(node); + case SyntaxKind.AwaitExpression: + return emitAwaitExpression(node); + case SyntaxKind.PrefixUnaryExpression: + return emitPrefixUnaryExpression(node); + case SyntaxKind.PostfixUnaryExpression: + return emitPostfixUnaryExpression(node); + case SyntaxKind.BinaryExpression: + return emitBinaryExpression(node); + case SyntaxKind.ConditionalExpression: + return emitConditionalExpression(node); + case SyntaxKind.TemplateExpression: + return emitTemplateExpression(node); + case SyntaxKind.YieldExpression: + return emitYieldExpression(node); + case SyntaxKind.SpreadElementExpression: + return emitSpreadElementExpression(node); + case SyntaxKind.ClassExpression: + return emitClassExpression(node); + case SyntaxKind.OmittedExpression: + return; + case SyntaxKind.AsExpression: + return emitAsExpression(node); + case SyntaxKind.NonNullExpression: + return emitNonNullExpression(node); - function emitConstructor(node: ConstructorDeclaration) { - emitModifiers(node, node.modifiers); - write("constructor"); - emitSignatureAndBody(node, emitSignatureHead); - } + // JSX + case SyntaxKind.JsxElement: + return emitJsxElement(node); + case SyntaxKind.JsxSelfClosingElement: + return emitJsxSelfClosingElement(node); - function emitAccessorDeclaration(node: AccessorDeclaration) { - emitDecorators(node, node.decorators); - emitModifiers(node, node.modifiers); - write(node.kind === SyntaxKind.GetAccessor ? "get " : "set "); - emit(node.name); - emitSignatureAndBody(node, emitSignatureHead); + // Transformation nodes + case SyntaxKind.PartiallyEmittedExpression: + return emitPartiallyEmittedExpression(node); } + } - function emitCallSignature(node: CallSignatureDeclaration) { - emitDecorators(node, node.decorators); - emitModifiers(node, node.modifiers); - emitTypeParameters(node, node.typeParameters); - emitParameters(node, node.parameters); - emitWithPrefix(": ", node.type); - write(";"); + // + // Literals/Pseudo-literals + // + + // SyntaxKind.NumericLiteral + // 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); } - - 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(";"); + else { + write(text); } + } - function emitIndexSignature(node: IndexSignatureDeclaration) { - emitDecorators(node, node.decorators); - emitModifiers(node, node.modifiers); - emitParametersForIndexSignature(node, node.parameters); - emitWithPrefix(": ", node.type); - write(";"); - } + // + // Identifiers + // - function emitSemicolonClassElement(node: SemicolonClassElement) { - write(";"); + function emitIdentifier(node: Identifier) { + if (node.emitFlags & NodeEmitFlags.UMDDefine) { + writeLines(umdHelper); + } + else { + write(getTextOfNode(node, /*includeTrivia*/ false)); } + } - // - // Types - // + // + // Names + // - function emitTypePredicate(node: TypePredicateNode) { - emit(node.parameterName); - write(" is "); - emit(node.type); - } + function emitQualifiedName(node: QualifiedName) { + emitEntityName(node.left); + write("."); + emit(node.right); + } - function emitTypeReference(node: TypeReferenceNode) { - emit(node.typeName); - emitTypeArguments(node, node.typeArguments); + function emitEntityName(node: EntityName) { + if (node.kind === SyntaxKind.Identifier) { + emitExpression(node); } - - function emitFunctionType(node: FunctionTypeNode) { - emitTypeParameters(node, node.typeParameters); - emitParametersForArrow(node, node.parameters); - write(" => "); - emit(node.type); + else { + emit(node); } + } - function emitConstructorType(node: ConstructorTypeNode) { - write("new "); - emitTypeParameters(node, node.typeParameters); - emitParametersForArrow(node, node.parameters); - write(" => "); - emit(node.type); - } + function emitComputedPropertyName(node: ComputedPropertyName) { + write("["); + emitExpression(node.expression); + write("]"); + } - function emitTypeQuery(node: TypeQueryNode) { - write("typeof "); - emit(node.exprName); - } + // + // Signature elements + // - function emitTypeLiteral(node: TypeLiteralNode) { - write("{"); - emitList(node, node.members, ListFormat.TypeLiteralMembers); - write("}"); - } + function emitTypeParameter(node: TypeParameterDeclaration) { + emit(node.name); + emitWithPrefix(" extends ", node.constraint); + } - function emitArrayType(node: ArrayTypeNode) { - emit(node.elementType); - write("[]"); - } + 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 emitTupleType(node: TupleTypeNode) { - write("["); - emitList(node, node.elementTypes, ListFormat.TupleTypeElements); - write("]"); - } + function emitDecorator(decorator: Decorator) { + write("@"); + emitExpression(decorator.expression); + } - function emitUnionType(node: UnionTypeNode) { - emitList(node, node.types, ListFormat.UnionTypeConstituents); - } + // + // 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 emitIntersectionType(node: IntersectionTypeNode) { - emitList(node, node.types, ListFormat.IntersectionTypeConstituents); - } + function emitPropertyDeclaration(node: PropertyDeclaration) { + emitDecorators(node, node.decorators); + emitModifiers(node, node.modifiers); + emit(node.name); + emitWithPrefix(": ", node.type); + emitExpressionWithPrefix(" = ", node.initializer); + write(";"); + } - function emitParenthesizedType(node: ParenthesizedTypeNode) { - write("("); - emit(node.type); - 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 emitThisType(node: ThisTypeNode) { - write("this"); - } + function emitMethodDeclaration(node: MethodDeclaration) { + emitDecorators(node, node.decorators); + emitModifiers(node, node.modifiers); + writeIfPresent(node.asteriskToken, "*"); + emit(node.name); + emitSignatureAndBody(node, emitSignatureHead); + } - // - // Binding patterns - // + function emitConstructor(node: ConstructorDeclaration) { + emitModifiers(node, node.modifiers); + write("constructor"); + emitSignatureAndBody(node, emitSignatureHead); + } - function emitObjectBindingPattern(node: ObjectBindingPattern) { - const elements = node.elements; - if (elements.length === 0) { - write("{}"); - } - else { - write("{"); - emitList(node, elements, ListFormat.ObjectBindingPatternElements); - write("}"); - } - } + 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 emitArrayBindingPattern(node: ArrayBindingPattern) { - const elements = node.elements; - if (elements.length === 0) { - write("[]"); - } - else { - write("["); - emitList(node, node.elements, ListFormat.ArrayBindingPatternElements); - write("]"); - } - } + 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 emitBindingElement(node: BindingElement) { - emitWithSuffix(node.propertyName, ": "); - writeIfPresent(node.dotDotDotToken, "..."); - emit(node.name); - emitExpressionWithPrefix(" = ", node.initializer); - } + 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(";"); + } - // - // Expressions - // + function emitIndexSignature(node: IndexSignatureDeclaration) { + emitDecorators(node, node.decorators); + emitModifiers(node, node.modifiers); + emitParametersForIndexSignature(node, node.parameters); + emitWithPrefix(": ", node.type); + write(";"); + } - 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 emitSemicolonClassElement(node: SemicolonClassElement) { + write(";"); + } - function emitObjectLiteralExpression(node: ObjectLiteralExpression) { - const properties = node.properties; - if (properties.length === 0) { - write("{}"); - } - else { - const indentedFlag = getNodeEmitFlags(node) & NodeEmitFlags.Indented; - if (indentedFlag) { - increaseIndent(); - } + // + // Types + // - 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 emitTypePredicate(node: TypePredicateNode) { + emit(node.parameterName); + write(" is "); + emit(node.type); + } - if (indentedFlag) { - decreaseIndent(); - } - } - } + function emitTypeReference(node: TypeReferenceNode) { + emit(node.typeName); + emitTypeArguments(node, node.typeArguments); + } - function emitPropertyAccessExpression(node: PropertyAccessExpression) { - if (tryEmitConstantValue(node)) { - return; - } + function emitFunctionType(node: FunctionTypeNode) { + emitTypeParameters(node, node.typeParameters); + emitParametersForArrow(node, node.parameters); + write(" => "); + emit(node.type); + } - const indentBeforeDot = needsIndentation(node, node.expression, node.dotToken); - const indentAfterDot = needsIndentation(node, node.dotToken, node.name); - const shouldEmitDotDot = !indentBeforeDot && needsDotDotForPropertyAccess(node.expression); + function emitConstructorType(node: ConstructorTypeNode) { + write("new "); + emitTypeParameters(node, node.typeParameters); + emitParametersForArrow(node, node.parameters); + write(" => "); + emit(node.type); + } - emitExpression(node.expression); - increaseIndentIf(indentBeforeDot); - write(shouldEmitDotDot ? ".." : "."); - increaseIndentIf(indentAfterDot); - emit(node.name); - decreaseIndentIf(indentBeforeDot, indentAfterDot); - } - - // 1..toString is a valid property access, emit a dot after the literal - // Also emit a dot if expression is a integer const enum value - it will appear in generated code as numeric literal - function needsDotDotForPropertyAccess(expression: Expression) { - if (expression.kind === SyntaxKind.NumericLiteral) { - // check if numeric literal was originally written with a dot - const text = getLiteralTextOfNode(expression); - return text.indexOf(tokenToString(SyntaxKind.DotToken)) < 0; - } - else { - // check if constant enum value is integer - const constantValue = tryGetConstEnumValue(expression); - // isFinite handles cases when constantValue is undefined - return isFinite(constantValue) && Math.floor(constantValue) === constantValue; - } - } + function emitTypeQuery(node: TypeQueryNode) { + write("typeof "); + emit(node.exprName); + } - function emitElementAccessExpression(node: ElementAccessExpression) { - if (tryEmitConstantValue(node)) { - return; - } + function emitTypeLiteral(node: TypeLiteralNode) { + write("{"); + emitList(node, node.members, ListFormat.TypeLiteralMembers); + write("}"); + } - emitExpression(node.expression); - write("["); - emitExpression(node.argumentExpression); - write("]"); - } + function emitArrayType(node: ArrayTypeNode) { + emit(node.elementType); + write("[]"); + } - function emitCallExpression(node: CallExpression) { - emitExpression(node.expression); - emitExpressionList(node, node.arguments, ListFormat.CallExpressionArguments); - } + function emitTupleType(node: TupleTypeNode) { + write("["); + emitList(node, node.elementTypes, ListFormat.TupleTypeElements); + write("]"); + } - function emitNewExpression(node: NewExpression) { - write("new "); - emitExpression(node.expression); - emitExpressionList(node, node.arguments, ListFormat.NewExpressionArguments); - } + function emitUnionType(node: UnionTypeNode) { + emitList(node, node.types, ListFormat.UnionTypeConstituents); + } - function emitTaggedTemplateExpression(node: TaggedTemplateExpression) { - emitExpression(node.tag); - write(" "); - emitExpression(node.template); - } + function emitIntersectionType(node: IntersectionTypeNode) { + emitList(node, node.types, ListFormat.IntersectionTypeConstituents); + } - function emitTypeAssertionExpression(node: TypeAssertion) { - if (node.type) { - write("<"); - emit(node.type); - write(">"); - } + function emitParenthesizedType(node: ParenthesizedTypeNode) { + write("("); + emit(node.type); + write(")"); + } - emitExpression(node.expression); - } + function emitThisType(node: ThisTypeNode) { + write("this"); + } - function emitParenthesizedExpression(node: ParenthesizedExpression) { - write("("); - emitExpression(node.expression); - write(")"); - } + // + // Binding patterns + // - function emitFunctionExpression(node: FunctionExpression) { - emitFunctionDeclarationOrExpression(node); + function emitObjectBindingPattern(node: ObjectBindingPattern) { + const elements = node.elements; + if (elements.length === 0) { + write("{}"); } - - function emitArrowFunction(node: ArrowFunction) { - emitDecorators(node, node.decorators); - emitModifiers(node, node.modifiers); - emitSignatureAndBody(node, emitArrowFunctionHead); + else { + write("{"); + emitList(node, elements, ListFormat.ObjectBindingPatternElements); + write("}"); } + } - function emitArrowFunctionHead(node: ArrowFunction) { - emitTypeParameters(node, node.typeParameters); - emitParametersForArrow(node, node.parameters); - emitWithPrefix(": ", node.type); - write(" =>"); + function emitArrayBindingPattern(node: ArrayBindingPattern) { + const elements = node.elements; + if (elements.length === 0) { + write("[]"); } - - function emitDeleteExpression(node: DeleteExpression) { - write("delete "); - emitExpression(node.expression); + else { + write("["); + emitList(node, node.elements, ListFormat.ArrayBindingPatternElements); + write("]"); } + } - function emitTypeOfExpression(node: TypeOfExpression) { - write("typeof "); - emitExpression(node.expression); - } + function emitBindingElement(node: BindingElement) { + emitWithSuffix(node.propertyName, ": "); + writeIfPresent(node.dotDotDotToken, "..."); + emit(node.name); + emitExpressionWithPrefix(" = ", node.initializer); + } - function emitVoidExpression(node: VoidExpression) { - write("void "); - emitExpression(node.expression); - } + // + // Expressions + // - function emitAwaitExpression(node: AwaitExpression) { - write("await "); - emitExpression(node.expression); + function emitArrayLiteralExpression(node: ArrayLiteralExpression) { + const elements = node.elements; + if (elements.length === 0) { + write("[]"); } - - 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 emitSpreadElementExpression(node: SpreadElementExpression) { - write("..."); - emitExpression(node.expression); + else { + const preferNewLine = node.multiLine ? ListFormat.PreferNewLine : ListFormat.None; + emitExpressionList(node, elements, ListFormat.ArrayLiteralExpressionElements | preferNewLine); } + } - function emitClassExpression(node: ClassExpression) { - emitClassDeclarationOrExpression(node); + function emitObjectLiteralExpression(node: ObjectLiteralExpression) { + const properties = node.properties; + if (properties.length === 0) { + write("{}"); } + else { + const indentedFlag = node.emitFlags & NodeEmitFlags.Indented; + if (indentedFlag) { + increaseIndent(); + } - function emitExpressionWithTypeArguments(node: ExpressionWithTypeArguments) { - emitExpression(node.expression); - emitTypeArguments(node, node.typeArguments); - } + 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 emitAsExpression(node: AsExpression) { - emitExpression(node.expression); - if (node.type) { - write(" as "); - emit(node.type); + if (indentedFlag) { + decreaseIndent(); } } + } - function emitNonNullExpression(node: NonNullExpression) { - emitExpression(node.expression); - write("!"); + function emitPropertyAccessExpression(node: PropertyAccessExpression) { + if (tryEmitConstantValue(node)) { + return; } - // - // Misc - // + const indentBeforeDot = needsIndentation(node, node.expression, node.dotToken); + const indentAfterDot = needsIndentation(node, node.dotToken, node.name); + const shouldEmitDotDot = !indentBeforeDot && needsDotDotForPropertyAccess(node.expression); - function emitTemplateSpan(node: TemplateSpan) { - emitExpression(node.expression); - emit(node.literal); + emitExpression(node.expression); + increaseIndentIf(indentBeforeDot); + write(shouldEmitDotDot ? ".." : "."); + increaseIndentIf(indentAfterDot); + emit(node.name); + decreaseIndentIf(indentBeforeDot, indentAfterDot); + } + + // 1..toString is a valid property access, emit a dot after the literal + // Also emit a dot if expression is a integer const enum value - it will appear in generated code as numeric literal + function needsDotDotForPropertyAccess(expression: Expression) { + if (expression.kind === SyntaxKind.NumericLiteral) { + // check if numeric literal was originally written with a dot + const text = getLiteralTextOfNode(expression); + return text.indexOf(tokenToString(SyntaxKind.DotToken)) < 0; + } + else { + // check if constant enum value is integer + const constantValue = tryGetConstEnumValue(expression); + // isFinite handles cases when constantValue is undefined + return isFinite(constantValue) && Math.floor(constantValue) === constantValue; } + } - // - // Statements - // - - function emitBlock(node: Block, format?: ListFormat) { - 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 emitElementAccessExpression(node: ElementAccessExpression) { + if (tryEmitConstantValue(node)) { + return; } - function emitBlockStatements(node: Block) { - if (getNodeEmitFlags(node) & NodeEmitFlags.SingleLine) { - emitList(node, node.statements, ListFormat.SingleLineBlockStatements); - } - else { - emitList(node, node.statements, ListFormat.MultiLineBlockStatements); - } - } + emitExpression(node.expression); + write("["); + emitExpression(node.argumentExpression); + write("]"); + } - function emitVariableStatement(node: VariableStatement) { - emitModifiers(node, node.modifiers); - emit(node.declarationList); - write(";"); + function emitCallExpression(node: CallExpression) { + emitExpression(node.expression); + emitExpressionList(node, node.arguments, ListFormat.CallExpressionArguments); + } + + function emitNewExpression(node: NewExpression) { + write("new "); + emitExpression(node.expression); + 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(">"); } - function emitEmptyStatement(node: EmptyStatement) { - 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 emitExpressionStatement(node: ExpressionStatement) { - emitExpression(node.expression); - write(";"); + 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 emitSpreadElementExpression(node: SpreadElementExpression) { + 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 emitIfStatement(node: IfStatement) { - const openParenPos = writeToken(SyntaxKind.IfKeyword, node.pos, node); + function emitNonNullExpression(node: NonNullExpression) { + emitExpression(node.expression); + write("!"); + } + + // + // Misc + // + + function emitTemplateSpan(node: TemplateSpan) { + emitExpression(node.expression); + emit(node.literal); + } + + // + // Statements + // + + function emitBlock(node: Block, format?: ListFormat) { + if (isSingleLineEmptyBlock(node)) { + writeToken(SyntaxKind.OpenBraceToken, node.pos, /*contextNode*/ node); write(" "); - writeToken(SyntaxKind.OpenParenToken, openParenPos, node); - emitExpression(node.expression); - writeToken(SyntaxKind.CloseParenToken, node.expression.end, node); - emitEmbeddedStatement(node.thenStatement); - if (node.elseStatement) { - writeLine(); - writeToken(SyntaxKind.ElseKeyword, node.thenStatement.end, node); - if (node.elseStatement.kind === SyntaxKind.IfStatement) { - write(" "); - emit(node.elseStatement); - } - else { - emitEmbeddedStatement(node.elseStatement); - } - } + 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 emitDoStatement(node: DoStatement) { - write("do"); - emitEmbeddedStatement(node.statement); - if (isBlock(node.statement)) { + function emitBlockStatements(node: Block) { + if (node.emitFlags & NodeEmitFlags.SingleLine) { + emitList(node, node.statements, ListFormat.SingleLineBlockStatements); + } + else { + emitList(node, node.statements, ListFormat.MultiLineBlockStatements); + } + } + + function emitVariableStatement(node: VariableStatement) { + emitModifiers(node, node.modifiers); + emit(node.declarationList); + write(";"); + } + + function emitEmptyStatement(node: EmptyStatement) { + 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.thenStatement); + if (node.elseStatement) { + writeLine(); + writeToken(SyntaxKind.ElseKeyword, node.thenStatement.end, node); + if (node.elseStatement.kind === SyntaxKind.IfStatement) { write(" "); + emit(node.elseStatement); } else { - writeLine(); + emitEmbeddedStatement(node.elseStatement); } - - write("while ("); - emitExpression(node.expression); - write(");"); } + } - function emitWhileStatement(node: WhileStatement) { - write("while ("); - emitExpression(node.expression); - write(")"); - emitEmbeddedStatement(node.statement); + function emitDoStatement(node: DoStatement) { + write("do"); + emitEmbeddedStatement(node.statement); + if (isBlock(node.statement)) { + write(" "); + } + else { + writeLine(); } - function emitForStatement(node: ForStatement) { - if (getNodeEmitFlags(node) & NodeEmitFlags.SourceMapAdjustRestParameterLoop) { - // TODO(rbuckton): This should be removed once source maps are aligned with the old - // emitter and new baselines are taken. This exists solely to - // align with the old emitter. - const openParenPos = writeToken(SyntaxKind.ForKeyword, node.pos); - write(" "); - writeToken(SyntaxKind.OpenParenToken, openParenPos); - setNodeEmitFlags(node.initializer, NodeEmitFlags.NoTrailingSourceMap | getNodeEmitFlags(node.initializer)); - emitForBinding(node.initializer); - write(";"); - emitEnd(node.initializer); - setNodeEmitFlags(node.condition, NodeEmitFlags.NoTrailingSourceMap | getNodeEmitFlags(node.condition)); - write(" "); - emitExpression(node.condition); - write(";"); - emitEnd(node.condition); - write(" "); - emitExpression(node.incrementor); - write(")"); - emitEmbeddedStatement(node.statement); - return; - } + write("while ("); + emitExpression(node.expression); + write(");"); + } + function emitWhileStatement(node: WhileStatement) { + write("while ("); + emitExpression(node.expression); + write(")"); + emitEmbeddedStatement(node.statement); + } + + function emitForStatement(node: ForStatement) { + if (node.emitFlags & NodeEmitFlags.SourceMapAdjustRestParameterLoop) { + // TODO(rbuckton): This should be removed once source maps are aligned with the old + // emitter and new baselines are taken. This exists solely to + // align with the old emitter. const openParenPos = writeToken(SyntaxKind.ForKeyword, node.pos); write(" "); - writeToken(SyntaxKind.OpenParenToken, openParenPos, /*contextNode*/ node); - emitForBinding(node.initializer); + writeToken(SyntaxKind.OpenParenToken, openParenPos); + const initializer = node.initializer; + initializer.emitFlags |= NodeEmitFlags.NoTrailingSourceMap; + emitForBinding(initializer); write(";"); - emitExpressionWithPrefix(" ", node.condition); + emitEnd(initializer); + const condition = node.condition; + condition.emitFlags |= NodeEmitFlags.NoTrailingSourceMap; + write(" "); + emitExpression(condition); write(";"); - emitExpressionWithPrefix(" ", node.incrementor); + emitEnd(condition); + write(" "); + emitExpression(node.incrementor); write(")"); emitEmbeddedStatement(node.statement); - } + return; + } + + 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.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.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.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.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.statement); + } - function emitForBinding(node: VariableDeclarationList | Expression) { - if (node !== undefined) { - if (node.kind === SyntaxKind.VariableDeclarationList) { - emit(node); - } - else { - emitExpression(node); - } + 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 emitContinueStatement(node: ContinueStatement) { + writeToken(SyntaxKind.ContinueKeyword, node.pos); + emitWithPrefix(" ", node.label); + write(";"); + } - function emitReturnStatement(node: ReturnStatement) { - writeToken(SyntaxKind.ReturnKeyword, node.pos, /*contextNode*/ node); - emitExpressionWithPrefix(" ", node.expression); - write(";"); - } + function emitBreakStatement(node: BreakStatement) { + writeToken(SyntaxKind.BreakKeyword, node.pos); + emitWithPrefix(" ", node.label); + write(";"); + } - function emitWithStatement(node: WithStatement) { - write("with ("); - emitExpression(node.expression); - write(")"); - emitEmbeddedStatement(node.statement); - } + function emitReturnStatement(node: ReturnStatement) { + writeToken(SyntaxKind.ReturnKeyword, node.pos, /*contextNode*/ node); + emitExpressionWithPrefix(" ", node.expression); + write(";"); + } - 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 emitWithStatement(node: WithStatement) { + write("with ("); + emitExpression(node.expression); + write(")"); + emitEmbeddedStatement(node.statement); + } - function emitLabeledStatement(node: LabeledStatement) { - emit(node.label); - write(": "); - emit(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 emitThrowStatement(node: ThrowStatement) { - write("throw"); - emitExpressionWithPrefix(" ", node.expression); - write(";"); - } + function emitLabeledStatement(node: LabeledStatement) { + emit(node.label); + write(": "); + emit(node.statement); + } - function emitTryStatement(node: TryStatement) { - write("try "); - emit(node.tryBlock); - emit(node.catchClause); - if (node.finallyBlock) { - writeLine(); - write("finally "); - emit(node.finallyBlock); - } - } + function emitThrowStatement(node: ThrowStatement) { + write("throw"); + emitExpressionWithPrefix(" ", node.expression); + write(";"); + } - function emitDebuggerStatement(node: DebuggerStatement) { - writeToken(SyntaxKind.DebuggerKeyword, node.pos); - write(";"); + function emitTryStatement(node: TryStatement) { + write("try "); + emit(node.tryBlock); + emit(node.catchClause); + if (node.finallyBlock) { + writeLine(); + write("finally "); + emit(node.finallyBlock); } + } - // - // Declarations - // - - function emitVariableDeclaration(node: VariableDeclaration) { - emit(node.name); - emitExpressionWithPrefix(" = ", node.initializer); - } + function emitDebuggerStatement(node: DebuggerStatement) { + writeToken(SyntaxKind.DebuggerKeyword, node.pos); + write(";"); + } - function emitVariableDeclarationList(node: VariableDeclarationList) { - write(isLet(node) ? "let " : isConst(node) ? "const " : "var "); - emitList(node, node.declarations, ListFormat.VariableDeclarationList); - } + // + // Declarations + // - function emitFunctionDeclaration(node: FunctionDeclaration) { - emitFunctionDeclarationOrExpression(node); - } + function emitVariableDeclaration(node: VariableDeclaration) { + emit(node.name); + emitExpressionWithPrefix(" = ", node.initializer); + } - function emitFunctionDeclarationOrExpression(node: FunctionDeclaration | FunctionExpression) { - emitDecorators(node, node.decorators); - emitModifiers(node, node.modifiers); - write(node.asteriskToken ? "function* " : "function "); - emitSpecialized(node.name, NodeEmitFlags.NoSourceMap); - emitSignatureAndBody(node, emitSignatureHead); - } + function emitVariableDeclarationList(node: VariableDeclarationList) { + write(isLet(node) ? "let " : isConst(node) ? "const " : "var "); + emitList(node, node.declarations, ListFormat.VariableDeclarationList); + } - function emitSignatureAndBody(node: FunctionLikeDeclaration, emitSignatureHead: (node: SignatureDeclaration) => void) { - const body = node.body; - if (body) { - if (isBlock(body)) { - const indentedFlag = getNodeEmitFlags(node) & NodeEmitFlags.Indented; - if (indentedFlag) { - increaseIndent(); - } + function emitFunctionDeclaration(node: FunctionDeclaration) { + emitFunctionDeclarationOrExpression(node); + } - const savedTempFlags = tempFlags; - tempFlags = 0; - emitSignatureHead(node); - emitBlockFunctionBody(node, body); - if (indentedFlag) { - decreaseIndent(); - } + function emitFunctionDeclarationOrExpression(node: FunctionDeclaration | FunctionExpression) { + emitDecorators(node, node.decorators); + emitModifiers(node, node.modifiers); + write(node.asteriskToken ? "function* " : "function "); + emitSpecialized(node.name, NodeEmitFlags.NoSourceMap); + emitSignatureAndBody(node, emitSignatureHead); + } - tempFlags = savedTempFlags; + function emitSignatureAndBody(node: FunctionLikeDeclaration, emitSignatureHead: (node: SignatureDeclaration) => void) { + const body = node.body; + if (body) { + if (isBlock(body)) { + const indentedFlag = node.emitFlags & NodeEmitFlags.Indented; + if (indentedFlag) { + increaseIndent(); } - else { - emitSignatureHead(node); - write(" "); - emitExpression(body); + + const savedTempFlags = tempFlags; + tempFlags = 0; + emitSignatureHead(node); + emitBlockFunctionBody(node, body); + if (indentedFlag) { + decreaseIndent(); } + + tempFlags = savedTempFlags; } else { emitSignatureHead(node); - write(";"); + write(" "); + emitExpression(body); } - } - - function emitSignatureHead(node: FunctionDeclaration | FunctionExpression | MethodDeclaration | AccessorDeclaration | ConstructorDeclaration) { - emitTypeParameters(node, node.typeParameters); - emitParameters(node, node.parameters); - emitWithPrefix(": ", node.type); + else { + emitSignatureHead(node); + write(";"); } - function shouldEmitBlockFunctionBodyOnSingleLine(parentNode: Node, 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. + function emitSignatureHead(node: FunctionDeclaration | FunctionExpression | MethodDeclaration | AccessorDeclaration | ConstructorDeclaration) { + emitTypeParameters(node, node.typeParameters); + emitParameters(node, node.parameters); + emitWithPrefix(": ", node.type); + } - if (getNodeEmitFlags(body) & NodeEmitFlags.SingleLine) { - return true; - } + function shouldEmitBlockFunctionBodyOnSingleLine(parentNode: Node, body: Block) { + // We must emit a function body as a single-line body in the following case: + // * The body has NodeEmitFlags.SingleLine specified. - if (body.multiLine) { - return false; - } + // 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 (!nodeIsSynthesized(body) && !rangeIsOnSingleLine(body, currentSourceFile)) { - return false; - } + if (body.emitFlags & NodeEmitFlags.SingleLine) { + return true; + } - if (shouldWriteLeadingLineTerminator(body, body.statements, ListFormat.PreserveLines) - || shouldWriteClosingLineTerminator(body, body.statements, ListFormat.PreserveLines)) { + 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; } - let previousStatement: Statement; - for (const statement of body.statements) { - if (shouldWriteSeparatingLineTerminator(previousStatement, statement, ListFormat.PreserveLines)) { - return false; - } + previousStatement = statement; + } - previousStatement = statement; - } + return true; + } - return true; + function emitBlockFunctionBody(parentNode: Node, body: Block) { + // TODO(rbuckton): This should be removed once source maps are aligned with the old + // emitter and new baselines are taken. This exists solely to + // align with the old emitter. + if (body.emitFlags & NodeEmitFlags.SourceMapEmitOpenBraceAsToken) { + write(" "); + writeToken(SyntaxKind.OpenBraceToken, body.pos); + } + else { + write(" {"); } - function emitBlockFunctionBody(parentNode: Node, body: Block) { - // TODO(rbuckton): This should be removed once source maps are aligned with the old - // emitter and new baselines are taken. This exists solely to - // align with the old emitter. - if (getNodeEmitFlags(body) & NodeEmitFlags.SourceMapEmitOpenBraceAsToken) { - write(" "); - writeToken(SyntaxKind.OpenBraceToken, body.pos); - } - else { - write(" {"); - } + increaseIndent(); - increaseIndent(); + emitBodyWithDetachedComments(body, body.statements, + shouldEmitBlockFunctionBodyOnSingleLine(parentNode, body) + ? emitBlockFunctionBodyOnSingleLine + : emitBlockFunctionBodyWorker); + + decreaseIndent(); + writeToken(SyntaxKind.CloseBraceToken, body.statements.end, body); + } - emitBodyWithDetachedComments(body, body.statements, - shouldEmitBlockFunctionBodyOnSingleLine(parentNode, body) - ? emitBlockFunctionBodyOnSingleLine - : emitBlockFunctionBodyWorker); + 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(); - writeToken(SyntaxKind.CloseBraceToken, body.statements.end, body); + emitList(body, body.statements, ListFormat.SingleLineFunctionBodyStatements); + increaseIndent(); } - - function emitBlockFunctionBodyOnSingleLine(body: Block) { - emitBlockFunctionBodyWorker(body, /*emitBlockFunctionBodyOnSingleLine*/ true); + else { + emitList(body, body.statements, ListFormat.MultiLineFunctionBodyStatements, statementOffset); } + } - 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); + function emitClassDeclaration(node: ClassDeclaration) { + emitClassDeclarationOrExpression(node); + } - if (statementOffset === 0 && !helpersEmitted && emitBlockFunctionBodyOnSingleLine) { - decreaseIndent(); - emitList(body, body.statements, ListFormat.SingleLineFunctionBodyStatements); - increaseIndent(); - } - else { - emitList(body, body.statements, ListFormat.MultiLineFunctionBodyStatements, statementOffset); - } - } + function emitClassDeclarationOrExpression(node: ClassDeclaration | ClassExpression) { + emitDecorators(node, node.decorators); + emitModifiers(node, node.modifiers); + write("class"); + emitSpecializedWithPrefix(" ", node.name, NodeEmitFlags.NoSourceMap); - function emitClassDeclaration(node: ClassDeclaration) { - emitClassDeclarationOrExpression(node); + const indentedFlag = node.emitFlags & NodeEmitFlags.Indented; + if (indentedFlag) { + increaseIndent(); } - function emitClassDeclarationOrExpression(node: ClassDeclaration | ClassExpression) { - emitDecorators(node, node.decorators); - emitModifiers(node, node.modifiers); - write("class"); - emitSpecializedWithPrefix(" ", node.name, NodeEmitFlags.NoSourceMap); + emitTypeParameters(node, node.typeParameters); + emitList(node, node.heritageClauses, ListFormat.ClassHeritageClauses); - const indentedFlag = getNodeEmitFlags(node) & NodeEmitFlags.Indented; - if (indentedFlag) { - increaseIndent(); - } + const savedTempFlags = tempFlags; + tempFlags = 0; - emitTypeParameters(node, node.typeParameters); - emitList(node, node.heritageClauses, ListFormat.ClassHeritageClauses); + write(" {"); + emitList(node, node.members, ListFormat.ClassMembers); + write("}"); - const savedTempFlags = tempFlags; - tempFlags = 0; + if (indentedFlag) { + decreaseIndent(); + } - write(" {"); - emitList(node, node.members, ListFormat.ClassMembers); - write("}"); + tempFlags = savedTempFlags; + } - 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("}"); + } - tempFlags = savedTempFlags; - } + 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 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 emitEnumDeclaration(node: EnumDeclaration) { + emitModifiers(node, node.modifiers); + write("enum "); + emit(node.name); - 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(";"); + 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; } - function emitEnumDeclaration(node: EnumDeclaration) { - emitModifiers(node, node.modifiers); - write("enum "); - emit(node.name); + write(" "); + emit(body); + } + function emitModuleBlock(node: ModuleBlock) { + if (isSingleLineEmptyBlock(node)) { + write("{ }"); + } + else { const savedTempFlags = tempFlags; tempFlags = 0; - - write(" {"); - emitList(node, node.members, ListFormat.EnumMembers); + write("{"); + increaseIndent(); + emitBlockStatements(node); write("}"); tempFlags = savedTempFlags; } + } - function emitModuleDeclaration(node: ModuleDeclaration) { - emitModifiers(node, node.modifiers); - write(node.flags & NodeFlags.Namespace ? "namespace " : "module "); - emit(node.name); + function emitCaseBlock(node: CaseBlock) { + writeToken(SyntaxKind.OpenBraceToken, node.pos); + emitList(node, node.clauses, ListFormat.CaseBlockClauses); + writeToken(SyntaxKind.CloseBraceToken, node.clauses.end); + } - let body = node.body; - while (body.kind === SyntaxKind.ModuleDeclaration) { - write("."); - emit((body).name); - body = (body).body; - } + function emitImportEqualsDeclaration(node: ImportEqualsDeclaration) { + emitModifiers(node, node.modifiers); + write("import "); + emit(node.name); + write(" = "); + emitModuleReference(node.moduleReference); + write(";"); + } - write(" "); - emit(body); + function emitModuleReference(node: ModuleReference) { + if (node.kind === SyntaxKind.Identifier) { + emitExpression(node); } - - function emitModuleBlock(node: ModuleBlock) { - if (isSingleLineEmptyBlock(node)) { - write("{ }"); - } - else { - const savedTempFlags = tempFlags; - tempFlags = 0; - write("{"); - increaseIndent(); - emitBlockStatements(node); - write("}"); - tempFlags = savedTempFlags; - } + else { + emit(node); } + } - function emitCaseBlock(node: CaseBlock) { - writeToken(SyntaxKind.OpenBraceToken, node.pos); - emitList(node, node.clauses, ListFormat.CaseBlockClauses); - writeToken(SyntaxKind.CloseBraceToken, node.clauses.end); + function emitImportDeclaration(node: ImportDeclaration) { + emitModifiers(node, node.modifiers); + write("import "); + if (node.importClause) { + emit(node.importClause); + write(" from "); } + emitExpression(node.moduleSpecifier); + write(";"); + } - function emitImportEqualsDeclaration(node: ImportEqualsDeclaration) { - emitModifiers(node, node.modifiers); - write("import "); - emit(node.name); - write(" = "); - emitModuleReference(node.moduleReference); - write(";"); + function emitImportClause(node: ImportClause) { + emit(node.name); + if (node.name && node.namedBindings) { + write(", "); } + emit(node.namedBindings); + } - function emitModuleReference(node: ModuleReference) { - if (node.kind === SyntaxKind.Identifier) { - emitExpression(node); - } - else { - emit(node); - } - } + function emitNamespaceImport(node: NamespaceImport) { + write("* as "); + emit(node.name); + } - function emitImportDeclaration(node: ImportDeclaration) { - emitModifiers(node, node.modifiers); - write("import "); - if (node.importClause) { - emit(node.importClause); - write(" from "); - } - emitExpression(node.moduleSpecifier); - write(";"); - } + function emitNamedImports(node: NamedImports) { + emitNamedImportsOrExports(node); + } - function emitImportClause(node: ImportClause) { - emit(node.name); - if (node.name && node.namedBindings) { - write(", "); - } - emit(node.namedBindings); - } + function emitImportSpecifier(node: ImportSpecifier) { + emitImportOrExportSpecifier(node); + } - function emitNamespaceImport(node: NamespaceImport) { - write("* as "); - emit(node.name); - } + function emitExportAssignment(node: ExportAssignment) { + write(node.isExportEquals ? "export = " : "export default "); + emitExpression(node.expression); + write(";"); + } - function emitNamedImports(node: NamedImports) { - emitNamedImportsOrExports(node); + function emitExportDeclaration(node: ExportDeclaration) { + write("export "); + if (node.exportClause) { + emit(node.exportClause); } - - function emitImportSpecifier(node: ImportSpecifier) { - emitImportOrExportSpecifier(node); + else { + write("*"); } - - function emitExportAssignment(node: ExportAssignment) { - write(node.isExportEquals ? "export = " : "export default "); - emitExpression(node.expression); - write(";"); + if (node.moduleSpecifier) { + write(" from "); + emitExpression(node.moduleSpecifier); } + 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 emitNamedExports(node: NamedExports) { - emitNamedImportsOrExports(node); - } + function emitExportSpecifier(node: ExportSpecifier) { + emitImportOrExportSpecifier(node); + } - function emitExportSpecifier(node: ExportSpecifier) { - emitImportOrExportSpecifier(node); - } + function emitNamedImportsOrExports(node: NamedImportsOrExports) { + write("{"); + emitList(node, node.elements, ListFormat.NamedImportsOrExportsElements); + write("}"); + } - function emitNamedImportsOrExports(node: NamedImportsOrExports) { - write("{"); - emitList(node, node.elements, ListFormat.NamedImportsOrExportsElements); - write("}"); + function emitImportOrExportSpecifier(node: ImportOrExportSpecifier) { + if (node.propertyName) { + emit(node.propertyName); + write(" as "); } - function emitImportOrExportSpecifier(node: ImportOrExportSpecifier) { - if (node.propertyName) { - emit(node.propertyName); - write(" as "); - } + emit(node.name); + } - emit(node.name); - } + // + // Module references + // - // - // Module references - // + function emitExternalModuleReference(node: ExternalModuleReference) { + write("require("); + emitExpression(node.expression); + write(")"); + } - function emitExternalModuleReference(node: ExternalModuleReference) { - write("require("); - emitExpression(node.expression); - write(")"); - } + // + // JSX + // - // - // JSX - // + function emitJsxElement(node: JsxElement) { + emit(node.openingElement); + emitList(node, node.children, ListFormat.JsxElementChildren); + emit(node.closingElement); + } - 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 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 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 emitJsxText(node: JsxText) { - writer.writeLiteral(getTextOfNode(node, /*includeTrivia*/ true)); - } + function emitJsxClosingElement(node: JsxClosingElement) { + write(""); + } - function emitJsxClosingElement(node: JsxClosingElement) { - write(""); - } + function emitJsxAttribute(node: JsxAttribute) { + emit(node.name); + emitWithPrefix("=", node.initializer); + } - function emitJsxAttribute(node: JsxAttribute) { - emit(node.name); - emitWithPrefix("=", node.initializer); - } + function emitJsxSpreadAttribute(node: JsxSpreadAttribute) { + write("{..."); + emitExpression(node.expression); + write("}"); + } - function emitJsxSpreadAttribute(node: JsxSpreadAttribute) { - write("{..."); + function emitJsxExpression(node: JsxExpression) { + if (node.expression) { + write("{"); emitExpression(node.expression); write("}"); } + } - function emitJsxExpression(node: JsxExpression) { - if (node.expression) { - write("{"); - emitExpression(node.expression); - write("}"); - } + function emitJsxTagName(node: EntityName) { + if (node.kind === SyntaxKind.Identifier) { + emitExpression(node); } - - function emitJsxTagName(node: EntityName) { - if (node.kind === SyntaxKind.Identifier) { - emitExpression(node); - } - else { - emit(node); - } + else { + emit(node); } + } - // - // Clauses - // + // + // Clauses + // - function emitCaseClause(node: CaseClause) { - write("case "); - emitExpression(node.expression); - write(":"); + function emitCaseClause(node: CaseClause) { + write("case "); + emitExpression(node.expression); + write(":"); - emitCaseOrDefaultClauseStatements(node, node.statements); - } + emitCaseOrDefaultClauseStatements(node, node.statements); + } + + function emitDefaultClause(node: DefaultClause) { + write("default:"); + 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 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) - ); + function emitHeritageClause(node: HeritageClause) { + write(" "); + writeTokenText(node.token); + write(" "); + emitList(node, node.types, ListFormat.HeritageClauseTypes); + } - if (emitAsSingleStatement) { - write(" "); - emit(statements[0]); - } - else { - emitList(parentNode, statements, ListFormat.CaseOrDefaultClauseStatements); - } - } + 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); + } - function emitHeritageClause(node: HeritageClause) { - write(" "); - writeTokenText(node.token); - write(" "); - emitList(node, node.types, ListFormat.HeritageClauseTypes); - } + // + // 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 (!shouldSkipLeadingCommentsForNode(initializer)) { + const commentRange = initializer.commentRange || initializer; + emitTrailingCommentsOfPosition(commentRange.pos); + } + + emitExpression(node.initializer); + } - 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); + function emitShorthandPropertyAssignment(node: ShorthandPropertyAssignment) { + emit(node.name); + if (node.objectAssignmentInitializer) { + write(" = "); + emitExpression(node.objectAssignmentInitializer); } + } - // - // Property assignments - // + // + // Enum + // - 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 (!shouldSkipLeadingCommentsForNode(initializer)) { - const commentRange = initializer.commentRange || initializer; - emitTrailingCommentsOfPosition(commentRange.pos); - } + function emitEnumMember(node: EnumMember) { + emit(node.name); + emitExpressionWithPrefix(" = ", node.initializer); + } - emitExpression(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 emitShorthandPropertyAssignment(node: ShorthandPropertyAssignment) { - emit(node.name); - if (node.objectAssignmentInitializer) { - write(" = "); - emitExpression(node.objectAssignmentInitializer); + 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; } } - // - // Enum - // + return statements.length; + } - function emitEnumMember(node: EnumMember) { - emit(node.name); - emitExpressionWithPrefix(" = ", node.initializer); + function emitHelpers(node: Node) { + const emitFlags = node.emitFlags; + let helpersEmitted = false; + if (emitFlags & NodeEmitFlags.EmitEmitHelpers) { + helpersEmitted = emitEmitHelpers(currentSourceFile); } - // - // Top-level nodes - // + if (emitFlags & NodeEmitFlags.EmitExportStar) { + writeLines(exportStarHelper); + helpersEmitted = true; + } - function emitSourceFile(node: SourceFile) { - writeLine(); - emitShebang(); - emitBodyWithDetachedComments(node, node.statements, emitSourceFileWorker); + if (emitFlags & NodeEmitFlags.EmitSuperHelper) { + writeLines(superHelper); + helpersEmitted = true; } - 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; + if (emitFlags & NodeEmitFlags.EmitAdvancedSuperHelper) { + writeLines(advancedSuperHelper); + helpersEmitted = true; } - // Transformation nodes + return helpersEmitted; + } - function emitPartiallyEmittedExpression(node: PartiallyEmittedExpression) { - emitExpression(node.expression); - } + function emitEmitHelpers(node: SourceFile) { + let helpersEmitted = false; - /** - * 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; - } + // Only emit helpers if the user did not say otherwise. + if (!compilerOptions.noEmitHelpers) { + // Only Emit __extends function when target ES5. + // For target ES6 and above, we can emit classDeclaration as is. + if ((languageVersion < ScriptTarget.ES6) && (!extendsEmitted && node.flags & NodeFlags.HasClassExtends)) { + writeLines(extendsHelper); + extendsEmitted = true; + helpersEmitted = true; } - return statements.length; - } - - function emitHelpers(node: Node) { - const emitFlags = getNodeEmitFlags(node); - let helpersEmitted = false; - if (emitFlags & NodeEmitFlags.EmitEmitHelpers) { - helpersEmitted = emitEmitHelpers(currentSourceFile); + if (compilerOptions.jsx !== JsxEmit.Preserve && !assignEmitted && (node.flags & NodeFlags.HasJsxSpreadAttribute)) { + writeLines(assignHelper); + assignEmitted = true; } - if (emitFlags & NodeEmitFlags.EmitExportStar) { - writeLines(exportStarHelper); + if (!decorateEmitted && node.flags & NodeFlags.HasDecorators) { + writeLines(decorateHelper); + if (compilerOptions.emitDecoratorMetadata) { + writeLines(metadataHelper); + } + + decorateEmitted = true; helpersEmitted = true; } - if (emitFlags & NodeEmitFlags.EmitSuperHelper) { - writeLines(superHelper); + if (!paramEmitted && node.flags & NodeFlags.HasParamDecorators) { + writeLines(paramHelper); + paramEmitted = true; helpersEmitted = true; } - if (emitFlags & NodeEmitFlags.EmitAdvancedSuperHelper) { - writeLines(advancedSuperHelper); + if (!awaiterEmitted && node.flags & NodeFlags.HasAsyncFunctions) { + writeLines(awaiterHelper); + awaiterEmitted = true; helpersEmitted = true; } - return helpersEmitted; + if (helpersEmitted) { + writeLine(); + } } - function emitEmitHelpers(node: SourceFile) { - let helpersEmitted = false; - - // Only emit helpers if the user did not say otherwise. - if (!compilerOptions.noEmitHelpers) { - // Only Emit __extends function when target ES5. - // For target ES6 and above, we can emit classDeclaration as is. - if ((languageVersion < ScriptTarget.ES6) && (!extendsEmitted && node.flags & NodeFlags.HasClassExtends)) { - writeLines(extendsHelper); - extendsEmitted = true; - helpersEmitted = true; - } - - if (compilerOptions.jsx !== JsxEmit.Preserve && !assignEmitted && (node.flags & NodeFlags.HasJsxSpreadAttribute)) { - writeLines(assignHelper); - assignEmitted = true; - } - - if (!decorateEmitted && node.flags & NodeFlags.HasDecorators) { - writeLines(decorateHelper); - if (compilerOptions.emitDecoratorMetadata) { - writeLines(metadataHelper); - } - - decorateEmitted = true; - helpersEmitted = true; - } - - if (!paramEmitted && node.flags & NodeFlags.HasParamDecorators) { - writeLines(paramHelper); - paramEmitted = true; - helpersEmitted = true; - } - - if (!awaiterEmitted && node.flags & NodeFlags.HasAsyncFunctions) { - writeLines(awaiterHelper); - awaiterEmitted = true; - helpersEmitted = true; - } + return helpersEmitted; + } - if (helpersEmitted) { + function writeLines(text: string): void { + const lines = text.split(/\r\n|\r|\n/g); + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + if (line.length) { + if (i > 0) { writeLine(); } + write(line); } + } + } - return helpersEmitted; + // + // Helpers + // + + function emitShebang() { + const shebang = getShebang(currentText); + if (shebang) { + write(shebang); + writeLine(); } + } - function writeLines(text: string): void { - const lines = text.split(/\r\n|\r|\n/g); - for (let i = 0; i < lines.length; i++) { - const line = lines[i]; - if (line.length) { - if (i > 0) { - writeLine(); - } - write(line); - } - } + function emitModifiers(node: Node, modifiers: NodeArray) { + if (modifiers && modifiers.length) { + emitList(node, modifiers, ListFormat.Modifiers); + write(" "); } + } - // - // Helpers - // + function emitWithPrefix(prefix: string, node: Node) { + emitNodeWithPrefix(prefix, node, emit); + } - function emitShebang() { - const shebang = getShebang(currentText); - if (shebang) { - write(shebang); - writeLine(); - } + // TODO(rbuckton): This should be removed once source maps are aligned with the old + // emitter and new baselines are taken. This exists solely to + // align with the old emitter. + function emitSpecializedWithPrefix(prefix: string, node: Node, flags: NodeEmitFlags) { + if (node) { + write(prefix); + emitSpecialized(node, flags); } + } - function emitModifiers(node: Node, modifiers: NodeArray) { - if (modifiers && modifiers.length) { - emitList(node, modifiers, ListFormat.Modifiers); - write(" "); - } + 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 emitWithPrefix(prefix: string, node: Node) { - emitNodeWithPrefix(prefix, node, emit); + function emitWithSuffix(node: Node, suffix: string) { + if (node) { + emit(node); + write(suffix); } + } - // TODO(rbuckton): This should be removed once source maps are aligned with the old - // emitter and new baselines are taken. This exists solely to - // align with the old emitter. - function emitSpecializedWithPrefix(prefix: string, node: Node, flags: NodeEmitFlags) { - if (node) { - write(prefix); - emitSpecialized(node, flags); + function tryEmitSubstitute(node: Node, emitNode: (node: Node) => void, isExpression: boolean) { + if (isSubstitutionEnabled(node) && (node.emitFlags & NodeEmitFlags.NoSubstitution) === 0) { + const substitute = onSubstituteNode(node, isExpression); + if (substitute !== node) { + substitute.emitFlags |= NodeEmitFlags.NoSubstitution; + emitNode(substitute); + return true; } } - function emitExpressionWithPrefix(prefix: string, node: Node) { - emitNodeWithPrefix(prefix, node, emitExpression); - } + return false; + } - function emitNodeWithPrefix(prefix: string, node: Node, emit: (node: Node) => void) { - if (node) { - write(prefix); - emit(node); + function tryEmitConstantValue(node: PropertyAccessExpression | ElementAccessExpression): boolean { + const constantValue = tryGetConstEnumValue(node); + if (constantValue !== undefined) { + write(String(constantValue)); + if (!compilerOptions.removeComments) { + const propertyName = isPropertyAccessExpression(node) + ? declarationNameToString(node.name) + : getTextOfNode(node.argumentExpression); + write(` /* ${propertyName} */`); } - } - function emitWithSuffix(node: Node, suffix: string) { - if (node) { - emit(node); - write(suffix); - } + return true; } - function tryEmitSubstitute(node: Node, emitNode: (node: Node) => void, isExpression: boolean) { - if (isSubstitutionEnabled(node) && (getNodeEmitFlags(node) & NodeEmitFlags.NoSubstitution) === 0) { - const substitute = onSubstituteNode(node, isExpression); - if (substitute !== node) { - setNodeEmitFlags(substitute, NodeEmitFlags.NoSubstitution | getNodeEmitFlags(substitute)); - emitNode(substitute); - return true; - } - } + return false; + } - return false; + function emitEmbeddedStatement(node: Statement) { + if (isBlock(node)) { + write(" "); + emit(node); + } + else { + writeLine(); + increaseIndent(); + emit(node); + decreaseIndent(); } + } - function tryEmitConstantValue(node: PropertyAccessExpression | ElementAccessExpression): boolean { - const constantValue = tryGetConstEnumValue(node); - if (constantValue !== undefined) { - write(String(constantValue)); - if (!compilerOptions.removeComments) { - const propertyName = isPropertyAccessExpression(node) - ? declarationNameToString(node.name) - : getTextOfNode(node.argumentExpression); - write(` /* ${propertyName} */`); - } + function emitDecorators(parentNode: Node, decorators: NodeArray) { + emitList(parentNode, decorators, ListFormat.Decorators); + } - return true; - } + function emitTypeArguments(parentNode: Node, typeArguments: NodeArray) { + emitList(parentNode, typeArguments, ListFormat.TypeArguments); + } - return false; - } + function emitTypeParameters(parentNode: Node, typeParameters: NodeArray) { + emitList(parentNode, typeParameters, ListFormat.TypeParameters); + } - function emitEmbeddedStatement(node: Statement) { - if (isBlock(node)) { - write(" "); - emit(node); - } - else { - writeLine(); - increaseIndent(); - emit(node); - decreaseIndent(); - } - } + function emitParameters(parentNode: Node, parameters: NodeArray) { + emitList(parentNode, parameters, ListFormat.Parameters); + } - function emitDecorators(parentNode: Node, decorators: NodeArray) { - emitList(parentNode, decorators, ListFormat.Decorators); + function emitParametersForArrow(parentNode: Node, parameters: NodeArray) { + if (parameters && + parameters.length === 1 && + parameters[0].type === undefined && + parameters[0].pos === parentNode.pos) { + emit(parameters[0]); } - - function emitTypeArguments(parentNode: Node, typeArguments: NodeArray) { - emitList(parentNode, typeArguments, ListFormat.TypeArguments); + else { + emitParameters(parentNode, parameters); } + } - function emitTypeParameters(parentNode: Node, typeParameters: NodeArray) { - emitList(parentNode, typeParameters, ListFormat.TypeParameters); - } + function emitParametersForIndexSignature(parentNode: Node, parameters: NodeArray) { + emitList(parentNode, parameters, ListFormat.IndexSignatureParameters); + } - function emitParameters(parentNode: Node, parameters: NodeArray) { - emitList(parentNode, parameters, ListFormat.Parameters); - } + function emitList(parentNode: Node, children: NodeArray, format: ListFormat, start?: number, count?: number) { + emitNodeList(emit, parentNode, children, format, start, count); + } - 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 emitExpressionList(parentNode: Node, children: NodeArray, format: ListFormat, start?: number, count?: number) { + emitNodeList(emitExpression, parentNode, children, format, start, count); + } - function emitParametersForIndexSignature(parentNode: Node, parameters: NodeArray) { - emitList(parentNode, parameters, ListFormat.IndexSignatureParameters); + 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; } - function emitList(parentNode: Node, children: NodeArray, format: ListFormat, start?: number, count?: number) { - emitNodeList(emit, parentNode, children, format, start, count); + const isEmpty = isUndefined || children.length === 0 || start >= children.length || count === 0; + if (isEmpty && format & ListFormat.OptionalIfEmpty) { + return; } - function emitExpressionList(parentNode: Node, children: NodeArray, format: ListFormat, start?: number, count?: number) { - emitNodeList(emitExpression, parentNode, children, format, start, count); + if (format & ListFormat.BracketsMask) { + write(getOpeningBracket(format)); } - 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; + if (isEmpty) { + // Write a line terminator if the parent node was multi-line + if (format & ListFormat.MultiLine) { + writeLine(); } - - const isEmpty = isUndefined || children.length === 0 || start >= children.length || count === 0; - if (isEmpty && format & ListFormat.OptionalIfEmpty) { - return; + else if (format & ListFormat.SpaceBetweenBraces) { + write(" "); } - - if (format & ListFormat.BracketsMask) { - write(getOpeningBracket(format)); + } + 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; } - - 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 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(); - } + // 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 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; } - } - if (shouldEmitInterveningComments) { - const commentRange = child.commentRange || child; - emitTrailingCommentsOfPosition(commentRange.pos); - } - else { - shouldEmitInterveningComments = mayEmitInterveningComments; + writeLine(); + shouldEmitInterveningComments = false; } - - // Emit this child. - emit(child); - - if (shouldDecreaseIndentAfterEmit) { - decreaseIndent(); - shouldDecreaseIndentAfterEmit = false; + else if (previousSibling && format & ListFormat.SpaceBetweenSiblings) { + write(" "); } - - previousSibling = child; } - // Write a trailing comma, if requested. - const hasTrailingComma = (format & ListFormat.AllowTrailingComma) && children.hasTrailingComma; - if (format & ListFormat.CommaDelimited && hasTrailingComma) { - write(","); + if (shouldEmitInterveningComments) { + const commentRange = child.commentRange || child; + emitTrailingCommentsOfPosition(commentRange.pos); + } + else { + shouldEmitInterveningComments = mayEmitInterveningComments; } - // Decrease the indent, if requested. - if (format & ListFormat.Indented) { + // Emit this child. + emit(child); + + if (shouldDecreaseIndentAfterEmit) { decreaseIndent(); + shouldDecreaseIndentAfterEmit = false; } - // Write the closing line terminator or closing whitespace. - if (shouldWriteClosingLineTerminator(parentNode, children, format)) { - writeLine(); - } - else if (format & ListFormat.SpaceBetweenBraces) { - write(" "); - } + previousSibling = child; } - if (format & ListFormat.BracketsMask) { - write(getClosingBracket(format)); + // Write a trailing comma, if requested. + const hasTrailingComma = (format & ListFormat.AllowTrailingComma) && children.hasTrailingComma; + if (format & ListFormat.CommaDelimited && hasTrailingComma) { + write(","); } - } - function writeIfAny(nodes: NodeArray, text: string) { - if (nodes && nodes.length > 0) { - write(text); + // Decrease the indent, if requested. + if (format & ListFormat.Indented) { + decreaseIndent(); } - } - function writeIfPresent(node: Node, text: string) { - if (node !== undefined) { - write(text); + // Write the closing line terminator or closing whitespace. + if (shouldWriteClosingLineTerminator(parentNode, children, format)) { + writeLine(); + } + else if (format & ListFormat.SpaceBetweenBraces) { + write(" "); } } - function writeToken(token: SyntaxKind, pos: number, contextNode?: Node) { - const tokenStartPos = emitTokenStart(token, pos, contextNode, shouldSkipLeadingSourceMapForToken, getTokenSourceMapRange); - const tokenEndPos = writeTokenText(token, tokenStartPos); - return emitTokenEnd(token, tokenEndPos, contextNode, shouldSkipTrailingSourceMapForToken, getTokenSourceMapRange); + if (format & ListFormat.BracketsMask) { + write(getClosingBracket(format)); } + } - function shouldSkipLeadingSourceMapForToken(contextNode: Node) { - return (getNodeEmitFlags(contextNode) & NodeEmitFlags.NoTokenLeadingSourceMaps) !== 0; + function writeIfAny(nodes: NodeArray, text: string) { + if (nodes && nodes.length > 0) { + write(text); } + } - function shouldSkipTrailingSourceMapForToken(contextNode: Node) { - return (getNodeEmitFlags(contextNode) & NodeEmitFlags.NoTokenTrailingSourceMaps) !== 0; + function writeIfPresent(node: Node, text: string) { + if (node !== undefined) { + write(text); } + } + + function writeToken(token: SyntaxKind, pos: number, contextNode?: Node) { + const tokenStartPos = emitTokenStart(token, pos, contextNode, shouldSkipLeadingSourceMapForToken, getTokenSourceMapRange); + const tokenEndPos = writeTokenText(token, tokenStartPos); + return emitTokenEnd(token, tokenEndPos, contextNode, shouldSkipTrailingSourceMapForToken, getTokenSourceMapRange); + } + + function shouldSkipLeadingSourceMapForToken(contextNode: Node) { + return (contextNode.emitFlags & NodeEmitFlags.NoTokenLeadingSourceMaps) !== 0; + } + + function shouldSkipTrailingSourceMapForToken(contextNode: Node) { + return (contextNode.emitFlags & NodeEmitFlags.NoTokenTrailingSourceMaps) !== 0; + } + + function writeTokenText(token: SyntaxKind, pos?: number) { + const tokenString = tokenToString(token); + write(tokenString); + return positionIsSynthesized(pos) ? -1 : pos + tokenString.length; + } - function writeTokenText(token: SyntaxKind, pos?: number) { - const tokenString = tokenToString(token); - write(tokenString); - return positionIsSynthesized(pos) ? -1 : pos + tokenString.length; + function writeTokenNode(node: Node) { + if (node) { + emitStart(/*range*/ node, /*contextNode*/ node, shouldSkipLeadingSourceMapForNode, shouldSkipSourceMapForChildren, getSourceMapRange); + writeTokenText(node.kind); + emitEnd(/*range*/ node, /*contextNode*/ node, shouldSkipTrailingSourceMapForNode, shouldSkipSourceMapForChildren, getSourceMapRange); } + } - function writeTokenNode(node: Node) { - if (node) { - emitStart(/*range*/ node, /*contextNode*/ node, shouldSkipLeadingSourceMapForNode, shouldSkipSourceMapForChildren, getSourceMapRange); - writeTokenText(node.kind); - emitEnd(/*range*/ node, /*contextNode*/ node, shouldSkipTrailingSourceMapForNode, shouldSkipSourceMapForChildren, getSourceMapRange); - } + function increaseIndentIf(value: boolean, valueToWriteWhenNotIndenting?: string) { + if (value) { + increaseIndent(); + writeLine(); + } + else if (valueToWriteWhenNotIndenting) { + write(valueToWriteWhenNotIndenting); } + } - 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(); + } + } - // 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; } - function shouldWriteLeadingLineTerminator(parentNode: Node, children: NodeArray, format: ListFormat) { - if (format & ListFormat.MultiLine) { + if (format & ListFormat.PreserveLines) { + if (format & ListFormat.PreferNewLine) { 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); - } + const firstChild = children[0]; + if (firstChild === undefined) { + return !rangeIsOnSingleLine(parentNode, currentSourceFile); + } + else if (positionIsSynthesized(parentNode.pos) || nodeIsSynthesized(firstChild)) { + return synthesizedNodeStartsOnNewLine(firstChild, format); } else { - return false; + return !rangeStartPositionsAreOnSameLine(parentNode, firstChild, currentSourceFile); } } + else { + return false; + } + } - function shouldWriteSeparatingLineTerminator(previousNode: Node, nextNode: Node, format: ListFormat) { - if (format & ListFormat.MultiLine) { - return true; + 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 (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 if (nodeIsSynthesized(previousNode) || nodeIsSynthesized(nextNode)) { + return synthesizedNodeStartsOnNewLine(previousNode, format) || synthesizedNodeStartsOnNewLine(nextNode, format); } else { - return nextNode.startsOnNewLine; + 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; + 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; } - 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); - } + const lastChild = lastOrUndefined(children); + if (lastChild === undefined) { + return !rangeIsOnSingleLine(parentNode, currentSourceFile); + } + else if (positionIsSynthesized(parentNode.pos) || nodeIsSynthesized(lastChild)) { + return synthesizedNodeStartsOnNewLine(lastChild, format); } else { - return false; + 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; + function synthesizedNodeStartsOnNewLine(node: Node, format?: ListFormat) { + if (nodeIsSynthesized(node)) { + const startsOnNewLine = node.startsOnNewLine; + if (startsOnNewLine === undefined) { + return (format & ListFormat.PreferNewLine) !== 0; } - return (format & ListFormat.PreferNewLine) !== 0; + return startsOnNewLine; } - function needsIndentation(parent: Node, node1: Node, node2: Node): boolean { - parent = skipSynthesizedParentheses(parent); - node1 = skipSynthesizedParentheses(node1); - node2 = skipSynthesizedParentheses(node2); + return (format & ListFormat.PreferNewLine) !== 0; + } - // Always use a newline for synthesized code if the synthesizer desires it. - if (node2.startsOnNewLine) { - return true; - } + function needsIndentation(parent: Node, node1: Node, node2: Node): boolean { + parent = skipSynthesizedParentheses(parent); + node1 = skipSynthesizedParentheses(node1); + node2 = skipSynthesizedParentheses(node2); - return !nodeIsSynthesized(parent) - && !nodeIsSynthesized(node1) - && !nodeIsSynthesized(node2) - && !rangeEndIsOnSameLineAsRangeStart(node1, node2, currentSourceFile); + // Always use a newline for synthesized code if the synthesizer desires it. + if (node2.startsOnNewLine) { + return true; } - function skipSynthesizedParentheses(node: Node) { - while (node.kind === SyntaxKind.ParenthesizedExpression && nodeIsSynthesized(node)) { - node = (node).expression; - } + return !nodeIsSynthesized(parent) + && !nodeIsSynthesized(node1) + && !nodeIsSynthesized(node2) + && !rangeEndIsOnSameLineAsRangeStart(node1, node2, currentSourceFile); + } - return node; + function skipSynthesizedParentheses(node: Node) { + while (node.kind === SyntaxKind.ParenthesizedExpression && nodeIsSynthesized(node)) { + node = (node).expression; } - 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 node; + } - return getSourceTextOfNodeFromSourceFile(currentSourceFile, node, includeTrivia); + 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; } - 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 getSourceTextOfNodeFromSourceFile(currentSourceFile, node, includeTrivia); + } - return getLiteralText(node, currentSourceFile, languageVersion); + 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); + } } - function tryGetConstEnumValue(node: Node): number { - if (compilerOptions.isolatedModules) { - return undefined; - } + return getLiteralText(node, currentSourceFile, languageVersion); + } - return isPropertyAccessExpression(node) || isElementAccessExpression(node) - ? resolver.getConstantValue(node) - : undefined; + function tryGetConstEnumValue(node: Node): number { + if (compilerOptions.isolatedModules) { + return undefined; } - function isSingleLineEmptyBlock(block: Block) { - return !block.multiLine - && block.statements.length === 0 - && rangeEndIsOnSameLineAsRangeStart(block, block, currentSourceFile); - } + return isPropertyAccessExpression(node) || isElementAccessExpression(node) + ? resolver.getConstantValue(node) + : undefined; + } - function isUniqueName(name: string): boolean { - return !resolver.hasGlobalName(name) && - !hasProperty(currentFileIdentifiers, name) && - !hasProperty(generatedNameSet, name); - } + function isSingleLineEmptyBlock(block: Block) { + return !block.multiLine + && block.statements.length === 0 + && rangeEndIsOnSameLineAsRangeStart(block, block, currentSourceFile); + } - function isUniqueLocalName(name: string, container: Node): boolean { - for (let node = container; isNodeDescendantOf(node, container); node = node.nextContainer) { - if (node.locals && hasProperty(node.locals, name)) { - // We conservatively include alias symbols to cover cases where they're emitted as locals - if (node.locals[name].flags & (SymbolFlags.Value | SymbolFlags.ExportValue | SymbolFlags.Alias)) { - return false; - } + function isUniqueName(name: string): boolean { + return !resolver.hasGlobalName(name) && + !hasProperty(currentFileIdentifiers, name) && + !hasProperty(generatedNameSet, name); + } + + function isUniqueLocalName(name: string, container: Node): boolean { + for (let node = container; isNodeDescendantOf(node, container); node = node.nextContainer) { + if (node.locals && hasProperty(node.locals, name)) { + // We conservatively include alias symbols to cover cases where they're emitted as locals + if (node.locals[name].flags & (SymbolFlags.Value | SymbolFlags.ExportValue | SymbolFlags.Alias)) { + return false; } } - return true; } + 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"; + /** + * 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)) { - 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)) { - return generatedNameSet[generatedName] = generatedName; - } - i++; - } + // 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 += "_"; } - - 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"); - } - - /** - * 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(); - default: - return makeTempVariableName(TempFlags.Auto); + let i = 1; + while (true) { + const generatedName = baseName + i; + if (isUniqueName(generatedName)) { + return generatedNameSet[generatedName] = generatedName; } + i++; } + } - /** - * 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); - } + 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"); + } - Debug.fail("Unsupported GeneratedIdentifierKind."); + /** + * 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(); + default: + return makeTempVariableName(TempFlags.Auto); } + } - /** - * 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; - } + /** + * 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."); + } - original = node.original; - } + /** + * 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; - // otherwise, return the original node for the source; - return 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); - const nodeId = getNodeId(node); - return nodeIdToGeneratedName[nodeId] || (nodeIdToGeneratedName[nodeId] = unescapeIdentifier(generateNameForNode(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))); + // 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; + } + + /** + * 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); + const nodeId = getNodeId(node); + return nodeIdToGeneratedName[nodeId] || (nodeIdToGeneratedName[nodeId] = unescapeIdentifier(generateNameForNode(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))); } } diff --git a/src/compiler/transformer.ts b/src/compiler/transformer.ts index 60240372f0fed..e71c8a9256b75 100644 --- a/src/compiler/transformer.ts +++ b/src/compiler/transformer.ts @@ -23,7 +23,52 @@ namespace ts { EmitNotifications = 1 << 1, } - /* @internal */ + export interface TransformationResult { + /** + * Gets the transformed source files. + */ + getSourceFiles(): SourceFile[]; + + /** + * Gets the TextRange to use for source maps for a token of a node. + */ + getTokenSourceMapRange(node: Node, token: SyntaxKind): TextRange; + + /** + * Determines whether expression substitutions are enabled for the provided node. + */ + isSubstitutionEnabled(node: Node): boolean; + + /** + * Determines whether before/after emit notifications should be raised in the pretty + * printer when it emits a node. + */ + isEmitNotificationEnabled(node: Node): boolean; + + /** + * Hook used by transformers to substitute expressions just before they + * are emitted by the pretty printer. + * + * @param node The node to substitute. + * @param isExpression A value indicating whether the node is in an expression context. + */ + onSubstituteNode(node: Node, isExpression: boolean): Node; + + /** + * Hook used to allow transformers to capture state before or after + * the printer emits a node. + * + * @param node The node to emit. + * @param emitCallback A callback used to emit the node. + */ + onEmitNode(node: Node, emitCallback: (node: Node) => void): void; + + /** + * Reset transient transformation properties on parse tree nodes. + */ + dispose(): void; + } + export interface TransformationContext extends LexicalEnvironment { getCompilerOptions(): CompilerOptions; getEmitResolver(): EmitResolver; @@ -155,7 +200,7 @@ namespace ts { * @param sourceFiles An array of source files * @param transforms An array of Transformers. */ - export function transformFiles(resolver: EmitResolver, host: EmitHost, sourceFiles: SourceFile[], transformers: Transformer[]) { + export function transformFiles(resolver: EmitResolver, host: EmitHost, sourceFiles: SourceFile[], transformers: Transformer[]): TransformationResult { const transformId = nextTransformId; nextTransformId++; @@ -163,7 +208,7 @@ namespace ts { const lexicalEnvironmentVariableDeclarationsStack: VariableDeclaration[][] = []; const lexicalEnvironmentFunctionDeclarationsStack: FunctionDeclaration[][] = []; const enabledSyntaxKindFeatures = new Array(SyntaxKind.Count); - const sourceTreeNodesWithAnnotations: Node[] = []; + const parseTreeNodesWithAnnotations: Node[] = []; let lastTokenSourceMapRangeNode: Node; let lastTokenSourceMapRangeToken: SyntaxKind; @@ -171,7 +216,6 @@ namespace ts { let lexicalEnvironmentStackOffset = 0; let hoistedVariableDeclarations: VariableDeclaration[]; let hoistedFunctionDeclarations: FunctionDeclaration[]; - let currentSourceFile: SourceFile; let lexicalEnvironmentDisabled: boolean; // The transformation context is provided to each transformer as part of transformer @@ -204,7 +248,37 @@ namespace ts { const transformation = chain(...transformers)(context); // Transform each source file. - return map(sourceFiles, transformSourceFile); + const transformed = map(sourceFiles, transformSourceFile); + + // Disable modification of the lexical environment. + lexicalEnvironmentDisabled = true; + + return { + getSourceFiles: () => transformed, + getTokenSourceMapRange, + isSubstitutionEnabled, + isEmitNotificationEnabled, + onSubstituteNode: context.onSubstituteNode, + onEmitNode: context.onEmitNode, + dispose() { + // During transformation we may need to annotate a parse tree node with transient + // transformation properties. As parse tree nodes live longer than transformation + // nodes, we need to make sure we reclaim any memory allocated for custom ranges + // from these nodes to ensure we do not hold onto entire subtrees just for position + // information. We also need to reset these nodes to a pre-transformation state + // for incremental parsing scenarios so that we do not impact later emit. + for (const node of parseTreeNodesWithAnnotations) { + if (node.transformId === transformId) { + node.transformId = 0; + node.emitFlags = 0; + node.commentRange = undefined; + node.sourceMapRange = undefined; + } + } + + parseTreeNodesWithAnnotations.length = 0; + } + }; /** * Transforms a source file. @@ -216,21 +290,7 @@ namespace ts { return sourceFile; } - currentSourceFile = sourceFile; - sourceFile = transformation(sourceFile); - - // Cleanup source tree nodes with annotations - for (const node of sourceTreeNodesWithAnnotations) { - if (node.transformId === transformId) { - node.transformId = 0; - node.emitFlags = 0; - node.commentRange = undefined; - node.sourceMapRange = undefined; - } - } - - sourceTreeNodesWithAnnotations.length = 0; - return sourceFile; + return transformation(sourceFile); } /** @@ -281,15 +341,6 @@ namespace ts { * @param emit A callback used to emit the node in the printer. */ function onEmitNode(node: Node, emit: (node: Node) => void) { - // Ensure that lexical environment modifications are disabled during the print phase. - if (!lexicalEnvironmentDisabled) { - const savedLexicalEnvironmentDisabled = lexicalEnvironmentDisabled; - lexicalEnvironmentDisabled = true; - emit(node); - lexicalEnvironmentDisabled = savedLexicalEnvironmentDisabled; - return; - } - emit(node); } @@ -302,9 +353,9 @@ namespace ts { function beforeSetAnnotation(node: Node) { if ((node.flags & NodeFlags.Synthesized) === 0 && node.transformId !== transformId) { // To avoid holding onto transformation artifacts, we keep track of any - // source tree node we are annotating. This allows us to clean them up after + // parse tree node we are annotating. This allows us to clean them up after // all transformations have completed. - sourceTreeNodesWithAnnotations.push(node); + parseTreeNodesWithAnnotations.push(node); node.transformId = transformId; } } @@ -520,7 +571,11 @@ namespace ts { function chain(...args: ((t: T) => (u: U) => U)[]): (t: T) => (u: U) => U; function chain(a: (t: T) => (u: U) => U, b: (t: T) => (u: U) => U, c: (t: T) => (u: U) => U, d: (t: T) => (u: U) => U, e: (t: T) => (u: U) => U): (t: T) => (u: U) => U { if (e) { - const args = arrayOf<(t: T) => (u: U) => U>(arguments); + const args: ((t: T) => (u: U) => U)[] = []; + for (let i = 0; i < arguments.length; i++) { + args[i] = arguments[i]; + } + return t => compose(...map(args, f => f(t))); } else if (d) { @@ -549,7 +604,11 @@ namespace ts { function compose(...args: ((t: T) => T)[]): (t: T) => T; function compose(a: (t: T) => T, b: (t: T) => T, c: (t: T) => T, d: (t: T) => T, e: (t: T) => T): (t: T) => T { if (e) { - const args = arrayOf(arguments); + const args: ((t: T) => T)[] = []; + for (let i = 0; i < arguments.length; i++) { + args[i] = arguments[i]; + } + return t => reduceLeft<(t: T) => T, T>(args, (u, f) => f(u), t); } else if (d) { @@ -568,16 +627,4 @@ namespace ts { return t => t; } } - - /** - * Makes an array from an ArrayLike. - */ - function arrayOf(arrayLike: ArrayLike) { - const length = arrayLike.length; - const array: T[] = new Array(length); - for (let i = 0; i < length; i++) { - array[i] = arrayLike[i]; - } - return array; - } } \ No newline at end of file diff --git a/src/compiler/transformers/es6.ts b/src/compiler/transformers/es6.ts index b2e79c67df726..2f4f090a02e5e 100644 --- a/src/compiler/transformers/es6.ts +++ b/src/compiler/transformers/es6.ts @@ -1425,15 +1425,13 @@ namespace ts { // If we are here it is most likely because our expression is a destructuring assignment. switch (node.expression.kind) { case SyntaxKind.ParenthesizedExpression: - return createStatement( - visitParenthesizedExpression(node.expression, /*needsDestructuringValue*/ false), - /*location*/ node + return updateStatement(node, + visitParenthesizedExpression(node.expression, /*needsDestructuringValue*/ false) ); case SyntaxKind.BinaryExpression: - return createStatement( - visitBinaryExpression(node.expression, /*needsDestructuringValue*/ false), - /*location*/ node + return updateStatement(node, + visitBinaryExpression(node.expression, /*needsDestructuringValue*/ false) ); } @@ -2831,7 +2829,7 @@ namespace ts { // Only substitute the identifier if we have enabled substitutions for block-scoped // bindings. if (enabledSubstitutions & ES6SubstitutionFlags.BlockScopedBindings) { - const original = getSourceTreeNodeOfType(node, isIdentifier); + const original = getParseTreeNode(node, isIdentifier); if (original && isNameOfDeclarationWithCollidingName(original)) { return getGeneratedNameForNode(original); } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index e170493d09ae4..5a4183ffbdccf 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -300,8 +300,8 @@ namespace ts { return getTokenPosOfNode(node.jsDocComments[0]); } - // For a syntax list, it is possible that one of its children has JSDocComment nodes, while - // the syntax list itself considers them as normal trivia. Therefore if we simply skip + // For a syntax list, it is possible that one of its children has JSDocComment nodes, while + // the syntax list itself considers them as normal trivia. Therefore if we simply skip // trivia for the list, we may have skipped the JSDocComment as well. So we should process its // first child to determine the actual position of its first token. if (node.kind === SyntaxKind.SyntaxList && (node)._children.length > 0) { @@ -1926,27 +1926,55 @@ namespace ts { return node; } - export function isSourceTreeNode(node: Node): boolean { + /** + * Gets a value indicating whether a node originated in the parse tree. + * + * @param node The node to test. + */ + export function isParseTreeNode(node: Node): boolean { return (node.flags & NodeFlags.Synthesized) === 0; } - export function getSourceTreeNode(node: Node): Node { - if (isSourceTreeNode(node)) { + /** + * Gets the original parse tree node for a node. + * + * @param node The original node. + * @returns The original parse tree node if found; otherwise, undefined. + */ + export function getParseTreeNode(node: Node): Node; + + /** + * Gets the original parse tree node for a node. + * + * @param node The original node. + * @param nodeTest A callback used to ensure the correct type of parse tree node is returned. + * @returns The original parse tree node if found; otherwise, undefined. + */ + export function getParseTreeNode(node: Node, nodeTest?: (node: Node) => node is T): T; + export function getParseTreeNode(node: Node, nodeTest?: (node: Node) => boolean): Node { + if (isParseTreeNode(node)) { return node; } node = getOriginalNode(node); - if (isSourceTreeNode(node)) { + if (isParseTreeNode(node) && (!nodeTest || nodeTest(node))) { return node; } return undefined; } - export function getSourceTreeNodeOfType(node: T, nodeTest: (node: Node) => node is T): T { - const source = getSourceTreeNode(node); - return source && nodeTest(source) ? source : undefined; + export function getOriginalSourceFiles(sourceFiles: SourceFile[]) { + const originalSourceFiles: SourceFile[] = []; + for (const sourceFile of sourceFiles) { + const originalSourceFile = getParseTreeNode(sourceFile, isSourceFile); + if (originalSourceFile) { + originalSourceFiles.push(originalSourceFile); + } + } + + return originalSourceFiles; } export function getOriginalNodeId(node: Node) { @@ -2457,6 +2485,109 @@ namespace ts { declarationFilePath: string; } + /** + * Gets the source files that are expected to have an emit output. + * + * Originally part of `forEachExpectedEmitFile`, this functionality was extracted to support + * transformations. + * + * @param host An EmitHost. + * @param targetSourceFile An optional target source file to emit. + */ + export function getSourceFilesToEmit(host: EmitHost, targetSourceFile?: SourceFile) { + const options = host.getCompilerOptions(); + if (options.outFile || options.out) { + const moduleKind = getEmitModuleKind(options); + const moduleEmitEnabled = moduleKind === ModuleKind.AMD || moduleKind === ModuleKind.System; + const sourceFiles = host.getSourceFiles(); + // Can emit only sources that are not declaration file and are either non module code or module with --module or --target es6 specified + return filter(sourceFiles, moduleEmitEnabled ? isNonDeclarationFile : isBundleEmitNonExternalModule); + } + else { + const sourceFiles = targetSourceFile === undefined ? host.getSourceFiles() : [targetSourceFile]; + return filter(sourceFiles, isNonDeclarationFile); + } + } + + function isNonDeclarationFile(sourceFile: SourceFile) { + return !isDeclarationFile(sourceFile); + } + + function isBundleEmitNonExternalModule(sourceFile: SourceFile) { + return !isDeclarationFile(sourceFile) && !isExternalModule(sourceFile); + } + + /** + * Iterates over each source file to emit. The source files are expected to have been + * transformed for use by the pretty printer. + * + * Originally part of `forEachExpectedEmitFile`, this functionality was extracted to support + * transformations. + * + * @param host An EmitHost. + * @param sourceFiles The transformed source files to emit. + * @param action The action to execute. + */ + export function forEachTransformedEmitFile(host: EmitHost, sourceFiles: SourceFile[], + action: (jsFilePath: string, sourceMapFilePath: string, declarationFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean) => void) { + const options = host.getCompilerOptions(); + // Emit on each source file + if (options.outFile || options.out) { + onBundledEmit(host, sourceFiles); + } + else { + for (const sourceFile of sourceFiles) { + if (!isDeclarationFile(sourceFile)) { + onSingleFileEmit(host, sourceFile); + } + } + } + + function onSingleFileEmit(host: EmitHost, sourceFile: SourceFile) { + // JavaScript files are always LanguageVariant.JSX, as JSX syntax is allowed in .js files also. + // So for JavaScript files, '.jsx' is only emitted if the input was '.jsx', and JsxEmit.Preserve. + // For TypeScript, the only time to emit with a '.jsx' extension, is on JSX input, and JsxEmit.Preserve + let extension = ".js"; + if (options.jsx === JsxEmit.Preserve) { + if (isSourceFileJavaScript(sourceFile)) { + if (fileExtensionIs(sourceFile.fileName, ".jsx")) { + extension = ".jsx"; + } + } + else if (sourceFile.languageVariant === LanguageVariant.JSX) { + // TypeScript source file preserving JSX syntax + extension = ".jsx"; + } + } + const jsFilePath = getOwnEmitOutputFilePath(sourceFile, host, extension); + const sourceMapFilePath = getSourceMapFilePath(jsFilePath, options); + const declarationFilePath = !isSourceFileJavaScript(sourceFile) ? getDeclarationEmitOutputFilePath(sourceFile, host) : undefined; + action(jsFilePath, sourceMapFilePath, declarationFilePath, [sourceFile], /*isBundledEmit*/ false); + } + + function onBundledEmit(host: EmitHost, sourceFiles: SourceFile[]) { + if (sourceFiles.length) { + 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); + } + } + } + + function getSourceMapFilePath(jsFilePath: string, options: CompilerOptions) { + return options.sourceMap ? jsFilePath + ".map" : undefined; + } + + /** + * Iterates over the source files that are expected to have an emit output. This function + * is used by the legacy emitter and the declaration emitter and should not be used by + * the tree transforming emitter. + * + * @param host An EmitHost. + * @param action The action to execute. + * @param targetSourceFile An optional target source file to emit. + */ export function forEachExpectedEmitFile(host: EmitHost, action: (emitFileNames: EmitFileNames, sourceFiles: SourceFile[], isBundledEmit: boolean) => void, targetSourceFile?: SourceFile) { @@ -2517,10 +2648,6 @@ namespace ts { action(emitFileNames, bundledSources, /*isBundledEmit*/true); } } - - function getSourceMapFilePath(jsFilePath: string, options: CompilerOptions) { - return options.sourceMap ? jsFilePath + ".map" : undefined; - } } export function getSourceFilePathInNewDir(sourceFile: SourceFile, host: EmitHost, newDirPath: string) {