2025-10-15 10:12:44 +03:00

5954 lines
204 KiB
Go

// Package printer exports a Printer for pretty-printing TS ASTs and writer interfaces and implementations for using them
// Intended ultimate usage:
//
// func nodeToInlineStr(node *ast.Node) {
// // Reuse singleton single-line writer (TODO: thread safety?)
// p = printer.NewPrinter(printer.PrinterOptions{ RemoveComments: true }, printer.PrintHandlers{})
// p.Write(node, nil /*sourceFile*/, printer.SingleLineTextWriter)
// return printer.SingleLineTextWriter.getText()
// }
//
// // or
//
// func nodeToStr(node *ast.Node, options CompilerOptions) {
// // Use own writer
// p := printer.NewPrinter(printer.PrinterOptions{ NewLine: options.NewLine}, printer.PrintHandlers{})
// return p.Emit(node, nil /*sourceFile*/)
// }
package printer
import (
"fmt"
"slices"
"strings"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/core"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/scanner"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/sourcemap"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/stringutil"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath"
)
type PrinterOptions struct {
RemoveComments bool
NewLine core.NewLineKind
// OmitTrailingSemicolon bool
NoEmitHelpers bool
// Module core.ModuleKind
// ModuleResolution core.ModuleResolutionKind
// Target core.ScriptTarget
SourceMap bool
InlineSourceMap bool
InlineSources bool
OmitBraceSourceMapPositions bool
// ExtendedDiagnostics bool
OnlyPrintJSDocStyle bool
NeverAsciiEscape bool
// StripInternal bool
PreserveSourceNewlines bool
TerminateUnterminatedLiterals bool // !!!
}
type PrintHandlers struct {
// A hook used by the Printer when generating unique names to avoid collisions with
// globally defined names that exist outside of the current source file.
HasGlobalName func(name string) bool
// !!!
////// A hook used by the Printer to provide notifications prior to emitting a node. A
////// compatible implementation **must** invoke `emitCallback` with the provided `hint` and
////// `node` values.
////// @param hint A hint indicating the intended purpose of the node.
////// @param node The node to emit.
////// @param emitCallback A callback that, when invoked, will emit the node.
////// @example
////// ```ts
////// var printer = createPrinter(printerOptions, {
////// onEmitNode(hint, node, emitCallback) {
////// // set up or track state prior to emitting the node...
////// emitCallback(hint, node);
////// // restore state after emitting the node...
////// }
////// });
////// ```
////OnEmitNode func(hint EmitHint, node *ast.Node, emitCallback func(hint EmitHint, node *ast.Node))
// !!!
////// A hook used to check if an emit notification is required for a node.
////// @param node The node to emit.
////IsEmitNotificationEnabled func(node *ast.Node) bool
// !!!
////// A hook used by the Printer to perform just-in-time substitution of a node. This is
////// primarily used by node transformations that need to substitute one node for another,
////// such as replacing `myExportedVar` with `exports.myExportedVar`.
////// @param hint A hint indicating the intended purpose of the node.
////// @param node The node to emit.
////// @example
////// ```ts
////// var printer = createPrinter(printerOptions, {
////// substituteNode(hint, node) {
////// // perform substitution if necessary...
////// return node;
////// }
////// });
////// ```
////SubstituteNode func(hint EmitHint, node *ast.Node) *ast.Node
// !!!
////OnEmitSourceMapOfNode func(hint EmitHint, node *ast.Node, emitCallback func(hint EmitHint, node *ast.Node))
////OnEmitSourceMapOfToken func(nodeOpt *ast.Node | undefined, token: ast.Kind, writeKind WriteKind, pos int, emitCallback func(token ast.Kind, writeKind WriteKind, pos int) int) int
////OnEmitSourceMapOfPosition func(pos int)
OnBeforeEmitNode func(nodeOpt *ast.Node)
OnAfterEmitNode func(nodeOpt *ast.Node)
OnBeforeEmitNodeList func(nodesOpt *ast.NodeList)
OnAfterEmitNodeList func(nodesOpt *ast.NodeList)
OnBeforeEmitToken func(nodeOpt *ast.TokenNode)
OnAfterEmitToken func(nodeOpt *ast.TokenNode)
}
type Printer struct {
PrintHandlers
Options PrinterOptions
emitContext *EmitContext
currentSourceFile *ast.SourceFile
uniqueHelperNames map[string]*ast.IdentifierNode
externalHelpersModuleName *ast.IdentifierNode
nextListElementPos int
writer EmitTextWriter
ownWriter EmitTextWriter
writeKind WriteKind
sourceMapsDisabled bool
sourceMapGenerator *sourcemap.Generator
sourceMapSource sourcemap.Source
sourceMapSourceIndex sourcemap.SourceIndex
sourceMapSourceIsJson bool
mostRecentSourceMapSource sourcemap.Source
mostRecentSourceMapSourceIndex sourcemap.SourceIndex
containerPos int
containerEnd int
declarationListContainerEnd int
detachedCommentsInfo core.Stack[detachedCommentsInfo]
commentsDisabled bool
inExtends bool // whether we are emitting the `extends` clause of a ConditionalType or InferType
nameGenerator NameGenerator
makeFileLevelOptimisticUniqueName func(string) string
commentStatePool core.Pool[commentState]
sourceMapStatePool core.Pool[sourceMapState]
}
type detachedCommentsInfo struct {
nodePos int
detachedCommentEndPos int
}
type commentState struct {
emitFlags EmitFlags // holds the emit flags for the current node
commentRange core.TextRange // holds the comment range calculated for the current node
containerPos int // captures the value of containerPos prior to entering an node
containerEnd int // captures the value of containerEnd prior to entering an node
declarationListContainerEnd int // captures the value of declarationListContainerEnd prior to entering an node
}
type sourceMapState struct {
emitFlags EmitFlags // holds the emit flags for the current node
sourceMapRange core.TextRange // holds the source map range calculated for the current node
hasTokenSourceMapRange bool // captures whether the source map range was set for the current node
}
type printerState struct {
commentState *commentState
sourceMapState *sourceMapState
}
func NewPrinter(options PrinterOptions, handlers PrintHandlers, emitContext *EmitContext) *Printer {
printer := &Printer{
PrintHandlers: handlers,
Options: options,
emitContext: emitContext,
}
// wire up name generator
if printer.emitContext == nil {
printer.emitContext = NewEmitContext()
}
printer.nameGenerator.Context = printer.emitContext
printer.nameGenerator.GetTextOfNode = func(node *ast.Node) string { return printer.getTextOfNode(node, false) }
printer.nameGenerator.IsFileLevelUniqueNameInCurrentFile = printer.isFileLevelUniqueNameInCurrentFile
printer.containerPos = -1
printer.containerEnd = -1
printer.declarationListContainerEnd = -1
printer.commentsDisabled = options.RemoveComments
return printer
}
func (p *Printer) getLiteralTextOfNode(node *ast.LiteralLikeNode, sourceFile *ast.SourceFile, flags getLiteralTextFlags) string {
if ast.IsStringLiteral(node) {
if textSourceNode, ok := p.emitContext.textSource[node]; ok && textSourceNode != nil {
var text string
switch textSourceNode.Kind {
default:
return p.getLiteralTextOfNode(textSourceNode, ast.GetSourceFileOfNode(textSourceNode), flags)
case ast.KindNumericLiteral:
text = textSourceNode.Text()
case ast.KindIdentifier, ast.KindPrivateIdentifier, ast.KindJsxNamespacedName:
text = p.getTextOfNode(textSourceNode, false)
}
switch {
case flags&getLiteralTextFlagsJsxAttributeEscape != 0:
return "\"" + escapeJsxAttributeString(text, QuoteCharDoubleQuote) + "\""
case flags&getLiteralTextFlagsNeverAsciiEscape != 0 || p.emitContext.EmitFlags(node)&EFNoAsciiEscaping != 0:
return "\"" + EscapeString(text, QuoteCharDoubleQuote) + "\""
default:
return "\"" + escapeNonAsciiString(text, QuoteCharDoubleQuote) + "\""
}
}
}
// !!! Printer option to control whether to terminate unterminated literals
// !!! If necessary, printer option to control whether to preserve numeric separators
if p.emitContext.EmitFlags(node)&EFNoAsciiEscaping != 0 {
flags |= getLiteralTextFlagsNeverAsciiEscape
}
return getLiteralText(node, core.Coalesce(sourceFile, p.currentSourceFile), flags)
}
// `node` must be one of Identifier | PrivateIdentifier | LiteralExpression | JsxNamespacedName
func (p *Printer) getTextOfNode(node *ast.Node, includeTrivia bool) string {
if ast.IsMemberName(node) && p.emitContext.autoGenerate[node] != nil {
return p.nameGenerator.GenerateName(node)
}
if ast.IsStringLiteral(node) {
if textSourceNode := p.emitContext.textSource[node]; textSourceNode != nil {
return p.getTextOfNode(textSourceNode, includeTrivia)
}
}
switch node.Kind {
case ast.KindIdentifier,
ast.KindPrivateIdentifier,
ast.KindJsxNamespacedName:
// !!! If `node` is not a parse tree node, verify its original node comes from the same source file
if p.currentSourceFile == nil || node.Parent == nil || ast.NodeIsSynthesized(node) {
return node.Text()
}
case ast.KindStringLiteral,
ast.KindNumericLiteral,
ast.KindBigIntLiteral,
ast.KindNoSubstitutionTemplateLiteral,
ast.KindTemplateHead,
ast.KindTemplateMiddle,
ast.KindTemplateTail:
return p.getLiteralTextOfNode(node, nil /*sourceFile*/, getLiteralTextFlagsNone)
default:
panic(fmt.Sprintf("unexpected node: %v", node.Kind))
}
return scanner.GetSourceTextOfNodeFromSourceFile(p.currentSourceFile, node, includeTrivia)
}
//
// Low-level writing
//
type WriteKind int
const (
WriteKindNone WriteKind = iota
WriteKindKeyword
WriteKindOperator
WriteKindPunctuation
WriteKindStringLiteral
WriteKindParameter
WriteKindProperty
WriteKindComment
WriteKindLiteral
)
func (p *Printer) writeAs(text string, writeKind WriteKind) {
switch writeKind {
case WriteKindNone:
p.writer.Write(text)
case WriteKindParameter:
p.writeParameter(text)
case WriteKindKeyword:
p.writeKeyword(text)
case WriteKindOperator:
p.writeOperator(text)
case WriteKindProperty:
p.writeProperty(text)
case WriteKindPunctuation:
p.writePunctuation(text)
case WriteKindStringLiteral:
p.writer.WriteStringLiteral(text)
case WriteKindComment:
p.writeComment(text)
case WriteKindLiteral:
p.writeLiteral(text)
default:
panic(fmt.Sprintf("unexpected printer.WriteKind: %v", writeKind))
}
}
func (p *Printer) write(text string) {
p.writeAs(text, p.writeKind)
}
func (p *Printer) setWriteKind(kind WriteKind) WriteKind {
previous := p.writeKind
p.writeKind = kind
return previous
}
func (p *Printer) writeSymbol(text string, optSymbol *ast.Symbol) {
if optSymbol == nil {
p.write(text)
} else {
p.writer.WriteSymbol(text, optSymbol)
}
}
func (p *Printer) writeLiteral(text string) {
p.writer.WriteLiteral(text)
}
func (p *Printer) writePunctuation(text string) {
p.writer.WritePunctuation(text)
}
func (p *Printer) writeOperator(text string) {
p.writer.WriteOperator(text)
}
func (p *Printer) writeKeyword(text string) {
p.writer.WriteKeyword(text)
}
func (p *Printer) writeProperty(text string) {
p.writer.WriteProperty(text)
}
func (p *Printer) writeParameter(text string) {
p.writer.WriteParameter(text)
}
func (p *Printer) writeComment(text string) {
p.writer.WriteComment(text)
}
func (p *Printer) writeSpace() {
p.writer.WriteSpace(" ")
}
func (p *Printer) writeLine() {
p.writer.WriteLine()
}
func (p *Printer) writeLineRepeat(count int) {
for range count {
p.writeLine()
}
}
func (p *Printer) writeLines(text string) {
lines := stringutil.SplitLines(text)
indentation := stringutil.GuessIndentation(lines)
for _, line := range lines {
if indentation > 0 {
line = line[indentation:]
}
if len(line) > 0 {
p.writeLine()
p.write(line)
}
}
}
func (p *Printer) writeTrailingSemicolon() {
p.writer.WriteTrailingSemicolon(";")
}
func (p *Printer) increaseIndent() {
p.writer.IncreaseIndent()
}
func (p *Printer) decreaseIndent() {
p.writer.DecreaseIndent()
}
func (p *Printer) increaseIndentIf(indentRequested bool) {
if indentRequested {
p.increaseIndent()
}
}
func (p *Printer) decreaseIndentIf(indentRequested bool) {
if indentRequested {
p.decreaseIndent()
}
}
func (p *Printer) writeLineOrSpace(parentNode *ast.Node, prevChildNode *ast.Node, nextChildNode *ast.Node) {
if p.shouldEmitOnSingleLine(parentNode) {
p.writeSpace()
} else if p.Options.PreserveSourceNewlines {
lines := p.getLinesBetweenNodes(parentNode, prevChildNode, nextChildNode)
if lines > 0 {
p.writeLineRepeat(lines)
} else {
p.writeSpace()
}
} else {
p.writeLine()
}
}
func (p *Printer) writeLinesAndIndent(lineCount int, writeSpaceIfNotIndenting bool) {
if lineCount > 0 {
p.increaseIndent()
p.writeLineRepeat(lineCount)
} else if writeSpaceIfNotIndenting {
p.writeSpace()
}
}
func (p *Printer) writeLineSeparatorsAndIndentBefore(node *ast.Node, parent *ast.Node) bool {
if p.Options.PreserveSourceNewlines {
leadingNewlines := p.getLeadingLineTerminatorCount(parent, node, LFNone)
if leadingNewlines > 0 {
p.writeLinesAndIndent(leadingNewlines /*writeSpaceIfNotIndenting*/, false)
return true
}
}
return false
}
func (p *Printer) writeLineSeparatorsAfter(node *ast.Node, parent *ast.Node) {
if p.Options.PreserveSourceNewlines {
trailingNewlines := p.getClosingLineTerminatorCount(parent, node, LFNone, core.NewTextRange(-1, -1) /*childrenTextRange*/)
if trailingNewlines > 0 {
p.writeLineRepeat(trailingNewlines)
}
}
}
func (p *Printer) getLinesBetweenNodes(parent *ast.Node, node1 *ast.Node, node2 *ast.Node) int {
if p.shouldElideIndentation(parent) {
return 0
}
parent = skipSynthesizedParentheses(parent)
node1 = skipSynthesizedParentheses(node1)
node2 = skipSynthesizedParentheses(node2)
// Always use a newline for synthesized code if the synthesizer desires it.
if p.shouldEmitOnNewLine(node2, LFNone) {
return 1
}
if p.currentSourceFile != nil && !ast.NodeIsSynthesized(parent) && !ast.NodeIsSynthesized(node1) && !ast.NodeIsSynthesized(node2) {
if p.Options.PreserveSourceNewlines {
return p.getEffectiveLines(
func(includeComments bool) int {
return getLinesBetweenRangeEndAndRangeStart(
node1.Loc,
node2.Loc,
p.currentSourceFile,
includeComments,
)
},
)
}
return core.IfElse(rangeEndIsOnSameLineAsRangeStart(node1.Loc, node2.Loc, p.currentSourceFile), 0, 1)
}
return 0
}
func (p *Printer) getEffectiveLines(getLineDifference func(includeComments bool) int) int {
// If 'preserveSourceNewlines' is disabled, we should never call this function
// because it could be more expensive than alternative approximations.
if !p.Options.PreserveSourceNewlines {
panic("Should not be called when preserveSourceNewlines is false")
}
// We start by measuring the line difference from a position to its adjacent comments,
// so that this is counted as a one-line difference, not two:
//
// node1;
// // NODE2 COMMENT
// node2;
lines := getLineDifference( /*includeComments*/ true)
if lines == 0 {
// However, if the line difference considering comments was 0, we might have this:
//
// node1; // NODE2 COMMENT
// node2;
//
// in which case we should be ignoring node2's comment, so this too is counted as
// a one-line difference, not zero.
return getLineDifference( /*includeComments*/ false)
}
return lines
}
func (p *Printer) getLeadingLineTerminatorCount(parentNode *ast.Node, firstChild *ast.Node, format ListFormat) int {
if format&LFPreserveLines != 0 || p.Options.PreserveSourceNewlines {
if format&LFPreferNewLine != 0 {
return 1
}
if firstChild == nil {
return core.IfElse(parentNode == nil || p.currentSourceFile != nil && rangeIsOnSingleLine(parentNode.Loc, p.currentSourceFile), 0, 1)
}
if p.nextListElementPos > 0 && firstChild.Pos() == p.nextListElementPos {
// If this child starts at the beginning of a list item in a parent list, its leading
// line terminators have already been written as the separating line terminators of the
// parent list. Example:
//
// class Foo {
// constructor() {}
// public foo() {}
// }
//
// The outer list is the list of class members, with one line terminator between the
// constructor and the method. The constructor is written, the separating line terminator
// is written, and then we start emitting the method. Its modifiers ([public]) constitute an inner
// list, so we look for its leading line terminators. If we didn't know that we had already
// written a newline as part of the parent list, it would appear that we need to write a
// leading newline to start the modifiers.
return 0
}
if firstChild.Kind == ast.KindJsxText {
// JsxText will be written with its leading whitespace, so don't add more manually.
return 0
}
if p.currentSourceFile != nil && parentNode != nil &&
!ast.PositionIsSynthesized(parentNode.Pos()) &&
!ast.NodeIsSynthesized(firstChild) &&
(firstChild.Parent == nil /*|| getOriginalNode(firstChild.Parent) == getOriginalNode(parentNode)*/) {
if p.Options.PreserveSourceNewlines {
return p.getEffectiveLines(
func(includeComments bool) int {
return getLinesBetweenPositionAndPrecedingNonWhitespaceCharacter(
firstChild.Pos(),
parentNode.Pos(),
p.currentSourceFile,
includeComments,
)
},
)
}
return core.IfElse(rangeStartPositionsAreOnSameLine(parentNode.Loc, firstChild.Loc, p.currentSourceFile), 0, 1)
}
if p.shouldEmitOnNewLine(firstChild, format) {
return 1
}
}
return core.IfElse(format&LFMultiLine != 0, 1, 0)
}
func (p *Printer) getSeparatingLineTerminatorCount(previousNode *ast.Node, nextNode *ast.Node, format ListFormat) int {
if format&LFPreserveLines != 0 || p.Options.PreserveSourceNewlines {
if previousNode == nil || nextNode == nil {
return 0
}
if nextNode.Kind == ast.KindJsxText {
// JsxText will be written with its leading whitespace, so don't add more manually.
return 0
} else if p.currentSourceFile != nil && !ast.NodeIsSynthesized(previousNode) && !ast.NodeIsSynthesized(nextNode) {
if p.Options.PreserveSourceNewlines && siblingNodePositionsAreComparable(previousNode, nextNode) {
return p.getEffectiveLines(
func(includeComments bool) int {
return getLinesBetweenRangeEndAndRangeStart(
previousNode.Loc,
nextNode.Loc,
p.currentSourceFile,
includeComments,
)
},
)
} else if !p.Options.PreserveSourceNewlines && originalNodesHaveSameParent(previousNode, nextNode) {
// If `preserveSourceNewlines` is `false` we do not intend to preserve the effective lines between the
// previous and next node. Instead we naively check whether nodes are on separate lines within the
// same node parent. If so, we intend to preserve a single line terminator. This is less precise and
// expensive than checking with `preserveSourceNewlines` as above, but the goal is not to preserve the
// effective source lines between two sibling nodes.
return core.IfElse(rangeEndIsOnSameLineAsRangeStart(previousNode.Loc, nextNode.Loc, p.currentSourceFile), 0, 1)
}
// If the two nodes are not comparable, add a line terminator based on the format that can indicate
// whether new lines are preferred or not.
return core.IfElse(format&LFPreferNewLine != 0, 1, 0)
} else if p.shouldEmitOnNewLine(previousNode, format) || p.shouldEmitOnNewLine(nextNode, format) {
return 1
}
} else if p.shouldEmitOnNewLine(nextNode, LFNone) {
return 1
}
return core.IfElse(format&LFMultiLine != 0, 1, 0)
}
func (p *Printer) getClosingLineTerminatorCount(parentNode *ast.Node, lastChild *ast.Node, format ListFormat, childrenTextRange core.TextRange) int {
if format&LFPreserveLines != 0 || p.Options.PreserveSourceNewlines {
if format&LFPreferNewLine != 0 {
return 1
}
if lastChild == nil {
return core.IfElse(parentNode == nil || p.currentSourceFile != nil && rangeIsOnSingleLine(parentNode.Loc, p.currentSourceFile), 0, 1)
}
if p.currentSourceFile != nil && parentNode != nil && !ast.PositionIsSynthesized(parentNode.Pos()) && !ast.NodeIsSynthesized(lastChild) && (lastChild.Parent == nil || lastChild.Parent == parentNode) {
if p.Options.PreserveSourceNewlines {
end := greatestEnd(lastChild.End(), childrenTextRange)
return p.getEffectiveLines(
func(includeComments bool) int {
return getLinesBetweenPositionAndNextNonWhitespaceCharacter(
end,
parentNode.End(),
p.currentSourceFile,
includeComments,
)
},
)
}
return core.IfElse(rangeEndPositionsAreOnSameLine(parentNode.Loc, lastChild.Loc, p.currentSourceFile), 0, 1)
}
if p.shouldEmitOnNewLine(lastChild, format) {
return 1
}
}
if format&LFMultiLine != 0 && format&LFNoTrailingNewLine == 0 {
return 1
}
return 0
}
func (p *Printer) writeCommentRange(comment ast.CommentRange) {
if p.currentSourceFile == nil {
return
}
text := p.currentSourceFile.Text()
lineMap := p.currentSourceFile.ECMALineMap()
p.writeCommentRangeWorker(text, lineMap, comment.Kind, comment.TextRange)
}
func (p *Printer) writeCommentRangeWorker(text string, lineMap []core.TextPos, kind ast.Kind, loc core.TextRange) {
if kind == ast.KindMultiLineCommentTrivia {
indentSize := len(getIndentString(1))
firstLine := scanner.ComputeLineOfPosition(lineMap, loc.Pos())
lineCount := len(lineMap)
firstCommentLineIndent := -1
pos := loc.Pos()
currentLine := firstLine
for ; pos < loc.End(); currentLine++ {
var nextLineStart int
if currentLine+1 == lineCount {
nextLineStart = len(text) + 1
} else {
nextLineStart = int(lineMap[currentLine+1])
}
if pos != loc.Pos() {
// If we are not emitting first line, we need to write the spaces to adjust the alignment
if firstCommentLineIndent == -1 {
firstCommentLineIndent = calculateIndent(text, int(lineMap[firstLine]), loc.Pos())
}
// These are number of spaces writer is going to write at current indent
currentWriterIndentSpacing := p.writer.GetIndent() * indentSize
// Number of spaces we want to be writing
// eg: Assume writer indent
// module m {
// /* starts at character 9 this is line 1
// * starts at character pos 4 line --1 = 8 - 8 + 3
// More left indented comment */ --2 = 8 - 8 + 2
// class c { }
// }
// module m {
// /* this is line 1 -- Assume current writer indent 8
// * line --3 = 8 - 4 + 5
// More right indented comment */ --4 = 8 - 4 + 11
// class c { }
// }
spacesToEmit := currentWriterIndentSpacing - firstCommentLineIndent + calculateIndent(text, pos, nextLineStart)
if spacesToEmit > 0 {
numberOfSingleSpacesToEmit := spacesToEmit % indentSize
indentSizeSpaceString := getIndentString((spacesToEmit - numberOfSingleSpacesToEmit) / indentSize)
// Write indent size string ( in eg 1: = "", 2: "" , 3: string with 8 spaces 4: string with 12 spaces
p.writer.RawWrite(indentSizeSpaceString)
// Emit the single spaces (in eg: 1: 3 spaces, 2: 2 spaces, 3: 1 space, 4: 3 spaces)
for numberOfSingleSpacesToEmit > 0 {
p.writer.RawWrite(" ")
numberOfSingleSpacesToEmit--
}
} else {
// No spaces to emit write empty string
p.writer.RawWrite("")
}
}
// Write the comment line text
end := min(loc.End(), nextLineStart-1)
currentLineText := strings.TrimSpace(text[pos:end])
if len(currentLineText) > 0 {
p.writeComment(currentLineText)
if end != loc.End() {
p.writeLine()
}
} else {
// Empty string - make sure we write empty line
p.writer.WriteLineForce(true)
}
pos = nextLineStart
}
} else {
// Single line comment of style //....
p.writeComment(text[loc.Pos():loc.End()])
}
}
//
// Custom emit behavior stubs (i.e., from `EmitNode`, `EmitFlags`, etc.)
//
func (p *Printer) shouldEmitComments(node *ast.Node) bool {
return !p.commentsDisabled &&
p.currentSourceFile != nil &&
!ast.IsSourceFile(node)
}
func (p *Printer) shouldWriteComment(comment ast.CommentRange) bool {
return !p.Options.OnlyPrintJSDocStyle ||
p.currentSourceFile != nil && isJSDocLikeText(p.currentSourceFile.Text(), comment) ||
p.currentSourceFile != nil && IsPinnedComment(p.currentSourceFile.Text(), comment)
}
func (p *Printer) shouldEmitIndented(node *ast.Node) bool {
return p.emitContext.EmitFlags(node)&EFIndented != 0
}
func (p *Printer) shouldElideIndentation(node *ast.Node) bool {
return p.emitContext.EmitFlags(node)&EFNoIndentation != 0
}
func (p *Printer) shouldEmitOnSingleLine(node *ast.Node) bool {
return p.emitContext.EmitFlags(node)&EFSingleLine != 0
}
func (p *Printer) shouldEmitOnMultipleLines(node *ast.Node) bool {
return p.emitContext.EmitFlags(node)&EFMultiLine != 0
}
func (p *Printer) shouldEmitBlockFunctionBodyOnSingleLine(body *ast.Block) bool {
// We must emit a function body as a single-line body in the following case:
// * The body has NodeEmitFlags.SingleLine specified.
// We must emit a function body as a multi-line body in the following cases:
// * The body is explicitly marked as multi-line.
// * A non-synthesized body's start and end position are on different lines.
// * Any statement in the body starts on a new line.
if p.shouldEmitOnSingleLine(body.AsNode()) {
return true
}
if body.Multiline {
return false
}
if !ast.NodeIsSynthesized(body.AsNode()) && p.currentSourceFile != nil && !rangeIsOnSingleLine(body.Loc, p.currentSourceFile) {
return false
}
if p.getLeadingLineTerminatorCount(body.AsNode(), core.FirstOrNil(body.Statements.Nodes), LFPreserveLines) > 0 ||
p.getClosingLineTerminatorCount(body.AsNode(), core.LastOrNil(body.Statements.Nodes), LFPreserveLines, body.Statements.Loc) > 0 {
return false
}
var previousStatement *ast.Statement
for _, statement := range body.Statements.Nodes {
if p.getSeparatingLineTerminatorCount(previousStatement, statement, LFPreserveLines) > 0 {
return false
}
previousStatement = statement
}
return true
}
func (p *Printer) shouldEmitOnNewLine(node *ast.Node, format ListFormat) bool {
// !!! TODO: enable multiline emit
// if p.emitContext.EmitFlags(node)&EFStartOnNewLine != 0 {
// return true
// }
return format&LFPreferNewLine != 0
}
func (p *Printer) shouldEmitSourceMaps(node *ast.Node) bool {
return !p.sourceMapsDisabled &&
p.sourceMapSource != nil &&
!ast.IsSourceFile(node) &&
!ast.IsInJsonFile(node)
}
func (p *Printer) shouldEmitTokenSourceMaps(token ast.Kind, pos int, contextNode *ast.Node, flags tokenEmitFlags) bool {
// We don't emit source positions for most tokens as it tends to be quite noisy, however
// we need to emit source positions for open and close braces so that tools like istanbul
// can map branches for code coverage. However, we still omit brace source positions when
// the output is a declaration file.
return flags&tefNoSourceMaps == 0 &&
p.shouldEmitSourceMaps(contextNode) &&
!p.Options.OmitBraceSourceMapPositions && (token == ast.KindOpenBraceToken || token == ast.KindCloseBraceToken)
}
func (p *Printer) shouldEmitLeadingComments(node *ast.Node) bool {
return p.emitContext.EmitFlags(node)&EFNoLeadingComments == 0
}
func (p *Printer) shouldEmitTrailingComments(node *ast.Node) bool {
return p.emitContext.EmitFlags(node)&EFNoTrailingComments == 0
}
func (p *Printer) shouldEmitNestedComments(node *ast.Node) bool {
return p.emitContext.EmitFlags(node)&EFNoNestedComments == 0
}
func (p *Printer) shouldEmitDetachedComments(node *ast.Node) bool {
if !ast.IsSourceFile(node) {
return true
}
file := node.AsSourceFile()
// 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.
return len(file.Statements.Nodes) == 0 ||
!ast.IsPrologueDirective(file.Statements.Nodes[0]) ||
ast.NodeIsSynthesized(file.Statements.Nodes[0])
}
func (p *Printer) hasCommentsAtPosition(pos int) bool {
// !!!
return false
}
func (p *Printer) shouldEmitIndirectCall(node *ast.Node) bool {
return p.emitContext.EmitFlags(node)&EFIndirectCall != 0
}
func (p *Printer) shouldAllowTrailingComma(node *ast.Node, list *ast.NodeList) bool {
if p.currentSourceFile == nil || p.currentSourceFile.ScriptKind == core.ScriptKindJSON {
return false
}
switch node.Kind {
case ast.KindObjectLiteralExpression:
return true
case ast.KindArrayLiteralExpression,
ast.KindArrowFunction,
ast.KindConstructor,
ast.KindGetAccessor,
ast.KindSetAccessor,
ast.KindTypeAliasDeclaration,
ast.KindJSTypeAliasDeclaration,
ast.KindFunctionType,
ast.KindConstructorType,
ast.KindCallSignature,
ast.KindConstructSignature,
ast.KindTaggedTemplateExpression,
ast.KindObjectBindingPattern,
ast.KindArrayBindingPattern,
ast.KindNamedImports,
ast.KindNamedExports,
ast.KindImportAttributes:
return true
case ast.KindClassExpression,
ast.KindClassDeclaration:
return list == node.ClassLikeData().TypeParameters
case ast.KindInterfaceDeclaration:
return list == node.AsInterfaceDeclaration().TypeParameters
case ast.KindFunctionDeclaration,
ast.KindFunctionExpression,
ast.KindMethodDeclaration:
return true
case ast.KindCallExpression:
return true
case ast.KindNewExpression:
return true
}
return false
}
//
// Tokens/Keywords
//
func (p *Printer) writeTokenText(token ast.Kind, writeKind WriteKind, pos int) int {
// !!! emit leading and trailing comments
// !!! emit leading and trailing source maps
tokenString := scanner.TokenToString(token)
p.writeAs(tokenString, writeKind)
if ast.PositionIsSynthesized(pos) {
return pos
} else {
return pos + len(tokenString)
}
}
func (p *Printer) emitToken(token ast.Kind, pos int, writeKind WriteKind, contextNode *ast.Node) int {
return p.emitTokenEx(token, pos, writeKind, contextNode, tefNone)
}
func (p *Printer) emitTokenEx(token ast.Kind, pos int, writeKind WriteKind, contextNode *ast.Node, flags tokenEmitFlags) int {
state, pos := p.enterToken(token, pos, contextNode, flags)
pos = p.writeTokenText(token, writeKind, pos)
p.exitToken(token, pos, contextNode, state)
return pos
}
func (p *Printer) emitKeywordNode(node *ast.TokenNode) {
p.emitKeywordNodeEx(node, tefNone)
}
func (p *Printer) emitKeywordNodeEx(node *ast.TokenNode, flags tokenEmitFlags) {
if node == nil {
return
}
state := p.enterTokenNode(node, flags)
p.writeTokenText(node.Kind, WriteKindKeyword, node.Pos())
p.exitTokenNode(node, state)
}
func (p *Printer) emitPunctuationNode(node *ast.TokenNode) {
p.emitPunctuationNodeEx(node, tefNone)
}
func (p *Printer) emitPunctuationNodeEx(node *ast.TokenNode, flags tokenEmitFlags) {
if node == nil {
return
}
state := p.enterTokenNode(node, flags)
p.writeTokenText(node.Kind, WriteKindPunctuation, node.Pos())
p.exitTokenNode(node, state)
}
func (p *Printer) emitTokenNode(node *ast.TokenNode) {
p.emitTokenNodeEx(node, tefNone)
}
func (p *Printer) emitTokenNodeEx(node *ast.TokenNode, flags tokenEmitFlags) {
if node == nil {
return
}
switch {
case ast.IsKeywordKind(node.Kind):
p.emitKeywordNodeEx(node, flags)
case ast.IsPunctuationKind(node.Kind):
p.emitPunctuationNodeEx(node, flags)
default:
panic(fmt.Sprintf("unexpected TokenNode: %v", node.Kind))
}
}
//
// Literals
//
// Emits literals of the following kinds
//
// SyntaxKindNumericLiteral
// SyntaxKindBigIntLiteral
// SyntaxKindStringLiteral
// SyntaxKindNoSubstitutionTemplateLiteral
// SyntaxKindRegularExpressionLiteral
// SyntaxKindTemplateHead
// SyntaxKindTemplateMiddle
// SyntaxKindTemplateTail
func (p *Printer) emitLiteral(node *ast.LiteralLikeNode, flags getLiteralTextFlags) {
// Add NeverAsciiEscape flag if the printer option is set
if p.Options.NeverAsciiEscape {
flags |= getLiteralTextFlagsNeverAsciiEscape
}
text := p.getLiteralTextOfNode(node, nil /*sourceFile*/, flags)
// !!! Printer option to control source map emit, which causes us to use a different write method on the
// emit text writer:
////if (
//// (printerOptions.sourceMap || printerOptions.inlineSourceMap)
//// && (node.kind === SyntaxKindStringLiteral || isTemplateLiteralKind(node.kind))
////) {
//// writeLiteral(text);
////} else {
// Quick info expects all literals to be called with writeStringLiteral, as there's no specific type for
// numberLiterals
p.writer.WriteStringLiteral(text)
// }
}
func (p *Printer) emitNumericLiteral(node *ast.NumericLiteral) {
state := p.enterNode(node.AsNode())
p.emitLiteral(node.AsNode(), getLiteralTextFlagsAllowNumericSeparator)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitBigIntLiteral(node *ast.BigIntLiteral) {
state := p.enterNode(node.AsNode())
p.emitLiteral(node.AsNode(), getLiteralTextFlagsNone) // TODO: Preserve numeric literal separators after Strada migration
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitStringLiteral(node *ast.StringLiteral) {
state := p.enterNode(node.AsNode())
p.emitLiteral(node.AsNode(), getLiteralTextFlagsNone)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitNoSubstitutionTemplateLiteral(node *ast.NoSubstitutionTemplateLiteral) {
state := p.enterNode(node.AsNode())
p.emitLiteral(node.AsNode(), getLiteralTextFlagsNone)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitRegularExpressionLiteral(node *ast.RegularExpressionLiteral) {
state := p.enterNode(node.AsNode())
p.emitLiteral(node.AsNode(), getLiteralTextFlagsNone)
p.exitNode(node.AsNode(), state)
}
//
// Pseudo-literals
//
func (p *Printer) emitTemplateHead(node *ast.TemplateHead) {
state := p.enterNode(node.AsNode())
p.emitLiteral(node.AsNode(), getLiteralTextFlagsNone)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitTemplateMiddle(node *ast.TemplateMiddle) {
state := p.enterNode(node.AsNode())
p.emitLiteral(node.AsNode(), getLiteralTextFlagsNone)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitTemplateTail(node *ast.TemplateTail) {
state := p.enterNode(node.AsNode())
p.emitLiteral(node.AsNode(), getLiteralTextFlagsNone)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitTemplateMiddleTail(node *ast.TemplateMiddleOrTail) {
switch node.Kind {
case ast.KindTemplateMiddle:
p.emitTemplateMiddle(node.AsTemplateMiddle())
case ast.KindTemplateTail:
p.emitTemplateTail(node.AsTemplateTail())
}
}
//
// Snippet Elements
//
// !!! Snippet elements
//
// Names
//
func (p *Printer) emitIdentifierText(node *ast.Identifier) {
text := p.getTextOfNode(node.AsNode(), false /*includeTrivia*/)
// !!! In the old emitter, an Identifier could have a Symbol associated with it. That
// doesn't seem to be the case in the new emitter. Do we need to get the symbol from somewhere else?
////p.writeSymbol(text, node.Symbol())
p.write(text)
// !!! In the old emitter, an Identifier could have type arguments for use with quickinfo:
////p.emitList(node, getIdentifierTypeArguments(node), LFTypeParameters); // Call emitList directly since it could be an array of TypeParameterDeclarations _or_ type arguments
}
func (p *Printer) emitIdentifierName(node *ast.Identifier) {
state := p.enterNode(node.AsNode())
p.emitIdentifierText(node)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitIdentifierNameNode(node *ast.IdentifierNode) {
if node == nil {
return
}
p.emitIdentifierName(node.AsIdentifier())
}
func (p *Printer) getUniqueHelperName(name string) *ast.IdentifierNode {
helperName := p.uniqueHelperNames[name]
if helperName == nil {
helperName := p.emitContext.Factory.NewUniqueNameEx(name, AutoGenerateOptions{Flags: GeneratedIdentifierFlagsFileLevel | GeneratedIdentifierFlagsOptimistic})
p.generateName(helperName)
p.uniqueHelperNames[name] = helperName
return helperName
}
return helperName.Clone(p.emitContext.Factory)
}
func (p *Printer) emitIdentifierReference(node *ast.Identifier) {
if (p.externalHelpersModuleName != nil || p.uniqueHelperNames != nil) &&
p.emitContext.EmitFlags(node.AsNode())&EFHelperName != 0 {
if p.externalHelpersModuleName != nil {
// Substitute `__helper` with `tslib_1.__helper`
helper := p.emitContext.Factory.NewPropertyAccessExpression(
p.externalHelpersModuleName.Clone(p.emitContext.Factory),
nil, /*questionDotToken*/
node.Clone(p.emitContext.Factory),
ast.NodeFlagsNone,
)
p.emitContext.AssignCommentAndSourceMapRanges(helper, node.AsNode())
p.emitPropertyAccessExpression(helper.AsPropertyAccessExpression())
return
}
if p.uniqueHelperNames != nil {
// Substitute `__helper` with `__helper_1` if there is a conflict in an ES module.
helperName := p.getUniqueHelperName(node.Text)
p.emitContext.AssignCommentAndSourceMapRanges(helperName, node.AsNode())
node = helperName.AsIdentifier()
}
}
state := p.enterNode(node.AsNode())
p.emitIdentifierText(node)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitBindingIdentifier(node *ast.Identifier) {
if p.uniqueHelperNames != nil &&
p.emitContext.EmitFlags(node.AsNode())&EFHelperName != 0 {
// Substitute `__helper` with `__helper_1` if there is a conflict in an ES module.
helperName := p.getUniqueHelperName(node.Text)
p.emitContext.AssignCommentAndSourceMapRanges(helperName, node.AsNode())
node = helperName.AsIdentifier()
}
state := p.enterNode(node.AsNode())
p.emitIdentifierText(node)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitLabelIdentifier(node *ast.Identifier) {
state := p.enterNode(node.AsNode())
p.emitIdentifierText(node)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitPrivateIdentifier(node *ast.PrivateIdentifier) {
state := p.enterNode(node.AsNode())
p.write(p.getTextOfNode(node.AsNode(), false /*includeTrivia*/))
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitQualifiedName(node *ast.QualifiedName) {
state := p.enterNode(node.AsNode())
p.emitEntityName(node.Left)
p.writePunctuation(".")
p.emitIdentifierName(node.Right.AsIdentifier())
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitComputedPropertyName(node *ast.ComputedPropertyName) {
state := p.enterNode(node.AsNode())
p.writePunctuation("[")
p.emitExpression(node.Expression, ast.OperatorPrecedenceDisallowComma)
p.writePunctuation("]")
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitEntityName(node *ast.EntityName) {
switch node.Kind {
case ast.KindIdentifier:
p.emitIdentifierReference(node.AsIdentifier())
case ast.KindQualifiedName:
p.emitQualifiedName(node.AsQualifiedName())
default:
panic(fmt.Sprintf("unexpected EntityName: %v", node.Kind))
}
}
func (p *Printer) emitBindingName(node *ast.BindingName) {
if node == nil {
return
}
switch node.Kind {
case ast.KindIdentifier:
p.emitBindingIdentifier(node.AsIdentifier())
case ast.KindObjectBindingPattern:
p.emitObjectBindingPattern(node.AsBindingPattern())
case ast.KindArrayBindingPattern:
p.emitArrayBindingPattern(node.AsBindingPattern())
default:
panic(fmt.Sprintf("unexpected BindingName: %v", node.Kind))
}
}
func (p *Printer) emitPropertyName(node *ast.PropertyName) {
if node == nil {
return
}
savedWriteKind := p.writeKind
p.writeKind = WriteKindProperty
switch node.Kind {
case ast.KindIdentifier:
p.emitIdentifierName(node.AsIdentifier())
case ast.KindPrivateIdentifier:
p.emitPrivateIdentifier(node.AsPrivateIdentifier())
case ast.KindStringLiteral:
p.emitStringLiteral(node.AsStringLiteral())
case ast.KindNoSubstitutionTemplateLiteral:
p.emitNoSubstitutionTemplateLiteral(node.AsNoSubstitutionTemplateLiteral())
case ast.KindNumericLiteral:
p.emitNumericLiteral(node.AsNumericLiteral())
case ast.KindBigIntLiteral:
p.emitBigIntLiteral(node.AsBigIntLiteral())
case ast.KindComputedPropertyName:
p.emitComputedPropertyName(node.AsComputedPropertyName())
default:
panic(fmt.Sprintf("unexpected PropertyName: %v", node.Kind))
}
p.writeKind = savedWriteKind
}
func (p *Printer) emitMemberName(node *ast.MemberName) {
if node == nil {
return
}
switch node.Kind {
case ast.KindIdentifier:
p.emitIdentifierName(node.AsIdentifier())
case ast.KindPrivateIdentifier:
p.emitPrivateIdentifier(node.AsPrivateIdentifier())
default:
panic(fmt.Sprintf("unexpected MemberName: %v", node.Kind))
}
}
func (p *Printer) emitModuleName(node *ast.ModuleName) {
if node == nil {
return
}
switch node.Kind {
case ast.KindIdentifier:
p.emitBindingIdentifier(node.AsIdentifier())
case ast.KindStringLiteral:
p.emitStringLiteral(node.AsStringLiteral())
default:
panic(fmt.Sprintf("unexpected ModuleName: %v", node.Kind))
}
}
func (p *Printer) emitModuleExportName(node *ast.ModuleExportName) {
if node == nil {
return
}
switch node.Kind {
case ast.KindIdentifier:
p.emitIdentifierName(node.AsIdentifier())
case ast.KindStringLiteral:
p.emitStringLiteral(node.AsStringLiteral())
default:
panic(fmt.Sprintf("unexpected ModuleExportName: %v", node.Kind))
}
}
func (p *Printer) emitImportAttributeName(node *ast.ImportAttributeName) {
switch node.Kind {
case ast.KindIdentifier:
p.emitIdentifierName(node.AsIdentifier())
case ast.KindStringLiteral:
p.emitStringLiteral(node.AsStringLiteral())
default:
panic(fmt.Sprintf("unexpected ImportAttributeName: %v", node.Kind))
}
}
func (p *Printer) emitNestedModuleName(node *ast.ModuleName) {
if node == nil {
return
}
switch node.Kind {
case ast.KindIdentifier:
p.emitIdentifierName(node.AsIdentifier())
case ast.KindStringLiteral:
p.emitStringLiteral(node.AsStringLiteral())
default:
panic(fmt.Sprintf("unexpected ModuleName: %v", node.Kind))
}
}
//
// Signature elements
//
func (p *Printer) emitModifierList(parentNode *ast.Node, modifiers *ast.ModifierList, allowDecorators bool) int {
if modifiers == nil || len(modifiers.Nodes) == 0 {
return parentNode.Pos()
}
if core.Every(modifiers.Nodes, ast.IsModifier) {
// if all modifier-likes are `Modifier`, simply emit the list as modifiers.
p.emitList((*Printer).emitKeywordNode, parentNode, &modifiers.NodeList, LFModifiers)
} else if core.Every(modifiers.Nodes, ast.IsDecorator) {
if !allowDecorators {
return parentNode.Pos()
}
// if all modifier-likes are `Decorator`, simply emit the list as decorators.
p.emitList((*Printer).emitModifierLike, parentNode, &modifiers.NodeList, LFDecorators)
} else {
if p.OnBeforeEmitNodeList != nil {
p.OnBeforeEmitNodeList(&modifiers.NodeList)
}
// partition modifiers into contiguous chunks of `Modifier` or `Decorator` so as to
// use consistent formatting for each chunk
type Mode int
const (
ModeNone Mode = iota
ModeModifiers
ModeDecorators
)
lastMode := ModeNone
mode := ModeNone
start := 0
pos := 0
var lastModifier *ast.ModifierLike
for start < len(modifiers.Nodes) {
for pos < len(modifiers.Nodes) {
lastModifier = modifiers.Nodes[pos]
if ast.IsDecorator(lastModifier) {
mode = ModeDecorators
} else {
mode = ModeModifiers
}
if lastMode == ModeNone {
lastMode = mode
} else if mode != lastMode {
break
}
pos++
}
textRange := core.NewTextRange(-1, -1)
if start == 0 {
textRange = core.NewTextRange(modifiers.Pos(), textRange.End())
}
if pos == len(modifiers.Nodes)-1 {
textRange = core.NewTextRange(textRange.Pos(), modifiers.End())
}
if allowDecorators || lastMode == ModeModifiers {
p.emitListItems(
(*Printer).emitModifierLike,
parentNode,
modifiers.Nodes[start:pos],
core.IfElse(lastMode == ModeModifiers, LFModifiers, LFDecorators),
false, /*hasTrailingComma*/
textRange,
)
}
start = pos
lastMode = mode
pos++
}
if p.OnAfterEmitNodeList != nil {
p.OnAfterEmitNodeList(&modifiers.NodeList)
}
}
return greatestEnd(parentNode.Pos(), modifiers, core.LastOrNil(modifiers.Nodes))
}
func (p *Printer) emitTypeParameter(node *ast.TypeParameterDeclaration) {
state := p.enterNode(node.AsNode())
p.emitModifierList(node.AsNode(), node.Modifiers(), false /*allowDecorators*/)
p.emitBindingIdentifier(node.Name().AsIdentifier())
if node.Constraint != nil {
p.writeSpace()
p.writeKeyword("extends")
p.writeSpace()
p.emitTypeNodeOutsideExtends(node.Constraint)
}
if node.DefaultType != nil {
p.writeSpace()
p.writeOperator("=")
p.writeSpace()
p.emitTypeNodeOutsideExtends(node.DefaultType)
}
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitTypeParameterNode(node *ast.TypeParameterDeclarationNode) {
// NOTE: QuickInfo uses TypeFormatFlagsWriteTypeArgumentsOfSignature to instruct the NodeBuilder to store type arguments
// (i.e. type nodes) instead of type parameter declarations in the type parameter list.
if ast.IsTypeParameterDeclaration(node) {
p.emitTypeParameter(node.AsTypeParameter())
} else {
p.emitTypeArgument(node)
}
}
func (p *Printer) emitParameterName(node *ast.BindingName) {
savedWriteKind := p.writeKind
p.writeKind = WriteKindParameter
p.emitBindingName(node)
p.writeKind = savedWriteKind
}
func (p *Printer) emitParameter(node *ast.ParameterDeclaration) {
state := p.enterNode(node.AsNode())
p.emitModifierList(node.AsNode(), node.Modifiers(), true /*allowDecorators*/)
p.emitTokenNode(node.DotDotDotToken)
p.emitParameterName(node.Name())
p.emitTokenNode(node.QuestionToken)
p.emitTypeAnnotation(node.Type)
// The comment position has to fallback to any present node within the parameter declaration because as it turns
// out, the parser can make parameter declarations with _just_ an initializer.
p.emitInitializer(node.Initializer, greatestEnd(node.Pos(), node.Type, node.QuestionToken, node.Name(), node.Modifiers()), node.AsNode())
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitParameterNode(node *ast.ParameterDeclarationNode) {
p.emitParameter(node.AsParameterDeclaration())
}
func (p *Printer) emitDecorator(node *ast.Decorator) {
p.writePunctuation("@")
p.emitExpression(node.Expression, ast.OperatorPrecedenceMember)
}
func (p *Printer) emitModifierLike(node *ast.ModifierLike) {
switch {
case ast.IsDecorator(node):
p.emitDecorator(node.AsDecorator())
case ast.IsModifier(node):
p.emitKeywordNode(node)
default:
panic(fmt.Sprintf("unhandled ModifierLike: %v", node.Kind))
}
}
func (p *Printer) emitTypeParameters(parentNode *ast.Node, nodes *ast.TypeParameterList) {
if nodes == nil {
return
}
p.emitList((*Printer).emitTypeParameterNode, parentNode, nodes, LFTypeParameters|core.IfElse(ast.IsArrowFunction(parentNode) /*p.shouldAllowTrailingComma(parentNode, nodes)*/, LFAllowTrailingComma, LFNone)) // TODO: preserve trailing comma after Strada migration
}
func (p *Printer) emitTypeAnnotation(node *ast.TypeNode) {
if node == nil {
return
}
p.writePunctuation(":")
p.writeSpace()
p.emitTypeNodeOutsideExtends(node)
}
func (p *Printer) emitInitializer(node *ast.Expression, equalTokenPos int, contextNode *ast.Node) {
if node == nil {
return
}
p.writeSpace()
p.emitToken(ast.KindEqualsToken, equalTokenPos, WriteKindOperator, contextNode)
p.writeSpace()
p.emitExpression(node, ast.OperatorPrecedenceDisallowComma)
}
func (p *Printer) emitParameters(parentNode *ast.Node, parameters *ast.ParameterList) {
p.generateAllNames(parameters)
p.emitList((*Printer).emitParameterNode, parentNode, parameters, LFParameters /*|core.IfElse(p.shouldAllowTrailingComma(parentNode, parameters), LFAllowTrailingComma, LFNone)*/) // TODO: preserve trailing comma after Strada migration
}
func canEmitSimpleArrowHead(parentNode *ast.Node, parameters *ast.ParameterList) bool {
// only arrow functions with a single parameter may have simple arrow head
if !ast.IsArrowFunction(parentNode) || len(parameters.Nodes) != 1 {
return false
}
parent := parentNode.AsArrowFunction()
parameter := parameters.Nodes[0].AsParameterDeclaration()
return parameter.Pos() == greatestEnd(parent.Pos(), parent.Modifiers()) && // may not have parsed tokens between modifiers/start of parent and parameter
parent.TypeParameters == nil && // parent may not have type parameters
parent.Type == nil && // parent may not have return type annotation
!parameters.HasTrailingComma() && // parameters may not have a trailing comma
parameter.Modifiers() == nil && // parameter may not have decorators or modifiers
parameter.DotDotDotToken == nil && // parameter may not be rest
parameter.QuestionToken == nil && // parameter may not be optional
parameter.Type == nil && // parameter may not have a type annotation
parameter.Initializer == nil && // parameter may not have an initializer
ast.IsIdentifier(parameter.Name()) // parameter name must be identifier
}
func (p *Printer) emitParametersForArrow(parentNode *ast.Node /*FunctionTypeNode | ConstructorTypeNode | ArrowFunction*/, parameters *ast.ParameterList) {
if canEmitSimpleArrowHead(parentNode, parameters) {
p.generateAllNames(parameters)
p.emitList((*Printer).emitParameterNode, parentNode, parameters, LFSingleArrowParameter)
} else {
p.emitParameters(parentNode, parameters)
}
}
func (p *Printer) emitParametersForIndexSignature(parentNode *ast.Node, parameters *ast.ParameterList) {
p.generateAllNames(parameters)
p.emitList((*Printer).emitParameterNode, parentNode, parameters, LFIndexSignatureParameters)
}
func (p *Printer) emitSignature(node *ast.Node) {
n := node.FunctionLikeData()
// !!! In old emitter, quickinfo used type arguments in place of type parameters on instantiated signatures
////if n.TypeArguments != nil {
//// p.emitTypeArguments(node, n.TypeArguments)
////} else {
p.emitTypeParameters(node, n.TypeParameters)
////}
p.emitParameters(node, n.Parameters)
p.emitTypeAnnotation(n.Type)
}
func (p *Printer) emitFunctionBody(body *ast.Block) {
state := p.enterNode(body.AsNode())
p.generateNames(body.AsNode())
// !!! Emit with comment after Strada migration
////p.emitTokenWithComment(ast.KindOpenBraceToken, body.Pos(), WriteKindPunctuation, body.AsNode())
p.writePunctuation("{")
p.increaseIndent()
detachedState := p.emitDetachedCommentsBeforeStatementList(body.AsNode(), body.Statements.Loc)
statementOffset := p.emitPrologueDirectives(body.Statements)
pos := p.writer.GetTextPos()
p.emitHelpers(body.AsNode())
if p.shouldEmitBlockFunctionBodyOnSingleLine(body) && statementOffset == 0 && pos == p.writer.GetTextPos() {
p.decreaseIndent()
p.emitList((*Printer).emitStatement, body.AsNode(), body.Statements, LFSingleLineFunctionBodyStatements)
p.increaseIndent()
} else {
p.emitList((*Printer).emitStatement, body.AsNode(), body.Statements, LFMultiLineFunctionBodyStatements)
}
p.emitDetachedCommentsAfterStatementList(body.AsNode(), body.Statements.Loc, detachedState)
p.decreaseIndent()
// !!! Emit comment after Strada migration
////p.emitTokenEx(ast.KindCloseBraceToken, body.Statements.End(), WriteKindPunctuation, body.AsNode(), tefNone)
p.emitTokenEx(ast.KindCloseBraceToken, body.Statements.End(), WriteKindPunctuation, body.AsNode(), tefNoComments)
p.exitNode(body.AsNode(), state)
}
func (p *Printer) emitFunctionBodyNode(node *ast.BlockNode) {
if node == nil {
p.writeTrailingSemicolon()
return
}
p.writeSpace()
p.emitFunctionBody(node.AsBlock())
}
//
// Type Members
//
func (p *Printer) emitPropertySignature(node *ast.PropertySignatureDeclaration) {
state := p.enterNode(node.AsNode())
p.emitModifierList(node.AsNode(), node.Modifiers(), false /*allowDecorators*/)
p.emitPropertyName(node.Name())
p.emitTokenNode(node.PostfixToken)
p.emitTypeAnnotation(node.Type)
p.writeTrailingSemicolon()
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitPropertyDeclaration(node *ast.PropertyDeclaration) {
state := p.enterNode(node.AsNode())
p.emitModifierList(node.AsNode(), node.Modifiers(), true /*allowDecorators*/)
p.emitPropertyName(node.Name())
p.emitTokenNode(node.PostfixToken)
p.emitTypeAnnotation(node.Type)
p.emitInitializer(node.Initializer, greatestEnd(node.Name().End(), node.Type, node.PostfixToken), node.AsNode())
p.writeTrailingSemicolon()
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitMethodSignature(node *ast.MethodSignatureDeclaration) {
state := p.enterNode(node.AsNode())
p.emitModifierList(node.AsNode(), node.Modifiers(), false /*allowDecorators*/)
p.emitPropertyName(node.Name())
p.emitTokenNode(node.PostfixToken)
indented := p.shouldEmitIndented(node.AsNode())
p.increaseIndentIf(indented)
p.pushNameGenerationScope(node.AsNode())
p.emitSignature(node.AsNode())
p.writeTrailingSemicolon()
p.popNameGenerationScope(node.AsNode())
p.decreaseIndentIf(indented)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitMethodDeclaration(node *ast.MethodDeclaration) {
state := p.enterNode(node.AsNode())
p.emitModifierList(node.AsNode(), node.Modifiers(), true /*allowDecorators*/)
p.emitTokenNode(node.AsteriskToken)
p.emitPropertyName(node.Name())
p.emitTokenNode(node.PostfixToken)
indented := p.shouldEmitIndented(node.AsNode())
p.increaseIndentIf(indented)
p.pushNameGenerationScope(node.AsNode())
p.emitSignature(node.AsNode())
p.emitFunctionBodyNode(node.Body)
p.popNameGenerationScope(node.AsNode())
p.decreaseIndentIf(indented)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitClassStaticBlockDeclaration(node *ast.ClassStaticBlockDeclaration) {
state := p.enterNode(node.AsNode())
p.writeKeyword("static")
p.pushNameGenerationScope(node.AsNode())
p.emitFunctionBodyNode(node.Body)
p.popNameGenerationScope(node.AsNode())
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitConstructor(node *ast.ConstructorDeclaration) {
state := p.enterNode(node.AsNode())
p.emitModifierList(node.AsNode(), node.Modifiers(), false /*allowDecorators*/)
p.writeKeyword("constructor")
indented := p.shouldEmitIndented(node.AsNode())
p.increaseIndentIf(indented)
p.pushNameGenerationScope(node.AsNode())
p.emitSignature(node.AsNode())
p.emitFunctionBodyNode(node.Body)
p.popNameGenerationScope(node.AsNode())
p.decreaseIndentIf(indented)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitAccessorDeclaration(token ast.Kind, node *ast.AccessorDeclarationBase) {
state := p.enterNode(node.AsNode())
pos := p.emitModifierList(node.AsNode(), node.Modifiers(), true /*allowDecorators*/)
p.emitToken(token, pos, WriteKindKeyword, node.AsNode())
p.writeSpace()
p.emitPropertyName(node.Name())
indented := p.shouldEmitIndented(node.AsNode())
p.increaseIndentIf(indented)
p.pushNameGenerationScope(node.AsNode())
p.emitSignature(node.AsNode())
p.emitFunctionBodyNode(node.Body)
p.popNameGenerationScope(node.AsNode())
p.decreaseIndentIf(indented)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitGetAccessorDeclaration(node *ast.GetAccessorDeclaration) {
p.emitAccessorDeclaration(ast.KindGetKeyword, &node.AccessorDeclarationBase)
}
func (p *Printer) emitSetAccessorDeclaration(node *ast.SetAccessorDeclaration) {
p.emitAccessorDeclaration(ast.KindSetKeyword, &node.AccessorDeclarationBase)
}
func (p *Printer) emitCallSignature(node *ast.CallSignatureDeclaration) {
state := p.enterNode(node.AsNode())
indented := p.shouldEmitIndented(node.AsNode())
p.increaseIndentIf(indented)
p.pushNameGenerationScope(node.AsNode())
p.emitSignature(node.AsNode())
p.writeTrailingSemicolon()
p.popNameGenerationScope(node.AsNode())
p.decreaseIndentIf(indented)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitConstructSignature(node *ast.ConstructSignatureDeclaration) {
state := p.enterNode(node.AsNode())
p.writeKeyword("new")
p.writeSpace()
indented := p.shouldEmitIndented(node.AsNode())
p.increaseIndentIf(indented)
p.pushNameGenerationScope(node.AsNode())
p.emitSignature(node.AsNode())
p.writeTrailingSemicolon()
p.popNameGenerationScope(node.AsNode())
p.decreaseIndentIf(indented)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitIndexSignature(node *ast.IndexSignatureDeclaration) {
state := p.enterNode(node.AsNode())
p.emitModifierList(node.AsNode(), node.Modifiers(), false /*allowDecorators*/)
indented := p.shouldEmitIndented(node.AsNode())
p.increaseIndentIf(indented)
p.pushNameGenerationScope(node.AsNode())
p.emitParametersForIndexSignature(node.AsNode(), node.Parameters)
p.emitTypeAnnotation(node.Type)
p.writeTrailingSemicolon()
p.popNameGenerationScope(node.AsNode())
p.decreaseIndentIf(indented)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitClassElement(node *ast.ClassElement) {
switch node.Kind {
case ast.KindPropertyDeclaration:
p.emitPropertyDeclaration(node.AsPropertyDeclaration())
case ast.KindMethodDeclaration:
p.emitMethodDeclaration(node.AsMethodDeclaration())
case ast.KindClassStaticBlockDeclaration:
p.emitClassStaticBlockDeclaration(node.AsClassStaticBlockDeclaration())
case ast.KindConstructor:
p.emitConstructor(node.AsConstructorDeclaration())
case ast.KindGetAccessor:
p.emitGetAccessorDeclaration(node.AsGetAccessorDeclaration())
case ast.KindSetAccessor:
p.emitSetAccessorDeclaration(node.AsSetAccessorDeclaration())
case ast.KindIndexSignature:
p.emitIndexSignature(node.AsIndexSignatureDeclaration())
case ast.KindSemicolonClassElement:
p.emitSemicolonClassElement(node.AsSemicolonClassElement())
case ast.KindNotEmittedStatement:
p.emitNotEmittedStatement(node.AsNotEmittedStatement())
case ast.KindJSTypeAliasDeclaration:
p.emitTypeAliasDeclaration(node.AsTypeAliasDeclaration())
default:
panic(fmt.Sprintf("unexpected ClassElement: %v", node.Kind))
}
}
func (p *Printer) emitTypeElement(node *ast.TypeElement) {
switch node.Kind {
case ast.KindPropertySignature:
p.emitPropertySignature(node.AsPropertySignatureDeclaration())
case ast.KindMethodSignature:
p.emitMethodSignature(node.AsMethodSignatureDeclaration())
case ast.KindCallSignature:
p.emitCallSignature(node.AsCallSignatureDeclaration())
case ast.KindConstructSignature:
p.emitConstructSignature(node.AsConstructSignatureDeclaration())
case ast.KindGetAccessor:
p.emitGetAccessorDeclaration(node.AsGetAccessorDeclaration())
case ast.KindSetAccessor:
p.emitSetAccessorDeclaration(node.AsSetAccessorDeclaration())
case ast.KindIndexSignature:
p.emitIndexSignature(node.AsIndexSignatureDeclaration())
case ast.KindNotEmittedTypeElement:
p.emitNotEmittedTypeElement(node.AsNotEmittedTypeElement())
default:
panic(fmt.Sprintf("unexpected TypeElement: %v", node.Kind))
}
}
func (p *Printer) emitObjectLiteralElement(node *ast.ObjectLiteralElement) {
switch node.Kind {
case ast.KindPropertyAssignment:
p.emitPropertyAssignment(node.AsPropertyAssignment())
case ast.KindShorthandPropertyAssignment:
p.emitShorthandPropertyAssignment(node.AsShorthandPropertyAssignment())
case ast.KindSpreadAssignment:
p.emitSpreadAssignment(node.AsSpreadAssignment())
case ast.KindMethodDeclaration:
p.emitMethodDeclaration(node.AsMethodDeclaration())
case ast.KindGetAccessor:
p.emitGetAccessorDeclaration(node.AsGetAccessorDeclaration())
case ast.KindSetAccessor:
p.emitSetAccessorDeclaration(node.AsSetAccessorDeclaration())
default:
panic(fmt.Sprintf("unhandled ObjectLiteralElement: %v", node.Kind))
}
}
//
// Types
//
func (p *Printer) emitKeywordTypeNode(node *ast.KeywordTypeNode) {
p.emitKeywordNode(node.AsNode())
}
func (p *Printer) emitTypePredicateParameterName(node *ast.TypePredicateParameterName) {
switch node.Kind {
case ast.KindIdentifier:
p.emitIdentifierReference(node.AsIdentifier())
case ast.KindThisType:
p.emitThisType(node.AsThisTypeNode())
default:
panic(fmt.Sprintf("unexpected TypePredicateParameterName: %v", node.Kind))
}
}
func (p *Printer) emitTypePredicate(node *ast.TypePredicateNode) {
state := p.enterNode(node.AsNode())
if node.AssertsModifier != nil {
p.emitTokenNode(node.AssertsModifier)
p.writeSpace()
}
p.emitTypePredicateParameterName(node.ParameterName)
if node.Type != nil {
p.writeSpace()
p.writeKeyword("is")
p.writeSpace()
p.emitTypeNodeOutsideExtends(node.Type)
}
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitTypeArgument(node *ast.TypeNode) {
p.emitTypeNodeOutsideExtends(node)
}
func (p *Printer) emitTypeArguments(parentNode *ast.Node, nodes *ast.TypeArgumentList) {
if nodes == nil {
return
}
p.emitList((*Printer).emitTypeArgument, parentNode, nodes, LFTypeArguments /*|core.IfElse(p.shouldAllowTrailingComma(parentNode, nodes), LFAllowTrailingComma, LFNone)*/) // TODO: preserve trailing comma after Strada migration
}
func (p *Printer) emitTypeReference(node *ast.TypeReferenceNode) {
state := p.enterNode(node.AsNode())
p.emitEntityName(node.TypeName)
p.emitTypeArguments(node.AsNode(), node.TypeArguments)
p.exitNode(node.AsNode(), state)
}
// Emits the return type of a FunctionTypeNode or ConstructorTypeNode, including the arrow (`=>`)
func (p *Printer) emitReturnType(node *ast.TypeNode) {
if node == nil {
return
}
p.writePunctuation("=>")
p.writeSpace()
if p.inExtends && node.Kind == ast.KindInferType && node.AsInferTypeNode().TypeParameter.AsTypeParameter().Constraint != nil {
// if the parent FunctionTypeNode or ConstructorTypeNode is in the `extends` clause of a ConditionalTypeNode,
// we must parenthesize `infer ... extends ...` so as not to result in an ambiguous parse.
//
// `T extends () => infer U extends V ? W : X` would parse the `? W : X` as part of a ConditionalTypeNode in the
// return type of the FunctionTypeNode, thus we must emit as `T extends () => (infer U extends V) ? W : X`
p.emitTypeNodePreservingExtends(node, ast.TypePrecedenceHighest)
} else {
p.emitTypeNodePreservingExtends(node, ast.TypePrecedenceLowest)
}
}
func (p *Printer) emitFunctionType(node *ast.FunctionTypeNode) {
state := p.enterNode(node.AsNode())
indented := p.shouldEmitIndented(node.AsNode())
p.increaseIndentIf(indented)
p.pushNameGenerationScope(node.AsNode())
// !!! in the old emitter, quickinfo uses type arguments in place of type parameters for instantiated signatures
p.emitTypeParameters(node.AsNode(), node.TypeParameters)
p.emitParameters(node.AsNode(), node.Parameters)
p.writeSpace()
p.emitReturnType(node.Type)
p.popNameGenerationScope(node.AsNode())
p.decreaseIndentIf(indented)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitConstructorType(node *ast.ConstructorTypeNode) {
state := p.enterNode(node.AsNode())
p.emitModifierList(node.AsNode(), node.Modifiers(), false /*allowDecorators*/)
p.writeKeyword("new")
p.writeSpace()
indented := p.shouldEmitIndented(node.AsNode())
p.increaseIndentIf(indented)
p.pushNameGenerationScope(node.AsNode())
// !!! in the old emitter, quickinfo uses type arguments in place of type parameters for instantiated signatures
p.emitTypeParameters(node.AsNode(), node.TypeParameters)
p.emitParameters(node.AsNode(), node.Parameters)
p.writeSpace()
p.emitReturnType(node.Type)
p.popNameGenerationScope(node.AsNode())
p.decreaseIndentIf(indented)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitTypeQuery(node *ast.TypeQueryNode) {
state := p.enterNode(node.AsNode())
p.writeKeyword("typeof")
p.writeSpace()
p.emitEntityName(node.ExprName)
p.emitTypeArguments(node.AsNode(), node.TypeArguments)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitTypeLiteral(node *ast.TypeLiteralNode) {
state := p.enterNode(node.AsNode())
p.pushNameGenerationScope(node.AsNode())
p.generateAllMemberNames(node.Members)
p.writePunctuation("{")
flags := core.IfElse(p.shouldEmitOnSingleLine(node.AsNode()), LFSingleLineTypeLiteralMembers, LFMultiLineTypeLiteralMembers)
p.emitList((*Printer).emitTypeElement, node.AsNode(), node.Members, flags|LFNoSpaceIfEmpty)
p.writePunctuation("}")
p.popNameGenerationScope(node.AsNode())
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitArrayType(node *ast.ArrayTypeNode) {
state := p.enterNode(node.AsNode())
p.emitTypeNode(node.ElementType, ast.TypePrecedencePostfix)
p.writePunctuation("[")
p.writePunctuation("]")
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitTupleElementType(node *ast.Node) {
p.emitTypeNodeOutsideExtends(node)
}
func (p *Printer) emitTupleType(node *ast.TupleTypeNode) {
state := p.enterNode(node.AsNode())
p.emitToken(ast.KindOpenBracketToken, node.Pos(), WriteKindPunctuation, node.AsNode())
flags := core.IfElse(p.shouldEmitOnSingleLine(node.AsNode()), LFSingleLineTupleTypeElements, LFMultiLineTupleTypeElements)
p.emitList((*Printer).emitTupleElementType, node.AsNode(), node.Elements, flags|LFNoSpaceIfEmpty)
p.emitToken(ast.KindCloseBracketToken, node.Elements.End(), WriteKindPunctuation, node.AsNode())
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitRestType(node *ast.RestTypeNode) {
state := p.enterNode(node.AsNode())
p.writePunctuation("...")
p.emitTypeNodeOutsideExtends(node.Type)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitOptionalType(node *ast.OptionalTypeNode) {
state := p.enterNode(node.AsNode())
// !!! May need extra parenthesization if we also have JSDocNullableType
p.emitTypeNode(node.Type, ast.TypePrecedencePostfix)
p.writePunctuation("?")
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitNamedTupleMember(node *ast.NamedTupleMember) {
state := p.enterNode(node.AsNode())
p.emitPunctuationNode(node.DotDotDotToken)
p.emitIdentifierName(node.Name().AsIdentifier())
p.emitPunctuationNode(node.QuestionToken)
p.emitToken(ast.KindColonToken, greatestEnd(node.Name().End(), node.QuestionToken), WriteKindPunctuation, node.AsNode())
p.writeSpace()
p.emitTypeNodeOutsideExtends(node.Type)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitUnionTypeConstituent(node *ast.TypeNode) {
p.emitTypeNode(node, ast.TypePrecedenceTypeOperator)
}
func (p *Printer) emitUnionType(node *ast.UnionTypeNode) {
state := p.enterNode(node.AsNode())
p.emitList((*Printer).emitUnionTypeConstituent, node.AsNode(), node.Types, LFUnionTypeConstituents)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitIntersectionTypeConstituent(node *ast.TypeNode) {
p.emitTypeNode(node, ast.TypePrecedenceTypeOperator)
}
func (p *Printer) emitIntersectionType(node *ast.IntersectionTypeNode) {
state := p.enterNode(node.AsNode())
p.emitList((*Printer).emitIntersectionTypeConstituent, node.AsNode(), node.Types, LFIntersectionTypeConstituents /*, parenthesizer.parenthesizeConstituentTypeOfIntersectionType*/) // !!!
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitConditionalType(node *ast.ConditionalTypeNode) {
state := p.enterNode(node.AsNode())
p.emitTypeNode(node.CheckType, ast.TypePrecedenceUnion)
p.writeSpace()
p.writeKeyword("extends")
p.writeSpace()
p.emitTypeNodeInExtends(node.ExtendsType)
p.writeSpace()
p.writePunctuation("?")
p.writeSpace()
p.emitTypeNodeOutsideExtends(node.TrueType)
p.writeSpace()
p.writePunctuation(":")
p.writeSpace()
p.emitTypeNodeOutsideExtends(node.FalseType)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitInferTypeParameter(node *ast.TypeParameterDeclaration) {
state := p.enterNode(node.AsNode())
p.emitBindingIdentifier(node.Name().AsIdentifier())
if node.Constraint != nil {
p.writeSpace()
p.writeKeyword("extends")
p.writeSpace()
p.emitTypeNodeInExtends(node.Constraint)
}
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitInferType(node *ast.InferTypeNode) {
state := p.enterNode(node.AsNode())
p.writeKeyword("infer")
p.writeSpace()
p.emitInferTypeParameter(node.TypeParameter.AsTypeParameter())
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitParenthesizedType(node *ast.ParenthesizedTypeNode) {
state := p.enterNode(node.AsNode())
p.writePunctuation("(")
p.emitTypeNodeOutsideExtends(node.Type)
p.writePunctuation(")")
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitThisType(node *ast.ThisTypeNode) {
state := p.enterNode(node.AsNode())
p.writeKeyword("this")
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitTypeOperator(node *ast.TypeOperatorNode) {
state := p.enterNode(node.AsNode())
p.emitToken(node.Operator, node.Pos(), WriteKindKeyword, node.AsNode())
p.writeSpace()
p.emitTypeNode(node.Type, core.IfElse(node.Operator == ast.KindReadonlyKeyword, ast.TypePrecedencePostfix, ast.TypePrecedenceTypeOperator))
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitIndexedAccessType(node *ast.IndexedAccessTypeNode) {
state := p.enterNode(node.AsNode())
p.emitTypeNode(node.ObjectType, ast.TypePrecedencePostfix)
p.writePunctuation("[")
p.emitTypeNodeOutsideExtends(node.IndexType)
p.writePunctuation("]")
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitMappedTypeParameter(node *ast.TypeParameterDeclaration) {
state := p.enterNode(node.AsNode())
p.emitBindingIdentifier(node.Name().AsIdentifier())
p.writeSpace()
p.writeKeyword("in")
p.writeSpace()
p.emitTypeNodeOutsideExtends(node.Constraint)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitMappedType(node *ast.MappedTypeNode) {
state := p.enterNode(node.AsNode())
singleLine := p.shouldEmitOnSingleLine(node.AsNode())
p.writePunctuation("{")
if singleLine {
p.writeSpace()
} else {
p.writeLine()
p.increaseIndent()
}
if node.ReadonlyToken != nil {
p.emitTokenNode(node.ReadonlyToken)
if node.ReadonlyToken.Kind != ast.KindReadonlyKeyword {
p.writeKeyword("readonly")
}
p.writeSpace()
}
p.writePunctuation("[")
p.emitMappedTypeParameter(node.TypeParameter.AsTypeParameter())
if node.NameType != nil {
p.writeSpace()
p.writeKeyword("as")
p.writeSpace()
p.emitTypeNodeOutsideExtends(node.NameType)
}
p.writePunctuation("]")
if node.QuestionToken != nil {
p.emitPunctuationNode(node.QuestionToken)
if node.QuestionToken.Kind != ast.KindQuestionToken {
p.writePunctuation("?")
}
}
p.writePunctuation(":")
p.writeSpace()
p.emitTypeNodeOutsideExtends(node.Type)
p.writeTrailingSemicolon()
if node.Members != nil {
if singleLine {
p.writeSpace()
} else {
p.writeLine()
}
p.emitList((*Printer).emitTypeElement, node.AsNode(), node.Members, LFPreserveLines)
}
if singleLine {
p.writeSpace()
} else {
p.writeLine()
p.decreaseIndent()
}
p.writePunctuation("}")
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitLiteralType(node *ast.LiteralTypeNode) {
state := p.enterNode(node.AsNode())
p.emitExpression(node.Literal, ast.OperatorPrecedenceComma)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitTemplateTypeSpan(node *ast.TemplateLiteralTypeSpan) {
state := p.enterNode(node.AsNode())
p.emitTypeNodeOutsideExtends(node.Type)
p.emitTemplateMiddleTail(node.Literal)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitTemplateTypeSpanNode(node *ast.TemplateLiteralTypeSpanNode) {
p.emitTemplateTypeSpan(node.AsTemplateLiteralTypeSpan())
}
func (p *Printer) emitTemplateType(node *ast.TemplateLiteralTypeNode) {
state := p.enterNode(node.AsNode())
p.emitTemplateHead(node.Head.AsTemplateHead())
p.emitList((*Printer).emitTemplateTypeSpanNode, node.AsNode(), node.TemplateSpans, LFTemplateExpressionSpans)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitImportTypeNodeAttributes(node *ast.ImportAttributes) {
state := p.enterNode(node.AsNode())
p.writePunctuation("{")
p.writeSpace()
p.writeKeyword(core.IfElse(node.Token == ast.KindAssertKeyword, "assert", "with"))
p.writePunctuation(":")
p.writeSpace()
p.emitList((*Printer).emitImportAttributeNode, node.AsNode(), node.Attributes, LFImportAttributes)
p.writeSpace()
p.writePunctuation("}")
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitImportTypeNode(node *ast.ImportTypeNode) {
state := p.enterNode(node.AsNode())
if node.IsTypeOf {
p.writeKeyword("typeof")
p.writeSpace()
}
p.writeKeyword("import")
p.writePunctuation("(")
p.emitTypeNodeOutsideExtends(node.Argument)
if node.Attributes != nil {
p.writePunctuation(",")
p.writeSpace()
p.emitImportTypeNodeAttributes(node.Attributes.AsImportAttributes())
}
p.writePunctuation(")")
if node.Qualifier != nil {
p.writePunctuation(".")
p.emitEntityName(node.Qualifier)
}
p.emitTypeArguments(node.AsNode(), node.TypeArguments)
p.exitNode(node.AsNode(), state)
}
// emits a Type node in the `extends` clause of a ConditionalType
func (p *Printer) emitTypeNodeInExtends(node *ast.TypeNode) {
savedInExtends := p.inExtends
p.inExtends = true
p.emitTypeNodePreservingExtends(node, ast.TypePrecedenceLowest)
p.inExtends = savedInExtends
}
// emits a Type node not in the `extends` clause of a ConditionalType or InferType
func (p *Printer) emitTypeNodeOutsideExtends(node *ast.TypeNode) {
savedInExtends := p.inExtends
p.inExtends = false
p.emitTypeNodePreservingExtends(node, ast.TypePrecedenceLowest)
p.inExtends = savedInExtends
}
// emits a Type node preserving whether or not we are currently in the `extends` clause of a ConditionalType or InferType
func (p *Printer) emitTypeNodePreservingExtends(node *ast.TypeNode, precedence ast.TypePrecedence) {
p.emitTypeNode(node, precedence)
}
func (p *Printer) emitTypeNode(node *ast.TypeNode, precedence ast.TypePrecedence) {
if p.inExtends && precedence <= ast.TypePrecedenceConditional {
// in the `extends` clause of a ConditionalType or InferType, a ConditionalType must be parenthesized
precedence = ast.TypePrecedenceFunction
}
savedInExtends := p.inExtends
parens := ast.GetTypeNodePrecedence(node) < precedence
if parens {
p.inExtends = false
p.writePunctuation("(")
}
switch node.Kind {
// Keyword Types
case ast.KindAnyKeyword,
ast.KindUnknownKeyword,
ast.KindNumberKeyword,
ast.KindBigIntKeyword,
ast.KindObjectKeyword,
ast.KindBooleanKeyword,
ast.KindStringKeyword,
ast.KindSymbolKeyword,
ast.KindVoidKeyword,
ast.KindUndefinedKeyword,
ast.KindNeverKeyword,
ast.KindIntrinsicKeyword:
p.emitKeywordTypeNode(node.AsKeywordTypeNode())
// Types
case ast.KindTypePredicate:
p.emitTypePredicate(node.AsTypePredicateNode())
case ast.KindTypeReference:
p.emitTypeReference(node.AsTypeReferenceNode())
case ast.KindFunctionType:
p.emitFunctionType(node.AsFunctionTypeNode())
case ast.KindConstructorType:
p.emitConstructorType(node.AsConstructorTypeNode())
case ast.KindTypeQuery:
p.emitTypeQuery(node.AsTypeQueryNode())
case ast.KindTypeLiteral:
p.emitTypeLiteral(node.AsTypeLiteralNode())
case ast.KindArrayType:
p.emitArrayType(node.AsArrayTypeNode())
case ast.KindTupleType:
p.emitTupleType(node.AsTupleTypeNode())
case ast.KindOptionalType:
p.emitOptionalType(node.AsOptionalTypeNode())
case ast.KindRestType:
p.emitRestType(node.AsRestTypeNode())
case ast.KindUnionType:
p.emitUnionType(node.AsUnionTypeNode())
case ast.KindIntersectionType:
p.emitIntersectionType(node.AsIntersectionTypeNode())
case ast.KindConditionalType:
p.emitConditionalType(node.AsConditionalTypeNode())
case ast.KindInferType:
p.emitInferType(node.AsInferTypeNode())
case ast.KindParenthesizedType:
p.emitParenthesizedType(node.AsParenthesizedTypeNode())
case ast.KindThisType:
p.emitThisType(node.AsThisTypeNode())
case ast.KindTypeOperator:
p.emitTypeOperator(node.AsTypeOperatorNode())
case ast.KindIndexedAccessType:
p.emitIndexedAccessType(node.AsIndexedAccessTypeNode())
case ast.KindMappedType:
p.emitMappedType(node.AsMappedTypeNode())
case ast.KindLiteralType:
p.emitLiteralType(node.AsLiteralTypeNode())
case ast.KindNamedTupleMember:
p.emitNamedTupleMember(node.AsNamedTupleMember())
case ast.KindTemplateLiteralType:
p.emitTemplateType(node.AsTemplateLiteralTypeNode())
case ast.KindTemplateLiteralTypeSpan:
p.emitTemplateTypeSpan(node.AsTemplateLiteralTypeSpan())
case ast.KindImportType:
p.emitImportTypeNode(node.AsImportTypeNode())
case ast.KindExpressionWithTypeArguments:
// !!! Should this actually be considered a type?
p.emitExpressionWithTypeArguments(node.AsExpressionWithTypeArguments())
case ast.KindJSDocAllType:
p.emitJSDocAllType(node)
case ast.KindJSDocNonNullableType:
p.emitJSDocNonNullableType(node.AsJSDocNonNullableType())
case ast.KindJSDocNullableType:
p.emitJSDocNullableType(node.AsJSDocNullableType())
case ast.KindJSDocOptionalType:
p.emitJSDocOptionalType(node.AsJSDocOptionalType())
case ast.KindJSDocVariadicType:
p.emitJSDocVariadicType(node.AsJSDocVariadicType())
default:
panic(fmt.Sprintf("unhandled TypeNode: %v", node.Kind))
}
if parens {
p.writePunctuation(")")
}
p.inExtends = savedInExtends
}
//
// Binding patterns
//
func (p *Printer) emitObjectBindingPattern(node *ast.BindingPattern) {
state := p.enterNode(node.AsNode())
p.writePunctuation("{")
p.emitList((*Printer).emitBindingElementNode, node.AsNode(), node.Elements, LFObjectBindingPatternElements)
p.writePunctuation("}")
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitArrayBindingPattern(node *ast.BindingPattern) {
state := p.enterNode(node.AsNode())
p.writePunctuation("[")
p.emitList((*Printer).emitBindingElementNode, node.AsNode(), node.Elements, LFArrayBindingPatternElements)
p.writePunctuation("]")
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitBindingElement(node *ast.BindingElement) {
state := p.enterNode(node.AsNode())
p.emitTokenNode(node.DotDotDotToken)
if node.PropertyName != nil {
p.emitPropertyName(node.PropertyName)
p.writePunctuation(":")
p.writeSpace()
}
// Old parser used `OmittedExpression` as a substitute for `Elision`. New parser uses a `BindingElement` with nil members
if name := node.Name(); name != nil {
p.emitBindingName(name)
p.emitInitializer(node.Initializer, node.Name().End(), node.AsNode())
}
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitBindingElementNode(node *ast.BindingElementNode) {
p.emitBindingElement(node.AsBindingElement())
}
func (p *Printer) emitJSDocAllType(node *ast.Node) {
p.emitKeywordNode(node)
}
func (p *Printer) emitJSDocNonNullableType(node *ast.JSDocNonNullableType) {
state := p.enterNode(node.AsNode())
p.writePunctuation("!")
p.emitTypeNode(node.Type, ast.TypePrecedenceNonArray)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitJSDocNullableType(node *ast.JSDocNullableType) {
state := p.enterNode(node.AsNode())
p.writePunctuation("?")
p.emitTypeNode(node.Type, ast.TypePrecedenceNonArray)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitJSDocOptionalType(node *ast.JSDocOptionalType) {
state := p.enterNode(node.AsNode())
p.emitTypeNode(node.Type, ast.TypePrecedenceJSDoc)
p.writePunctuation("=")
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitJSDocVariadicType(node *ast.JSDocVariadicType) {
state := p.enterNode(node.AsNode())
p.writePunctuation("...")
p.emitTypeNode(node.Type, ast.TypePrecedenceJSDoc)
p.exitNode(node.AsNode(), state)
}
//
// Expressions
//
func (p *Printer) emitKeywordExpression(node *ast.KeywordExpression) {
p.emitKeywordNode(node.AsNode())
}
func (p *Printer) emitArrayLiteralExpressionElement(node *ast.Expression) {
p.emitExpression(node, ast.OperatorPrecedenceSpread)
}
func (p *Printer) emitArrayLiteralExpression(node *ast.ArrayLiteralExpression) {
state := p.enterNode(node.AsNode())
p.emitList((*Printer).emitArrayLiteralExpressionElement, node.AsNode(), node.Elements, LFArrayLiteralExpressionElements|core.IfElse(node.MultiLine, LFPreferNewLine, LFNone))
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitObjectLiteralExpression(node *ast.ObjectLiteralExpression) {
state := p.enterNode(node.AsNode())
indented := p.shouldEmitIndented(node.AsNode())
p.increaseIndentIf(indented)
p.pushNameGenerationScope(node.AsNode())
p.generateAllMemberNames(node.Properties)
p.emitList((*Printer).emitObjectLiteralElement, node.AsNode(), node.Properties, LFObjectLiteralExpressionProperties|
core.IfElse(node.MultiLine, LFPreferNewLine, LFNone)|
core.IfElse(p.shouldAllowTrailingComma(node.AsNode(), node.Properties), LFAllowTrailingComma, LFNone))
p.popNameGenerationScope(node.AsNode())
p.decreaseIndentIf(indented)
p.exitNode(node.AsNode(), state)
}
// 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
func (p *Printer) mayNeedDotDotForPropertyAccess(expression *ast.Expression) bool {
expression = ast.SkipPartiallyEmittedExpressions(expression)
if ast.IsNumericLiteral(expression) {
// check if numeric literal is a decimal literal that was originally written with a dot
text := p.getLiteralTextOfNode(expression /*sourceFile*/, nil, getLiteralTextFlagsNeverAsciiEscape)
// If the number will be printed verbatim and it doesn't already contain a dot or an exponent indicator, add one
// if the expression doesn't have any comments that will be emitted.
return expression.AsNumericLiteral().TokenFlags&ast.TokenFlagsWithSpecifier == 0 &&
!strings.Contains(text, scanner.TokenToString(ast.KindDotToken)) &&
!strings.Contains(text, "E") &&
!strings.Contains(text, "e")
}
return false
}
func (p *Printer) emitPropertyAccessExpression(node *ast.PropertyAccessExpression) {
state := p.enterNode(node.AsNode())
p.emitExpression(node.Expression, core.IfElse(ast.IsOptionalChain(node.AsNode()), ast.OperatorPrecedenceOptionalChain, ast.OperatorPrecedenceMember))
token := node.QuestionDotToken
if token == nil {
token = p.emitContext.Factory.NewToken(ast.KindDotToken)
token.Loc = core.NewTextRange(node.Expression.End(), node.Name().Pos())
p.emitContext.AddEmitFlags(token, EFNoSourceMap)
}
linesBeforeDot := p.getLinesBetweenNodes(node.AsNode(), node.Expression, token)
p.writeLineRepeat(linesBeforeDot)
p.increaseIndentIf(linesBeforeDot > 0)
shouldEmitDotDot := token.Kind != ast.KindQuestionDotToken &&
p.mayNeedDotDotForPropertyAccess(node.Expression) &&
!p.writer.HasTrailingComment() &&
!p.writer.HasTrailingWhitespace()
if shouldEmitDotDot {
p.writePunctuation(".")
}
p.emitTokenNode(token)
linesAfterDot := p.getLinesBetweenNodes(node.AsNode(), token, node.Name())
p.writeLineRepeat(linesAfterDot)
p.increaseIndentIf(linesAfterDot > 0)
p.emitMemberName(node.Name())
p.decreaseIndentIf(linesAfterDot > 0)
p.decreaseIndentIf(linesBeforeDot > 0)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitElementAccessExpression(node *ast.ElementAccessExpression) {
state := p.enterNode(node.AsNode())
p.emitExpression(node.Expression, core.IfElse(ast.IsOptionalChain(node.AsNode()), ast.OperatorPrecedenceOptionalChain, ast.OperatorPrecedenceMember))
p.emitTokenNode(node.QuestionDotToken)
p.emitToken(ast.KindOpenBracketToken, greatestEnd(-1, node.Expression, node.QuestionDotToken), WriteKindPunctuation, node.AsNode())
p.emitExpression(node.ArgumentExpression, ast.OperatorPrecedenceComma)
p.emitToken(ast.KindCloseBracketToken, node.ArgumentExpression.End(), WriteKindPunctuation, node.AsNode())
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitArgument(node *ast.Expression) {
p.emitExpression(node, ast.OperatorPrecedenceSpread)
}
func (p *Printer) emitCallee(callee *ast.Expression, parentNode *ast.Node) {
if p.shouldEmitIndirectCall(parentNode) {
p.writePunctuation("(")
p.writeLiteral("0")
p.writePunctuation(",")
p.writeSpace()
p.emitExpression(callee, ast.OperatorPrecedenceComma)
p.writePunctuation(")")
} else if parentNode.Kind == ast.KindCallExpression && isNewExpressionWithoutArguments(ast.SkipPartiallyEmittedExpressions(callee)) {
// Parenthesize `new C` inside of a CallExpression so it is treated as `(new C)()` and not `new C()`
p.emitExpression(callee, ast.OperatorPrecedenceParentheses)
} else {
p.emitExpression(callee, core.IfElse(ast.IsOptionalChain(parentNode), ast.OperatorPrecedenceOptionalChain, ast.OperatorPrecedenceMember))
}
}
func (p *Printer) emitCallExpression(node *ast.CallExpression) {
state := p.enterNode(node.AsNode())
p.emitCallee(node.Expression, node.AsNode())
p.emitTokenNode(node.QuestionDotToken)
p.emitTypeArguments(node.AsNode(), node.TypeArguments)
p.emitList((*Printer).emitArgument, node.AsNode(), node.Arguments, LFCallExpressionArguments)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitNewExpression(node *ast.NewExpression) {
state := p.enterNode(node.AsNode())
p.emitToken(ast.KindNewKeyword, node.Pos(), WriteKindKeyword, node.AsNode())
p.writeSpace()
if ast.SkipPartiallyEmittedExpressions(node.Expression).Kind == ast.KindCallExpression {
// Parenthesize `C()` inside of a NewExpression so it is treated as `new (C())` and not `new C()`
p.emitExpression(node.Expression, ast.OperatorPrecedenceParentheses)
} else {
p.emitExpression(node.Expression, ast.OperatorPrecedenceMember)
}
p.emitTypeArguments(node.AsNode(), node.TypeArguments)
p.emitList((*Printer).emitArgument, node.AsNode(), node.Arguments, LFNewExpressionArguments)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitTemplateLiteral(node *ast.TemplateLiteral) {
switch node.Kind {
case ast.KindNoSubstitutionTemplateLiteral:
p.emitNoSubstitutionTemplateLiteral(node.AsNoSubstitutionTemplateLiteral())
case ast.KindTemplateExpression:
p.emitTemplateExpression(node.AsTemplateExpression())
default:
panic(fmt.Sprintf("unhandled TemplateLiteral: %v", node.Kind))
}
}
func (p *Printer) emitTaggedTemplateExpression(node *ast.TaggedTemplateExpression) {
state := p.enterNode(node.AsNode())
p.emitCallee(node.Tag, node.AsNode())
p.emitTypeArguments(node.AsNode(), node.TypeArguments)
p.writeSpace()
p.emitTemplateLiteral(node.Template)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitTypeAssertionExpression(node *ast.TypeAssertion) {
state := p.enterNode(node.AsNode())
p.writePunctuation("<")
p.emitTypeNodeOutsideExtends(node.Type)
p.writePunctuation(">")
p.emitExpression(node.Expression, ast.OperatorPrecedenceUpdate)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitParenthesizedExpression(node *ast.ParenthesizedExpression) {
state := p.enterNode(node.AsNode())
openParenPos := p.emitToken(ast.KindOpenParenToken, node.Pos(), WriteKindPunctuation, node.AsNode())
indented := p.writeLineSeparatorsAndIndentBefore(node.Expression, node.AsNode())
p.emitExpression(node.Expression, ast.OperatorPrecedenceComma)
p.writeLineSeparatorsAfter(node.Expression, node.AsNode())
p.decreaseIndentIf(indented)
p.emitToken(ast.KindCloseParenToken, greatestEnd(openParenPos, node.Expression), WriteKindPunctuation, node.AsNode())
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitFunctionExpression(node *ast.FunctionExpression) {
state := p.enterNode(node.AsNode())
p.generateNameIfNeeded(node.Name())
p.emitModifierList(node.AsNode(), node.Modifiers(), false /*allowDecorators*/)
p.writeKeyword("function")
p.emitTokenNode(node.AsteriskToken)
p.writeSpace()
p.emitIdentifierNameNode(node.Name())
indented := p.shouldEmitIndented(node.AsNode())
p.increaseIndentIf(indented)
p.pushNameGenerationScope(node.AsNode())
p.emitSignature(node.AsNode())
p.emitFunctionBodyNode(node.Body)
p.popNameGenerationScope(node.AsNode())
p.decreaseIndentIf(indented)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitConciseBody(node *ast.BlockOrExpression) {
switch {
case ast.IsBlock(node):
p.emitFunctionBody(node.AsBlock())
case ast.IsObjectLiteralExpression(ast.GetLeftmostExpression(node, false /*stopAtCallExpressions*/)):
p.emitExpression(node, ast.OperatorPrecedenceParentheses)
case ast.IsExpression(node):
p.emitExpression(node, ast.OperatorPrecedenceYield)
default:
panic(fmt.Sprintf("unexpected ConciseBody: %v", node.Kind))
}
}
func (p *Printer) emitArrowFunction(node *ast.ArrowFunction) {
state := p.enterNode(node.AsNode())
p.emitModifierList(node.AsNode(), node.Modifiers(), false /*allowDecorators*/)
indented := p.shouldEmitIndented(node.AsNode())
p.increaseIndentIf(indented)
p.pushNameGenerationScope(node.AsNode())
p.emitTypeParameters(node.AsNode(), node.TypeParameters)
p.emitParametersForArrow(node.AsNode(), node.Parameters)
p.emitTypeAnnotation(node.Type)
p.writeSpace()
p.emitTokenNode(node.EqualsGreaterThanToken)
p.writeSpace()
p.emitConciseBody(node.Body)
p.popNameGenerationScope(node.AsNode())
p.decreaseIndentIf(indented)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitDeleteExpression(node *ast.DeleteExpression) {
state := p.enterNode(node.AsNode())
p.emitToken(ast.KindDeleteKeyword, node.Pos(), WriteKindKeyword, node.AsNode())
p.writeSpace()
p.emitExpression(node.Expression, ast.OperatorPrecedenceUnary)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitTypeOfExpression(node *ast.TypeOfExpression) {
state := p.enterNode(node.AsNode())
p.emitToken(ast.KindTypeOfKeyword, node.Pos(), WriteKindKeyword, node.AsNode())
p.writeSpace()
p.emitExpression(node.Expression, ast.OperatorPrecedenceUnary)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitVoidExpression(node *ast.VoidExpression) {
state := p.enterNode(node.AsNode())
p.emitToken(ast.KindVoidKeyword, node.Pos(), WriteKindKeyword, node.AsNode())
p.writeSpace()
p.emitExpression(node.Expression, ast.OperatorPrecedenceUnary)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitAwaitExpression(node *ast.AwaitExpression) {
state := p.enterNode(node.AsNode())
p.emitToken(ast.KindAwaitKeyword, node.Pos(), WriteKindKeyword, node.AsNode())
p.writeSpace()
p.emitExpression(node.Expression, ast.OperatorPrecedenceUnary)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitPrefixUnaryExpression(node *ast.PrefixUnaryExpression) {
state := p.enterNode(node.AsNode())
operator := node.Operator
operand := node.Operand
p.emitToken(operator, node.Pos(), WriteKindOperator, node.AsNode())
// 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.
if operand.Kind == ast.KindPrefixUnaryExpression {
inner := operand.AsPrefixUnaryExpression().Operator
if (operator == ast.KindPlusToken && (inner == ast.KindPlusToken || inner == ast.KindPlusPlusToken)) ||
(operator == ast.KindMinusToken && (inner == ast.KindMinusToken || inner == ast.KindMinusMinusToken)) {
p.writeSpace()
}
}
p.emitExpression(node.Operand, ast.OperatorPrecedenceUnary)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitPostfixUnaryExpression(node *ast.PostfixUnaryExpression) {
state := p.enterNode(node.AsNode())
p.emitExpression(node.Operand, ast.OperatorPrecedenceLeftHandSide)
p.emitToken(node.Operator, node.Operand.End(), WriteKindOperator, node.AsNode())
p.exitNode(node.AsNode(), state)
}
// This function determines whether an expression consists of a homogeneous set of
// literal expressions or binary plus expressions that all share the same literal kind.
// It is used to determine whether the right-hand operand of a binary plus expression can be
// emitted without parentheses.
func (p *Printer) getLiteralKindOfBinaryPlusOperand(node *ast.Expression) ast.Kind {
node = ast.SkipPartiallyEmittedExpressions(node)
if ast.IsLiteralKind(node.Kind) {
return node.Kind
}
if node.Kind == ast.KindBinaryExpression {
if n := node.AsBinaryExpression(); n.OperatorToken.Kind == ast.KindPlusToken {
// !!! Determine if caching this is worthwhile over recomputing
////if n.cachedLiteralKind != KindUnknown {
//// return n.cachedLiteralKind;
////}
leftKind := p.getLiteralKindOfBinaryPlusOperand(n.Left)
literalKind := ast.KindUnknown
if ast.IsLiteralKind(leftKind) && leftKind == p.getLiteralKindOfBinaryPlusOperand(n.Right) {
literalKind = leftKind
}
////n.cachedLiteralKind = literalKind;
return literalKind
}
}
return ast.KindUnknown
}
func (p *Printer) getBinaryExpressionPrecedence(node *ast.BinaryExpression) (leftPrec ast.OperatorPrecedence, rightPrec ast.OperatorPrecedence) {
precedence := ast.GetExpressionPrecedence(node.AsNode())
leftPrec = precedence
rightPrec = precedence
switch precedence {
case ast.OperatorPrecedenceComma:
// No need to parenthesize the right operand when the binary operator and
// operand are both ,:
// x,(a,b) => x,a,b
break
case ast.OperatorPrecedenceAssignment:
// assignment is right-associative
leftPrec = ast.OperatorPrecedenceLeftHandSide
case ast.OperatorPrecedenceLogicalOR:
rightPrec = ast.OperatorPrecedenceLogicalAND
case ast.OperatorPrecedenceLogicalAND:
rightPrec = ast.OperatorPrecedenceBitwiseOR
case ast.OperatorPrecedenceBitwiseOR:
// No need to parenthesize the right operand when the binary operator and
// operand are both | due to the associative property of mathematics:
// x|(a|b) => x|a|b
break
case ast.OperatorPrecedenceBitwiseXOR:
// No need to parenthesize the right operand when the binary operator and
// operand are both ^ due to the associative property of mathematics:
// x^(a^b) => x^a^b
break
case ast.OperatorPrecedenceBitwiseAND:
// No need to parenthesize the right operand when the binary operator and
// operand are both & due to the associative property of mathematics:
// x&(a&b) => x&a&b
break
case ast.OperatorPrecedenceEquality:
rightPrec = ast.OperatorPrecedenceRelational
case ast.OperatorPrecedenceRelational:
rightPrec = ast.OperatorPrecedenceShift
case ast.OperatorPrecedenceShift:
rightPrec = ast.OperatorPrecedenceAdditive
case ast.OperatorPrecedenceAdditive:
if node.OperatorToken.Kind == ast.KindPlusToken && isBinaryOperation(node.Right, ast.KindPlusToken) {
leftKind := p.getLiteralKindOfBinaryPlusOperand(node.Left)
if ast.IsLiteralKind(leftKind) && leftKind == p.getLiteralKindOfBinaryPlusOperand(node.Right) {
// No need to parenthesize the right operand when the binary operator
// is plus (+) if both the left and right operands consist solely of either
// literals of the same kind or binary plus (+) expressions for literals of
// the same kind (recursively).
// "a"+(1+2) => "a"+(1+2)
// "a"+("b"+"c") => "a"+"b"+"c"
break
}
}
rightPrec = ast.OperatorPrecedenceMultiplicative
case ast.OperatorPrecedenceMultiplicative:
if node.OperatorToken.Kind == ast.KindAsteriskToken && isBinaryOperation(node.Right, ast.KindAsteriskToken) {
// No need to parenthesize the right operand when the binary operator and
// operand are both * due to the associative property of mathematics:
// x*(a*b) => x*a*b
break
}
rightPrec = ast.OperatorPrecedenceExponentiation
case ast.OperatorPrecedenceExponentiation:
// exponentiation is right-associative
leftPrec = ast.OperatorPrecedenceUpdate
default:
panic(fmt.Sprintf("unhandled precedence: %v", precedence))
}
return leftPrec, rightPrec
}
func (p *Printer) emitBinaryExpression(node *ast.BinaryExpression) {
leftPrec, rightPrec := p.getBinaryExpressionPrecedence(node)
state := p.enterNode(node.AsNode())
p.emitExpression(node.Left, leftPrec)
linesBeforeOperator := p.getLinesBetweenNodes(node.AsNode(), node.Left, node.OperatorToken)
linesAfterOperator := p.getLinesBetweenNodes(node.AsNode(), node.OperatorToken, node.Right)
p.writeLinesAndIndent(linesBeforeOperator, node.OperatorToken.Kind != ast.KindCommaToken /*writeSpaceIfNotIndenting*/)
p.emitTokenNodeEx(node.OperatorToken, tefNoSourceMaps)
p.writeLinesAndIndent(linesAfterOperator, true /*writeSpaceIfNotIndenting*/) // Binary operators should have a space before the comment starts
p.emitExpression(node.Right, rightPrec)
p.decreaseIndentIf(linesAfterOperator > 0)
p.decreaseIndentIf(linesBeforeOperator > 0)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitShortCircuitExpression(node *ast.Expression) {
if isBinaryOperation(ast.SkipPartiallyEmittedExpressions(node), ast.KindQuestionQuestionToken) {
p.emitExpression(node, ast.OperatorPrecedenceCoalesce)
} else {
p.emitExpression(node, ast.OperatorPrecedenceLogicalOR)
}
}
func (p *Printer) emitConditionalExpression(node *ast.ConditionalExpression) {
state := p.enterNode(node.AsNode())
linesBeforeQuestion := p.getLinesBetweenNodes(node.AsNode(), node.Condition, node.QuestionToken)
linesAfterQuestion := p.getLinesBetweenNodes(node.AsNode(), node.QuestionToken, node.WhenTrue)
linesBeforeColon := p.getLinesBetweenNodes(node.AsNode(), node.WhenTrue, node.ColonToken)
linesAfterColon := p.getLinesBetweenNodes(node.AsNode(), node.ColonToken, node.WhenFalse)
p.emitShortCircuitExpression(node.Condition)
p.writeLinesAndIndent(linesBeforeQuestion /*writeSpaceIfNotIndenting*/, true)
p.emitPunctuationNode(node.QuestionToken)
p.writeLinesAndIndent(linesAfterQuestion /*writeSpaceIfNotIndenting*/, true)
p.emitExpression(node.WhenTrue, ast.OperatorPrecedenceYield)
p.decreaseIndentIf(linesAfterQuestion > 0)
p.decreaseIndentIf(linesBeforeQuestion > 0)
p.writeLinesAndIndent(linesBeforeColon /*writeSpaceIfNotIndenting*/, true)
p.emitPunctuationNode(node.ColonToken)
p.writeLinesAndIndent(linesAfterColon /*writeSpaceIfNotIndenting*/, true)
p.emitExpression(node.WhenFalse, ast.OperatorPrecedenceYield)
p.decreaseIndentIf(linesAfterColon > 0)
p.decreaseIndentIf(linesBeforeColon > 0)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitTemplateExpression(node *ast.TemplateExpression) {
state := p.enterNode(node.AsNode())
p.emitTemplateHead(node.Head.AsTemplateHead())
p.emitList((*Printer).emitTemplateSpanNode, node.AsNode(), node.TemplateSpans, LFTemplateExpressionSpans)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitYieldExpression(node *ast.YieldExpression) {
state := p.enterNode(node.AsNode())
p.emitToken(ast.KindYieldKeyword, node.Pos(), WriteKindKeyword, node.AsNode())
p.emitPunctuationNode(node.AsteriskToken)
if node.Expression != nil {
p.writeSpace()
p.emitExpressionNoASI(node.Expression, ast.OperatorPrecedenceDisallowComma)
}
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitSpreadElement(node *ast.SpreadElement) {
state := p.enterNode(node.AsNode())
p.emitToken(ast.KindDotDotDotToken, node.Pos(), WriteKindPunctuation, node.AsNode())
p.emitExpression(node.Expression, ast.OperatorPrecedenceDisallowComma)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitClassExpression(node *ast.ClassExpression) {
state := p.enterNode(node.AsNode())
p.generateNameIfNeeded(node.Name())
p.emitModifierList(node.AsNode(), node.Modifiers(), true /*allowDecorators*/)
p.emitToken(ast.KindClassKeyword, greatestEnd(node.Pos(), node.Modifiers()), WriteKindKeyword, node.AsNode())
if node.Name() != nil {
p.writeSpace()
p.emitIdentifierName(node.Name().AsIdentifier())
}
indented := p.shouldEmitIndented(node.AsNode())
p.increaseIndentIf(indented)
p.emitTypeParameters(node.AsNode(), node.TypeParameters)
p.emitList((*Printer).emitHeritageClauseNode, node.AsNode(), node.HeritageClauses, LFClassHeritageClauses)
p.writeSpace()
p.writePunctuation("{")
p.pushNameGenerationScope(node.AsNode())
p.generateAllMemberNames(node.Members)
p.emitList((*Printer).emitClassElement, node.AsNode(), node.Members, LFClassMembers)
p.popNameGenerationScope(node.AsNode())
p.writePunctuation("}")
p.decreaseIndentIf(indented)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitOmittedExpression(node *ast.Node) {
p.exitNode(node, p.enterNode(node))
}
func (p *Printer) emitExpressionWithTypeArguments(node *ast.ExpressionWithTypeArguments) {
state := p.enterNode(node.AsNode())
p.emitExpression(node.Expression, ast.OperatorPrecedenceMember)
p.emitTypeArguments(node.AsNode(), node.TypeArguments)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitExpressionWithTypeArgumentsNode(node *ast.ExpressionWithTypeArgumentsNode) {
p.emitExpressionWithTypeArguments(node.AsExpressionWithTypeArguments())
}
func (p *Printer) emitAsExpression(node *ast.AsExpression) {
state := p.enterNode(node.AsNode())
p.emitExpression(node.Expression, ast.OperatorPrecedenceRelational)
p.writeSpace()
p.writeKeyword("as")
p.writeSpace()
p.emitTypeNodeOutsideExtends(node.Type)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitSatisfiesExpression(node *ast.SatisfiesExpression) {
state := p.enterNode(node.AsNode())
p.emitExpression(node.Expression, ast.OperatorPrecedenceRelational)
p.writeSpace()
p.writeKeyword("satisfies")
p.writeSpace()
p.emitTypeNodeOutsideExtends(node.Type)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitNonNullExpression(node *ast.NonNullExpression) {
state := p.enterNode(node.AsNode())
p.emitExpression(node.Expression, ast.OperatorPrecedenceMember)
p.writeOperator("!")
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitMetaProperty(node *ast.MetaProperty) {
state := p.enterNode(node.AsNode())
p.emitToken(node.KeywordToken, node.Pos(), WriteKindPunctuation, node.AsNode())
p.writePunctuation(".")
p.emitIdentifierName(node.Name().AsIdentifier())
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitPartiallyEmittedExpression(node *ast.PartiallyEmittedExpression, precedence ast.OperatorPrecedence) {
// avoid reprinting parens for nested partially emitted expressions
type entry struct {
node *ast.PartiallyEmittedExpression
state printerState
}
var stack core.Stack[entry]
for {
state := p.enterNode(node.AsNode())
stack.Push(entry{node, state})
if !ast.IsPartiallyEmittedExpression(node.Expression) {
break
}
node = node.Expression.AsPartiallyEmittedExpression()
}
p.emitExpression(node.Expression, precedence)
// unwind stack
for stack.Len() > 0 {
entry := stack.Pop()
p.exitNode(node.AsNode(), entry.state)
node = entry.node
}
}
func (p *Printer) willEmitLeadingNewLine(node *ast.Expression) bool {
return false // !!! check if node will emit a leading comment that contains a trailing newline
}
func (p *Printer) emitExpressionNoASI(node *ast.Expression, precedence ast.OperatorPrecedence) {
// !!! restore parens when necessary to ensure a leading single-line comment doesn't introduce ASI:
// function f() {
// return (// comment
// a as T
// )
// }
// If we do not restore the parens, we would produce the following incorrect output:
// function f() {
// return // comment
// a;
// }
// Due to ASI, this would result in a `return` with no value followed by an unreachable expression statement.
if !p.commentsDisabled && node.Kind == ast.KindPartiallyEmittedExpression && p.willEmitLeadingNewLine(node) {
// !!! if there is an original parse tree node, restore it with location to preserve comments and source maps.
p.emitExpression(node, ast.OperatorPrecedenceParentheses)
} else {
p.emitExpression(node, precedence)
}
}
func (p *Printer) emitExpression(node *ast.Expression, precedence ast.OperatorPrecedence) {
parens := ast.GetExpressionPrecedence(ast.SkipPartiallyEmittedExpressions(node)) < precedence
if parens {
p.writePunctuation("(")
}
switch node.Kind {
// Keywords
case ast.KindTrueKeyword, ast.KindFalseKeyword, ast.KindNullKeyword:
p.emitTokenNode(node)
case ast.KindThisKeyword, ast.KindSuperKeyword, ast.KindImportKeyword:
p.emitKeywordExpression(node.AsKeywordExpression())
// Literals
case ast.KindNumericLiteral:
p.emitNumericLiteral(node.AsNumericLiteral())
case ast.KindBigIntLiteral:
p.emitBigIntLiteral(node.AsBigIntLiteral())
case ast.KindStringLiteral:
p.emitStringLiteral(node.AsStringLiteral())
case ast.KindRegularExpressionLiteral:
p.emitRegularExpressionLiteral(node.AsRegularExpressionLiteral())
case ast.KindNoSubstitutionTemplateLiteral:
p.emitNoSubstitutionTemplateLiteral(node.AsNoSubstitutionTemplateLiteral())
// Identifiers
case ast.KindIdentifier:
p.emitIdentifierReference(node.AsIdentifier())
case ast.KindPrivateIdentifier:
p.emitPrivateIdentifier(node.AsPrivateIdentifier())
// Expressions
case ast.KindArrayLiteralExpression:
p.emitArrayLiteralExpression(node.AsArrayLiteralExpression())
case ast.KindObjectLiteralExpression:
p.emitObjectLiteralExpression(node.AsObjectLiteralExpression())
case ast.KindPropertyAccessExpression:
p.emitPropertyAccessExpression(node.AsPropertyAccessExpression())
case ast.KindElementAccessExpression:
p.emitElementAccessExpression(node.AsElementAccessExpression())
case ast.KindCallExpression:
p.emitCallExpression(node.AsCallExpression())
case ast.KindNewExpression:
p.emitNewExpression(node.AsNewExpression())
case ast.KindTaggedTemplateExpression:
p.emitTaggedTemplateExpression(node.AsTaggedTemplateExpression())
case ast.KindTypeAssertionExpression:
p.emitTypeAssertionExpression(node.AsTypeAssertion())
case ast.KindParenthesizedExpression:
p.emitParenthesizedExpression(node.AsParenthesizedExpression())
case ast.KindFunctionExpression:
p.emitFunctionExpression(node.AsFunctionExpression())
case ast.KindArrowFunction:
p.emitArrowFunction(node.AsArrowFunction())
case ast.KindDeleteExpression:
p.emitDeleteExpression(node.AsDeleteExpression())
case ast.KindTypeOfExpression:
p.emitTypeOfExpression(node.AsTypeOfExpression())
case ast.KindVoidExpression:
p.emitVoidExpression(node.AsVoidExpression())
case ast.KindAwaitExpression:
p.emitAwaitExpression(node.AsAwaitExpression())
case ast.KindPrefixUnaryExpression:
p.emitPrefixUnaryExpression(node.AsPrefixUnaryExpression())
case ast.KindPostfixUnaryExpression:
p.emitPostfixUnaryExpression(node.AsPostfixUnaryExpression())
case ast.KindBinaryExpression:
p.emitBinaryExpression(node.AsBinaryExpression())
case ast.KindConditionalExpression:
p.emitConditionalExpression(node.AsConditionalExpression())
case ast.KindTemplateExpression:
p.emitTemplateExpression(node.AsTemplateExpression())
case ast.KindYieldExpression:
p.emitYieldExpression(node.AsYieldExpression())
case ast.KindSpreadElement:
p.emitSpreadElement(node.AsSpreadElement())
case ast.KindClassExpression:
p.emitClassExpression(node.AsClassExpression())
case ast.KindOmittedExpression:
p.emitOmittedExpression(node)
case ast.KindAsExpression:
p.emitAsExpression(node.AsAsExpression())
case ast.KindNonNullExpression:
p.emitNonNullExpression(node.AsNonNullExpression())
case ast.KindExpressionWithTypeArguments:
p.emitExpressionWithTypeArguments(node.AsExpressionWithTypeArguments())
case ast.KindSatisfiesExpression:
p.emitSatisfiesExpression(node.AsSatisfiesExpression())
case ast.KindMetaProperty:
p.emitMetaProperty(node.AsMetaProperty())
case ast.KindSyntheticExpression:
panic("SyntheticExpression should never be printed.")
case ast.KindMissingDeclaration:
break
// JSX
case ast.KindJsxElement:
p.emitJsxElement(node.AsJsxElement())
case ast.KindJsxSelfClosingElement:
p.emitJsxSelfClosingElement(node.AsJsxSelfClosingElement())
case ast.KindJsxFragment:
p.emitJsxFragment(node.AsJsxFragment())
// Synthesized list
case ast.KindSyntaxList:
panic("SyntaxList should not be printed")
// Transformation nodes
case ast.KindNotEmittedStatement:
return
case ast.KindPartiallyEmittedExpression:
p.emitPartiallyEmittedExpression(node.AsPartiallyEmittedExpression(), precedence)
case ast.KindSyntheticReferenceExpression:
panic("SyntheticReferenceExpression should not be printed")
// !!!
////case ast.KindCommaListExpression:
//// p.emitCommaList(node.AsCommaListExpression())
default:
panic(fmt.Sprintf("unexpected Expression: %v", node.Kind))
}
if parens {
p.writePunctuation(")")
}
}
//
// Misc
//
func (p *Printer) emitTemplateSpan(node *ast.TemplateSpan) {
state := p.enterNode(node.AsNode())
p.emitExpression(node.Expression, ast.OperatorPrecedenceComma)
p.emitTemplateMiddleTail(node.Literal)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitTemplateSpanNode(node *ast.TemplateSpanNode) {
p.emitTemplateSpan(node.AsTemplateSpan())
}
func (p *Printer) emitSemicolonClassElement(node *ast.SemicolonClassElement) {
state := p.enterNode(node.AsNode())
p.writeTrailingSemicolon()
p.exitNode(node.AsNode(), state)
}
//
// Statements
//
func (p *Printer) isEmptyBlock(block *ast.Node, statements *ast.StatementList) bool {
return len(statements.Nodes) == 0 &&
(p.currentSourceFile == nil || rangeEndIsOnSameLineAsRangeStart(block.Loc, block.Loc, p.currentSourceFile))
}
func (p *Printer) emitBlock(node *ast.Block) {
state := p.enterNode(node.AsNode())
p.generateNames(node.AsNode())
p.emitToken(ast.KindOpenBraceToken, node.Pos(), WriteKindPunctuation, node.AsNode())
format := core.IfElse(!node.Multiline && p.isEmptyBlock(node.AsNode(), node.Statements) || p.shouldEmitOnSingleLine(node.AsNode()),
LFSingleLineBlockStatements,
LFMultiLineBlockStatements)
p.emitList((*Printer).emitStatement, node.AsNode(), node.Statements, format)
p.emitTokenEx(ast.KindCloseBraceToken, node.Statements.End(), WriteKindPunctuation, node.AsNode(), core.IfElse(format&LFMultiLine != 0, tefIndentLeadingComments, tefNone))
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitVariableStatement(node *ast.VariableStatement) {
state := p.enterNode(node.AsNode())
p.emitModifierList(node.AsNode(), node.Modifiers(), false /*allowDecorators*/)
p.emitVariableDeclarationList(node.DeclarationList.AsVariableDeclarationList())
p.writeTrailingSemicolon()
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitEmptyStatement(node *ast.EmptyStatement, isEmbeddedStatement bool) {
state := p.enterNode(node.AsNode())
// While most trailing semicolons are possibly insignificant, an embedded "empty"
// statement is significant and cannot be elided by a trailing-semicolon-omitting writer.
if isEmbeddedStatement {
p.writePunctuation(";")
} else {
p.writeTrailingSemicolon()
}
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitExpressionStatement(node *ast.ExpressionStatement) {
state := p.enterNode(node.AsNode())
if p.currentSourceFile != nil && p.currentSourceFile.ScriptKind == core.ScriptKindJSON {
// !!! In strada, this was handled by an undefined parenthesizerRule, so this is a hack.
p.emitExpression(node.Expression, ast.OperatorPrecedenceComma)
} else if isImmediatelyInvokedFunctionExpressionOrArrowFunction(node.Expression) {
// !!! introduce parentheses around callee
p.emitExpression(node.Expression, ast.OperatorPrecedenceParentheses)
} else {
switch ast.GetLeftmostExpression(node.Expression, false /*stopAtCallExpression*/).Kind {
case ast.KindFunctionExpression, ast.KindClassExpression, ast.KindObjectLiteralExpression:
p.emitExpression(node.Expression, ast.OperatorPrecedenceParentheses)
default:
p.emitExpression(node.Expression, ast.OperatorPrecedenceComma)
}
}
// Emit semicolon in non json files
// or if json file that created synthesized expression(eg.define expression statement when --out and amd code generation)
if p.currentSourceFile == nil ||
p.currentSourceFile.ScriptKind != core.ScriptKindJSON ||
ast.NodeIsSynthesized(node.Expression) {
p.writeTrailingSemicolon()
}
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitIfStatement(node *ast.IfStatement) {
state := p.enterNode(node.AsNode())
pos := p.emitToken(ast.KindIfKeyword, node.Pos(), WriteKindKeyword, node.AsNode())
p.writeSpace()
p.emitToken(ast.KindOpenParenToken, pos, WriteKindPunctuation, node.AsNode())
p.emitExpression(node.Expression, ast.OperatorPrecedenceLowest)
p.emitToken(ast.KindCloseParenToken, node.Expression.End(), WriteKindPunctuation, node.AsNode())
p.emitEmbeddedStatement(node.AsNode(), node.ThenStatement)
if node.ElseStatement != nil {
p.writeLineOrSpace(node.AsNode(), node.ThenStatement, node.ElseStatement)
p.emitToken(ast.KindElseKeyword, node.ThenStatement.End(), WriteKindKeyword, node.AsNode())
if node.ElseStatement.Kind == ast.KindIfStatement {
p.writeSpace()
p.emitIfStatement(node.ElseStatement.AsIfStatement())
} else {
p.emitEmbeddedStatement(node.AsNode(), node.ElseStatement)
}
}
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitWhileClause(node *ast.Node, expression *ast.Expression, startPos int) {
pos := p.emitToken(ast.KindWhileKeyword, startPos, WriteKindKeyword, node)
p.writeSpace()
p.emitToken(ast.KindOpenParenToken, pos, WriteKindPunctuation, node)
p.emitExpression(expression, ast.OperatorPrecedenceLowest)
p.emitToken(ast.KindCloseParenToken, expression.End(), WriteKindPunctuation, node)
}
func (p *Printer) emitDoStatement(node *ast.DoStatement) {
state := p.enterNode(node.AsNode())
p.emitToken(ast.KindDoKeyword, node.Pos(), WriteKindKeyword, node.AsNode())
p.emitEmbeddedStatement(node.AsNode(), node.Statement)
if ast.IsBlock(node.Statement) && !p.Options.PreserveSourceNewlines {
p.writeSpace()
} else {
p.writeLineOrSpace(node.AsNode(), node.Statement, node.Expression)
}
p.emitWhileClause(node.AsNode(), node.Expression, node.Statement.End())
p.writeTrailingSemicolon()
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitWhileStatement(node *ast.WhileStatement) {
state := p.enterNode(node.AsNode())
p.emitWhileClause(node.AsNode(), node.Expression, node.Pos())
p.emitEmbeddedStatement(node.AsNode(), node.Statement)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitForInitializer(node *ast.ForInitializer) {
if node.Kind == ast.KindVariableDeclarationList {
p.emitVariableDeclarationList(node.AsVariableDeclarationList())
} else {
p.emitExpression(node, ast.OperatorPrecedenceLowest)
}
}
func (p *Printer) emitForStatement(node *ast.ForStatement) {
state := p.enterNode(node.AsNode())
pos := p.emitToken(ast.KindForKeyword, node.Pos(), WriteKindKeyword, node.AsNode())
p.writeSpace()
pos = p.emitToken(ast.KindOpenParenToken, pos, WriteKindPunctuation, node.AsNode())
if node.Initializer != nil {
p.emitForInitializer(node.Initializer)
pos = node.Initializer.End()
}
pos = p.emitToken(ast.KindSemicolonToken, pos, WriteKindPunctuation, node.AsNode())
if node.Condition != nil {
p.writeSpace()
p.emitExpression(node.Condition, ast.OperatorPrecedenceLowest)
pos = node.Condition.End()
}
pos = p.emitToken(ast.KindSemicolonToken, pos, WriteKindPunctuation, node.AsNode())
if node.Incrementor != nil {
p.writeSpace()
p.emitExpression(node.Incrementor, ast.OperatorPrecedenceLowest)
pos = node.Incrementor.End()
}
p.emitToken(ast.KindCloseParenToken, pos, WriteKindPunctuation, node.AsNode())
p.emitEmbeddedStatement(node.AsNode(), node.Statement)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitForInStatement(node *ast.ForInOrOfStatement) {
state := p.enterNode(node.AsNode())
pos := p.emitToken(ast.KindForKeyword, node.Pos(), WriteKindKeyword, node.AsNode())
p.writeSpace()
p.emitToken(ast.KindOpenParenToken, pos, WriteKindPunctuation, node.AsNode())
p.emitForInitializer(node.Initializer)
p.writeSpace()
p.emitToken(ast.KindInKeyword, node.Initializer.End(), WriteKindKeyword, node.AsNode())
p.writeSpace()
p.emitExpression(node.Expression, ast.OperatorPrecedenceLowest)
p.emitToken(ast.KindCloseParenToken, node.Expression.End(), WriteKindPunctuation, node.AsNode())
p.emitEmbeddedStatement(node.AsNode(), node.Statement)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitForOfStatement(node *ast.ForInOrOfStatement) {
state := p.enterNode(node.AsNode())
openParenPos := p.emitToken(ast.KindForKeyword, node.Pos(), WriteKindKeyword, node.AsNode())
p.writeSpace()
if node.AwaitModifier != nil {
p.emitKeywordNode(node.AwaitModifier)
p.writeSpace()
}
p.emitToken(ast.KindOpenParenToken, openParenPos, WriteKindPunctuation, node.AsNode())
p.emitForInitializer(node.Initializer)
p.writeSpace()
p.emitToken(ast.KindOfKeyword, node.Initializer.End(), WriteKindKeyword, node.AsNode())
p.writeSpace()
p.emitExpression(node.Expression, ast.OperatorPrecedenceLowest)
p.emitToken(ast.KindCloseParenToken, node.Expression.End(), WriteKindPunctuation, node.AsNode())
p.emitEmbeddedStatement(node.AsNode(), node.Statement)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitContinueStatement(node *ast.ContinueStatement) {
state := p.enterNode(node.AsNode())
p.emitToken(ast.KindContinueKeyword, node.Pos(), WriteKindKeyword, node.AsNode())
if node.Label != nil {
p.writeSpace()
p.emitLabelIdentifier(node.Label.AsIdentifier())
}
p.writeTrailingSemicolon()
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitBreakStatement(node *ast.BreakStatement) {
state := p.enterNode(node.AsNode())
p.emitToken(ast.KindBreakKeyword, node.Pos(), WriteKindKeyword, node.AsNode())
if node.Label != nil {
p.writeSpace()
p.emitLabelIdentifier(node.Label.AsIdentifier())
}
p.writeTrailingSemicolon()
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitReturnStatement(node *ast.ReturnStatement) {
state := p.enterNode(node.AsNode())
p.emitToken(ast.KindReturnKeyword, node.Pos(), WriteKindKeyword, node.AsNode())
if node.Expression != nil {
p.writeSpace()
p.emitExpressionNoASI(node.Expression, ast.OperatorPrecedenceLowest)
}
p.writeTrailingSemicolon()
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitWithStatement(node *ast.WithStatement) {
state := p.enterNode(node.AsNode())
pos := p.emitToken(ast.KindWithKeyword, node.Pos(), WriteKindKeyword, node.AsNode())
p.writeSpace()
p.emitToken(ast.KindOpenParenToken, pos, WriteKindPunctuation, node.AsNode())
p.emitExpression(node.Expression, ast.OperatorPrecedenceLowest)
p.emitToken(ast.KindCloseParenToken, node.Expression.End(), WriteKindPunctuation, node.AsNode())
p.emitEmbeddedStatement(node.AsNode(), node.Statement)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitSwitchStatement(node *ast.SwitchStatement) {
state := p.enterNode(node.AsNode())
pos := p.emitToken(ast.KindSwitchKeyword, node.Pos(), WriteKindKeyword, node.AsNode())
p.writeSpace()
p.emitToken(ast.KindOpenParenToken, pos, WriteKindPunctuation, node.AsNode())
p.emitExpression(node.Expression, ast.OperatorPrecedenceLowest)
p.emitToken(ast.KindCloseParenToken, node.Expression.End(), WriteKindPunctuation, node.AsNode())
p.writeSpace()
p.emitCaseBlock(node.CaseBlock.AsCaseBlock())
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitLabeledStatement(node *ast.LabeledStatement) {
state := p.enterNode(node.AsNode())
p.emitLabelIdentifier(node.Label.AsIdentifier())
p.emitToken(ast.KindColonToken, node.Label.End(), WriteKindPunctuation, node.AsNode())
// TODO: use emitEmbeddedStatement rather than writeSpace/emitStatement here after Strada migration as it is
// more consistent with similar emit elsewhere. writeSpace/emitStatement is used here to reduce spurious
// diffs when testing the Strada migration.
////p.emitEmbeddedStatement(node.AsNode(), node.Statement)
p.writeSpace()
p.emitStatement(node.Statement)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitThrowStatement(node *ast.ThrowStatement) {
state := p.enterNode(node.AsNode())
p.emitToken(ast.KindThrowKeyword, node.Pos(), WriteKindKeyword, node.AsNode())
p.writeSpace()
p.emitExpressionNoASI(node.Expression, ast.OperatorPrecedenceLowest)
p.writeTrailingSemicolon()
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitTryStatement(node *ast.TryStatement) {
state := p.enterNode(node.AsNode())
p.emitToken(ast.KindTryKeyword, node.Pos(), WriteKindKeyword, node.AsNode())
p.writeSpace()
p.emitBlock(node.TryBlock.AsBlock())
if node.CatchClause != nil {
p.writeLineOrSpace(node.AsNode(), node.TryBlock, node.CatchClause)
p.emitCatchClause(node.CatchClause.AsCatchClause())
}
if node.FinallyBlock != nil {
p.writeLineOrSpace(node.AsNode(), core.Coalesce(node.CatchClause, node.TryBlock), node.FinallyBlock)
p.emitToken(ast.KindFinallyKeyword, core.Coalesce(node.CatchClause, node.TryBlock).End(), WriteKindKeyword, node.AsNode())
p.writeSpace()
p.emitBlock(node.FinallyBlock.AsBlock())
}
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitDebuggerStatement(node *ast.DebuggerStatement) {
state := p.enterNode(node.AsNode())
p.emitToken(ast.KindDebuggerKeyword, node.Pos(), WriteKindKeyword, node.AsNode())
p.writeTrailingSemicolon()
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitNotEmittedStatement(node *ast.NotEmittedStatement) {
p.exitNode(node.AsNode(), p.enterNode(node.AsNode()))
}
func (p *Printer) emitNotEmittedTypeElement(node *ast.NotEmittedTypeElement) {
p.exitNode(node.AsNode(), p.enterNode(node.AsNode()))
}
//
// Declarations
//
func (p *Printer) emitVariableDeclaration(node *ast.VariableDeclaration) {
state := p.enterNode(node.AsNode())
p.emitBindingName(node.Name())
p.emitPunctuationNode(node.ExclamationToken)
p.emitTypeAnnotation(node.Type)
// !!! old compiler can set a type node purely for emit. Is this necessary?
p.emitInitializer(node.Initializer, greatestEnd(node.Name().End(), node.Type /*, node.Name().emitNode?.typeNode*/), node.AsNode())
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitVariableDeclarationNode(node *ast.VariableDeclarationNode) {
p.emitVariableDeclaration(node.AsVariableDeclaration())
}
func (p *Printer) emitVariableDeclarationList(node *ast.VariableDeclarationList) {
state := p.enterNode(node.AsNode())
switch {
case ast.IsVarLet(node.AsNode()):
p.writeKeyword("let")
case ast.IsVarConst(node.AsNode()):
p.writeKeyword("const")
case ast.IsVarUsing(node.AsNode()):
p.writeKeyword("using")
case ast.IsVarAwaitUsing(node.AsNode()):
p.writeKeyword("await")
p.writeSpace()
p.writeKeyword("using")
default:
p.writeKeyword("var")
}
p.writeSpace()
p.emitList((*Printer).emitVariableDeclarationNode, node.AsNode(), node.Declarations, LFVariableDeclarationList)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitFunctionDeclaration(node *ast.FunctionDeclaration) {
state := p.enterNode(node.AsNode())
p.generateNameIfNeeded(node.Name())
p.emitModifierList(node.AsNode(), node.Modifiers(), false /*allowDecorators*/)
p.writeKeyword("function")
p.emitTokenNode(node.AsteriskToken)
p.writeSpace()
if name := node.Name(); name != nil {
p.emitIdentifierName(name.AsIdentifier())
}
indented := p.shouldEmitIndented(node.AsNode())
p.increaseIndentIf(indented)
p.pushNameGenerationScope(node.AsNode())
p.emitSignature(node.AsNode())
p.emitFunctionBodyNode(node.Body)
p.popNameGenerationScope(node.AsNode())
p.decreaseIndentIf(indented)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitClassDeclaration(node *ast.ClassDeclaration) {
state := p.enterNode(node.AsNode())
p.generateNameIfNeeded(node.Name())
p.emitModifierList(node.AsNode(), node.Modifiers(), true /*allowDecorators*/)
p.emitToken(ast.KindClassKeyword, greatestEnd(node.Pos(), node.Modifiers()), WriteKindKeyword, node.AsNode())
if node.Name() != nil {
p.writeSpace()
p.emitIdentifierName(node.Name().AsIdentifier())
}
indented := p.shouldEmitIndented(node.AsNode())
p.increaseIndentIf(indented)
p.emitTypeParameters(node.AsNode(), node.TypeParameters)
p.emitList((*Printer).emitHeritageClauseNode, node.AsNode(), node.HeritageClauses, LFClassHeritageClauses)
p.writeSpace()
p.writePunctuation("{")
p.pushNameGenerationScope(node.AsNode())
p.generateAllMemberNames(node.Members)
p.emitList((*Printer).emitClassElement, node.AsNode(), node.Members, LFClassMembers)
p.popNameGenerationScope(node.AsNode())
p.writePunctuation("}")
p.decreaseIndentIf(indented)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitInterfaceDeclaration(node *ast.InterfaceDeclaration) {
state := p.enterNode(node.AsNode())
p.emitModifierList(node.AsNode(), node.Modifiers(), false /*allowDecorators*/)
p.writeKeyword("interface")
p.writeSpace()
p.emitBindingIdentifier(node.Name().AsIdentifier())
p.emitTypeParameters(node.AsNode(), node.TypeParameters)
p.emitList((*Printer).emitHeritageClauseNode, node.AsNode(), node.HeritageClauses, LFHeritageClauses)
p.writeSpace()
p.writePunctuation("{")
p.pushNameGenerationScope(node.AsNode())
p.generateAllMemberNames(node.Members)
p.emitList((*Printer).emitTypeElement, node.AsNode(), node.Members, LFInterfaceMembers)
p.popNameGenerationScope(node.AsNode())
p.writePunctuation("}")
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitTypeAliasDeclaration(node *ast.TypeAliasDeclaration) {
state := p.enterNode(node.AsNode())
p.emitModifierList(node.AsNode(), node.Modifiers(), false /*allowDecorators*/)
p.writeKeyword("type")
p.writeSpace()
p.emitBindingIdentifier(node.Name().AsIdentifier())
p.emitTypeParameters(node.AsNode(), node.TypeParameters)
p.writeSpace()
p.writePunctuation("=")
p.writeSpace()
p.emitTypeNodeOutsideExtends(node.Type)
p.writeTrailingSemicolon()
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitEnumDeclaration(node *ast.EnumDeclaration) {
state := p.enterNode(node.AsNode())
p.emitModifierList(node.AsNode(), node.Modifiers(), false /*allowDecorators*/)
p.writeKeyword("enum")
p.writeSpace()
p.emitBindingIdentifier(node.Name().AsIdentifier())
p.writeSpace()
p.writePunctuation("{")
p.emitList((*Printer).emitEnumMemberNode, node.AsNode(), node.Members, LFEnumMembers)
p.writePunctuation("}")
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitModuleDeclaration(node *ast.ModuleDeclaration) {
state := p.enterNode(node.AsNode())
p.emitModifierList(node.AsNode(), node.Modifiers(), false /*allowDecorators*/)
if node.Keyword != ast.KindGlobalKeyword {
p.writeKeyword(core.IfElse(node.Keyword == ast.KindNamespaceKeyword, "namespace", "module"))
p.writeSpace()
}
p.emitModuleName(node.Name())
body := node.Body
for body != nil && ast.IsModuleDeclaration(body) {
module := body.AsModuleDeclaration()
p.writePunctuation(".")
p.emitNestedModuleName(module.Name())
body = module.Body
}
if body == nil {
p.writeTrailingSemicolon()
} else {
p.writeSpace()
p.emitModuleBlock(body.AsModuleBlock())
}
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitModuleBlock(node *ast.ModuleBlock) {
state := p.enterNode(node.AsNode())
p.generateNames(node.AsNode())
p.emitToken(ast.KindOpenBraceToken, node.Pos(), WriteKindPunctuation, node.AsNode())
format := core.IfElse(p.isEmptyBlock(node.AsNode(), node.Statements) || p.shouldEmitOnSingleLine(node.AsNode()),
LFSingleLineBlockStatements,
LFMultiLineBlockStatements)
p.emitList((*Printer).emitStatement, node.AsNode(), node.Statements, format)
p.emitTokenEx(ast.KindCloseBraceToken, node.Statements.End(), WriteKindPunctuation, node.AsNode(), core.IfElse(format&LFMultiLine != 0, tefIndentLeadingComments, tefNone))
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitCaseBlock(node *ast.CaseBlock) {
state := p.enterNode(node.AsNode())
p.emitToken(ast.KindOpenBraceToken, node.Pos(), WriteKindPunctuation, node.AsNode())
p.emitList((*Printer).emitCaseOrDefaultClauseNode, node.AsNode(), node.Clauses, LFCaseBlockClauses)
p.emitTokenEx(ast.KindCloseBraceToken, node.Clauses.End(), WriteKindPunctuation, node.AsNode(), tefIndentLeadingComments)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitImportEqualsDeclaration(node *ast.ImportEqualsDeclaration) {
state := p.enterNode(node.AsNode())
p.emitModifierList(node.AsNode(), node.Modifiers(), false /*allowDecorators*/)
pos := p.emitToken(ast.KindImportKeyword, greatestEnd(node.Pos(), node.Modifiers()), WriteKindKeyword, node.AsNode())
p.writeSpace()
if node.IsTypeOnly {
p.emitToken(ast.KindTypeKeyword, pos, WriteKindKeyword, node.AsNode())
p.writeSpace()
}
p.emitBindingIdentifier(node.Name().AsIdentifier())
p.writeSpace()
p.emitToken(ast.KindEqualsToken, node.Name().End(), WriteKindPunctuation, node.AsNode())
p.writeSpace()
p.emitModuleReference(node.ModuleReference)
p.writeTrailingSemicolon()
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitModuleReference(node *ast.ModuleReference) {
switch node.Kind {
case ast.KindIdentifier:
p.emitIdentifierReference(node.AsIdentifier())
case ast.KindQualifiedName:
p.emitQualifiedName(node.AsQualifiedName())
case ast.KindExternalModuleReference:
p.emitExternalModuleReference(node.AsExternalModuleReference())
default:
panic(fmt.Sprintf("unhandled ModuleReference: %v", node.Kind))
}
}
func (p *Printer) emitImportDeclaration(node *ast.ImportDeclaration) {
state := p.enterNode(node.AsNode())
p.emitModifierList(node.AsNode(), node.Modifiers(), false /*allowDecorators*/)
p.emitToken(ast.KindImportKeyword, greatestEnd(node.Pos(), node.Modifiers()), WriteKindKeyword, node.AsNode())
p.writeSpace()
if node.ImportClause != nil {
p.emitImportClause(node.ImportClause.AsImportClause())
p.writeSpace()
p.emitToken(ast.KindFromKeyword, node.ImportClause.End(), WriteKindKeyword, node.AsNode())
p.writeSpace()
}
p.emitExpression(node.ModuleSpecifier, ast.OperatorPrecedenceLowest)
if node.Attributes != nil {
p.writeSpace()
p.emitImportAttributes(node.Attributes.AsImportAttributes())
}
p.writeTrailingSemicolon()
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitImportClause(node *ast.ImportClause) {
state := p.enterNode(node.AsNode())
if node.PhaseModifier != ast.KindUnknown {
p.emitToken(node.PhaseModifier, node.Pos(), WriteKindKeyword, node.AsNode())
p.writeSpace()
}
if name := node.Name(); name != nil {
p.emitBindingIdentifier(node.Name().AsIdentifier())
if node.NamedBindings != nil {
p.emitToken(ast.KindCommaToken, name.End(), WriteKindPunctuation, node.AsNode())
p.writeSpace()
}
}
p.emitNamedImportBindings(node.NamedBindings)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitNamespaceImport(node *ast.NamespaceImport) {
state := p.enterNode(node.AsNode())
pos := p.emitToken(ast.KindAsteriskToken, node.Pos(), WriteKindPunctuation, node.AsNode())
p.writeSpace()
p.emitToken(ast.KindAsKeyword, pos, WriteKindKeyword, node.AsNode())
p.writeSpace()
p.emitBindingIdentifier(node.Name().AsIdentifier())
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitNamedImports(node *ast.NamedImports) {
state := p.enterNode(node.AsNode())
p.writePunctuation("{")
p.emitList((*Printer).emitImportSpecifierNode, node.AsNode(), node.Elements, LFNamedImportsOrExportsElements)
p.writePunctuation("}")
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitNamedImportBindings(node *ast.NamedImportBindings) {
if node == nil {
return
}
switch node.Kind {
case ast.KindNamespaceImport:
p.emitNamespaceImport(node.AsNamespaceImport())
case ast.KindNamedImports:
p.emitNamedImports(node.AsNamedImports())
default:
panic(fmt.Sprintf("unhandled NamedImportBindings: %v", node.Kind))
}
}
func (p *Printer) emitImportSpecifier(node *ast.ImportSpecifier) {
state := p.enterNode(node.AsNode())
if node.IsTypeOnly {
p.writeKeyword("type")
p.writeSpace()
}
if node.PropertyName != nil {
p.emitModuleExportName(node.PropertyName)
p.writeSpace()
p.emitToken(ast.KindAsKeyword, node.PropertyName.End(), WriteKindKeyword, node.AsNode())
p.writeSpace()
}
p.emitBindingIdentifier(node.Name().AsIdentifier())
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitImportSpecifierNode(node *ast.ImportSpecifierNode) {
p.emitImportSpecifier(node.AsImportSpecifier())
}
func (p *Printer) emitExportAssignment(node *ast.ExportAssignment) {
state := p.enterNode(node.AsNode())
nextPos := p.emitToken(ast.KindExportKeyword, node.Pos(), WriteKindKeyword, node.AsNode())
p.writeSpace()
if node.IsExportEquals {
p.emitToken(ast.KindEqualsToken, nextPos, WriteKindOperator, node.AsNode())
} else {
p.emitToken(ast.KindDefaultKeyword, nextPos, WriteKindKeyword, node.AsNode())
}
p.writeSpace()
if node.IsExportEquals {
p.emitExpression(node.Expression, ast.OperatorPrecedenceAssignment)
} else {
// parenthesize `class` and `function` expressions so as not to conflict with exported `class` and `function` declarations
expr := ast.GetLeftmostExpression(node.Expression, false /*stopAtCallExpressions*/)
if ast.IsClassExpression(expr) || ast.IsFunctionExpression(expr) {
p.emitExpression(node.Expression, ast.OperatorPrecedenceParentheses)
} else {
p.emitExpression(node.Expression, ast.OperatorPrecedenceAssignment)
}
}
p.writeTrailingSemicolon()
p.exitNode(node.AsNode(), state)
}
// export declare var <name> = <initialiser>;
func (p *Printer) emitCommonJSExport(node *ast.CommonJSExport) {
state := p.enterNode(node.AsNode())
p.emitToken(ast.KindExportKeyword, node.Pos(), WriteKindKeyword, node.AsNode())
p.writeSpace()
p.writeKeyword("var")
p.writeSpace()
if node.Name().Kind == ast.KindStringLiteral {
// TODO: This doesn't work for illegal names.
p.write(node.Name().AsStringLiteral().Text)
} else {
p.emitBindingName(node.Name())
}
p.emitInitializer(node.Initializer, node.Name().End(), node.AsNode())
p.writeTrailingSemicolon()
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitExportDeclaration(node *ast.ExportDeclaration) {
state := p.enterNode(node.AsNode())
p.emitModifierList(node.AsNode(), node.Modifiers(), false /*allowDecorators*/)
pos := p.emitToken(ast.KindExportKeyword, node.Pos(), WriteKindKeyword, node.AsNode())
p.writeSpace()
if node.IsTypeOnly {
pos = p.emitToken(ast.KindTypeKeyword, pos, WriteKindKeyword, node.AsNode())
p.writeSpace()
}
if node.ExportClause != nil {
p.emitNamedExportBindings(node.ExportClause)
} else {
pos = p.emitToken(ast.KindAsteriskToken, pos, WriteKindPunctuation, node.AsNode())
}
if node.ModuleSpecifier != nil {
p.writeSpace()
p.emitToken(ast.KindFromKeyword, greatestEnd(pos, node.ExportClause), WriteKindKeyword, node.AsNode())
p.writeSpace()
p.emitExpression(node.ModuleSpecifier, ast.OperatorPrecedenceLowest)
}
if node.Attributes != nil {
p.writeSpace()
p.emitImportAttributes(node.Attributes.AsImportAttributes())
}
p.writeTrailingSemicolon()
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitImportAttributes(node *ast.ImportAttributes) {
state := p.enterNode(node.AsNode())
p.emitToken(node.Token, node.Pos(), WriteKindKeyword, node.AsNode())
p.writeSpace()
p.emitList((*Printer).emitImportAttributeNode, node.AsNode(), node.Attributes, LFImportAttributes)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitImportAttribute(node *ast.ImportAttribute) {
state := p.enterNode(node.AsNode())
p.emitImportAttributeName(node.Name())
p.writePunctuation(":")
p.writeSpace()
/// !!! emit trailing comments of value
p.emitExpression(node.Value, ast.OperatorPrecedenceDisallowComma)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitImportAttributeNode(node *ast.ImportAttributeNode) {
p.emitImportAttribute(node.AsImportAttribute())
}
func (p *Printer) emitNamespaceExportDeclaration(node *ast.NamespaceExportDeclaration) {
state := p.enterNode(node.AsNode())
pos := p.emitToken(ast.KindExportKeyword, node.Pos(), WriteKindKeyword, node.AsNode())
p.writeSpace()
pos = p.emitToken(ast.KindAsKeyword, pos, WriteKindKeyword, node.AsNode())
p.writeSpace()
p.emitToken(ast.KindNamespaceKeyword, pos, WriteKindKeyword, node.AsNode())
p.writeSpace()
p.emitBindingIdentifier(node.Name().AsIdentifier())
p.writeTrailingSemicolon()
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitNamespaceExport(node *ast.NamespaceExport) {
state := p.enterNode(node.AsNode())
pos := p.emitToken(ast.KindAsteriskToken, node.Pos(), WriteKindPunctuation, node.AsNode())
p.writeSpace()
p.emitToken(ast.KindAsKeyword, pos, WriteKindKeyword, node.AsNode())
p.writeSpace()
p.emitModuleExportName(node.Name())
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitNamedExports(node *ast.NamedExports) {
state := p.enterNode(node.AsNode())
p.writePunctuation("{")
p.emitList((*Printer).emitExportSpecifierNode, node.AsNode(), node.Elements, LFNamedImportsOrExportsElements)
p.writePunctuation("}")
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitNamedExportBindings(node *ast.NamedExportBindings) {
switch node.Kind {
case ast.KindNamespaceExport:
p.emitNamespaceExport(node.AsNamespaceExport())
case ast.KindNamedExports:
p.emitNamedExports(node.AsNamedExports())
default:
panic(fmt.Sprintf("unhandled NamedExportBindings: %v", node.Kind))
}
}
func (p *Printer) emitExportSpecifier(node *ast.ExportSpecifier) {
state := p.enterNode(node.AsNode())
if node.IsTypeOnly {
p.writeKeyword("type")
p.writeSpace()
}
if node.PropertyName != nil {
p.emitModuleExportName(node.PropertyName)
p.writeSpace()
p.emitToken(ast.KindAsKeyword, node.PropertyName.End(), WriteKindKeyword, node.AsNode())
p.writeSpace()
}
p.emitModuleExportName(node.Name())
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitExportSpecifierNode(node *ast.ExportSpecifierNode) {
p.emitExportSpecifier(node.AsExportSpecifier())
}
func (p *Printer) emitEmbeddedStatement(parentNode *ast.Node, node *ast.Statement) {
if ast.IsBlock(node) ||
p.shouldEmitOnSingleLine(parentNode) ||
p.Options.PreserveSourceNewlines && p.getLeadingLineTerminatorCount(parentNode, node, LFNone) == 0 {
p.writeSpace()
p.emitStatement(node)
} else {
p.writeLine()
p.increaseIndent()
if node.Kind == ast.KindEmptyStatement {
p.emitEmptyStatement(node.AsEmptyStatement(), true /*isEmbeddedStatement*/)
} else {
p.emitStatement(node)
}
p.decreaseIndent()
}
}
func (p *Printer) emitStatement(node *ast.Statement) {
switch node.Kind {
// Statements
case ast.KindBlock:
p.emitBlock(node.AsBlock())
case ast.KindEmptyStatement:
p.emitEmptyStatement(node.AsEmptyStatement(), false /*isEmbeddedStatement*/)
case ast.KindVariableStatement:
p.emitVariableStatement(node.AsVariableStatement())
case ast.KindExpressionStatement:
p.emitExpressionStatement(node.AsExpressionStatement())
case ast.KindIfStatement:
p.emitIfStatement(node.AsIfStatement())
case ast.KindDoStatement:
p.emitDoStatement(node.AsDoStatement())
case ast.KindWhileStatement:
p.emitWhileStatement(node.AsWhileStatement())
case ast.KindForStatement:
p.emitForStatement(node.AsForStatement())
case ast.KindForInStatement:
p.emitForInStatement(node.AsForInOrOfStatement())
case ast.KindForOfStatement:
p.emitForOfStatement(node.AsForInOrOfStatement())
case ast.KindContinueStatement:
p.emitContinueStatement(node.AsContinueStatement())
case ast.KindBreakStatement:
p.emitBreakStatement(node.AsBreakStatement())
case ast.KindReturnStatement:
p.emitReturnStatement(node.AsReturnStatement())
case ast.KindWithStatement:
p.emitWithStatement(node.AsWithStatement())
case ast.KindSwitchStatement:
p.emitSwitchStatement(node.AsSwitchStatement())
case ast.KindLabeledStatement:
p.emitLabeledStatement(node.AsLabeledStatement())
case ast.KindThrowStatement:
p.emitThrowStatement(node.AsThrowStatement())
case ast.KindTryStatement:
p.emitTryStatement(node.AsTryStatement())
case ast.KindDebuggerStatement:
p.emitDebuggerStatement(node.AsDebuggerStatement())
case ast.KindNotEmittedStatement:
p.emitNotEmittedStatement(node.AsNotEmittedStatement())
// Declaration Statements
case ast.KindFunctionDeclaration:
p.emitFunctionDeclaration(node.AsFunctionDeclaration())
case ast.KindClassDeclaration:
p.emitClassDeclaration(node.AsClassDeclaration())
case ast.KindInterfaceDeclaration:
p.emitInterfaceDeclaration(node.AsInterfaceDeclaration())
case ast.KindTypeAliasDeclaration, ast.KindJSTypeAliasDeclaration:
p.emitTypeAliasDeclaration(node.AsTypeAliasDeclaration())
case ast.KindEnumDeclaration:
p.emitEnumDeclaration(node.AsEnumDeclaration())
case ast.KindModuleDeclaration:
p.emitModuleDeclaration(node.AsModuleDeclaration())
case ast.KindMissingDeclaration:
break
// Import/Export Statements
case ast.KindNamespaceExportDeclaration:
p.emitNamespaceExportDeclaration(node.AsNamespaceExportDeclaration())
case ast.KindImportEqualsDeclaration:
p.emitImportEqualsDeclaration(node.AsImportEqualsDeclaration())
case ast.KindImportDeclaration:
p.emitImportDeclaration(node.AsImportDeclaration())
case ast.KindExportAssignment, ast.KindJSExportAssignment:
p.emitExportAssignment(node.AsExportAssignment())
case ast.KindExportDeclaration:
p.emitExportDeclaration(node.AsExportDeclaration())
case ast.KindCommonJSExport:
p.emitCommonJSExport(node.AsCommonJSExport())
default:
panic(fmt.Sprintf("unhandled statement: %v", node.Kind))
}
}
//
// Module references
//
func (p *Printer) emitExternalModuleReference(node *ast.ExternalModuleReference) {
state := p.enterNode(node.AsNode())
p.writeKeyword("require")
p.writePunctuation("(")
p.emitExpression(node.Expression, ast.OperatorPrecedenceDisallowComma)
p.writePunctuation(")")
p.exitNode(node.AsNode(), state)
}
//
// JSX
//
func (p *Printer) emitJsxElement(node *ast.JsxElement) {
state := p.enterNode(node.AsNode())
p.emitJsxOpeningElement(node.OpeningElement.AsJsxOpeningElement())
p.emitList((*Printer).emitJsxChild, node.AsNode(), node.Children, LFJsxElementOrFragmentChildren)
p.emitJsxClosingElement(node.ClosingElement.AsJsxClosingElement())
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitJsxSelfClosingElement(node *ast.JsxSelfClosingElement) {
state := p.enterNode(node.AsNode())
p.writePunctuation("<")
p.emitJsxTagName(node.TagName)
p.emitTypeArguments(node.AsNode(), node.TypeArguments)
p.writeSpace()
p.emitJsxAttributes(node.Attributes.AsJsxAttributes())
p.writePunctuation("/>")
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitJsxFragment(node *ast.JsxFragment) {
state := p.enterNode(node.AsNode())
p.emitJsxOpeningFragment(node.OpeningFragment.AsJsxOpeningFragment())
p.emitList((*Printer).emitJsxChild, node.AsNode(), node.Children, LFJsxElementOrFragmentChildren)
p.emitJsxClosingFragment(node.ClosingFragment.AsJsxClosingFragment())
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitJsxOpeningElement(node *ast.JsxOpeningElement) {
state := p.enterNode(node.AsNode())
p.writePunctuation("<")
indented := p.writeLineSeparatorsAndIndentBefore(node.TagName, node.AsNode())
p.emitJsxTagName(node.TagName)
p.emitTypeArguments(node.AsNode(), node.TypeArguments)
if attributes := node.Attributes.AsJsxAttributes(); len(attributes.Properties.Nodes) > 0 {
p.writeSpace()
}
p.emitJsxAttributes(node.Attributes.AsJsxAttributes())
p.writeLineSeparatorsAfter(node.Attributes, node.AsNode())
p.decreaseIndentIf(indented)
p.writePunctuation(">")
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitJsxClosingElement(node *ast.JsxClosingElement) {
state := p.enterNode(node.AsNode())
p.writePunctuation("</")
p.emitJsxTagName(node.TagName)
p.writePunctuation(">")
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitJsxOpeningFragment(node *ast.JsxOpeningFragment) {
state := p.enterNode(node.AsNode())
p.writePunctuation("<")
p.writePunctuation(">")
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitJsxClosingFragment(node *ast.JsxClosingFragment) {
state := p.enterNode(node.AsNode())
p.writePunctuation("</")
p.writePunctuation(">")
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitJsxText(node *ast.JsxText) {
state := p.enterNode(node.AsNode())
// TODO(rbuckton): Should this be using `getLiteralTextOfNode` instead?
p.writeLiteral(node.Text)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitJsxAttributes(node *ast.JsxAttributes) {
state := p.enterNode(node.AsNode())
p.emitList((*Printer).emitJsxAttributeLike, node.AsNode(), node.Properties, LFJsxElementAttributes)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitJsxAttribute(node *ast.JsxAttribute) {
state := p.enterNode(node.AsNode())
p.emitJsxAttributeName(node.Name())
if node.Initializer != nil {
p.writePunctuation("=")
p.emitJsxAttributeValue(node.Initializer)
}
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitJsxSpreadAttribute(node *ast.JsxSpreadAttribute) {
state := p.enterNode(node.AsNode())
p.writePunctuation("{...")
p.emitExpression(node.Expression, ast.OperatorPrecedenceLowest)
p.writePunctuation("}")
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitJsxAttributeLike(node *ast.JsxAttributeLike) {
switch node.Kind {
case ast.KindJsxAttribute:
p.emitJsxAttribute(node.AsJsxAttribute())
case ast.KindJsxSpreadAttribute:
p.emitJsxSpreadAttribute(node.AsJsxSpreadAttribute())
default:
panic(fmt.Sprintf("unhandled JsxAttributeLike: %v", node.Kind))
}
}
func (p *Printer) emitJsxExpression(node *ast.JsxExpression) {
state := p.enterNode(node.AsNode())
if node.Expression != nil || !p.commentsDisabled && !ast.NodeIsSynthesized(node.AsNode()) && p.hasCommentsAtPosition(node.Pos()) { // preserve empty expressions if they contain comments!
indented := p.currentSourceFile != nil && !ast.NodeIsSynthesized(node.AsNode()) && GetLinesBetweenPositions(p.currentSourceFile, node.Pos(), node.End()) != 0
p.increaseIndentIf(indented)
end := p.emitToken(ast.KindOpenBraceToken, node.Pos(), WriteKindPunctuation, node.AsNode())
p.emitTokenNode(node.DotDotDotToken)
p.emitExpression(node.Expression, ast.OperatorPrecedenceDisallowComma)
p.emitToken(ast.KindCloseBraceToken, greatestEnd(end, node.Expression, node.DotDotDotToken), WriteKindPunctuation, node.AsNode())
p.decreaseIndentIf(indented)
}
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitJsxNamespacedName(node *ast.JsxNamespacedName) {
state := p.enterNode(node.AsNode())
p.emitIdentifierName(node.Namespace.AsIdentifier())
p.writePunctuation(":")
p.emitIdentifierName(node.Name().AsIdentifier())
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitJsxChild(node *ast.JsxChild) {
switch node.Kind {
case ast.KindJsxText:
p.emitJsxText(node.AsJsxText())
case ast.KindJsxExpression:
p.emitJsxExpression(node.AsJsxExpression())
case ast.KindJsxElement:
p.emitJsxElement(node.AsJsxElement())
case ast.KindJsxSelfClosingElement:
p.emitJsxSelfClosingElement(node.AsJsxSelfClosingElement())
case ast.KindJsxFragment:
p.emitJsxFragment(node.AsJsxFragment())
default:
panic(fmt.Sprintf("unhandled JsxChild: %v", node.Kind))
}
}
func (p *Printer) emitJsxTagName(node *ast.JsxTagNameExpression) {
switch node.Kind {
case ast.KindIdentifier:
p.emitIdentifierReference(node.AsIdentifier())
case ast.KindThisKeyword:
p.emitKeywordExpression(node.AsKeywordExpression())
case ast.KindJsxNamespacedName:
p.emitJsxNamespacedName(node.AsJsxNamespacedName())
case ast.KindPropertyAccessExpression:
p.emitPropertyAccessExpression(node.AsPropertyAccessExpression())
default:
panic(fmt.Sprintf("unhandled JsxTagName: %v", node.Kind))
}
}
func (p *Printer) emitJsxAttributeName(node *ast.JsxAttributeName) {
switch node.Kind {
case ast.KindIdentifier:
p.emitIdentifierName(node.AsIdentifier())
case ast.KindJsxNamespacedName:
p.emitJsxNamespacedName(node.AsJsxNamespacedName())
default:
panic(fmt.Sprintf("unhandled JsxAttributeName: %v", node.Kind))
}
}
func (p *Printer) emitJsxAttributeValue(node *ast.JsxAttributeValue) {
switch node.Kind {
case ast.KindStringLiteral:
p.emitStringLiteral(node.AsStringLiteral())
case ast.KindJsxExpression:
p.emitJsxExpression(node.AsJsxExpression())
case ast.KindJsxElement:
p.emitJsxElement(node.AsJsxElement())
case ast.KindJsxSelfClosingElement:
p.emitJsxSelfClosingElement(node.AsJsxSelfClosingElement())
case ast.KindJsxFragment:
p.emitJsxFragment(node.AsJsxFragment())
default:
panic(fmt.Sprintf("unhandled JsxAttributeValue: %v", node.Kind))
}
}
//
// Clauses
//
func (p *Printer) emitCaseOrDefaultClauseStatements(node *ast.CaseOrDefaultClause) {
emitAsSingleStatement := len(node.Statements.Nodes) == 1 &&
// treat synthesized nodes as located on the same line for emit purposes
(p.currentSourceFile == nil ||
ast.NodeIsSynthesized(node.AsNode()) ||
ast.NodeIsSynthesized(node.Statements.Nodes[0]) ||
rangeStartPositionsAreOnSameLine(node.Loc, node.Statements.Nodes[0].Loc, p.currentSourceFile))
format := LFCaseOrDefaultClauseStatements
if emitAsSingleStatement {
p.writeSpace()
format &= ^(LFMultiLine | LFIndented)
}
p.emitList((*Printer).emitStatement, node.AsNode(), node.Statements, format)
}
func (p *Printer) emitCaseClause(node *ast.CaseOrDefaultClause) {
state := p.enterNode(node.AsNode())
p.emitToken(ast.KindCaseKeyword, node.Pos(), WriteKindKeyword, node.AsNode())
p.writeSpace()
p.emitExpression(node.Expression, ast.OperatorPrecedenceLowest)
p.emitToken(ast.KindColonToken, node.Expression.End(), WriteKindPunctuation, node.AsNode())
p.emitCaseOrDefaultClauseStatements(node)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitDefaultClause(node *ast.CaseOrDefaultClause) {
state := p.enterNode(node.AsNode())
pos := p.emitToken(ast.KindDefaultKeyword, node.Pos(), WriteKindKeyword, node.AsNode())
p.emitToken(ast.KindColonToken, pos, WriteKindPunctuation, node.AsNode())
p.emitCaseOrDefaultClauseStatements(node)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitCaseOrDefaultClauseNode(node *ast.CaseOrDefaultClauseNode) {
switch node.Kind {
case ast.KindCaseClause:
p.emitCaseClause(node.AsCaseOrDefaultClause())
case ast.KindDefaultClause:
p.emitDefaultClause(node.AsCaseOrDefaultClause())
default:
panic(fmt.Sprintf("unhandled CaseOrDefaultClause: %v", node.Kind))
}
}
func (p *Printer) emitHeritageClause(node *ast.HeritageClause) {
state := p.enterNode(node.AsNode())
p.writeSpace()
p.emitToken(node.Token, node.Pos(), WriteKindKeyword, node.AsNode())
p.writeSpace()
p.emitList((*Printer).emitExpressionWithTypeArgumentsNode, node.AsNode(), node.Types, LFHeritageClauseTypes)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitHeritageClauseNode(node *ast.HeritageClauseNode) {
p.emitHeritageClause(node.AsHeritageClause())
}
func (p *Printer) emitCatchClause(node *ast.CatchClause) {
state := p.enterNode(node.AsNode())
openParenPos := p.emitToken(ast.KindCatchKeyword, node.Pos(), WriteKindKeyword, node.AsNode())
p.writeSpace()
if node.VariableDeclaration != nil {
p.emitToken(ast.KindOpenParenToken, openParenPos, WriteKindPunctuation, node.AsNode())
p.emitVariableDeclaration(node.VariableDeclaration.AsVariableDeclaration())
p.emitToken(ast.KindCloseParenToken, node.VariableDeclaration.End(), WriteKindPunctuation, node.AsNode())
p.writeSpace()
}
p.emitBlock(node.Block.AsBlock())
p.exitNode(node.AsNode(), state)
}
//
// Property assignments
//
func (p *Printer) emitPropertyAssignment(node *ast.PropertyAssignment) {
state := p.enterNode(node.AsNode())
p.emitPropertyName(node.Name())
p.writePunctuation(":")
p.writeSpace()
// 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.
initializer := node.Initializer
// !!! emit trailing comments of initializer
p.emitExpression(initializer, ast.OperatorPrecedenceDisallowComma)
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitShorthandPropertyAssignment(node *ast.ShorthandPropertyAssignment) {
state := p.enterNode(node.AsNode())
p.emitPropertyName(node.Name())
if node.ObjectAssignmentInitializer != nil {
p.writeSpace()
p.writePunctuation("=")
p.writeSpace()
p.emitExpression(node.ObjectAssignmentInitializer, ast.OperatorPrecedenceDisallowComma)
}
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitSpreadAssignment(node *ast.SpreadAssignment) {
state := p.enterNode(node.AsNode())
if node.Expression != nil {
p.emitToken(ast.KindDotDotDotToken, node.Pos(), WriteKindPunctuation, node.AsNode())
p.emitExpression(node.Expression, ast.OperatorPrecedenceDisallowComma)
}
p.exitNode(node.AsNode(), state)
}
//
// Enum
//
func (p *Printer) emitEnumMember(node *ast.EnumMember) {
state := p.enterNode(node.AsNode())
p.emitPropertyName(node.Name())
p.emitInitializer(node.Initializer, node.Name().End(), node.AsNode())
p.exitNode(node.AsNode(), state)
}
func (p *Printer) emitEnumMemberNode(node *ast.EnumMemberNode) {
p.emitEnumMember(node.AsEnumMember())
}
//
// JSDoc
//
func (p *Printer) emitJSDocNode(node *ast.Node) {
// !!!
panic("not implemented")
}
//
// Top-level nodes
//
func (p *Printer) emitShebangIfNeeded(node *ast.SourceFile) {
shebang := scanner.GetShebang(node.Text())
if shebang != "" {
p.writeComment(shebang)
p.writeLine()
}
}
func (p *Printer) emitPrologueDirectives(statements *ast.StatementList) int {
for i, statement := range statements.Nodes {
if ast.IsPrologueDirective(statement) {
p.writeLine()
p.emitStatement(statement)
} else {
return i
}
}
return len(statements.Nodes)
}
func (p *Printer) emitHelpers(node *ast.Node) bool {
helpersEmitted := false
sourceFile := p.currentSourceFile
shouldSkip := p.Options.NoEmitHelpers || (sourceFile != nil && p.emitContext.HasRecordedExternalHelpers(sourceFile))
helpers := slices.Clone(p.emitContext.GetEmitHelpers(node))
if len(helpers) > 0 {
slices.SortStableFunc(helpers, compareEmitHelpers)
for _, helper := range helpers {
if !helper.Scoped {
// Skip the helper if it can be skipped and the noEmitHelpers compiler
// option is set, or if it can be imported and the importHelpers compiler
// option is set.
if shouldSkip {
continue
}
}
if helper.TextCallback != nil {
p.writeLines(helper.TextCallback(p.makeFileLevelOptimisticUniqueName))
} else {
p.writeLines(helper.Text)
}
helpersEmitted = true
}
}
return helpersEmitted
}
func (p *Printer) emitSourceFile(node *ast.SourceFile) {
savedCurrentSourceFile := p.currentSourceFile
savedCommentsDisabled := p.commentsDisabled
p.currentSourceFile = node
p.writeLine()
state := p.emitDetachedCommentsBeforeStatementList(node.AsNode(), node.Statements.Loc)
p.pushNameGenerationScope(node.AsNode())
p.generateAllNames(node.Statements)
index := 0
if node.ScriptKind != core.ScriptKindJSON {
p.emitShebangIfNeeded(node)
index = p.emitPrologueDirectives(node.Statements)
p.emitHelpers(node.AsNode())
if node.IsDeclarationFile {
p.emitTripleSlashDirectives(node)
}
}
// !!! Emit triple-slash directives
p.emitListRange(
(*Printer).emitStatement,
node.AsNode(),
node.Statements,
LFMultiLine,
index,
-1, /*count*/
)
p.popNameGenerationScope(node.AsNode())
p.emitDetachedCommentsAfterStatementList(node.AsNode(), node.Statements.Loc, state)
p.currentSourceFile = savedCurrentSourceFile
p.commentsDisabled = savedCommentsDisabled
}
func (p *Printer) emitTripleSlashDirectives(node *ast.SourceFile) {
p.emitDirective("path", node.ReferencedFiles)
p.emitDirective("types", node.TypeReferenceDirectives)
p.emitDirective("lib", node.LibReferenceDirectives)
}
func (p *Printer) emitDirective(kind string, refs []*ast.FileReference) {
for _, ref := range refs {
var resolutionMode string
if ref.ResolutionMode != core.ResolutionModeNone {
resolutionMode = fmt.Sprintf(`resolution-mode="%s" `, core.IfElse(ref.ResolutionMode == core.ResolutionModeESM, "import", "require"))
}
p.writeComment(fmt.Sprintf("/// <reference %s=\"%s\" %s%s/>", kind, ref.FileName, resolutionMode, core.IfElse(ref.Preserve, `preserve="true" `, "")))
p.writeLine()
}
}
//
// Lists
//
func (p *Printer) emitList(emit func(p *Printer, node *ast.Node), parentNode *ast.Node, children *ast.NodeList, format ListFormat) {
if p.shouldEmitOnMultipleLines(parentNode) {
format |= LFPreferNewLine
}
p.emitListRange(emit, parentNode, children, format, -1 /*start*/, -1 /*count*/)
}
func (p *Printer) emitListRange(emit func(p *Printer, node *ast.Node), parentNode *ast.Node, children *ast.NodeList, format ListFormat, start int, count int) {
isNil := children == nil
length := 0
if !isNil {
length = len(children.Nodes)
}
if start < 0 {
start = 0
}
if count < 0 {
count = length - start
}
if isNil && format&LFOptionalIfNil != 0 {
return
}
isEmpty := isNil || start >= length || count <= 0
if isEmpty && format&LFOptionalIfEmpty != 0 {
if p.OnBeforeEmitNodeList != nil {
p.OnBeforeEmitNodeList(children)
}
if p.OnAfterEmitNodeList != nil {
p.OnAfterEmitNodeList(children)
}
return
}
if format&LFBracketsMask != 0 {
p.writePunctuation(getOpeningBracket(format))
if isEmpty && !isNil {
p.emitTrailingComments(children.Pos(), commentSeparatorBefore) // Emit comments within empty lists
}
}
if p.OnBeforeEmitNodeList != nil {
p.OnBeforeEmitNodeList(children)
}
if isEmpty {
// Write a line terminator if the parent node was multi-line
if format&LFMultiLine != 0 && !(p.Options.PreserveSourceNewlines && (parentNode == nil || p.currentSourceFile != nil && rangeIsOnSingleLine(parentNode.Loc, p.currentSourceFile))) {
p.writeLine()
} else if format&LFSpaceBetweenBraces != 0 && format&LFNoSpaceIfEmpty == 0 {
p.writeSpace()
}
} else {
end := min(start+count, length)
p.emitListItems(emit, parentNode, children.Nodes[start:end], format, p.hasTrailingComma(parentNode, children), children.Loc)
}
if p.OnAfterEmitNodeList != nil {
p.OnAfterEmitNodeList(children)
}
if format&LFBracketsMask != 0 {
if isEmpty && !isNil {
p.emitTrailingComments(children.Pos(), commentSeparatorBefore) // Emit comments within empty lists
}
p.writePunctuation(getClosingBracket(format))
}
}
func (p *Printer) hasTrailingComma(parentNode *ast.Node, children *ast.NodeList) bool {
// NodeList.HasTrailingComma() is unreliable on transformed nodes as some nodes may have been removed. In the event
// we believe we may need to emit a trailing comma, we must first look to the respective node list on the original
// node first.
if !children.HasTrailingComma() {
return false
}
originalParent := p.emitContext.MostOriginal(parentNode)
if originalParent == parentNode {
// if this node is the original node, we can trust the result
return true
}
if originalParent.Kind != parentNode.Kind {
// if the original node is some other kind of node, we cannot correlate the list
return false
}
// find the respective node list on the original parent
originalList := children
switch originalParent.Kind {
case ast.KindObjectLiteralExpression:
originalList = originalParent.AsObjectLiteralExpression().Properties
case ast.KindArrayLiteralExpression:
originalList = originalParent.AsArrayLiteralExpression().Elements
case ast.KindCallExpression, ast.KindNewExpression:
switch children {
case parentNode.TypeArgumentList():
originalList = originalParent.TypeArgumentList()
case parentNode.ArgumentList():
originalList = originalParent.ArgumentList()
}
case ast.KindConstructor,
ast.KindMethodDeclaration,
ast.KindGetAccessor,
ast.KindSetAccessor,
ast.KindFunctionDeclaration,
ast.KindFunctionExpression,
ast.KindArrowFunction,
ast.KindFunctionType,
ast.KindConstructorType,
ast.KindCallSignature,
ast.KindConstructSignature:
switch children {
case parentNode.TypeParameterList():
originalList = originalParent.TypeParameterList()
case parentNode.ParameterList():
originalList = originalParent.ParameterList()
}
case ast.KindClassDeclaration, ast.KindClassExpression, ast.KindInterfaceDeclaration, ast.KindTypeAliasDeclaration, ast.KindJSTypeAliasDeclaration:
switch children {
case parentNode.TypeParameterList():
originalList = originalParent.TypeParameterList()
}
case ast.KindObjectBindingPattern, ast.KindArrayBindingPattern:
switch children {
case parentNode.AsBindingPattern().Elements:
originalList = originalParent.AsBindingPattern().Elements
}
case ast.KindNamedImports:
originalList = originalParent.AsNamedImports().Elements
case ast.KindNamedExports:
originalList = originalParent.AsNamedExports().Elements
case ast.KindImportAttributes:
originalList = originalParent.AsImportAttributes().Attributes
}
// if we have the original list, we can use it's result.
if originalList != nil {
return originalList.HasTrailingComma()
}
return false
}
func (p *Printer) writeDelimiter(format ListFormat) {
switch format & LFDelimitersMask {
case LFNone:
break
case LFCommaDelimited:
p.writePunctuation(",")
case LFBarDelimited:
p.writeSpace()
p.writePunctuation("|")
case LFAsteriskDelimited:
p.writeSpace()
p.writePunctuation("*")
p.writeSpace()
case LFAmpersandDelimited:
p.writeSpace()
p.writePunctuation("&")
}
}
// Emits a list without brackets or raising events.
//
// NOTE: You probably don't want to call this directly and should be using `emitList` instead.
func (p *Printer) emitListItems(
emit func(p *Printer, node *ast.Node),
parentNode *ast.Node,
children []*ast.Node,
format ListFormat,
hasTrailingComma bool,
childrenTextRange core.TextRange,
) {
// Write the opening line terminator or leading whitespace.
mayEmitInterveningComments := format&LFNoInterveningComments == 0
shouldEmitInterveningComments := mayEmitInterveningComments
leadingLineTerminatorCount := 0
if len(children) > 0 {
leadingLineTerminatorCount = p.getLeadingLineTerminatorCount(parentNode, children[0], format)
}
if leadingLineTerminatorCount > 0 {
for range leadingLineTerminatorCount {
p.writeLine()
}
shouldEmitInterveningComments = false
} else if format&LFSpaceBetweenBraces != 0 {
p.writeSpace()
}
// Increase the indent, if requested.
if format&LFIndented != 0 {
p.increaseIndent()
}
parentEnd := greatestEnd(-1, parentNode)
// Emit each child.
var previousSibling *ast.Node
shouldDecreaseIndentAfterEmit := false
for _, child := range children {
// Write the delimiter if this is not the first node.
if format&LFAsteriskDelimited != 0 {
// always write JSDoc in the format "\n *"
p.writeLine()
p.writeDelimiter(format)
} else if previousSibling != nil {
// i.e
// function commentedParameters(
// /* Parameter a */
// a
// /* End of parameter a */ -> this comment isn't considered to be trailing comment of parameter "a" due to newline
// ,
if format&LFDelimitersMask != 0 && previousSibling.End() != parentEnd {
if !p.commentsDisabled && p.shouldEmitTrailingComments(previousSibling) {
p.emitLeadingComments(previousSibling.End(), false /*elided*/)
}
}
p.writeDelimiter(format)
// Write either a line terminator or whitespace to separate the elements.
separatingLineTerminatorCount := p.getSeparatingLineTerminatorCount(previousSibling, child, format)
if separatingLineTerminatorCount > 0 {
// If a synthesized node in a single-line list starts on a new
// line, we should increase the indent.
if format&(LFLinesMask|LFIndented) == LFSingleLine {
p.increaseIndent()
shouldDecreaseIndentAfterEmit = true
}
if shouldEmitInterveningComments && format&LFDelimitersMask != 0 && !ast.PositionIsSynthesized(child.Pos()) {
commentRange := getCommentRange(child)
p.emitTrailingComments(commentRange.Pos(), core.IfElse(format&LFSpaceBetweenSiblings != 0, commentSeparatorBefore, commentSeparatorNone))
}
for range separatingLineTerminatorCount {
p.writeLine()
}
shouldEmitInterveningComments = false
} else if format&LFSpaceBetweenSiblings != 0 {
p.writeSpace()
}
}
// Emit this child.
if shouldEmitInterveningComments {
commentRange := getCommentRange(child)
p.emitTrailingComments(commentRange.Pos(), commentSeparatorAfter)
} else {
shouldEmitInterveningComments = mayEmitInterveningComments
}
p.nextListElementPos = child.Pos()
emit(p, child)
if shouldDecreaseIndentAfterEmit {
p.decreaseIndent()
shouldDecreaseIndentAfterEmit = false
}
previousSibling = child
}
// Write a trailing comma, if requested.
skipTrailingComments := p.commentsDisabled || !p.shouldEmitTrailingComments(previousSibling)
emitTrailingComma := hasTrailingComma && format&LFAllowTrailingComma != 0 && format&LFCommaDelimited != 0
if emitTrailingComma {
if previousSibling != nil && !skipTrailingComments {
p.emitToken(ast.KindCommaToken, previousSibling.End(), WriteKindPunctuation, previousSibling)
} else {
p.writePunctuation(",")
}
}
// Emit any trailing comment of the last element in the list
// i.e
// var array = [...
// 2
// /* end of element 2 */
// ];
if previousSibling != nil && parentEnd != previousSibling.End() && format&LFDelimitersMask != 0 && !skipTrailingComments {
p.emitLeadingComments(greatestEnd(previousSibling.End(), childrenTextRange), false /*elided*/)
}
// Decrease the indent, if requested.
if format&LFIndented != 0 {
p.decreaseIndent()
}
// Write the closing line terminator or closing whitespace.
closingLineTerminatorCount := p.getClosingLineTerminatorCount(parentNode, core.LastOrNil(children), format, childrenTextRange)
if closingLineTerminatorCount > 0 {
for range closingLineTerminatorCount {
p.writeLine()
}
} else if format&(LFSpaceAfterList|LFSpaceBetweenBraces) != 0 {
p.writeSpace()
}
}
//
// General
//
func (p *Printer) Emit(node *ast.Node, sourceFile *ast.SourceFile) string {
// ensure a reusable writer
if p.ownWriter == nil {
p.ownWriter = NewTextWriter(p.Options.NewLine.GetNewLineCharacter())
}
p.Write(node, sourceFile, p.ownWriter, nil /*sourceMapGenerator*/)
text := p.ownWriter.String()
p.ownWriter.Clear()
return text
}
func (p *Printer) EmitSourceFile(sourceFile *ast.SourceFile) string {
return p.Emit(sourceFile.AsNode(), sourceFile)
}
func (p *Printer) setSourceFile(sourceFile *ast.SourceFile) {
p.currentSourceFile = sourceFile
p.uniqueHelperNames = nil
p.externalHelpersModuleName = nil
if sourceFile != nil {
if p.emitContext.EmitFlags(p.emitContext.MostOriginal(sourceFile.AsNode()))&EFExternalHelpers != 0 {
p.uniqueHelperNames = make(map[string]*ast.IdentifierNode)
}
p.externalHelpersModuleName = p.emitContext.GetExternalHelpersModuleName(sourceFile)
p.setSourceMapSource(sourceFile)
}
// !!!
}
func (p *Printer) Write(node *ast.Node, sourceFile *ast.SourceFile, writer EmitTextWriter, sourceMapGenerator *sourcemap.Generator) {
savedCurrentSourceFile := p.currentSourceFile
savedWriter := p.writer
savedUniqueHelperNames := p.uniqueHelperNames
savedSourceMapsDisabled := p.sourceMapsDisabled
savedSourceMapGenerator := p.sourceMapGenerator
savedSourceMapSource := p.sourceMapSource
savedSourceMapSourceIndex := p.sourceMapSourceIndex
p.sourceMapsDisabled = sourceMapGenerator == nil
p.sourceMapGenerator = sourceMapGenerator
p.sourceMapSource = nil
p.sourceMapSourceIndex = -1
p.setSourceFile(sourceFile)
p.writer = writer
p.writer.Clear()
switch node.Kind {
// Pseudo-literals
case ast.KindTemplateHead:
p.emitTemplateHead(node.AsTemplateHead())
case ast.KindTemplateMiddle:
p.emitTemplateMiddle(node.AsTemplateMiddle())
case ast.KindTemplateTail:
p.emitTemplateTail(node.AsTemplateTail())
// Identifiers
case ast.KindIdentifier:
p.emitIdentifierName(node.AsIdentifier())
// PrivateIdentifiers
case ast.KindPrivateIdentifier:
p.emitPrivateIdentifier(node.AsPrivateIdentifier())
// Parse tree nodes
// Names
case ast.KindQualifiedName:
p.emitQualifiedName(node.AsQualifiedName())
case ast.KindComputedPropertyName:
p.emitComputedPropertyName(node.AsComputedPropertyName())
// Signature elements
case ast.KindTypeParameter:
p.emitTypeParameter(node.AsTypeParameter())
case ast.KindParameter:
p.emitParameter(node.AsParameterDeclaration())
case ast.KindDecorator:
p.emitDecorator(node.AsDecorator())
// Type members
case ast.KindPropertySignature:
p.emitPropertySignature(node.AsPropertySignatureDeclaration())
case ast.KindPropertyDeclaration:
p.emitPropertyDeclaration(node.AsPropertyDeclaration())
case ast.KindMethodSignature:
p.emitMethodSignature(node.AsMethodSignatureDeclaration())
case ast.KindMethodDeclaration:
p.emitMethodDeclaration(node.AsMethodDeclaration())
case ast.KindClassStaticBlockDeclaration:
p.emitClassStaticBlockDeclaration(node.AsClassStaticBlockDeclaration())
case ast.KindConstructor:
p.emitConstructor(node.AsConstructorDeclaration())
case ast.KindGetAccessor:
p.emitGetAccessorDeclaration(node.AsGetAccessorDeclaration())
case ast.KindSetAccessor:
p.emitSetAccessorDeclaration(node.AsSetAccessorDeclaration())
case ast.KindCallSignature:
p.emitCallSignature(node.AsCallSignatureDeclaration())
case ast.KindConstructSignature:
p.emitConstructSignature(node.AsConstructSignatureDeclaration())
case ast.KindIndexSignature:
p.emitIndexSignature(node.AsIndexSignatureDeclaration())
// Binding patterns
case ast.KindObjectBindingPattern:
p.emitObjectBindingPattern(node.AsBindingPattern())
case ast.KindArrayBindingPattern:
p.emitArrayBindingPattern(node.AsBindingPattern())
case ast.KindBindingElement:
p.emitBindingElement(node.AsBindingElement())
// Misc
case ast.KindTemplateSpan:
p.emitTemplateSpan(node.AsTemplateSpan())
case ast.KindSemicolonClassElement:
p.emitSemicolonClassElement(node.AsSemicolonClassElement())
// Declarations (non-statement)
case ast.KindVariableDeclaration:
p.emitVariableDeclaration(node.AsVariableDeclaration())
case ast.KindVariableDeclarationList:
p.emitVariableDeclarationList(node.AsVariableDeclarationList())
case ast.KindModuleBlock:
p.emitModuleBlock(node.AsModuleBlock())
case ast.KindCaseBlock:
p.emitCaseBlock(node.AsCaseBlock())
case ast.KindImportClause:
p.emitImportClause(node.AsImportClause())
case ast.KindNamespaceImport:
p.emitNamespaceImport(node.AsNamespaceImport())
case ast.KindNamespaceExport:
p.emitNamespaceExport(node.AsNamespaceExport())
case ast.KindNamedImports:
p.emitNamedImports(node.AsNamedImports())
case ast.KindImportSpecifier:
p.emitImportSpecifier(node.AsImportSpecifier())
case ast.KindNamedExports:
p.emitNamedExports(node.AsNamedExports())
case ast.KindExportSpecifier:
p.emitExportSpecifier(node.AsExportSpecifier())
case ast.KindImportAttributes:
p.emitImportAttributes(node.AsImportAttributes())
case ast.KindImportAttribute:
p.emitImportAttribute(node.AsImportAttribute())
// Module references
case ast.KindExternalModuleReference:
p.emitExternalModuleReference(node.AsExternalModuleReference())
// JSX (non-expression)
case ast.KindJsxText:
p.emitJsxText(node.AsJsxText())
case ast.KindJsxOpeningElement:
p.emitJsxOpeningElement(node.AsJsxOpeningElement())
case ast.KindJsxOpeningFragment:
p.emitJsxOpeningFragment(node.AsJsxOpeningFragment())
case ast.KindJsxClosingElement:
p.emitJsxClosingElement(node.AsJsxClosingElement())
case ast.KindJsxClosingFragment:
p.emitJsxClosingFragment(node.AsJsxClosingFragment())
case ast.KindJsxAttribute:
p.emitJsxAttribute(node.AsJsxAttribute())
case ast.KindJsxAttributes:
p.emitJsxAttributes(node.AsJsxAttributes())
case ast.KindJsxSpreadAttribute:
p.emitJsxSpreadAttribute(node.AsJsxSpreadAttribute())
case ast.KindJsxExpression:
p.emitJsxExpression(node.AsJsxExpression())
case ast.KindJsxNamespacedName:
p.emitJsxNamespacedName(node.AsJsxNamespacedName())
// Clauses
case ast.KindCaseClause:
p.emitCaseClause(node.AsCaseOrDefaultClause())
case ast.KindDefaultClause:
p.emitDefaultClause(node.AsCaseOrDefaultClause())
case ast.KindHeritageClause:
p.emitHeritageClause(node.AsHeritageClause())
case ast.KindCatchClause:
p.emitCatchClause(node.AsCatchClause())
// Property assignments
case ast.KindPropertyAssignment:
p.emitPropertyAssignment(node.AsPropertyAssignment())
case ast.KindShorthandPropertyAssignment:
p.emitShorthandPropertyAssignment(node.AsShorthandPropertyAssignment())
case ast.KindSpreadAssignment:
p.emitSpreadAssignment(node.AsSpreadAssignment())
// Enum
case ast.KindEnumMember:
p.emitEnumMember(node.AsEnumMember())
// Top-level nodes
case ast.KindSourceFile:
p.emitSourceFile(node.AsSourceFile())
case ast.KindBundle:
panic("not implemented")
// Transformation nodes
case ast.KindNotEmittedTypeElement:
p.emitNotEmittedTypeElement(node.AsNotEmittedTypeElement())
default:
switch {
case ast.IsTypeNode(node):
p.emitTypeNodeOutsideExtends(node)
case ast.IsStatement(node):
p.emitStatement(node)
case ast.IsExpression(node):
p.emitExpression(node, ast.OperatorPrecedenceLowest)
case ast.IsKeywordKind(node.Kind):
p.emitKeywordNode(node)
case ast.IsPunctuationKind(node.Kind):
p.emitPunctuationNode(node)
case ast.IsJSDocKind(node.Kind):
p.emitJSDocNode(node)
default:
panic(fmt.Sprintf("unhandled Node: %v", node.Kind))
}
}
p.currentSourceFile = savedCurrentSourceFile
p.writer = savedWriter
p.uniqueHelperNames = savedUniqueHelperNames
p.sourceMapsDisabled = savedSourceMapsDisabled
p.sourceMapGenerator = savedSourceMapGenerator
p.sourceMapSource = savedSourceMapSource
p.sourceMapSourceIndex = savedSourceMapSourceIndex
}
//
// Comments
//
func (p *Printer) emitCommentsBeforeNode(node *ast.Node) *commentState {
if !p.shouldEmitComments(node) {
return nil
}
emitFlags := p.emitContext.EmitFlags(node)
commentRange := p.emitContext.CommentRange(node)
containerPos := p.containerPos
containerEnd := p.containerEnd
declarationListContainerEnd := p.declarationListContainerEnd
// Emit leading comments
p.emitLeadingCommentsOfNode(node, emitFlags, commentRange)
p.emitLeadingSyntheticCommentsOfNode(node, emitFlags)
if emitFlags&EFNoNestedComments != 0 {
p.commentsDisabled = true
}
c := p.commentStatePool.New()
*c = commentState{emitFlags, commentRange, containerPos, containerEnd, declarationListContainerEnd}
return c
}
func (p *Printer) emitCommentsAfterNode(node *ast.Node, state *commentState) {
if state == nil {
return
}
emitFlags := state.emitFlags
commentRange := state.commentRange
containerPos := state.containerPos
containerEnd := state.containerEnd
declarationListContainerEnd := state.declarationListContainerEnd
// Emit trailing comments
if emitFlags&EFNoNestedComments != 0 {
p.commentsDisabled = false
}
p.emitTrailingSyntheticCommentsOfNode(node, emitFlags)
p.emitTrailingCommentsOfNode(node, emitFlags, commentRange, containerPos, containerEnd, declarationListContainerEnd)
// !!! Preserve comments from type annotation:
// typeNode := node.Type()
// if typeNode != nil {
// p.emitTrailingCommentsOfNode(node, typeNode.Pos(), typeNode.End(), state)
// }
}
func (p *Printer) emitCommentsBeforeToken(token ast.Kind, pos int, contextNode *ast.Node, flags tokenEmitFlags) (*commentState, int) {
if flags&tefNoComments != 0 {
return nil, pos
}
startPos := pos
if p.currentSourceFile != nil {
pos = scanner.SkipTrivia(p.currentSourceFile.Text(), startPos)
}
node := p.emitContext.ParseNode(contextNode)
isSimilarNode := node != nil && node.Kind == contextNode.Kind
if !isSimilarNode {
return nil, pos
}
if contextNode.Pos() != startPos {
indentLeading := flags&tefIndentLeadingComments != 0
needsIndent := indentLeading && p.currentSourceFile != nil && !positionsAreOnSameLine(startPos, pos, p.currentSourceFile)
p.increaseIndentIf(needsIndent)
p.emitLeadingComments(startPos, false /*elided*/)
p.decreaseIndentIf(needsIndent)
}
return p.commentStatePool.New(), pos
}
func (p *Printer) emitCommentsAfterToken(token ast.Kind, pos int, contextNode *ast.Node, state *commentState) {
if state == nil {
return
}
if contextNode.End() != pos {
isJsxExprContext := contextNode.Kind == ast.KindJsxExpression
p.emitTrailingComments(pos, core.IfElse(isJsxExprContext, commentSeparatorNone, commentSeparatorBefore))
}
}
func (p *Printer) emitDetachedCommentsBeforeStatementList(node *ast.Node, detachedRange core.TextRange) *commentState {
if !p.shouldEmitDetachedComments(node) {
return nil
}
emitFlags := p.emitContext.EmitFlags(node)
containerPos := p.containerPos
containerEnd := p.containerEnd
declarationListContainerEnd := p.declarationListContainerEnd
skipLeadingComments := emitFlags&EFNoLeadingComments == 0 && !ast.PositionIsSynthesized(detachedRange.Pos())
if !skipLeadingComments {
p.emitDetachedCommentsAndUpdateCommentsInfo(detachedRange)
}
if emitFlags&EFNoNestedComments != 0 {
p.commentsDisabled = true
}
return &commentState{emitFlags, detachedRange, containerPos, containerEnd, declarationListContainerEnd}
}
func (p *Printer) emitDetachedCommentsAfterStatementList(node *ast.Node, detachedRange core.TextRange, state *commentState) {
if state == nil {
return
}
emitFlags := state.emitFlags
skipTrailingComments := p.commentsDisabled || ast.PositionIsSynthesized(detachedRange.End()) || emitFlags&EFNoTrailingComments != 0
if !skipTrailingComments {
hasWrittenComment := p.emitLeadingComments(detachedRange.End(), false /*elided*/)
if hasWrittenComment && !p.writer.IsAtStartOfLine() {
p.writeLine()
}
}
}
func (p *Printer) emitLeadingCommentsOfNode(node *ast.Node, emitFlags EmitFlags, commentRange core.TextRange) {
pos := commentRange.Pos()
end := commentRange.End()
// Save current container state on the stack.
if (!ast.PositionIsSynthesized(pos) || !ast.PositionIsSynthesized(end)) && pos != end {
// 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.
skipLeadingComments := ast.PositionIsSynthesized(pos) || emitFlags&EFNoLeadingComments != 0 || node.Kind == ast.KindJsxText
skipTrailingComments := ast.PositionIsSynthesized(pos) || emitFlags&EFNoTrailingComments != 0 || node.Kind == ast.KindJsxText
// Emit leading comments if the position is not synthesized and the node
// has not opted out from emitting leading comments.
if !skipLeadingComments {
p.emitLeadingComments(pos, node.Kind == ast.KindNotEmittedStatement /*elided*/)
}
if !skipLeadingComments || (pos >= 0 && (emitFlags&EFNoLeadingComments) != 0) {
// Advance the container position if comments get emitted or if they've been disabled explicitly using NoLeadingComments.
p.containerPos = pos
}
if !skipTrailingComments || (end >= 0 && (emitFlags&EFNoTrailingComments) != 0) {
// Advance the container end if comments get emitted or if they've been disabled explicitly using NoTrailingComments.
p.containerEnd = end
// 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 == ast.KindVariableDeclarationList {
p.declarationListContainerEnd = end
}
}
}
}
func (p *Printer) emitTrailingCommentsOfNode(node *ast.Node, emitFlags EmitFlags, commentRange core.TextRange, containerPos int, containerEnd int, declarationListContainerEnd int) {
pos := commentRange.Pos()
end := commentRange.End()
skipTrailingComments := end < 0 || (emitFlags&EFNoTrailingComments) != 0 || node.Kind == ast.KindJsxText
if (!ast.PositionIsSynthesized(pos) || !ast.PositionIsSynthesized(end)) && pos != end {
// Restore previous container state.
p.containerPos = containerPos
p.containerEnd = containerEnd
p.declarationListContainerEnd = declarationListContainerEnd
// 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 && node.Kind != ast.KindNotEmittedStatement {
p.emitTrailingComments(end, commentSeparatorBefore)
}
}
}
func (p *Printer) emitLeadingSyntheticCommentsOfNode(node *ast.Node, emitFlags EmitFlags) {
if emitFlags&EFNoLeadingComments != 0 {
return
}
synth := p.emitContext.GetSyntheticLeadingComments(node)
for _, c := range synth {
p.emitLeadingSynthesizedComment(c)
}
}
func (p *Printer) emitLeadingSynthesizedComment(comment SynthesizedComment) {
if comment.HasLeadingNewLine || comment.Kind == ast.KindSingleLineCommentTrivia {
p.writer.WriteLine()
}
p.writeSynthesizedComment(comment)
if comment.HasTrailingNewLine || comment.Kind == ast.KindSingleLineCommentTrivia {
p.writer.WriteLine()
} else {
p.writer.WriteSpace(" ")
}
}
func (p *Printer) emitTrailingSyntheticCommentsOfNode(node *ast.Node, emitFlags EmitFlags) {
if emitFlags&EFNoTrailingComments != 0 {
return
}
synth := p.emitContext.GetSyntheticTrailingComments(node)
for _, c := range synth {
p.emitTrailingSynthesizedComment(c)
}
}
func (p *Printer) emitTrailingSynthesizedComment(comment SynthesizedComment) {
if !p.writer.IsAtStartOfLine() {
p.writer.WriteSpace(" ")
}
p.writeSynthesizedComment(comment)
if comment.HasTrailingNewLine {
p.writer.WriteLine()
}
}
func formatSynthesizedComment(comment SynthesizedComment) string {
if comment.Kind == ast.KindMultiLineCommentTrivia {
return "/*" + comment.Text + "*/"
}
return "//" + comment.Text
}
func (p *Printer) writeSynthesizedComment(comment SynthesizedComment) {
text := formatSynthesizedComment(comment)
var lineMap []core.TextPos
if comment.Kind == ast.KindMultiLineCommentTrivia {
lineMap = core.ComputeECMALineStarts(text)
}
p.writeCommentRangeWorker(text, lineMap, comment.Kind, core.NewTextRange(0, len(text)))
}
func (p *Printer) emitLeadingComments(pos int, elided bool) bool {
// Emit the leading comments only if the container's pos doesn't match because the container should take care of emitting these comments
if p.currentSourceFile == nil || ast.PositionIsSynthesized(pos) || pos == p.containerPos {
return false
}
tripleSlash := core.TSUnknown
if !elided {
if pos == 0 && p.currentSourceFile != nil && p.currentSourceFile.IsDeclarationFile {
tripleSlash = core.TSFalse
}
} else if pos == 0 {
// If the node will not be emitted in JS, remove all the comments(normal, pinned and ///) associated with the node,
// unless it is a triple slash comment at the top of the file.
// For Example:
// /// <reference-path ...>
// declare var x;
// /// <reference-path ...>
// interface F {}
// The first /// will NOT be removed while the second one will be removed even though both node will not be emitted
tripleSlash = core.TSTrue
} else {
return false
}
// skip detached comments
if p.detachedCommentsInfo.Len() > 0 {
if info := p.detachedCommentsInfo.Peek(); info.nodePos == pos {
pos = p.detachedCommentsInfo.Pop().detachedCommentEndPos
}
}
var comments []ast.CommentRange
for comment := range scanner.GetLeadingCommentRanges(p.emitContext.Factory.AsNodeFactory(), p.currentSourceFile.Text(), pos) {
if p.shouldWriteComment(comment) && p.shouldEmitCommentIfTripleSlash(comment, tripleSlash) {
comments = append(comments, comment)
}
}
if len(comments) > 0 && p.shouldEmitNewLineBeforeLeadingCommentOfPosition(pos, comments[0].Pos()) {
p.writeLine()
}
// Leading comments are emitted as /*leading comment1*/space/*leading comment*/space
return p.emitComments(comments, commentSeparatorAfter)
}
func (p *Printer) shouldEmitCommentIfTripleSlash(comment ast.CommentRange, tripleSlash core.Tristate) bool {
switch tripleSlash {
case core.TSTrue:
return p.isTripleSlashComment(comment)
case core.TSFalse:
return !p.isTripleSlashComment(comment)
default:
return true
}
}
func (p *Printer) shouldEmitNewLineBeforeLeadingCommentOfPosition(pos int, commentPos int) bool {
// If the leading comments start on different line than the start of node, write new line
return p.currentSourceFile != nil &&
pos != commentPos &&
scanner.ComputeLineOfPosition(p.currentSourceFile.ECMALineMap(), pos) != scanner.ComputeLineOfPosition(p.currentSourceFile.ECMALineMap(), commentPos)
}
func (p *Printer) emitTrailingComments(pos int, commentSeparator commentSeparator) {
// Emit the trailing comments only if the container's end doesn't match because the container should take care of emitting these comments
if p.currentSourceFile == nil || p.containerEnd != -1 && (pos == p.containerEnd || pos == p.declarationListContainerEnd) {
return
}
var comments []ast.CommentRange
for comment := range scanner.GetTrailingCommentRanges(p.emitContext.Factory.AsNodeFactory(), p.currentSourceFile.Text(), pos) {
if p.shouldWriteComment(comment) {
comments = append(comments, comment)
}
}
// trailing comments are normally emitted as space/*trailing comment1*/space/*trailing comment2*/
p.emitComments(comments, commentSeparator)
}
func (p *Printer) emitDetachedCommentsAndUpdateCommentsInfo(textRange core.TextRange) {
if p.currentSourceFile == nil {
return
}
if currentDetachedCommentInfo, ok := p.emitDetachedComments(textRange); ok {
p.detachedCommentsInfo.Push(currentDetachedCommentInfo)
}
}
func (p *Printer) emitDetachedComments(textRange core.TextRange) (result detachedCommentsInfo, hasResult bool) {
if p.currentSourceFile == nil {
return result, hasResult
}
text := p.currentSourceFile.Text()
lineMap := p.currentSourceFile.ECMALineMap()
var leadingComments []ast.CommentRange
if p.commentsDisabled {
// removeComments is true, only reserve pinned comment at the top of file
// For example:
// /*! Pinned Comment */
//
// var x = 10;
if textRange.Pos() == 0 {
for comment := range scanner.GetLeadingCommentRanges(p.emitContext.Factory.AsNodeFactory(), text, textRange.Pos()) {
if IsPinnedComment(text, comment) {
leadingComments = append(leadingComments, comment)
}
}
}
} else {
// removeComments is false, just get detached as normal and bypass the process to filter comment
leadingComments = slices.Collect(scanner.GetLeadingCommentRanges(p.emitContext.Factory.AsNodeFactory(), text, textRange.Pos()))
}
if len(leadingComments) > 0 {
var detachedComments []ast.CommentRange
var lastComment ast.CommentRange
for i, comment := range leadingComments {
if i > 0 {
lastCommentLine := scanner.ComputeLineOfPosition(lineMap, lastComment.End())
commentLine := scanner.ComputeLineOfPosition(lineMap, comment.Pos())
if commentLine >= lastCommentLine+2 {
// There was a blank line between the last comment and this comment. This
// comment is not part of the copyright comments. Return what we have so
// far.
break
}
}
if p.shouldWriteComment(comment) {
detachedComments = append(detachedComments, comment)
}
lastComment = comment
}
if len(detachedComments) > 0 {
// All comments look like they could have been part of the copyright header. Make
// sure there is at least one blank line between it and the node. If not, it's not
// a copyright header.
lastCommentLine := scanner.ComputeLineOfPosition(lineMap, core.LastOrNil(detachedComments).End())
nodeLine := scanner.ComputeLineOfPosition(lineMap, scanner.SkipTrivia(text, textRange.Pos()))
if nodeLine >= lastCommentLine+2 {
// Valid detachedComments
if len(leadingComments) > 0 && p.shouldEmitNewLineBeforeLeadingCommentOfPosition(textRange.Pos(), leadingComments[0].Pos()) {
p.writeLine()
}
p.emitComments(detachedComments, commentSeparatorAfter)
result = detachedCommentsInfo{nodePos: textRange.Pos(), detachedCommentEndPos: core.LastOrNil(detachedComments).End()}
hasResult = true
}
}
}
return result, hasResult
}
type commentSeparator uint32
const (
commentSeparatorNone commentSeparator = iota
commentSeparatorBefore
commentSeparatorAfter
)
func (p *Printer) emitComments(comments []ast.CommentRange, commentSeparator commentSeparator) bool {
interveningSeparator := false
if len(comments) == 0 {
return false
}
if commentSeparator == commentSeparatorBefore && !p.writer.IsAtStartOfLine() {
p.writeSpace()
}
for _, comment := range comments {
if interveningSeparator {
p.writeSpace()
interveningSeparator = false
}
p.emitComment(comment)
if comment.Kind == ast.KindSingleLineCommentTrivia || comment.HasTrailingNewLine && commentSeparator != commentSeparatorNone {
p.writeLine()
} else {
interveningSeparator = commentSeparator != commentSeparatorNone
}
}
if interveningSeparator && commentSeparator == commentSeparatorAfter && !p.writer.IsAtStartOfLine() {
p.writeSpace()
}
return true
}
func (p *Printer) emitComment(comment ast.CommentRange) {
p.emitPos(comment.Pos())
p.writeCommentRange(comment)
p.emitPos(comment.End())
}
func (p *Printer) isTripleSlashComment(comment ast.CommentRange) bool {
return p.currentSourceFile != nil &&
IsRecognizedTripleSlashComment(p.currentSourceFile.Text(), comment)
}
//
// Source Maps
//
func (p *Printer) setSourceMapSource(source sourcemap.Source) {
if p.sourceMapsDisabled {
return
}
p.sourceMapSource = source
if p.mostRecentSourceMapSource == source {
p.sourceMapSourceIndex = p.mostRecentSourceMapSourceIndex
return
}
p.sourceMapSourceIsJson = tspath.FileExtensionIs(source.FileName(), tspath.ExtensionJson)
if p.sourceMapSourceIsJson {
return
}
p.sourceMapSourceIndex = p.sourceMapGenerator.AddSource(source.FileName())
if p.Options.InlineSources {
if err := p.sourceMapGenerator.SetSourceContent(p.sourceMapSourceIndex, source.Text()); err != nil {
panic(err)
}
}
p.mostRecentSourceMapSource = source
p.mostRecentSourceMapSourceIndex = p.sourceMapSourceIndex
}
func (p *Printer) emitPos(pos int) {
if p.sourceMapsDisabled || p.sourceMapSource == nil || p.sourceMapGenerator == nil || p.sourceMapSourceIsJson || ast.PositionIsSynthesized(pos) {
return
}
sourceLine, sourceCharacter := scanner.GetECMALineAndCharacterOfPosition(p.sourceMapSource, pos)
if err := p.sourceMapGenerator.AddSourceMapping(
p.writer.GetLine(),
p.writer.GetColumn(),
p.sourceMapSourceIndex,
sourceLine,
sourceCharacter,
); err != nil {
panic(err)
}
}
// TODO: Support emitting nameIndex for source maps
////func (p *Printer) emitPosName(pos int, name string) {
//// if p.sourceMapsDisabled || p.sourceMapSource == nil || p.sourceMapGenerator == nil || p.sourceMapSourceIsJson || ast.PositionIsSynthesized(pos) {
//// return
//// }
////
//// sourceLine, sourceCharacter := scanner.GetLineAndCharacterOfPosition(p.sourceMapSource, pos)
//// nameIndex := p.sourceMapGenerator.AddName(name)
//// if err := p.sourceMapGenerator.AddNamedSourceMapping(
//// p.writer.GetLine(),
//// p.writer.GetColumn(),
//// p.sourceMapSourceIndex,
//// sourceLine,
//// sourceCharacter,
//// nameIndex,
//// ); err != nil {
//// panic(err)
//// }
////}
func (p *Printer) emitSourcePos(source sourcemap.Source, pos int) {
if source != p.sourceMapSource {
savedSourceMapSource := p.sourceMapSource
savedSourceMapSourceIndex := p.sourceMapSourceIndex
p.setSourceMapSource(source)
p.emitPos(pos)
p.sourceMapSource = savedSourceMapSource
p.sourceMapSourceIndex = savedSourceMapSourceIndex
} else {
p.emitPos(pos)
}
}
// TODO: Support emitting nameIndex for source maps
////func (p *Printer) emitSourcePosName(source sourcemap.Source, pos int, name string) {
//// if source != p.sourceMapSource {
//// savedSourceMapSource := p.sourceMapSource
//// savedSourceMapSourceIndex := p.sourceMapSourceIndex
//// p.setSourceMapSource(source)
//// p.emitPosName(pos, name)
//// p.sourceMapSource = savedSourceMapSource
//// p.sourceMapSourceIndex = savedSourceMapSourceIndex
//// } else {
//// p.emitPosName(pos, name)
//// }
////}
func (p *Printer) emitSourceMapsBeforeNode(node *ast.Node) *sourceMapState {
if !p.shouldEmitSourceMaps(node) {
return nil
}
emitFlags := p.emitContext.EmitFlags(node)
loc := p.emitContext.SourceMapRange(node)
if !ast.IsNotEmittedStatement(node) &&
emitFlags&EFNoLeadingSourceMap == 0 &&
!ast.PositionIsSynthesized(loc.Pos()) {
p.emitSourcePos(p.sourceMapSource, scanner.SkipTrivia(p.currentSourceFile.Text(), loc.Pos())) // !!! support SourceMapRange from Strada?
}
if emitFlags&EFNoNestedSourceMaps != 0 {
p.sourceMapsDisabled = true
}
state := p.sourceMapStatePool.New()
*state = sourceMapState{emitFlags, loc, false}
return state
}
func (p *Printer) emitSourceMapsAfterNode(node *ast.Node, previousState *sourceMapState) {
if previousState == nil {
return
}
emitFlags := previousState.emitFlags
loc := previousState.sourceMapRange
if emitFlags&EFNoNestedSourceMaps != 0 {
p.sourceMapsDisabled = false
}
if !ast.IsNotEmittedStatement(node) &&
emitFlags&EFNoTrailingSourceMap == 0 &&
!ast.PositionIsSynthesized(loc.End()) {
p.emitSourcePos(p.sourceMapSource, loc.End()) // !!! support SourceMapRange from Strada?
}
}
func (p *Printer) emitSourceMapsBeforeToken(token ast.Kind, pos int, contextNode *ast.Node, flags tokenEmitFlags) *sourceMapState {
if !p.shouldEmitTokenSourceMaps(token, pos, contextNode, flags) {
return nil
}
emitFlags := p.emitContext.EmitFlags(contextNode)
loc, hasLoc := p.emitContext.TokenSourceMapRange(contextNode, token)
if emitFlags&EFNoTokenLeadingSourceMaps == 0 {
if hasLoc {
pos = loc.Pos()
}
if pos >= 0 {
p.emitSourcePos(p.sourceMapSource, pos) // !!! support SourceMapRange from Strada?
}
}
state := p.sourceMapStatePool.New()
*state = sourceMapState{emitFlags, loc, hasLoc}
return state
}
func (p *Printer) emitSourceMapsAfterToken(token ast.Kind, pos int, contextNode *ast.Node, previousState *sourceMapState) {
if previousState == nil {
return
}
emitFlags := previousState.emitFlags
loc := previousState.sourceMapRange
hasLoc := previousState.hasTokenSourceMapRange
if emitFlags&EFNoTokenTrailingSourceMaps == 0 {
if hasLoc {
pos = loc.End()
}
if pos >= 0 {
p.emitSourcePos(p.sourceMapSource, pos) // !!! support SourceMapRange from Strada?
}
}
}
//
// Name Generation
//
func (p *Printer) shouldReuseTempVariableScope(node *ast.Node) bool {
return node != nil && p.emitContext.EmitFlags(node)&EFReuseTempVariableScope != 0
}
func (p *Printer) pushNameGenerationScope(node *ast.Node) {
p.nameGenerator.PushScope(p.shouldReuseTempVariableScope(node))
}
func (p *Printer) popNameGenerationScope(node *ast.Node) {
p.nameGenerator.PopScope(p.shouldReuseTempVariableScope(node))
}
func (p *Printer) generateAllNames(nodes *ast.NodeList) {
if nodes == nil {
return
}
for _, node := range nodes.Nodes {
p.generateNames(node)
}
}
func (p *Printer) generateNames(node *ast.Node) {
if node == nil {
return
}
switch node.Kind {
case ast.KindBlock:
p.generateAllNames(node.AsBlock().Statements)
case ast.KindLabeledStatement:
p.generateNames(node.AsLabeledStatement().Statement)
case ast.KindWithStatement:
p.generateNames(node.AsWithStatement().Statement)
case ast.KindDoStatement:
p.generateNames(node.AsDoStatement().Statement)
case ast.KindWhileStatement:
p.generateNames(node.AsWhileStatement().Statement)
case ast.KindIfStatement:
p.generateNames(node.AsIfStatement().ThenStatement)
p.generateNames(node.AsIfStatement().ElseStatement)
case ast.KindForStatement:
p.generateNames(node.AsForStatement().Initializer)
p.generateNames(node.AsForStatement().Statement)
case ast.KindForOfStatement, ast.KindForInStatement:
p.generateNames(node.AsForInOrOfStatement().Initializer)
p.generateNames(node.AsForInOrOfStatement().Statement)
case ast.KindSwitchStatement:
p.generateNames(node.AsSwitchStatement().CaseBlock)
case ast.KindCaseBlock:
p.generateAllNames(node.AsCaseBlock().Clauses)
case ast.KindCaseClause, ast.KindDefaultClause:
p.generateAllNames(node.AsCaseOrDefaultClause().Statements)
case ast.KindTryStatement:
p.generateNames(node.AsTryStatement().TryBlock)
p.generateNames(node.AsTryStatement().CatchClause)
p.generateNames(node.AsTryStatement().FinallyBlock)
case ast.KindCatchClause:
p.generateNames(node.AsCatchClause().VariableDeclaration)
p.generateNames(node.AsCatchClause().Block)
case ast.KindVariableStatement:
p.generateNames(node.AsVariableStatement().DeclarationList)
case ast.KindVariableDeclarationList:
p.generateAllNames(node.AsVariableDeclarationList().Declarations)
case ast.KindVariableDeclaration, ast.KindParameter, ast.KindBindingElement, ast.KindClassDeclaration:
p.generateNameIfNeeded(node.Name())
case ast.KindFunctionDeclaration:
p.generateNameIfNeeded(node.Name())
if p.shouldReuseTempVariableScope(node) {
p.generateAllNames(node.AsFunctionDeclaration().Parameters)
p.generateNames(node.AsFunctionDeclaration().Body)
}
case ast.KindObjectBindingPattern, ast.KindArrayBindingPattern:
p.generateAllNames(node.AsBindingPattern().Elements)
case ast.KindImportDeclaration, ast.KindJSImportDeclaration:
p.generateNames(node.AsImportDeclaration().ImportClause)
case ast.KindImportClause:
p.generateNameIfNeeded(node.AsImportClause().Name())
p.generateNames(node.AsImportClause().NamedBindings)
case ast.KindNamespaceImport, ast.KindNamespaceExport:
p.generateNameIfNeeded(node.Name())
case ast.KindNamedImports:
p.generateAllNames(node.AsNamedImports().Elements)
case ast.KindImportSpecifier:
n := node.AsImportSpecifier()
if n.PropertyName != nil {
p.generateNameIfNeeded(n.PropertyName)
} else {
p.generateNameIfNeeded(n.Name())
}
}
}
func (p *Printer) generateAllMemberNames(nodes *ast.NodeList) {
if nodes == nil {
return
}
for _, node := range nodes.Nodes {
p.generateMemberNames(node)
}
}
func (p *Printer) generateMemberNames(node *ast.Node) {
if node == nil {
return
}
switch node.Kind {
case ast.KindPropertyAssignment,
ast.KindShorthandPropertyAssignment,
ast.KindPropertyDeclaration,
ast.KindPropertySignature,
ast.KindMethodDeclaration,
ast.KindMethodSignature,
ast.KindGetAccessor,
ast.KindSetAccessor:
p.generateNameIfNeeded(node.Name())
}
}
func (p *Printer) generateNameIfNeeded(name *ast.DeclarationName) {
if name != nil {
if ast.IsMemberName(name) {
p.generateName(name)
} else if ast.IsBindingPattern(name) {
p.generateNames(name)
}
}
}
// Generate the text for a generated identifier or private identifier
func (p *Printer) generateName(name *ast.MemberName) {
_ = p.nameGenerator.GenerateName(name)
}
// Returns a value indicating whether a name is unique globally or within the current file.
func (p *Printer) isFileLevelUniqueNameInCurrentFile(name string, _ bool) bool {
if p.currentSourceFile != nil {
return IsFileLevelUniqueName(p.currentSourceFile, name, p.HasGlobalName)
} else {
return true
}
}
//
// Scoped operations
//
func (p *Printer) enterNode(node *ast.Node) printerState {
state := printerState{}
if p.OnBeforeEmitNode != nil {
p.OnBeforeEmitNode(node)
}
state.commentState = p.emitCommentsBeforeNode(node)
state.sourceMapState = p.emitSourceMapsBeforeNode(node)
return state
}
func (p *Printer) exitNode(node *ast.Node, previousState printerState) {
p.emitSourceMapsAfterNode(node, previousState.sourceMapState)
p.emitCommentsAfterNode(node, previousState.commentState)
if p.OnAfterEmitNode != nil {
p.OnAfterEmitNode(node)
}
}
func (p *Printer) enterTokenNode(node *ast.Node, flags tokenEmitFlags) printerState {
state := printerState{}
if p.OnBeforeEmitToken != nil {
p.OnBeforeEmitToken(node)
}
if flags&tefNoComments == 0 {
state.commentState = p.emitCommentsBeforeNode(node)
}
if flags&tefNoSourceMaps == 0 {
state.sourceMapState = p.emitSourceMapsBeforeNode(node)
}
return state
}
func (p *Printer) exitTokenNode(node *ast.Node, previousState printerState) {
p.emitSourceMapsAfterNode(node, previousState.sourceMapState)
p.emitCommentsAfterNode(node, previousState.commentState)
if p.OnAfterEmitToken != nil {
p.OnAfterEmitToken(node)
}
}
type tokenEmitFlags uint32
const (
tefNoComments tokenEmitFlags = 1 << iota
tefIndentLeadingComments
tefNoSourceMaps
tefNone tokenEmitFlags = 0
)
func (p *Printer) enterToken(token ast.Kind, pos int, contextNode *ast.Node, flags tokenEmitFlags) (printerState, int) {
state := printerState{}
state.commentState, pos = p.emitCommentsBeforeToken(token, pos, contextNode, flags)
state.sourceMapState = p.emitSourceMapsBeforeToken(token, pos, contextNode, flags)
return state, pos
}
func (p *Printer) exitToken(token ast.Kind, pos int, contextNode *ast.Node, previousState printerState) {
p.emitSourceMapsAfterToken(token, pos, contextNode, previousState.sourceMapState)
p.emitCommentsAfterToken(token, pos, contextNode, previousState.commentState)
}
type ListFormat int
const (
LFNone ListFormat = 0
// Line separators
LFSingleLine ListFormat = 0 // Prints the list on a single line (default).
LFMultiLine ListFormat = 1 << 0 // Prints the list on multiple lines.
LFPreserveLines ListFormat = 1 << 1 // Prints the list using line preservation if possible.
LFLinesMask ListFormat = LFSingleLine | LFMultiLine | LFPreserveLines
// Delimiters
LFNotDelimited ListFormat = 0 // There is no delimiter between list items (default).
LFBarDelimited ListFormat = 1 << 2 // Each list item is space-and-bar (" |") delimited.
LFAmpersandDelimited ListFormat = 1 << 3 // Each list item is space-and-ampersand (" &") delimited.
LFCommaDelimited ListFormat = 1 << 4 // Each list item is comma (",") delimited.
LFAsteriskDelimited ListFormat = 1 << 5 // Each list item is asterisk ("\n *") delimited, used with JSDoc.
LFDelimitersMask ListFormat = LFBarDelimited | LFAmpersandDelimited | LFCommaDelimited | LFAsteriskDelimited
LFAllowTrailingComma ListFormat = 1 << 6 // Write a trailing comma (",") if present.
// Whitespace
LFIndented ListFormat = 1 << 7 // The list should be indented.
LFSpaceBetweenBraces ListFormat = 1 << 8 // Inserts a space after the opening brace and before the closing brace.
LFSpaceBetweenSiblings ListFormat = 1 << 9 // Inserts a space between each sibling node.
// Brackets/Braces
LFBraces ListFormat = 1 << 10 // The list is surrounded by "{" and "}".
LFParenthesis ListFormat = 1 << 11 // The list is surrounded by "(" and ")".
LFAngleBrackets ListFormat = 1 << 12 // The list is surrounded by "<" and ">".
LFSquareBrackets ListFormat = 1 << 13 // The list is surrounded by "[" and "]".
LFBracketsMask ListFormat = LFBraces | LFParenthesis | LFAngleBrackets | LFSquareBrackets
LFOptionalIfNil ListFormat = 1 << 14 // Do not emit brackets if the list is nil.
LFOptionalIfEmpty ListFormat = 1 << 15 // Do not emit brackets if the list is empty.
LFOptional ListFormat = LFOptionalIfNil | LFOptionalIfEmpty
// Other
LFPreferNewLine ListFormat = 1 << 16 // Prefer adding a LineTerminator between synthesized nodes.
LFNoTrailingNewLine ListFormat = 1 << 17 // Do not emit a trailing NewLine for a MultiLine list.
LFNoInterveningComments ListFormat = 1 << 18 // Do not emit comments between each node
LFNoSpaceIfEmpty ListFormat = 1 << 19 // If the literal is empty, do not add spaces between braces.
LFSingleElement ListFormat = 1 << 20
LFSpaceAfterList ListFormat = 1 << 21 // Add space after list
// Precomputed Formats
LFModifiers ListFormat = LFSingleLine | LFSpaceBetweenSiblings | LFNoInterveningComments | LFSpaceAfterList
LFHeritageClauses ListFormat = LFSingleLine | LFSpaceBetweenSiblings
LFSingleLineTypeLiteralMembers ListFormat = LFSingleLine | LFSpaceBetweenBraces | LFSpaceBetweenSiblings
LFMultiLineTypeLiteralMembers ListFormat = LFMultiLine | LFIndented | LFOptionalIfEmpty
LFSingleLineTupleTypeElements ListFormat = LFCommaDelimited | LFSpaceBetweenSiblings | LFSingleLine
LFMultiLineTupleTypeElements ListFormat = LFCommaDelimited | LFIndented | LFSpaceBetweenSiblings | LFMultiLine
LFUnionTypeConstituents ListFormat = LFBarDelimited | LFSpaceBetweenSiblings | LFSingleLine
LFIntersectionTypeConstituents ListFormat = LFAmpersandDelimited | LFSpaceBetweenSiblings | LFSingleLine
LFObjectBindingPatternElements ListFormat = LFSingleLine | LFAllowTrailingComma | LFSpaceBetweenBraces | LFCommaDelimited | LFSpaceBetweenSiblings | LFNoSpaceIfEmpty
LFArrayBindingPatternElements ListFormat = LFSingleLine | LFAllowTrailingComma | LFCommaDelimited | LFSpaceBetweenSiblings | LFNoSpaceIfEmpty
LFObjectLiteralExpressionProperties ListFormat = LFPreserveLines | LFCommaDelimited | LFSpaceBetweenSiblings | LFSpaceBetweenBraces | LFIndented | LFBraces | LFNoSpaceIfEmpty
LFImportAttributes ListFormat = LFPreserveLines | LFCommaDelimited | LFSpaceBetweenSiblings | LFSpaceBetweenBraces | LFIndented | LFBraces | LFNoSpaceIfEmpty
LFArrayLiteralExpressionElements ListFormat = LFPreserveLines | LFCommaDelimited | LFSpaceBetweenSiblings | LFAllowTrailingComma | LFIndented | LFSquareBrackets
LFCommaListElements ListFormat = LFCommaDelimited | LFSpaceBetweenSiblings | LFSingleLine
LFCallExpressionArguments ListFormat = LFCommaDelimited | LFSpaceBetweenSiblings | LFSingleLine | LFParenthesis
LFNewExpressionArguments ListFormat = LFCommaDelimited | LFSpaceBetweenSiblings | LFSingleLine | LFParenthesis | LFOptionalIfNil
LFTemplateExpressionSpans ListFormat = LFSingleLine | LFNoInterveningComments
LFSingleLineBlockStatements ListFormat = LFSpaceBetweenBraces | LFSpaceBetweenSiblings | LFSingleLine
LFMultiLineBlockStatements ListFormat = LFIndented | LFMultiLine
LFVariableDeclarationList ListFormat = LFCommaDelimited | LFSpaceBetweenSiblings | LFSingleLine
LFSingleLineFunctionBodyStatements ListFormat = LFSingleLine | LFSpaceBetweenSiblings | LFSpaceBetweenBraces
LFMultiLineFunctionBodyStatements ListFormat = LFMultiLine
LFClassHeritageClauses ListFormat = LFSingleLine
LFClassMembers ListFormat = LFIndented | LFMultiLine
LFInterfaceMembers ListFormat = LFIndented | LFMultiLine
LFEnumMembers ListFormat = LFCommaDelimited | LFIndented | LFMultiLine
LFCaseBlockClauses ListFormat = LFIndented | LFMultiLine
LFNamedImportsOrExportsElements ListFormat = LFCommaDelimited | LFSpaceBetweenSiblings | LFAllowTrailingComma | LFSingleLine | LFSpaceBetweenBraces | LFNoSpaceIfEmpty
LFJsxElementOrFragmentChildren ListFormat = LFSingleLine | LFNoInterveningComments
LFJsxElementAttributes ListFormat = LFSingleLine | LFSpaceBetweenSiblings | LFNoInterveningComments
LFCaseOrDefaultClauseStatements ListFormat = LFIndented | LFMultiLine | LFNoTrailingNewLine | LFOptionalIfEmpty
LFHeritageClauseTypes ListFormat = LFCommaDelimited | LFSpaceBetweenSiblings | LFSingleLine
LFSourceFileStatements ListFormat = LFMultiLine | LFNoTrailingNewLine
LFDecorators ListFormat = LFMultiLine | LFOptional | LFSpaceAfterList
LFTypeArguments ListFormat = LFCommaDelimited | LFSpaceBetweenSiblings | LFSingleLine | LFAngleBrackets | LFOptional
LFTypeParameters ListFormat = LFCommaDelimited | LFSpaceBetweenSiblings | LFSingleLine | LFAngleBrackets | LFOptional
LFParameters ListFormat = LFCommaDelimited | LFSpaceBetweenSiblings | LFSingleLine | LFParenthesis
LFSingleArrowParameter ListFormat = LFCommaDelimited | LFSpaceBetweenSiblings | LFSingleLine
LFIndexSignatureParameters ListFormat = LFCommaDelimited | LFSpaceBetweenSiblings | LFSingleLine | LFIndented | LFSquareBrackets
LFJSDocComment ListFormat = LFMultiLine | LFAsteriskDelimited
LFImportClauseEntries ListFormat = LFImportAttributes // Deprecated: Use LFImportAttributes
)
func getOpeningBracket(format ListFormat) string {
switch format & LFBracketsMask {
case LFBraces:
return "{"
case LFParenthesis:
return "("
case LFAngleBrackets:
return "<"
case LFSquareBrackets:
return "["
default:
panic(fmt.Sprintf("Unexpected bracket: %v", format&LFBracketsMask))
}
}
func getClosingBracket(format ListFormat) string {
switch format & LFBracketsMask {
case LFBraces:
return "}"
case LFParenthesis:
return ")"
case LFAngleBrackets:
return ">"
case LFSquareBrackets:
return "]"
default:
panic(fmt.Sprintf("Unexpected bracket: %v", format&LFBracketsMask))
}
}