diff --git a/src/compiler/comments.ts b/src/compiler/comments.ts index 1fec1e9562f..5c350b6a0e4 100644 --- a/src/compiler/comments.ts +++ b/src/compiler/comments.ts @@ -6,8 +6,10 @@ namespace ts { reset(): void; setSourceFile(sourceFile: SourceFile): void; setWriter(writer: EmitTextWriter): void; - emitNodeWithComments(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void; - emitBodyWithDetachedComments(node: Node, detachedRange: TextRange, emitCallback: (node: Node) => void): void; + emitLeadingCommentsOfNode(node: Node | undefined): void; + emitTrailingCommentsOfNode(node: Node | undefined): void; + emitLeadingDetachedCommentsOfBody(node: Node, detachedRange: TextRange): void; + emitTrailingDetachedCommentsOfBody(node: Node, detachedRange: TextRange): void; emitTrailingCommentsOfPosition(pos: number, prefixSpace?: boolean): void; emitLeadingCommentsOfPosition(pos: number): void; } @@ -15,6 +17,9 @@ namespace ts { export function createCommentWriter(printerOptions: PrinterOptions, emitPos: ((pos: number) => void) | undefined): CommentWriter { const extendedDiagnostics = printerOptions.extendedDiagnostics; const newLine = getNewLineCharacter(printerOptions); + const containerPosStack: number[] = []; + const containerEndStack: number[] = []; + const declarationListContainerEndStack: number[] = []; let writer: EmitTextWriter; let containerPos = -1; let containerEnd = -1; @@ -24,124 +29,135 @@ namespace ts { let currentLineMap: ReadonlyArray; let detachedCommentsInfo: { nodePos: number, detachedCommentEndPos: number}[]; let hasWrittenComment = false; - let disabled: boolean = printerOptions.removeComments; + let disabled: number = printerOptions.removeComments ? -1 : 0; return { reset, setWriter, setSourceFile, - emitNodeWithComments, - emitBodyWithDetachedComments, + emitLeadingCommentsOfNode, + emitTrailingCommentsOfNode, + emitLeadingDetachedCommentsOfBody, + emitTrailingDetachedCommentsOfBody, emitTrailingCommentsOfPosition, emitLeadingCommentsOfPosition, }; - function emitNodeWithComments(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) { - if (disabled) { - emitCallback(hint, node); - return; - } + function emitLeadingCommentsOfNode(node: Node | undefined) { + if (!node) return; + + const emitFlags = getEmitFlags(node); + if (!disabled) { + if (extendedDiagnostics) performance.mark("emitLeadingCommentsOfNode"); - if (node) { hasWrittenComment = false; - - const emitNode = node.emitNode; - const emitFlags = emitNode && emitNode.flags; - const { pos, end } = emitNode && emitNode.commentRange || node; - if ((pos < 0 && end < 0) || (pos === end)) { - // Both pos and end are synthesized, so just emit the node without comments. - emitNodeWithSynthesizedComments(hint, node, emitNode, emitFlags, emitCallback); - } - else { - if (extendedDiagnostics) { - performance.mark("preEmitNodeWithComment"); - } - - const isEmittedNode = node.kind !== SyntaxKind.NotEmittedStatement; - // We have to explicitly check that the node is JsxText because if the compilerOptions.jsx is "preserve" we will not do any transformation. - // It is expensive to walk entire tree just to set one kind of node to have no comments. - const skipLeadingComments = pos < 0 || (emitFlags & EmitFlags.NoLeadingComments) !== 0 || node.kind === SyntaxKind.JsxText; - const skipTrailingComments = end < 0 || (emitFlags & EmitFlags.NoTrailingComments) !== 0 || node.kind === SyntaxKind.JsxText; + const commentRange = getCommentRange(node); + if (!isSynthesized(commentRange)) { + const skipLeadingComments = shouldSkipLeadingComments(node.kind, commentRange, emitFlags); + const skipTrailingComments = shouldSkipTrailingComments(node.kind, commentRange, emitFlags); // Emit leading comments if the position is not synthesized and the node // has not opted out from emitting leading comments. if (!skipLeadingComments) { - emitLeadingComments(pos, isEmittedNode); + emitLeadingComments(commentRange.pos, node.kind !== SyntaxKind.NotEmittedStatement); } - // Save current container state on the stack. - const savedContainerPos = containerPos; - const savedContainerEnd = containerEnd; - const savedDeclarationListContainerEnd = declarationListContainerEnd; + pushContainer(node.kind, commentRange, skipLeadingComments, skipTrailingComments); + } - if (!skipLeadingComments) { - containerPos = pos; - } + forEach(getSyntheticLeadingComments(node), emitLeadingSynthesizedComment); - if (!skipTrailingComments) { - containerEnd = end; + if (extendedDiagnostics) performance.measure("commentTime", "emitLeadingCommentsOfNode"); + } - // To avoid invalid comment emit in a down-level binding pattern, we - // keep track of the last declaration list container's end - if (node.kind === SyntaxKind.VariableDeclarationList) { - declarationListContainerEnd = end; - } - } + pushDisabledState(emitFlags); + } - if (extendedDiagnostics) { - performance.measure("commentTime", "preEmitNodeWithComment"); - } + function emitTrailingCommentsOfNode(node: Node | undefined) { + if (!node) return; - emitNodeWithSynthesizedComments(hint, node, emitNode, emitFlags, emitCallback); + const emitFlags = getEmitFlags(node); + popDisabledState(emitFlags); - if (extendedDiagnostics) { - performance.mark("postEmitNodeWithComment"); - } + if (!disabled) { + if (extendedDiagnostics) performance.mark("emitTrailingCommentsOfNode"); - // Restore previous container state. - containerPos = savedContainerPos; - containerEnd = savedContainerEnd; - declarationListContainerEnd = savedDeclarationListContainerEnd; + forEach(getSyntheticTrailingComments(node), emitTrailingSynthesizedComment); + + const commentRange = getCommentRange(node); + if (!isSynthesized(commentRange)) { + const skipLeadingComments = shouldSkipLeadingComments(node.kind, commentRange, emitFlags); + const skipTrailingComments = shouldSkipTrailingComments(node.kind, commentRange, emitFlags); + popContainer(node.kind, skipLeadingComments, skipTrailingComments); // Emit trailing comments if the position is not synthesized and the node // has not opted out from emitting leading comments and is an emitted node. - if (!skipTrailingComments && isEmittedNode) { - emitTrailingComments(end); + if (!skipTrailingComments && node.kind !== SyntaxKind.NotEmittedStatement) { + emitTrailingComments(commentRange.end); } + } - if (extendedDiagnostics) { - performance.measure("commentTime", "postEmitNodeWithComment"); - } + if (extendedDiagnostics) performance.measure("commentTime", "emitTrailingCommentsOfNode"); + } + } + + function shouldSkipLeadingComments(kind: SyntaxKind, commentRange: TextRange, emitFlags: EmitFlags) { + // We have to explicitly check that the node is JsxText because if the compilerOptions.jsx is "preserve" we will not do any transformation. + // It is expensive to walk entire tree just to set one kind of node to have no comments. + return kind === SyntaxKind.JsxText || commentRange.pos < 0 || (emitFlags & EmitFlags.NoLeadingComments) !== 0; + } + + function shouldSkipTrailingComments(kind: SyntaxKind, commentRange: TextRange, emitFlags: EmitFlags) { + // We have to explicitly check that the node is JsxText because if the compilerOptions.jsx is "preserve" we will not do any transformation. + // It is expensive to walk entire tree just to set one kind of node to have no comments. + return kind === SyntaxKind.JsxText || commentRange.end < 0 || (emitFlags & EmitFlags.NoTrailingComments) !== 0; + } + + function isSynthesized(range: TextRange) { + return range.pos < 0 && range.end < 0 || range.pos === range.end; + } + + function pushDisabledState(emitFlags: EmitFlags) { + if (disabled >= 0 && emitFlags & EmitFlags.NoNestedComments) { + disabled++; + } + } + + function popDisabledState(emitFlags: EmitFlags) { + if (disabled > 0 && emitFlags & EmitFlags.NoNestedComments) { + disabled--; + } + } + + function pushContainer(kind: SyntaxKind, commentRange: TextRange, skipLeadingComments: boolean, skipTrailingComments: boolean) { + // Save current container state on the stack. + if (!skipLeadingComments) { + containerPosStack.push(containerPos); + containerPos = commentRange.pos; + } + + if (!skipTrailingComments) { + containerEndStack.push(containerEnd); + containerEnd = commentRange.end; + + // To avoid invalid comment emit in a down-level binding pattern, we + // keep track of the last declaration list container's end + if (kind === SyntaxKind.VariableDeclarationList) { + declarationListContainerEndStack.push(declarationListContainerEnd); + declarationListContainerEnd = commentRange.end; } } } - function emitNodeWithSynthesizedComments(hint: EmitHint, node: Node, emitNode: EmitNode, emitFlags: EmitFlags, emitCallback: (hint: EmitHint, node: Node) => void) { - const leadingComments = emitNode && emitNode.leadingComments; - if (some(leadingComments)) { - if (extendedDiagnostics) { - performance.mark("preEmitNodeWithSynthesizedComments"); - } - - forEach(leadingComments, emitLeadingSynthesizedComment); - - if (extendedDiagnostics) { - performance.measure("commentTime", "preEmitNodeWithSynthesizedComments"); - } + function popContainer(kind: SyntaxKind, skipLeadingComments: boolean, skipTrailingComments: boolean) { + if (!skipLeadingComments) { + containerPos = containerPosStack.pop(); } - emitNodeWithNestedComments(hint, node, emitFlags, emitCallback); - - const trailingComments = emitNode && emitNode.trailingComments; - if (some(trailingComments)) { - if (extendedDiagnostics) { - performance.mark("postEmitNodeWithSynthesizedComments"); - } - - forEach(trailingComments, emitTrailingSynthesizedComment); - - if (extendedDiagnostics) { - performance.measure("commentTime", "postEmitNodeWithSynthesizedComments"); + if (!skipTrailingComments) { + containerEnd = containerEndStack.pop(); + if (kind === SyntaxKind.VariableDeclarationList) { + declarationListContainerEnd = declarationListContainerEndStack.pop(); } } } @@ -181,47 +197,27 @@ namespace ts { : `//${comment.text}`; } - function emitNodeWithNestedComments(hint: EmitHint, node: Node, emitFlags: EmitFlags, emitCallback: (hint: EmitHint, node: Node) => void) { - if (emitFlags & EmitFlags.NoNestedComments) { - disabled = true; - emitCallback(hint, node); - disabled = false; - } - else { - emitCallback(hint, node); - } - } + function emitLeadingDetachedCommentsOfBody(node: Node, detachedRange: TextRange) { + if (extendedDiagnostics) performance.mark("emitLeadingDetachedCommentsOfBody"); - function emitBodyWithDetachedComments(node: Node, detachedRange: TextRange, emitCallback: (node: Node) => void) { - if (extendedDiagnostics) { - performance.mark("preEmitBodyWithDetachedComments"); - } - - const { pos, end } = detachedRange; const emitFlags = getEmitFlags(node); - const skipLeadingComments = pos < 0 || (emitFlags & EmitFlags.NoLeadingComments) !== 0; - const skipTrailingComments = disabled || end < 0 || (emitFlags & EmitFlags.NoTrailingComments) !== 0; + const skipLeadingComments = detachedRange.pos < 0 || (emitFlags & EmitFlags.NoLeadingComments) !== 0; if (!skipLeadingComments) { emitDetachedCommentsAndUpdateCommentsInfo(detachedRange); } - if (extendedDiagnostics) { - performance.measure("commentTime", "preEmitBodyWithDetachedComments"); - } + if (extendedDiagnostics) performance.measure("commentTime", "emitLeadingDetachedCommentsOfBody"); + pushDisabledState(emitFlags); + } - if (emitFlags & EmitFlags.NoNestedComments && !disabled) { - disabled = true; - emitCallback(node); - disabled = false; - } - else { - emitCallback(node); - } + function emitTrailingDetachedCommentsOfBody(node: Node, detachedRange: TextRange) { + if (extendedDiagnostics) performance.mark("emitTrailingDetachedCommentsOfBody"); - if (extendedDiagnostics) { - performance.mark("beginEmitBodyWithDetachedCommetns"); - } + const emitFlags = getEmitFlags(node); + popDisabledState(emitFlags); + + const skipTrailingComments = disabled || detachedRange.end < 0 || (emitFlags & EmitFlags.NoTrailingComments) !== 0; if (!skipTrailingComments) { emitLeadingComments(detachedRange.end, /*isEmittedNode*/ true); @@ -230,9 +226,7 @@ namespace ts { } } - if (extendedDiagnostics) { - performance.measure("commentTime", "beginEmitBodyWithDetachedCommetns"); - } + if (extendedDiagnostics) performance.measure("commentTime", "emitTrailingDetachedCommentsOfBody"); } function emitLeadingComments(pos: number, isEmittedNode: boolean) { @@ -392,7 +386,7 @@ namespace ts { } function emitDetachedCommentsAndUpdateCommentsInfo(range: TextRange) { - const currentDetachedCommentInfo = emitDetachedComments(currentText, currentLineMap, writer, writeComment, range, newLine, disabled); + const currentDetachedCommentInfo = emitDetachedComments(currentText, currentLineMap, writer, writeComment, range, newLine, !!disabled); if (currentDetachedCommentInfo) { if (detachedCommentsInfo) { detachedCommentsInfo.push(currentDetachedCommentInfo); diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 3c9866b3536..2dc9d127496 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -109,7 +109,8 @@ namespace ts { substituteNode: transform.substituteNode, // sourcemap hooks - onEmitSourceMapOfNode: sourceMap.emitNodeWithSourceMap, + onEmitLeadingSourceMapOfNode: sourceMap.emitLeadingSourceMapOfNode, + onEmitTrailingSourceMapOfNode: sourceMap.emitTrailingSourceMapOfNode, onEmitSourceMapOfToken: sourceMap.emitTokenWithSourceMap, onEmitSourceMapOfPosition: sourceMap.emitPos, @@ -258,10 +259,19 @@ namespace ts { } } + const enum PipelinePhase { + LeadingComments, + LeadingSourceMap, + Emit, + TrailingSourceMap, + TrailingComments + } + export function createPrinter(printerOptions: PrinterOptions = {}, handlers: PrintHandlers = {}): Printer { const { hasGlobalName, - onEmitSourceMapOfNode, + onEmitLeadingSourceMapOfNode, + onEmitTrailingSourceMapOfNode, onEmitSourceMapOfToken, onEmitSourceMapOfPosition, onEmitNode, @@ -277,8 +287,10 @@ namespace ts { const newLine = getNewLineCharacter(printerOptions); const comments = createCommentWriter(printerOptions, onEmitSourceMapOfPosition); const { - emitNodeWithComments, - emitBodyWithDetachedComments, + emitLeadingCommentsOfNode, + emitTrailingCommentsOfNode, + emitLeadingDetachedCommentsOfBody, + emitTrailingDetachedCommentsOfBody, emitTrailingCommentsOfPosition, emitLeadingCommentsOfPosition, } = comments; @@ -415,6 +427,7 @@ namespace ts { if (sourceFile) { setSourceFile(sourceFile); } + pipelineEmitWithNotification(hint, node); } @@ -442,23 +455,15 @@ namespace ts { setWriter(/*output*/ undefined); } - // TODO: Should this just be `emit`? - // See https://github.com/Microsoft/TypeScript/pull/18284#discussion_r137611034 - function emitIfPresent(node: Node | undefined) { - if (node) { - emit(node); - } - } - - function emit(node: Node) { + function emit(node: Node | undefined) { pipelineEmitWithNotification(EmitHint.Unspecified, node); } - function emitIdentifierName(node: Identifier) { + function emitIdentifierName(node: Identifier | undefined) { pipelineEmitWithNotification(EmitHint.IdentifierName, node); } - function emitExpression(node: Expression) { + function emitExpression(node: Expression | undefined) { pipelineEmitWithNotification(EmitHint.Expression, node); } @@ -472,423 +477,423 @@ namespace ts { } function pipelineEmitWithComments(hint: EmitHint, node: Node) { - node = trySubstituteNode(hint, node); - if (emitNodeWithComments && hint !== EmitHint.SourceFile) { - emitNodeWithComments(hint, node, pipelineEmitWithSourceMap); - } - else { - pipelineEmitWithSourceMap(hint, node); - } + pipelineEmit(hint, node, PipelinePhase.LeadingComments); } - function pipelineEmitWithSourceMap(hint: EmitHint, node: Node) { - if (onEmitSourceMapOfNode && hint !== EmitHint.SourceFile && hint !== EmitHint.IdentifierName) { - onEmitSourceMapOfNode(hint, node, pipelineEmitWithHint); - } - else { - pipelineEmitWithHint(hint, node); + function pipelineEmit(hint: EmitHint, node: Node | undefined, start: PipelinePhase) { + if (!node) return; + switch (start) { + case PipelinePhase.LeadingComments: + node = trySubstituteNode(hint, node); + if (hint !== EmitHint.SourceFile) { + emitLeadingCommentsOfNode(node); + } + + // falls through + + case PipelinePhase.LeadingSourceMap: + if (onEmitLeadingSourceMapOfNode && hint !== EmitHint.SourceFile && hint !== EmitHint.IdentifierName) { + onEmitLeadingSourceMapOfNode(node); + } + + // falls through + + case PipelinePhase.Emit: + pipelineEmitWithHint(hint, node); + // falls through + + case PipelinePhase.TrailingSourceMap: + if (onEmitTrailingSourceMapOfNode && hint !== EmitHint.SourceFile && hint !== EmitHint.IdentifierName) { + onEmitTrailingSourceMapOfNode(node); + } + + // falls through + + case PipelinePhase.TrailingComments: + if (hint !== EmitHint.SourceFile) { + emitTrailingCommentsOfNode(node); + } + + break; } } function pipelineEmitWithHint(hint: EmitHint, node: Node): void { - switch (hint) { - case EmitHint.SourceFile: return pipelineEmitSourceFile(node); - case EmitHint.IdentifierName: return pipelineEmitIdentifierName(node); - case EmitHint.Expression: return pipelineEmitExpression(node); - case EmitHint.MappedTypeParameter: return emitMappedTypeParameter(cast(node, isTypeParameterDeclaration)); - case EmitHint.Unspecified: return pipelineEmitUnspecified(node); - } - } - - function pipelineEmitSourceFile(node: Node): void { - Debug.assertNode(node, isSourceFile); - emitSourceFile(node); - } - - function pipelineEmitIdentifierName(node: Node): void { - Debug.assertNode(node, isIdentifier); - emitIdentifier(node); - } - - function emitMappedTypeParameter(node: TypeParameterDeclaration): void { - emit(node.name); - writeSpace(); - writeKeyword("in"); - writeSpace(); - emit(node.constraint); - } - - function pipelineEmitUnspecified(node: Node): void { - const kind = node.kind; - - // Reserved words - // Strict mode reserved words - // Contextual keywords - if (isKeyword(kind)) { - writeTokenNode(node, writeKeyword); - return; + if (hint === EmitHint.SourceFile) { + return emitSourceFile(cast(node, isSourceFile)); } - switch (kind) { - // Pseudo-literals - case SyntaxKind.TemplateHead: - case SyntaxKind.TemplateMiddle: - case SyntaxKind.TemplateTail: - return emitLiteral(node); - - // Identifiers - case SyntaxKind.Identifier: - return emitIdentifier(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.JSDocFunctionType: - return emitJSDocFunctionType(node as JSDocFunctionType); - 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.ConditionalType: - return emitConditionalType(node); - case SyntaxKind.InferType: - return emitInferType(node); - case SyntaxKind.ParenthesizedType: - return emitParenthesizedType(node); - case SyntaxKind.ExpressionWithTypeArguments: - return emitExpressionWithTypeArguments(node); - case SyntaxKind.ThisType: - return emitThisType(); - case SyntaxKind.TypeOperator: - return emitTypeOperator(node); - case SyntaxKind.IndexedAccessType: - return emitIndexedAccessType(node); - case SyntaxKind.MappedType: - return emitMappedType(node); - case SyntaxKind.LiteralType: - return emitLiteralType(node); - case SyntaxKind.JSDocAllType: - write("*"); - return; - case SyntaxKind.JSDocUnknownType: - write("?"); - return; - case SyntaxKind.JSDocNullableType: - return emitJSDocNullableType(node as JSDocNullableType); - case SyntaxKind.JSDocNonNullableType: - return emitJSDocNonNullableType(node as JSDocNonNullableType); - case SyntaxKind.JSDocOptionalType: - return emitJSDocOptionalType(node as JSDocOptionalType); - case SyntaxKind.JSDocVariadicType: - return emitJSDocVariadicType(node as JSDocVariadicType); - - // Binding patterns - case SyntaxKind.ObjectBindingPattern: - return emitObjectBindingPattern(node); - case SyntaxKind.ArrayBindingPattern: - return emitArrayBindingPattern(node); - case SyntaxKind.BindingElement: - return emitBindingElement(node); - - // Misc - case SyntaxKind.TemplateSpan: - return emitTemplateSpan(node); - case SyntaxKind.SemicolonClassElement: - return emitSemicolonClassElement(); - - // Statements - case SyntaxKind.Block: - return emitBlock(node); - case SyntaxKind.VariableStatement: - return emitVariableStatement(node); - case SyntaxKind.EmptyStatement: - return emitEmptyStatement(); - case SyntaxKind.ExpressionStatement: - return emitExpressionStatement(node); - case SyntaxKind.IfStatement: - return emitIfStatement(node); - case SyntaxKind.DoStatement: - return emitDoStatement(node); - case SyntaxKind.WhileStatement: - return emitWhileStatement(node); - case SyntaxKind.ForStatement: - return emitForStatement(node); - case SyntaxKind.ForInStatement: - return emitForInStatement(node); - case SyntaxKind.ForOfStatement: - return emitForOfStatement(node); - case SyntaxKind.ContinueStatement: - return emitContinueStatement(node); - case SyntaxKind.BreakStatement: - return emitBreakStatement(node); - case SyntaxKind.ReturnStatement: - return emitReturnStatement(node); - case SyntaxKind.WithStatement: - return emitWithStatement(node); - case SyntaxKind.SwitchStatement: - return emitSwitchStatement(node); - case SyntaxKind.LabeledStatement: - return emitLabeledStatement(node); - case SyntaxKind.ThrowStatement: - return emitThrowStatement(node); - case SyntaxKind.TryStatement: - return emitTryStatement(node); - case SyntaxKind.DebuggerStatement: - return emitDebuggerStatement(node); - - // Declarations - case SyntaxKind.VariableDeclaration: - return emitVariableDeclaration(node); - case SyntaxKind.VariableDeclarationList: - return emitVariableDeclarationList(node); - case SyntaxKind.FunctionDeclaration: - return emitFunctionDeclaration(node); - case SyntaxKind.ClassDeclaration: - return emitClassDeclaration(node); - case SyntaxKind.InterfaceDeclaration: - return emitInterfaceDeclaration(node); - case SyntaxKind.TypeAliasDeclaration: - return emitTypeAliasDeclaration(node); - case SyntaxKind.EnumDeclaration: - return emitEnumDeclaration(node); - case SyntaxKind.ModuleDeclaration: - return emitModuleDeclaration(node); - case SyntaxKind.ModuleBlock: - return emitModuleBlock(node); - case SyntaxKind.CaseBlock: - return emitCaseBlock(node); - case SyntaxKind.NamespaceExportDeclaration: - return emitNamespaceExportDeclaration(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: - case SyntaxKind.JsxOpeningFragment: - return emitJsxOpeningElementOrFragment(node); - case SyntaxKind.JsxClosingElement: - case SyntaxKind.JsxClosingFragment: - return emitJsxClosingElementOrFragment(node); - case SyntaxKind.JsxAttribute: - return emitJsxAttribute(node); - case SyntaxKind.JsxAttributes: - return emitJsxAttributes(node); - case SyntaxKind.JsxSpreadAttribute: - return emitJsxSpreadAttribute(node); - case SyntaxKind.JsxExpression: - return emitJsxExpression(node); - - // Clauses - case SyntaxKind.CaseClause: - return emitCaseClause(node); - case SyntaxKind.DefaultClause: - return emitDefaultClause(node); - case SyntaxKind.HeritageClause: - return emitHeritageClause(node); - case SyntaxKind.CatchClause: - return emitCatchClause(node); - - // Property assignments - case SyntaxKind.PropertyAssignment: - return emitPropertyAssignment(node); - case SyntaxKind.ShorthandPropertyAssignment: - return emitShorthandPropertyAssignment(node); - case SyntaxKind.SpreadAssignment: - return emitSpreadAssignment(node as SpreadAssignment); - - // Enum - case SyntaxKind.EnumMember: - return emitEnumMember(node); - - // JSDoc nodes (ignored) - // Transformation nodes (ignored) + if (hint === EmitHint.IdentifierName) { + return emitIdentifier(cast(node, isIdentifier)); } - // If the node is an expression, try to emit it as an expression with - // substitution. - if (isExpression(node)) { - return pipelineEmitExpression(trySubstituteNode(EmitHint.Expression, node)); + if (hint === EmitHint.MappedTypeParameter) { + return emitMappedTypeParameter(cast(node, isTypeParameterDeclaration)); } - if (isToken(node)) { - writeTokenNode(node, writePunctuation); - return; - } - } - - function pipelineEmitExpression(node: Node): void { - const kind = node.kind; - switch (kind) { - // Literals - case SyntaxKind.NumericLiteral: - return emitNumericLiteral(node); - - case SyntaxKind.StringLiteral: - case SyntaxKind.RegularExpressionLiteral: - case SyntaxKind.NoSubstitutionTemplateLiteral: - return emitLiteral(node); - - // Identifiers - case SyntaxKind.Identifier: - return emitIdentifier(node); - + if (hint === EmitHint.Unspecified) { // Reserved words - case SyntaxKind.FalseKeyword: - case SyntaxKind.NullKeyword: - case SyntaxKind.SuperKeyword: - case SyntaxKind.TrueKeyword: - case SyntaxKind.ThisKeyword: - case SyntaxKind.ImportKeyword: + // Strict mode reserved words + // Contextual keywords + if (isKeyword(node.kind)) { writeTokenNode(node, writeKeyword); return; + } - // Expressions - case SyntaxKind.ArrayLiteralExpression: - return emitArrayLiteralExpression(node); - case SyntaxKind.ObjectLiteralExpression: - return emitObjectLiteralExpression(node); - case SyntaxKind.PropertyAccessExpression: - return emitPropertyAccessExpression(node); - case SyntaxKind.ElementAccessExpression: - return emitElementAccessExpression(node); - case SyntaxKind.CallExpression: - return emitCallExpression(node); - case SyntaxKind.NewExpression: - return emitNewExpression(node); - case SyntaxKind.TaggedTemplateExpression: - return emitTaggedTemplateExpression(node); - case SyntaxKind.TypeAssertionExpression: - return emitTypeAssertionExpression(node); - case SyntaxKind.ParenthesizedExpression: - return emitParenthesizedExpression(node); - case SyntaxKind.FunctionExpression: - return emitFunctionExpression(node); - case SyntaxKind.ArrowFunction: - return emitArrowFunction(node); - case SyntaxKind.DeleteExpression: - return emitDeleteExpression(node); - case SyntaxKind.TypeOfExpression: - return emitTypeOfExpression(node); - case SyntaxKind.VoidExpression: - return emitVoidExpression(node); - case SyntaxKind.AwaitExpression: - return emitAwaitExpression(node); - case SyntaxKind.PrefixUnaryExpression: - return emitPrefixUnaryExpression(node); - case SyntaxKind.PostfixUnaryExpression: - return emitPostfixUnaryExpression(node); - case SyntaxKind.BinaryExpression: - return emitBinaryExpression(node); - case SyntaxKind.ConditionalExpression: - return emitConditionalExpression(node); - case SyntaxKind.TemplateExpression: - return emitTemplateExpression(node); - case SyntaxKind.YieldExpression: - return emitYieldExpression(node); - case SyntaxKind.SpreadElement: - return emitSpreadExpression(node); - case SyntaxKind.ClassExpression: - return emitClassExpression(node); - case SyntaxKind.OmittedExpression: - return; - case SyntaxKind.AsExpression: - return emitAsExpression(node); - case SyntaxKind.NonNullExpression: - return emitNonNullExpression(node); - case SyntaxKind.MetaProperty: - return emitMetaProperty(node); + switch (node.kind) { + // Pseudo-literals + case SyntaxKind.TemplateHead: + case SyntaxKind.TemplateMiddle: + case SyntaxKind.TemplateTail: + return emitLiteral(node); - // JSX - case SyntaxKind.JsxElement: - return emitJsxElement(node); - case SyntaxKind.JsxSelfClosingElement: - return emitJsxSelfClosingElement(node); - case SyntaxKind.JsxFragment: - return emitJsxFragment(node); + // Identifiers + case SyntaxKind.Identifier: + return emitIdentifier(node); - // Transformation nodes - case SyntaxKind.PartiallyEmittedExpression: - return emitPartiallyEmittedExpression(node); + // Parse tree nodes - case SyntaxKind.CommaListExpression: - return emitCommaList(node); + // 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.JSDocFunctionType: + return emitJSDocFunctionType(node as JSDocFunctionType); + 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.ConditionalType: + return emitConditionalType(node); + case SyntaxKind.InferType: + return emitInferType(node); + case SyntaxKind.ParenthesizedType: + return emitParenthesizedType(node); + case SyntaxKind.ExpressionWithTypeArguments: + return emitExpressionWithTypeArguments(node); + case SyntaxKind.ThisType: + return emitThisType(); + case SyntaxKind.TypeOperator: + return emitTypeOperator(node); + case SyntaxKind.IndexedAccessType: + return emitIndexedAccessType(node); + case SyntaxKind.MappedType: + return emitMappedType(node); + case SyntaxKind.LiteralType: + return emitLiteralType(node); + case SyntaxKind.JSDocAllType: + return write("*"); + case SyntaxKind.JSDocUnknownType: + return write("?"); + case SyntaxKind.JSDocNullableType: + return emitJSDocNullableType(node as JSDocNullableType); + case SyntaxKind.JSDocNonNullableType: + return emitJSDocNonNullableType(node as JSDocNonNullableType); + case SyntaxKind.JSDocOptionalType: + return emitJSDocOptionalType(node as JSDocOptionalType); + case SyntaxKind.JSDocVariadicType: + return emitJSDocVariadicType(node as JSDocVariadicType); + + // Binding patterns + case SyntaxKind.ObjectBindingPattern: + return emitObjectBindingPattern(node); + case SyntaxKind.ArrayBindingPattern: + return emitArrayBindingPattern(node); + case SyntaxKind.BindingElement: + return emitBindingElement(node); + + // Misc + case SyntaxKind.TemplateSpan: + return emitTemplateSpan(node); + case SyntaxKind.SemicolonClassElement: + return emitSemicolonClassElement(); + + // Statements + case SyntaxKind.Block: + return emitBlock(node); + case SyntaxKind.VariableStatement: + return emitVariableStatement(node); + case SyntaxKind.EmptyStatement: + return emitEmptyStatement(); + case SyntaxKind.ExpressionStatement: + return emitExpressionStatement(node); + case SyntaxKind.IfStatement: + return emitIfStatement(node); + case SyntaxKind.DoStatement: + return emitDoStatement(node); + case SyntaxKind.WhileStatement: + return emitWhileStatement(node); + case SyntaxKind.ForStatement: + return emitForStatement(node); + case SyntaxKind.ForInStatement: + return emitForInStatement(node); + case SyntaxKind.ForOfStatement: + return emitForOfStatement(node); + case SyntaxKind.ContinueStatement: + return emitContinueStatement(node); + case SyntaxKind.BreakStatement: + return emitBreakStatement(node); + case SyntaxKind.ReturnStatement: + return emitReturnStatement(node); + case SyntaxKind.WithStatement: + return emitWithStatement(node); + case SyntaxKind.SwitchStatement: + return emitSwitchStatement(node); + case SyntaxKind.LabeledStatement: + return emitLabeledStatement(node); + case SyntaxKind.ThrowStatement: + return emitThrowStatement(node); + case SyntaxKind.TryStatement: + return emitTryStatement(node); + case SyntaxKind.DebuggerStatement: + return emitDebuggerStatement(node); + + // Declarations + case SyntaxKind.VariableDeclaration: + return emitVariableDeclaration(node); + case SyntaxKind.VariableDeclarationList: + return emitVariableDeclarationList(node); + case SyntaxKind.FunctionDeclaration: + return emitFunctionDeclaration(node); + case SyntaxKind.ClassDeclaration: + return emitClassDeclaration(node); + case SyntaxKind.InterfaceDeclaration: + return emitInterfaceDeclaration(node); + case SyntaxKind.TypeAliasDeclaration: + return emitTypeAliasDeclaration(node); + case SyntaxKind.EnumDeclaration: + return emitEnumDeclaration(node); + case SyntaxKind.ModuleDeclaration: + return emitModuleDeclaration(node); + case SyntaxKind.ModuleBlock: + return emitModuleBlock(node); + case SyntaxKind.CaseBlock: + return emitCaseBlock(node); + case SyntaxKind.NamespaceExportDeclaration: + return emitNamespaceExportDeclaration(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: + case SyntaxKind.JsxOpeningFragment: + return emitJsxOpeningElementOrFragment(node); + case SyntaxKind.JsxClosingElement: + case SyntaxKind.JsxClosingFragment: + return emitJsxClosingElementOrFragment(node); + case SyntaxKind.JsxAttribute: + return emitJsxAttribute(node); + case SyntaxKind.JsxAttributes: + return emitJsxAttributes(node); + case SyntaxKind.JsxSpreadAttribute: + return emitJsxSpreadAttribute(node); + case SyntaxKind.JsxExpression: + return emitJsxExpression(node); + + // Clauses + case SyntaxKind.CaseClause: + return emitCaseClause(node); + case SyntaxKind.DefaultClause: + return emitDefaultClause(node); + case SyntaxKind.HeritageClause: + return emitHeritageClause(node); + case SyntaxKind.CatchClause: + return emitCatchClause(node); + + // Property assignments + case SyntaxKind.PropertyAssignment: + return emitPropertyAssignment(node); + case SyntaxKind.ShorthandPropertyAssignment: + return emitShorthandPropertyAssignment(node); + case SyntaxKind.SpreadAssignment: + return emitSpreadAssignment(node as SpreadAssignment); + + // Enum + case SyntaxKind.EnumMember: + return emitEnumMember(node); + + // JSDoc nodes (ignored) + // Transformation nodes (ignored) + } + + if (isExpression(node)) { + hint = EmitHint.Expression; + node = trySubstituteNode(EmitHint.Expression, node); + } + else if (isToken(node)) { + return writeTokenNode(node, writePunctuation); + } + } + + if (hint === EmitHint.Expression) { + switch (node.kind) { + // Literals + case SyntaxKind.NumericLiteral: + return emitNumericLiteral(node); + + case SyntaxKind.StringLiteral: + case SyntaxKind.RegularExpressionLiteral: + case SyntaxKind.NoSubstitutionTemplateLiteral: + return emitLiteral(node); + + // Identifiers + case SyntaxKind.Identifier: + return emitIdentifier(node); + + // Reserved words + case SyntaxKind.FalseKeyword: + case SyntaxKind.NullKeyword: + case SyntaxKind.SuperKeyword: + case SyntaxKind.TrueKeyword: + case SyntaxKind.ThisKeyword: + case SyntaxKind.ImportKeyword: + return writeTokenNode(node, writeKeyword); + + // Expressions + case SyntaxKind.ArrayLiteralExpression: + return emitArrayLiteralExpression(node); + case SyntaxKind.ObjectLiteralExpression: + return emitObjectLiteralExpression(node); + case SyntaxKind.PropertyAccessExpression: + return emitPropertyAccessExpression(node); + case SyntaxKind.ElementAccessExpression: + return emitElementAccessExpression(node); + case SyntaxKind.CallExpression: + return emitCallExpression(node); + case SyntaxKind.NewExpression: + return emitNewExpression(node); + case SyntaxKind.TaggedTemplateExpression: + return emitTaggedTemplateExpression(node); + case SyntaxKind.TypeAssertionExpression: + return emitTypeAssertionExpression(node); + case SyntaxKind.ParenthesizedExpression: + return emitParenthesizedExpression(node); + case SyntaxKind.FunctionExpression: + return emitFunctionExpression(node); + case SyntaxKind.ArrowFunction: + return emitArrowFunction(node); + case SyntaxKind.DeleteExpression: + return emitDeleteExpression(node); + case SyntaxKind.TypeOfExpression: + return emitTypeOfExpression(node); + case SyntaxKind.VoidExpression: + return emitVoidExpression(node); + case SyntaxKind.AwaitExpression: + return emitAwaitExpression(node); + case SyntaxKind.PrefixUnaryExpression: + return emitPrefixUnaryExpression(node); + case SyntaxKind.PostfixUnaryExpression: + return emitPostfixUnaryExpression(node); + case SyntaxKind.BinaryExpression: + return emitBinaryExpression(node); + case SyntaxKind.ConditionalExpression: + return emitConditionalExpression(node); + case SyntaxKind.TemplateExpression: + return emitTemplateExpression(node); + case SyntaxKind.YieldExpression: + return emitYieldExpression(node); + case SyntaxKind.SpreadElement: + return emitSpreadExpression(node); + case SyntaxKind.ClassExpression: + return emitClassExpression(node); + case SyntaxKind.OmittedExpression: + return; + case SyntaxKind.AsExpression: + return emitAsExpression(node); + case SyntaxKind.NonNullExpression: + return emitNonNullExpression(node); + case SyntaxKind.MetaProperty: + return emitMetaProperty(node); + + // JSX + case SyntaxKind.JsxElement: + return emitJsxElement(node); + case SyntaxKind.JsxSelfClosingElement: + return emitJsxSelfClosingElement(node); + case SyntaxKind.JsxFragment: + return emitJsxFragment(node); + + // Transformation nodes + case SyntaxKind.PartiallyEmittedExpression: + return emitPartiallyEmittedExpression(node); + + case SyntaxKind.CommaListExpression: + return emitCommaList(node); + } } } @@ -987,11 +992,11 @@ namespace ts { function emitParameter(node: ParameterDeclaration) { emitDecorators(node, node.decorators); emitModifiers(node, node.modifiers); - emitIfPresent(node.dotDotDotToken); + emit(node.dotDotDotToken); if (node.name) { emitNodeWithWriter(node.name, writeParameter); } - emitIfPresent(node.questionToken); + emit(node.questionToken); if (node.parent && node.parent.kind === SyntaxKind.JSDocFunctionType && !node.name) { emit(node.type); } @@ -1014,7 +1019,7 @@ namespace ts { emitDecorators(node, node.decorators); emitModifiers(node, node.modifiers); emitNodeWithWriter(node.name, writeProperty); - emitIfPresent(node.questionToken); + emit(node.questionToken); emitTypeAnnotation(node.type); writeSemicolon(); } @@ -1023,7 +1028,7 @@ namespace ts { emitDecorators(node, node.decorators); emitModifiers(node, node.modifiers); emit(node.name); - emitIfPresent(node.questionToken); + emit(node.questionToken); emitTypeAnnotation(node.type); emitInitializer(node.initializer); writeSemicolon(); @@ -1033,7 +1038,7 @@ namespace ts { emitDecorators(node, node.decorators); emitModifiers(node, node.modifiers); emit(node.name); - emitIfPresent(node.questionToken); + emit(node.questionToken); emitTypeParameters(node, node.typeParameters); emitParameters(node, node.parameters); emitTypeAnnotation(node.type); @@ -1043,9 +1048,9 @@ namespace ts { function emitMethodDeclaration(node: MethodDeclaration) { emitDecorators(node, node.decorators); emitModifiers(node, node.modifiers); - emitIfPresent(node.asteriskToken); + emit(node.asteriskToken); emit(node.name); - emitIfPresent(node.questionToken); + emit(node.questionToken); emitSignatureAndBody(node, emitSignatureHead); } @@ -1258,7 +1263,7 @@ namespace ts { pipelineEmitWithNotification(EmitHint.MappedTypeParameter, node.typeParameter); writePunctuation("]"); - emitIfPresent(node.questionToken); + emit(node.questionToken); writePunctuation(":"); writeSpace(); emit(node.type); @@ -1273,6 +1278,14 @@ namespace ts { writePunctuation("}"); } + function emitMappedTypeParameter(node: TypeParameterDeclaration): void { + emit(node.name); + writeSpace(); + writeKeyword("in"); + writeSpace(); + emit(node.constraint); + } + function emitLiteralType(node: LiteralTypeNode) { emitExpression(node.literal); } @@ -1294,7 +1307,7 @@ namespace ts { } function emitBindingElement(node: BindingElement) { - emitIfPresent(node.dotDotDotToken); + emit(node.dotDotDotToken); if (node.propertyName) { emit(node.propertyName); writePunctuation(":"); @@ -1815,7 +1828,7 @@ namespace ts { emitDecorators(node, node.decorators); emitModifiers(node, node.modifiers); writeKeyword("function"); - emitIfPresent(node.asteriskToken); + emit(node.asteriskToken); writeSpace(); emitIdentifierName(node.name); emitSignatureAndBody(node, emitSignatureHead); @@ -1914,12 +1927,9 @@ namespace ts { ? emitBlockFunctionBodyOnSingleLine : emitBlockFunctionBodyWorker; - if (emitBodyWithDetachedComments) { - emitBodyWithDetachedComments(body, body.statements, emitBlockFunctionBody); - } - else { - emitBlockFunctionBody(body); - } + emitLeadingDetachedCommentsOfBody(body, body.statements); + emitBlockFunctionBody(body); + emitTrailingDetachedCommentsOfBody(body, body.statements); decreaseIndent(); writeToken(SyntaxKind.CloseBraceToken, body.statements.end, writePunctuation, body); @@ -2260,7 +2270,7 @@ namespace ts { function emitJsxExpression(node: JsxExpression) { if (node.expression) { writePunctuation("{"); - emitIfPresent(node.dotDotDotToken); + emit(node.dotDotDotToken); emitExpression(node.expression); writePunctuation("}"); } @@ -2402,19 +2412,18 @@ namespace ts { function emitSourceFile(node: SourceFile) { writeLine(); - const statements = node.statements; - if (emitBodyWithDetachedComments) { - // Emit detached comment if there are no prologue directives or if the first node is synthesized. - // The synthesized node will have no leading comment so some comments may be missed. - const shouldEmitDetachedComment = statements.length === 0 || - !isPrologueDirective(statements[0]) || - nodeIsSynthesized(statements[0]); - if (shouldEmitDetachedComment) { - emitBodyWithDetachedComments(node, statements, emitSourceFileWorker); - return; - } + + // Emit detached comment if there are no prologue directives or if the first node is synthesized. + // The synthesized node will have no leading comment so some comments may be missed. + const firstStatement = firstOrUndefined(node.statements); + if (!firstStatement || !isPrologueDirective(firstStatement) || nodeIsSynthesized(firstStatement)) { + emitLeadingDetachedCommentsOfBody(node, node.statements); + emitSourceFileWorker(node); + emitTrailingDetachedCommentsOfBody(node, node.statements); + } + else { + emitSourceFileWorker(node); } - emitSourceFileWorker(node); } function emitSourceFileWorker(node: SourceFile) { diff --git a/src/compiler/sourcemap.ts b/src/compiler/sourcemap.ts index 2dabb97b08d..dc374ea09e4 100644 --- a/src/compiler/sourcemap.ts +++ b/src/compiler/sourcemap.ts @@ -34,6 +34,9 @@ namespace ts { */ emitPos(pos: number): void; + emitLeadingSourceMapOfNode(node: Node): void; + emitTrailingSourceMapOfNode(node: Node): void; + /** * Emits a node with possible leading and trailing source maps. * @@ -95,7 +98,7 @@ namespace ts { // Source map data let sourceMapData: SourceMapData; - let disabled: boolean = !(compilerOptions.sourceMap || compilerOptions.inlineSourceMap); + let disabled = !(compilerOptions.sourceMap || compilerOptions.inlineSourceMap) ? -1 : 0; return { initialize, @@ -103,6 +106,8 @@ namespace ts { getSourceMapData: () => sourceMapData, setSourceFile, emitPos, + emitLeadingSourceMapOfNode, + emitTrailingSourceMapOfNode, emitNodeWithSourceMap, emitTokenWithSourceMap, getText, @@ -320,47 +325,77 @@ namespace ts { * @param emitCallback The callback used to emit the node. */ function emitNodeWithSourceMap(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) { - if (disabled) { - return emitCallback(hint, node); + if (!node) return; + emitLeadingSourceMapOfNode(node); + emitCallback(hint, node); + emitTrailingSourceMapOfNode(node); + } + + function emitLeadingSourceMapOfNode(node: Node) { + if (!node) return; + + const emitFlags = getEmitFlags(node); + if (!disabled) { + const sourceMapRange = getSourceMapRange(node); + if (shouldEmitLeadingSourceMap(node.kind, emitFlags, sourceMapRange)) { + if (sourceMapRange.source && sourceMapRange.source !== currentSource) { + const savedCurrentSource = currentSource; + setSourceFile(sourceMapRange.source); + emitPos(skipSourceTrivia(sourceMapRange.pos)); + setSourceFile(savedCurrentSource); + } + else { + emitPos(skipSourceTrivia(sourceMapRange.pos)); + } + } } - if (node) { - const emitNode = node.emitNode; - const emitFlags = emitNode && emitNode.flags; - const range = emitNode && emitNode.sourceMapRange; - const { pos, end } = range || node; - let source = range && range.source; - const oldSource = currentSource; - if (source === oldSource) source = undefined; + pushDisabledState(emitFlags); + } - if (source) setSourceFile(source); + function emitTrailingSourceMapOfNode(node: Node) { + if (!node) return; - if (node.kind !== SyntaxKind.NotEmittedStatement - && (emitFlags & EmitFlags.NoLeadingSourceMap) === 0 - && pos >= 0) { - emitPos(skipSourceTrivia(pos)); + const emitFlags = getEmitFlags(node); + popDisabledState(emitFlags); + + if (!disabled) { + const sourceMapRange = getSourceMapRange(node); + if (shouldEmitTrailingSourceMap(node.kind, emitFlags, sourceMapRange)) { + if (sourceMapRange.source && sourceMapRange.source !== currentSource) { + const savedCurrentSource = currentSource; + setSourceFile(sourceMapRange.source); + emitPos(sourceMapRange.end); + setSourceFile(savedCurrentSource); + } + else { + emitPos(sourceMapRange.end); + } } + } + } - if (source) setSourceFile(oldSource); + function shouldEmitLeadingSourceMap(kind: SyntaxKind, emitFlags: EmitFlags, sourceMapRange: SourceMapRange) { + return kind !== SyntaxKind.NotEmittedStatement && + (emitFlags & EmitFlags.NoLeadingSourceMap) === 0 && + sourceMapRange.pos >= 0; + } - if (emitFlags & EmitFlags.NoNestedSourceMaps) { - disabled = true; - emitCallback(hint, node); - disabled = false; - } - else { - emitCallback(hint, node); - } + function shouldEmitTrailingSourceMap(kind: SyntaxKind, emitFlags: EmitFlags, sourceMapRange: SourceMapRange) { + return kind !== SyntaxKind.NotEmittedStatement && + (emitFlags & EmitFlags.NoTrailingSourceMap) === 0 && + sourceMapRange.end >= 0; + } - if (source) setSourceFile(source); + function pushDisabledState(emitFlags: EmitFlags) { + if (disabled >= 0 && emitFlags & EmitFlags.NoNestedSourceMaps) { + disabled++; + } + } - if (node.kind !== SyntaxKind.NotEmittedStatement - && (emitFlags & EmitFlags.NoTrailingSourceMap) === 0 - && end >= 0) { - emitPos(end); - } - - if (source) setSourceFile(oldSource); + function popDisabledState(emitFlags: EmitFlags) { + if (disabled > 0 && emitFlags & EmitFlags.NoNestedSourceMaps) { + disabled--; } } @@ -401,7 +436,7 @@ namespace ts { * * @param sourceFile The source file. */ - function setSourceFile(sourceFile: SourceMapSource) { + function setSourceFile(sourceFile: SourceMapSource | undefined) { if (disabled) { return; } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 3a3f41ef138..b8fcbaa3151 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -4942,7 +4942,8 @@ namespace ts { * ``` */ substituteNode?(hint: EmitHint, node: Node): Node; - /*@internal*/ onEmitSourceMapOfNode?: (hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) => void; + /*@internal*/ onEmitLeadingSourceMapOfNode?: (node: Node) => void; + /*@internal*/ onEmitTrailingSourceMapOfNode?: (node: Node) => void; /*@internal*/ onEmitSourceMapOfToken?: (node: Node, token: SyntaxKind, writer: (s: string) => void, pos: number, emitCallback: (token: SyntaxKind, writer: (s: string) => void, pos: number) => number) => number; /*@internal*/ onEmitSourceMapOfPosition?: (pos: number) => void; /*@internal*/ onEmitHelpers?: (node: Node, writeLines: (text: string) => void) => void;