diff --git a/kitcom/internal/tsgo/printer/changetrackerwriter.go b/kitcom/internal/tsgo/printer/changetrackerwriter.go deleted file mode 100644 index 150f618..0000000 --- a/kitcom/internal/tsgo/printer/changetrackerwriter.go +++ /dev/null @@ -1,231 +0,0 @@ -package printer - -import ( - "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/stringutil" -) - -type ChangeTrackerWriter struct { - textWriter - lastNonTriviaPosition int - pos map[triviaPositionKey]int - end map[triviaPositionKey]int -} - -type triviaPositionKey interface { // *astNode | *ast.NodeList - Pos() int - End() int -} - -func NewChangeTrackerWriter(newline string) *ChangeTrackerWriter { - ctw := &ChangeTrackerWriter{ - textWriter: textWriter{newLine: newline}, - lastNonTriviaPosition: 0, - pos: map[triviaPositionKey]int{}, - end: map[triviaPositionKey]int{}, - } - ctw.textWriter.Clear() - return ctw -} - -func (ct *ChangeTrackerWriter) GetPrintHandlers() PrintHandlers { - return PrintHandlers{ - OnBeforeEmitNode: func(nodeOpt *ast.Node) { - if nodeOpt != nil { - ct.setPos(nodeOpt) - } - }, - OnAfterEmitNode: func(nodeOpt *ast.Node) { - if nodeOpt != nil { - ct.setEnd(nodeOpt) - } - }, - OnBeforeEmitNodeList: func(nodesOpt *ast.NodeList) { - if nodesOpt != nil { - ct.setPos(nodesOpt) - } - }, - OnAfterEmitNodeList: func(nodesOpt *ast.NodeList) { - if nodesOpt != nil { - ct.setEnd(nodesOpt) - } - }, - OnBeforeEmitToken: func(nodeOpt *ast.TokenNode) { - if nodeOpt != nil { - ct.setPos(nodeOpt) - } - }, - OnAfterEmitToken: func(nodeOpt *ast.TokenNode) { - if nodeOpt != nil { - ct.setEnd(nodeOpt) - } - }, - } -} - -func (ct *ChangeTrackerWriter) setPos(node triviaPositionKey) { - ct.pos[node] = ct.lastNonTriviaPosition -} - -func (ct *ChangeTrackerWriter) setEnd(node triviaPositionKey) { - ct.end[node] = ct.lastNonTriviaPosition -} - -func (ct *ChangeTrackerWriter) getPos(node triviaPositionKey) int { - return ct.pos[node] -} - -func (ct *ChangeTrackerWriter) getEnd(node triviaPositionKey) int { - return ct.end[node] -} - -func (ct *ChangeTrackerWriter) setLastNonTriviaPosition(s string, force bool) { - if force || scanner.SkipTrivia(s, 0) != len(s) { - ct.lastNonTriviaPosition = ct.textWriter.GetTextPos() - i := 0 - for stringutil.IsWhiteSpaceLike(rune(s[len(s)-i-1])) { - i++ - } - // trim trailing whitespaces - ct.lastNonTriviaPosition -= i - } -} - -func (ct *ChangeTrackerWriter) AssignPositionsToNode(node *ast.Node, factory *ast.NodeFactory) *ast.Node { - var visitor *ast.NodeVisitor - visitor = &ast.NodeVisitor{ - Visit: func(n *ast.Node) *ast.Node { return ct.assignPositionsToNodeWorker(n, visitor) }, - Factory: factory, - Hooks: ast.NodeVisitorHooks{ - VisitNode: ct.assignPositionsToNodeWorker, - VisitNodes: ct.assignPositionsToNodeArray, - VisitToken: ct.assignPositionsToNodeWorker, - VisitModifiers: func(modifiers *ast.ModifierList, v *ast.NodeVisitor) *ast.ModifierList { - if modifiers != nil { - ct.assignPositionsToNodeArray(&modifiers.NodeList, v) - } - return modifiers - }, - }, - } - return ct.assignPositionsToNodeWorker(node, visitor) -} - -func (ct *ChangeTrackerWriter) assignPositionsToNodeWorker( - node *ast.Node, - v *ast.NodeVisitor, -) *ast.Node { - if node == nil { - return node - } - visited := node.VisitEachChild(v) - // create proxy node for non synthesized nodes - newNode := visited - if !ast.NodeIsSynthesized(visited) { - newNode = visited.Clone(v.Factory) - } - newNode.ForEachChild(func(child *ast.Node) bool { - child.Parent = newNode - return true - }) - newNode.Loc = core.NewTextRange(ct.getPos(node), ct.getEnd(node)) - return newNode -} - -func (ct *ChangeTrackerWriter) assignPositionsToNodeArray( - nodes *ast.NodeList, - v *ast.NodeVisitor, -) *ast.NodeList { - visited := v.VisitNodes(nodes) - if visited == nil { - return visited - } - if nodes == nil { - // Debug.assert(nodes); - panic("if nodes is nil, visited should not be nil") - } - // clone nodearray if necessary - nodeArray := visited - if visited == nodes { - nodeArray = visited.Clone(v.Factory) - } - - nodeArray.Loc = core.NewTextRange(ct.getPos(nodes), ct.getEnd(nodes)) - return nodeArray -} - -func (ct *ChangeTrackerWriter) Write(text string) { - ct.textWriter.Write(text) - ct.setLastNonTriviaPosition(text, false) -} - -func (ct *ChangeTrackerWriter) WriteTrailingSemicolon(text string) { - ct.textWriter.WriteTrailingSemicolon(text) - ct.setLastNonTriviaPosition(text, false) -} -func (ct *ChangeTrackerWriter) WriteComment(text string) { ct.textWriter.WriteComment(text) } -func (ct *ChangeTrackerWriter) WriteKeyword(text string) { - ct.textWriter.WriteKeyword(text) - ct.setLastNonTriviaPosition(text, false) -} - -func (ct *ChangeTrackerWriter) WriteOperator(text string) { - ct.textWriter.WriteOperator(text) - ct.setLastNonTriviaPosition(text, false) -} - -func (ct *ChangeTrackerWriter) WritePunctuation(text string) { - ct.textWriter.WritePunctuation(text) - ct.setLastNonTriviaPosition(text, false) -} - -func (ct *ChangeTrackerWriter) WriteSpace(text string) { - ct.textWriter.WriteSpace(text) - ct.setLastNonTriviaPosition(text, false) -} - -func (ct *ChangeTrackerWriter) WriteStringLiteral(text string) { - ct.textWriter.WriteStringLiteral(text) - ct.setLastNonTriviaPosition(text, false) -} - -func (ct *ChangeTrackerWriter) WriteParameter(text string) { - ct.textWriter.WriteParameter(text) - ct.setLastNonTriviaPosition(text, false) -} - -func (ct *ChangeTrackerWriter) WriteProperty(text string) { - ct.textWriter.WriteProperty(text) - ct.setLastNonTriviaPosition(text, false) -} - -func (ct *ChangeTrackerWriter) WriteSymbol(text string, symbol *ast.Symbol) { - ct.textWriter.WriteSymbol(text, symbol) - ct.setLastNonTriviaPosition(text, false) -} -func (ct *ChangeTrackerWriter) WriteLine() { ct.textWriter.WriteLine() } -func (ct *ChangeTrackerWriter) WriteLineForce(force bool) { ct.textWriter.WriteLineForce(force) } -func (ct *ChangeTrackerWriter) IncreaseIndent() { ct.textWriter.IncreaseIndent() } -func (ct *ChangeTrackerWriter) DecreaseIndent() { ct.textWriter.DecreaseIndent() } -func (ct *ChangeTrackerWriter) Clear() { ct.textWriter.Clear(); ct.lastNonTriviaPosition = 0 } -func (ct *ChangeTrackerWriter) String() string { return ct.textWriter.String() } -func (ct *ChangeTrackerWriter) RawWrite(s string) { - ct.textWriter.RawWrite(s) - ct.setLastNonTriviaPosition(s, false) -} - -func (ct *ChangeTrackerWriter) WriteLiteral(s string) { - ct.textWriter.WriteLiteral(s) - ct.setLastNonTriviaPosition(s, true) -} -func (ct *ChangeTrackerWriter) GetTextPos() int { return ct.textWriter.GetTextPos() } -func (ct *ChangeTrackerWriter) GetLine() int { return ct.textWriter.GetLine() } -func (ct *ChangeTrackerWriter) GetColumn() int { return ct.textWriter.GetColumn() } -func (ct *ChangeTrackerWriter) GetIndent() int { return ct.textWriter.GetIndent() } -func (ct *ChangeTrackerWriter) IsAtStartOfLine() bool { return ct.textWriter.IsAtStartOfLine() } -func (ct *ChangeTrackerWriter) HasTrailingComment() bool { return ct.textWriter.HasTrailingComment() } -func (ct *ChangeTrackerWriter) HasTrailingWhitespace() bool { - return ct.textWriter.HasTrailingWhitespace() -} diff --git a/kitcom/internal/tsgo/printer/emitcontext.go b/kitcom/internal/tsgo/printer/emitcontext.go deleted file mode 100644 index d6869bf..0000000 --- a/kitcom/internal/tsgo/printer/emitcontext.go +++ /dev/null @@ -1,987 +0,0 @@ -package printer - -import ( - "maps" - "slices" - "sync" - "sync/atomic" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/collections" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" -) - -// Stores side-table information used during transformation that can be read by the printer to customize emit -// -// NOTE: EmitContext is not guaranteed to be thread-safe. -type EmitContext struct { - Factory *NodeFactory // Required. The NodeFactory to use to create new nodes - autoGenerate map[*ast.MemberName]*AutoGenerateInfo - textSource map[*ast.StringLiteralNode]*ast.Node - original map[*ast.Node]*ast.Node - emitNodes core.LinkStore[*ast.Node, emitNode] - assignedName map[*ast.Node]*ast.Expression - classThis map[*ast.Node]*ast.IdentifierNode - varScopeStack core.Stack[*varScope] - letScopeStack core.Stack[*varScope] - emitHelpers collections.OrderedSet[*EmitHelper] -} - -type environmentFlags int - -const ( - environmentFlagsNone environmentFlags = 0 - environmentFlagsInParameters environmentFlags = 1 << 0 // currently visiting a parameter list - environmentFlagsVariablesHoistedInParameters environmentFlags = 1 << 1 // a temp variable was hoisted while visiting a parameter list -) - -type varScope struct { - variables []*ast.VariableDeclarationNode - functions []*ast.FunctionDeclarationNode - flags environmentFlags - initializationStatements []*ast.Node -} - -func NewEmitContext() *EmitContext { - c := &EmitContext{} - c.Factory = NewNodeFactory(c) - return c -} - -var emitContextPool = sync.Pool{ - New: func() any { - return NewEmitContext() - }, -} - -func GetEmitContext() (*EmitContext, func()) { - c := emitContextPool.Get().(*EmitContext) - return c, func() { - c.Reset() - emitContextPool.Put(c) - } -} - -func (c *EmitContext) Reset() { - *c = EmitContext{ - Factory: c.Factory, - } -} - -func (c *EmitContext) onCreate(node *ast.Node) { - node.Flags |= ast.NodeFlagsSynthesized -} - -func (c *EmitContext) onUpdate(updated *ast.Node, original *ast.Node) { - c.SetOriginal(updated, original) -} - -func (c *EmitContext) onClone(updated *ast.Node, original *ast.Node) { - if ast.IsIdentifier(updated) || ast.IsPrivateIdentifier(updated) { - if autoGenerate := c.autoGenerate[original]; autoGenerate != nil { - autoGenerateCopy := *autoGenerate - c.autoGenerate[updated] = &autoGenerateCopy - } - } -} - -// Creates a new NodeVisitor attached to this EmitContext -func (c *EmitContext) NewNodeVisitor(visit func(node *ast.Node) *ast.Node) *ast.NodeVisitor { - return ast.NewNodeVisitor(visit, c.Factory.AsNodeFactory(), ast.NodeVisitorHooks{ - VisitParameters: c.VisitParameters, - VisitFunctionBody: c.VisitFunctionBody, - VisitIterationBody: c.VisitIterationBody, - VisitTopLevelStatements: c.VisitVariableEnvironment, - VisitEmbeddedStatement: c.VisitEmbeddedStatement, - }) -} - -// -// Environment tracking -// - -// Starts a new VariableEnvironment used to track hoisted `var` statements and function declarations. -// -// see: https://tc39.es/ecma262/#table-additional-state-components-for-ecmascript-code-execution-contexts -// -// NOTE: This is the equivalent of `transformContext.startLexicalEnvironment` in Strada. -func (c *EmitContext) StartVariableEnvironment() { - c.varScopeStack.Push(&varScope{}) - c.StartLexicalEnvironment() -} - -// Ends the current VariableEnvironment, returning a list of statements that should be emitted at the start of the current scope. -// -// NOTE: This is the equivalent of `transformContext.endLexicalEnvironment` in Strada. -func (c *EmitContext) EndVariableEnvironment() []*ast.Statement { - scope := c.varScopeStack.Pop() - var statements []*ast.Statement - if len(scope.functions) > 0 { - statements = slices.Clone(scope.functions) - } - if len(scope.variables) > 0 { - varDeclList := c.Factory.NewVariableDeclarationList(ast.NodeFlagsNone, c.Factory.NewNodeList(scope.variables)) - varStatement := c.Factory.NewVariableStatement(nil /*modifiers*/, varDeclList) - c.SetEmitFlags(varStatement, EFCustomPrologue) - statements = append(statements, varStatement) - } - if len(scope.initializationStatements) > 0 { - statements = append(statements, scope.initializationStatements...) - } - return append(statements, c.EndLexicalEnvironment()...) -} - -// Invokes c.EndVariableEnvironment() and merges the results into `statements` -func (c *EmitContext) EndAndMergeVariableEnvironmentList(statements *ast.StatementList) *ast.StatementList { - var nodes []*ast.Statement - if statements != nil { - nodes = statements.Nodes - } - - if result, changed := c.endAndMergeVariableEnvironment(nodes); changed { - list := c.Factory.NewNodeList(result) - list.Loc = statements.Loc - return list - } - - return statements -} - -// Invokes c.EndVariableEnvironment() and merges the results into `statements` -func (c *EmitContext) EndAndMergeVariableEnvironment(statements []*ast.Statement) []*ast.Statement { - result, _ := c.endAndMergeVariableEnvironment(statements) - return result -} - -func (c *EmitContext) endAndMergeVariableEnvironment(statements []*ast.Statement) ([]*ast.Statement, bool) { - return c.mergeEnvironment(statements, c.EndVariableEnvironment()) -} - -// Adds a `var` declaration to the current VariableEnvironment -// -// NOTE: This is the equivalent of `transformContext.hoistVariableDeclaration` in Strada. -func (c *EmitContext) AddVariableDeclaration(name *ast.IdentifierNode) { - varDecl := c.Factory.NewVariableDeclaration(name, nil /*exclamationToken*/, nil /*typeNode*/, nil /*initializer*/) - c.SetEmitFlags(varDecl, EFNoNestedSourceMaps) - scope := c.varScopeStack.Peek() - scope.variables = append(scope.variables, varDecl) - if scope.flags&environmentFlagsInParameters != 0 { - scope.flags |= environmentFlagsVariablesHoistedInParameters - } -} - -// Adds a hoisted function declaration to the current VariableEnvironment -// -// NOTE: This is the equivalent of `transformContext.hoistFunctionDeclaration` in Strada. -func (c *EmitContext) AddHoistedFunctionDeclaration(node *ast.FunctionDeclarationNode) { - c.SetEmitFlags(node, EFCustomPrologue) - scope := c.varScopeStack.Peek() - scope.functions = append(scope.functions, node) -} - -// Starts a new LexicalEnvironment used to track block-scoped `let`, `const`, and `using` declarations. -// -// see: https://tc39.es/ecma262/#table-additional-state-components-for-ecmascript-code-execution-contexts -// -// NOTE: This is the equivalent of `transformContext.startBlockScope` in Strada. -// NOTE: This is *not* the same as `startLexicalEnvironment` in Strada as that method is incorrectly named. -func (c *EmitContext) StartLexicalEnvironment() { - c.letScopeStack.Push(&varScope{}) -} - -// Ends the current EndLexicalEnvironment, returning a list of statements that should be emitted at the start of the current scope. -// -// NOTE: This is the equivalent of `transformContext.endLexicalEnvironment` in Strada. -// NOTE: This is *not* the same as `endLexicalEnvironment` in Strada as that method is incorrectly named. -func (c *EmitContext) EndLexicalEnvironment() []*ast.Statement { - scope := c.letScopeStack.Pop() - var statements []*ast.Statement - if len(scope.variables) > 0 { - varDeclList := c.Factory.NewVariableDeclarationList(ast.NodeFlagsLet, c.Factory.NewNodeList(scope.variables)) - varStatement := c.Factory.NewVariableStatement(nil /*modifiers*/, varDeclList) - c.SetEmitFlags(varStatement, EFCustomPrologue) - statements = append(statements, varStatement) - } - return statements -} - -// Invokes c.EndLexicalEnvironment() and merges the results into `statements` -func (c *EmitContext) EndAndMergeLexicalEnvironmentList(statements *ast.StatementList) *ast.StatementList { - var nodes []*ast.Statement - if statements != nil { - nodes = statements.Nodes - } - - if result, changed := c.endAndMergeLexicalEnvironment(nodes); changed { - list := c.Factory.NewNodeList(result) - list.Loc = statements.Loc - return list - } - - return statements -} - -// Invokes c.EndLexicalEnvironment() and merges the results into `statements` -func (c *EmitContext) EndAndMergeLexicalEnvironment(statements []*ast.Statement) []*ast.Statement { - result, _ := c.endAndMergeLexicalEnvironment(statements) - return result -} - -// Invokes c.EndLexicalEnvironment() and merges the results into `statements` -func (c *EmitContext) endAndMergeLexicalEnvironment(statements []*ast.Statement) ([]*ast.Statement, bool) { - return c.mergeEnvironment(statements, c.EndLexicalEnvironment()) -} - -// Adds a `let` declaration to the current LexicalEnvironment. -func (c *EmitContext) AddLexicalDeclaration(name *ast.IdentifierNode) { - varDecl := c.Factory.NewVariableDeclaration(name, nil /*exclamationToken*/, nil /*typeNode*/, nil /*initializer*/) - c.SetEmitFlags(varDecl, EFNoNestedSourceMaps) - scope := c.letScopeStack.Peek() - scope.variables = append(scope.variables, varDecl) -} - -// Merges declarations produced by c.EndVariableEnvironment() or c.EndLexicalEnvironment() into a statement list -func (c *EmitContext) MergeEnvironmentList(statements *ast.StatementList, declarations []*ast.Statement) *ast.StatementList { - if result, changed := c.mergeEnvironment(statements.Nodes, declarations); changed { - list := c.Factory.NewNodeList(result) - list.Loc = statements.Loc - return list - } - return statements -} - -// Merges declarations produced by c.EndVariableEnvironment() or c.EndLexicalEnvironment() into a slice of statements -func (c *EmitContext) MergeEnvironment(statements []*ast.Statement, declarations []*ast.Statement) []*ast.Statement { - result, _ := c.mergeEnvironment(statements, declarations) - return result -} - -func (c *EmitContext) mergeEnvironment(statements []*ast.Statement, declarations []*ast.Statement) ([]*ast.Statement, bool) { - if len(declarations) == 0 { - return statements, false - } - - // When we merge new lexical statements into an existing statement list, we merge them in the following manner: - // - // Given: - // - // | Left | Right | - // |------------------------------------|-------------------------------------| - // | [standard prologues (left)] | [standard prologues (right)] | - // | [hoisted functions (left)] | [hoisted functions (right)] | - // | [hoisted variables (left)] | [hoisted variables (right)] | - // | [lexical init statements (left)] | [lexical init statements (right)] | - // | [other statements (left)] | | - // - // The resulting statement list will be: - // - // | Result | - // |-------------------------------------| - // | [standard prologues (right)] | - // | [standard prologues (left)] | - // | [hoisted functions (right)] | - // | [hoisted functions (left)] | - // | [hoisted variables (right)] | - // | [hoisted variables (left)] | - // | [lexical init statements (right)] | - // | [lexical init statements (left)] | - // | [other statements (left)] | - // - // NOTE: It is expected that new lexical init statements must be evaluated before existing lexical init statements, - // as the prior transformation may depend on the evaluation of the lexical init statements to be in the correct state. - - changed := false - - // find standard prologues on left in the following order: standard directives, hoisted functions, hoisted variables, other custom - leftStandardPrologueEnd := findSpanEnd(statements, ast.IsPrologueDirective, 0) - leftHoistedFunctionsEnd := findSpanEndWithEmitContext(c, statements, (*EmitContext).isHoistedFunction, leftStandardPrologueEnd) - leftHoistedVariablesEnd := findSpanEndWithEmitContext(c, statements, (*EmitContext).isHoistedVariableStatement, leftHoistedFunctionsEnd) - - // find standard prologues on right in the following order: standard directives, hoisted functions, hoisted variables, other custom - rightStandardPrologueEnd := findSpanEnd(declarations, ast.IsPrologueDirective, 0) - rightHoistedFunctionsEnd := findSpanEndWithEmitContext(c, declarations, (*EmitContext).isHoistedFunction, rightStandardPrologueEnd) - rightHoistedVariablesEnd := findSpanEndWithEmitContext(c, declarations, (*EmitContext).isHoistedVariableStatement, rightHoistedFunctionsEnd) - rightCustomPrologueEnd := findSpanEndWithEmitContext(c, declarations, (*EmitContext).isCustomPrologue, rightHoistedVariablesEnd) - if rightCustomPrologueEnd != len(declarations) { - panic("Expected declarations to be valid standard or custom prologues") - } - - left := statements - - // splice other custom prologues from right into left - if rightCustomPrologueEnd > rightHoistedVariablesEnd { - left = core.Splice(left, leftHoistedVariablesEnd, 0, declarations[rightHoistedVariablesEnd:rightCustomPrologueEnd]...) - changed = true - } - - // splice hoisted variables from right into left - if rightHoistedVariablesEnd > rightHoistedFunctionsEnd { - left = core.Splice(left, leftHoistedFunctionsEnd, 0, declarations[rightHoistedFunctionsEnd:rightHoistedVariablesEnd]...) - changed = true - } - - // splice hoisted functions from right into left - if rightHoistedFunctionsEnd > rightStandardPrologueEnd { - left = core.Splice(left, leftStandardPrologueEnd, 0, declarations[rightStandardPrologueEnd:rightHoistedFunctionsEnd]...) - changed = true - } - - // splice standard prologues from right into left (that are not already in left) - if rightStandardPrologueEnd > 0 { - if leftStandardPrologueEnd == 0 { - left = core.Splice(left, 0, 0, declarations[:rightStandardPrologueEnd]...) - changed = true - } else { - var leftPrologues collections.Set[string] - for i := range leftStandardPrologueEnd { - leftPrologue := statements[i] - leftPrologues.Add(leftPrologue.Expression().Text()) - } - for i := rightStandardPrologueEnd - 1; i >= 0; i-- { - rightPrologue := declarations[i] - if !leftPrologues.Has(rightPrologue.Expression().Text()) { - left = core.Concatenate([]*ast.Statement{rightPrologue}, left) - changed = true - } - } - } - } - - return left, changed -} - -func (c *EmitContext) isCustomPrologue(node *ast.Statement) bool { - return c.EmitFlags(node)&EFCustomPrologue != 0 -} - -func (c *EmitContext) isHoistedFunction(node *ast.Statement) bool { - return c.isCustomPrologue(node) && ast.IsFunctionDeclaration(node) -} - -func isHoistedVariable(node *ast.VariableDeclarationNode) bool { - return ast.IsIdentifier(node.Name()) && node.Initializer() == nil -} - -func (c *EmitContext) isHoistedVariableStatement(node *ast.Statement) bool { - return c.isCustomPrologue(node) && - ast.IsVariableStatement(node) && - core.Every(node.AsVariableStatement().DeclarationList.AsVariableDeclarationList().Declarations.Nodes, isHoistedVariable) -} - -// -// Name Generation -// - -// Gets whether a given name has an associated AutoGenerateInfo entry. -func (c *EmitContext) HasAutoGenerateInfo(node *ast.MemberName) bool { - if node != nil { - _, ok := c.autoGenerate[node] - return ok - } - return false -} - -// Gets the associated AutoGenerateInfo entry for a given name. -func (c *EmitContext) GetAutoGenerateInfo(name *ast.MemberName) *AutoGenerateInfo { - if name == nil { - return nil - } - return c.autoGenerate[name] -} - -// Walks the associated AutoGenerateInfo entries of a name to find the root Nopde from which the name should be generated. -func (c *EmitContext) GetNodeForGeneratedName(name *ast.MemberName) *ast.Node { - if autoGenerate := c.autoGenerate[name]; autoGenerate != nil && autoGenerate.Flags.IsNode() { - return c.getNodeForGeneratedNameWorker(autoGenerate.Node, autoGenerate.Id) - } - return name -} - -func (c *EmitContext) getNodeForGeneratedNameWorker(node *ast.Node, autoGenerateId AutoGenerateId) *ast.Node { - original := c.Original(node) - for original != nil { - node = original - if ast.IsMemberName(node) { - // if "node" is a different generated name (having a different "autoGenerateId"), use it and stop traversing. - autoGenerate := c.autoGenerate[node] - if autoGenerate == nil || autoGenerate.Flags.IsNode() && autoGenerate.Id != autoGenerateId { - break - } - if autoGenerate.Flags.IsNode() { - original = autoGenerate.Node - continue - } - } - original = c.Original(node) - } - return node -} - -type AutoGenerateOptions struct { - Flags GeneratedIdentifierFlags - Prefix string - Suffix string -} - -var nextAutoGenerateId atomic.Uint32 - -type AutoGenerateId uint32 - -type AutoGenerateInfo struct { - Flags GeneratedIdentifierFlags // Specifies whether to auto-generate the text for an identifier. - Id AutoGenerateId // Ensures unique generated identifiers get unique names, but clones get the same name. - Prefix string // Optional prefix to apply to the start of the generated name - Suffix string // Optional suffix to apply to the end of the generated name - Node *ast.Node // For a GeneratedIdentifierFlagsNode, the node from which to generate an identifier -} - -// -// Original Node Tracking -// - -// Sets the original node for a given node. -// -// NOTE: This is the equivalent to `setOriginalNode` in Strada. -func (c *EmitContext) SetOriginal(node *ast.Node, original *ast.Node) { - c.SetOriginalEx(node, original, false) -} - -func (c *EmitContext) SetOriginalEx(node *ast.Node, original *ast.Node, allowOverwrite bool) { - if original == nil { - panic("Original cannot be nil.") - } - - if c.original == nil { - c.original = make(map[*ast.Node]*ast.Node) - } - - existing, ok := c.original[node] - if !ok { - c.original[node] = original - if emitNode := c.emitNodes.TryGet(original); emitNode != nil { - c.emitNodes.Get(node).copyFrom(emitNode) - } - } else if !allowOverwrite && existing != original { - panic("Original node already set.") - } else if allowOverwrite { - c.original[node] = original - } -} - -// Gets the original node for a given node. -// -// NOTE: This is the equivalent to reading `node.original` in Strada. -func (c *EmitContext) Original(node *ast.Node) *ast.Node { - return c.original[node] -} - -// Gets the most original node associated with this node by walking Original pointers. -// -// NOTE: This method is analogous to `getOriginalNode` in the old compiler, but the name has changed to avoid accidental -// conflation with `SetOriginal`/`Original` -func (c *EmitContext) MostOriginal(node *ast.Node) *ast.Node { - if node != nil { - original := c.Original(node) - for original != nil { - node = original - original = c.Original(node) - } - } - return node -} - -// Gets the original parse tree node for a given node. -// -// NOTE: This is the equivalent to `getParseTreeNode` in Strada. -func (c *EmitContext) ParseNode(node *ast.Node) *ast.Node { - node = c.MostOriginal(node) - if node != nil && ast.IsParseTreeNode(node) { - return node - } - return nil -} - -// -// Emit-related Data -// - -type emitNodeFlags uint32 - -const ( - hasCommentRange emitNodeFlags = 1 << iota - hasSourceMapRange -) - -type SynthesizedComment struct { - Kind ast.Kind - Loc core.TextRange - HasLeadingNewLine bool - HasTrailingNewLine bool - Text string -} - -type emitNode struct { - flags emitNodeFlags - emitFlags EmitFlags - commentRange core.TextRange - sourceMapRange core.TextRange - tokenSourceMapRanges map[ast.Kind]core.TextRange - helpers []*EmitHelper - externalHelpersModuleName *ast.IdentifierNode - leadingComments []SynthesizedComment - trailingComments []SynthesizedComment -} - -// NOTE: This method is not guaranteed to be thread-safe -func (e *emitNode) copyFrom(source *emitNode) { - e.flags = source.flags - e.emitFlags = source.emitFlags - e.commentRange = source.commentRange - e.sourceMapRange = source.sourceMapRange - e.tokenSourceMapRanges = maps.Clone(source.tokenSourceMapRanges) - e.helpers = slices.Clone(source.helpers) - e.externalHelpersModuleName = source.externalHelpersModuleName -} - -func (c *EmitContext) EmitFlags(node *ast.Node) EmitFlags { - if emitNode := c.emitNodes.TryGet(node); emitNode != nil { - return emitNode.emitFlags - } - return EFNone -} - -func (c *EmitContext) SetEmitFlags(node *ast.Node, flags EmitFlags) { - c.emitNodes.Get(node).emitFlags = flags -} - -func (c *EmitContext) AddEmitFlags(node *ast.Node, flags EmitFlags) { - c.emitNodes.Get(node).emitFlags |= flags -} - -// Gets the range to use for a node when emitting comments. -func (c *EmitContext) CommentRange(node *ast.Node) core.TextRange { - if emitNode := c.emitNodes.TryGet(node); emitNode != nil && emitNode.flags&hasCommentRange != 0 { - return emitNode.commentRange - } - return node.Loc -} - -// Sets the range to use for a node when emitting comments. -func (c *EmitContext) SetCommentRange(node *ast.Node, loc core.TextRange) { - emitNode := c.emitNodes.Get(node) - emitNode.commentRange = loc - emitNode.flags |= hasCommentRange -} - -// Sets the range to use for a node when emitting comments. -func (c *EmitContext) AssignCommentRange(to *ast.Node, from *ast.Node) { - c.SetCommentRange(to, c.CommentRange(from)) -} - -// Gets the range to use for a node when emitting source maps. -func (c *EmitContext) SourceMapRange(node *ast.Node) core.TextRange { - if emitNode := c.emitNodes.TryGet(node); emitNode != nil && emitNode.flags&hasSourceMapRange != 0 { - return emitNode.sourceMapRange - } - return node.Loc -} - -// Sets the range to use for a node when emitting source maps. -func (c *EmitContext) SetSourceMapRange(node *ast.Node, loc core.TextRange) { - emitNode := c.emitNodes.Get(node) - emitNode.sourceMapRange = loc - emitNode.flags |= hasSourceMapRange -} - -// Sets the range to use for a node when emitting source maps. -func (c *EmitContext) AssignSourceMapRange(to *ast.Node, from *ast.Node) { - c.SetSourceMapRange(to, c.SourceMapRange(from)) -} - -// Sets the range to use for a node when emitting comments and source maps. -func (c *EmitContext) AssignCommentAndSourceMapRanges(to *ast.Node, from *ast.Node) { - emitNode := c.emitNodes.Get(to) - commentRange := c.CommentRange(from) - sourceMapRange := c.SourceMapRange(from) - emitNode.commentRange = commentRange - emitNode.sourceMapRange = sourceMapRange - emitNode.flags |= hasCommentRange | hasSourceMapRange -} - -// Gets the range for a token of a node when emitting source maps. -func (c *EmitContext) TokenSourceMapRange(node *ast.Node, kind ast.Kind) (core.TextRange, bool) { - if emitNode := c.emitNodes.TryGet(node); emitNode != nil && emitNode.tokenSourceMapRanges != nil { - if loc, ok := emitNode.tokenSourceMapRanges[kind]; ok { - return loc, true - } - } - return core.TextRange{}, false -} - -// Sets the range for a token of a node when emitting source maps. -func (c *EmitContext) SetTokenSourceMapRange(node *ast.Node, kind ast.Kind, loc core.TextRange) { - emitNode := c.emitNodes.Get(node) - if emitNode.tokenSourceMapRanges == nil { - emitNode.tokenSourceMapRanges = make(map[ast.Kind]core.TextRange) - } - emitNode.tokenSourceMapRanges[kind] = loc -} - -func (c *EmitContext) AssignedName(node *ast.Node) *ast.Expression { - return c.assignedName[node] -} - -func (c *EmitContext) SetAssignedName(node *ast.Node, name *ast.Expression) { - if c.assignedName == nil { - c.assignedName = make(map[*ast.Node]*ast.Expression) - } - c.assignedName[node] = name -} - -func (c *EmitContext) ClassThis(node *ast.Node) *ast.Expression { - return c.classThis[node] -} - -func (c *EmitContext) SetClassThis(node *ast.Node, classThis *ast.IdentifierNode) { - if c.classThis == nil { - c.classThis = make(map[*ast.Node]*ast.Expression) - } - c.classThis[node] = classThis -} - -func (c *EmitContext) RequestEmitHelper(helper *EmitHelper) { - if helper.Scoped { - panic("Cannot request a scoped emit helper") - } - for _, h := range helper.Dependencies { - c.RequestEmitHelper(h) - } - c.emitHelpers.Add(helper) -} - -func (c *EmitContext) ReadEmitHelpers() []*EmitHelper { - helpers := slices.Collect(c.emitHelpers.Values()) - c.emitHelpers.Clear() - return helpers -} - -func (c *EmitContext) AddEmitHelper(node *ast.Node, helper ...*EmitHelper) { - emitNode := c.emitNodes.Get(node) - emitNode.helpers = append(emitNode.helpers, helper...) -} - -func (c *EmitContext) MoveEmitHelpers(source *ast.Node, target *ast.Node, predicate func(helper *EmitHelper) bool) { - sourceEmitNode := c.emitNodes.TryGet(source) - if sourceEmitNode == nil { - return - } - sourceEmitHelpers := sourceEmitNode.helpers - if len(sourceEmitHelpers) == 0 { - return - } - - targetEmitNode := c.emitNodes.Get(target) - helpersRemoved := 0 - for i := range sourceEmitHelpers { - helper := sourceEmitHelpers[i] - if predicate(helper) { - helpersRemoved++ - targetEmitNode.helpers = core.AppendIfUnique(targetEmitNode.helpers, helper) - } else if helpersRemoved > 0 { - sourceEmitHelpers[i-helpersRemoved] = helper - } - } - - if helpersRemoved > 0 { - sourceEmitHelpers = sourceEmitHelpers[:len(sourceEmitHelpers)-helpersRemoved] - sourceEmitNode.helpers = sourceEmitHelpers - } -} - -func (c *EmitContext) GetEmitHelpers(node *ast.Node) []*EmitHelper { - emitNode := c.emitNodes.TryGet(node) - if emitNode != nil { - return emitNode.helpers - } - return nil -} - -func (c *EmitContext) GetExternalHelpersModuleName(node *ast.SourceFile) *ast.IdentifierNode { - if parseNode := c.ParseNode(node.AsNode()); parseNode != nil { - if emitNode := c.emitNodes.TryGet(parseNode); emitNode != nil { - return emitNode.externalHelpersModuleName - } - } - return nil -} - -func (c *EmitContext) SetExternalHelpersModuleName(node *ast.SourceFile, name *ast.IdentifierNode) { - parseNode := c.ParseNode(node.AsNode()) - if parseNode == nil { - panic("Node must be a parse tree node or have an Original pointer to a parse tree node.") - } - - emitNode := c.emitNodes.Get(parseNode) - emitNode.externalHelpersModuleName = name -} - -func (c *EmitContext) HasRecordedExternalHelpers(node *ast.SourceFile) bool { - if parseNode := c.ParseNode(node.AsNode()); parseNode != nil { - emitNode := c.emitNodes.TryGet(parseNode) - return emitNode != nil && (emitNode.externalHelpersModuleName != nil || emitNode.emitFlags&EFExternalHelpers != 0) - } - return false -} - -func (c *EmitContext) IsCallToHelper(firstSegment *ast.Expression, helperName string) bool { - return ast.IsCallExpression(firstSegment) && - ast.IsIdentifier(firstSegment.Expression()) && - (c.EmitFlags(firstSegment.Expression())&EFHelperName) != 0 && - firstSegment.Expression().Text() == helperName -} - -// -// Visitor Hooks -// - -func (c *EmitContext) VisitVariableEnvironment(nodes *ast.StatementList, visitor *ast.NodeVisitor) *ast.StatementList { - c.StartVariableEnvironment() - return c.EndAndMergeVariableEnvironmentList(visitor.VisitNodes(nodes)) -} - -func (c *EmitContext) VisitParameters(nodes *ast.ParameterList, visitor *ast.NodeVisitor) *ast.ParameterList { - c.StartVariableEnvironment() - scope := c.varScopeStack.Peek() - oldFlags := scope.flags - scope.flags |= environmentFlagsInParameters - nodes = visitor.VisitNodes(nodes) - - // As of ES2015, any runtime execution of that occurs in for a parameter (such as evaluating an - // initializer or a binding pattern), occurs in its own lexical scope. As a result, any expression - // that we might transform that introduces a temporary variable would fail as the temporary variable - // exists in a different lexical scope. To address this, we move any binding patterns and initializers - // in a parameter list to the body if we detect a variable being hoisted while visiting a parameter list - // when the emit target is greater than ES2015. (Which is now all targets.) - if scope.flags&environmentFlagsVariablesHoistedInParameters != 0 { - nodes = c.addDefaultValueAssignmentsIfNeeded(nodes) - } - scope.flags = oldFlags - // !!! c.suspendVariableEnvironment() - return nodes -} - -func (c *EmitContext) addDefaultValueAssignmentsIfNeeded(nodeList *ast.ParameterList) *ast.ParameterList { - if nodeList == nil { - return nodeList - } - var result []*ast.Node - nodes := nodeList.Nodes - for i, parameter := range nodes { - updated := c.addDefaultValueAssignmentIfNeeded(parameter.AsParameterDeclaration()) - if updated != parameter { - if result == nil { - result = slices.Clone(nodes) - } - result[i] = updated - } - } - if result != nil { - res := c.Factory.NewNodeList(result) - res.Loc = nodeList.Loc - return res - } - return nodeList -} - -func (c *EmitContext) addDefaultValueAssignmentIfNeeded(parameter *ast.ParameterDeclaration) *ast.Node { - // A rest parameter cannot have a binding pattern or an initializer, - // so let's just ignore it. - if parameter.DotDotDotToken != nil { - return parameter.AsNode() - } else if ast.IsBindingPattern(parameter.Name()) { - return c.addDefaultValueAssignmentForBindingPattern(parameter) - } else if parameter.Initializer != nil { - return c.addDefaultValueAssignmentForInitializer(parameter, parameter.Name(), parameter.Initializer) - } - return parameter.AsNode() -} - -func (c *EmitContext) addDefaultValueAssignmentForBindingPattern(parameter *ast.ParameterDeclaration) *ast.Node { - var initNode *ast.Node - if parameter.Initializer != nil { - initNode = c.Factory.NewConditionalExpression( - c.Factory.NewStrictEqualityExpression( - c.Factory.NewGeneratedNameForNode(parameter.AsNode()), - c.Factory.NewVoidZeroExpression(), - ), - c.Factory.NewToken(ast.KindQuestionToken), - parameter.Initializer, - c.Factory.NewToken(ast.KindColonToken), - c.Factory.NewGeneratedNameForNode(parameter.AsNode()), - ) - } else { - initNode = c.Factory.NewGeneratedNameForNode(parameter.AsNode()) - } - c.AddInitializationStatement(c.Factory.NewVariableStatement( - nil, - c.Factory.NewVariableDeclarationList(ast.NodeFlagsNone, c.Factory.NewNodeList([]*ast.Node{c.Factory.NewVariableDeclaration( - parameter.Name(), - nil, - parameter.Type, - initNode, - )})), - )) - return c.Factory.UpdateParameterDeclaration( - parameter, - parameter.Modifiers(), - parameter.DotDotDotToken, - c.Factory.NewGeneratedNameForNode(parameter.AsNode()), - parameter.QuestionToken, - parameter.Type, - nil, - ) -} - -func (c *EmitContext) addDefaultValueAssignmentForInitializer(parameter *ast.ParameterDeclaration, name *ast.Node, initializer *ast.Node) *ast.Node { - c.AddEmitFlags(initializer, EFNoSourceMap|EFNoComments) - nameClone := name.Clone(c.Factory) - c.AddEmitFlags(nameClone, EFNoSourceMap) - initAssignment := c.Factory.NewAssignmentExpression( - nameClone, - initializer, - ) - initAssignment.Loc = parameter.Loc - c.AddEmitFlags(initAssignment, EFNoComments) - initBlock := c.Factory.NewBlock(c.Factory.NewNodeList([]*ast.Node{c.Factory.NewExpressionStatement(initAssignment)}), false) - initBlock.Loc = parameter.Loc - c.AddEmitFlags(initBlock, EFSingleLine|EFNoTrailingSourceMap|EFNoTokenSourceMaps|EFNoComments) - c.AddInitializationStatement(c.Factory.NewIfStatement( - c.Factory.NewTypeCheck(name.Clone(c.Factory), "undefined"), - initBlock, - nil, - )) - return c.Factory.UpdateParameterDeclaration( - parameter, - parameter.Modifiers(), - parameter.DotDotDotToken, - parameter.Name(), - parameter.QuestionToken, - parameter.Type, - nil, - ) -} - -func (c *EmitContext) AddInitializationStatement(node *ast.Node) { - scope := c.varScopeStack.Peek() - if scope == nil { - panic("Tried to add an initialization statement without a surrounding variable scope") - } - c.AddEmitFlags(node, EFCustomPrologue) - scope.initializationStatements = append(scope.initializationStatements, node) -} - -func (c *EmitContext) VisitFunctionBody(node *ast.BlockOrExpression, visitor *ast.NodeVisitor) *ast.BlockOrExpression { - // !!! c.resumeVariableEnvironment() - updated := visitor.VisitNode(node) - declarations := c.EndVariableEnvironment() - if len(declarations) == 0 { - return updated - } - - if updated == nil { - return c.Factory.NewBlock(c.Factory.NewNodeList(declarations), true /*multiLine*/) - } - - if !ast.IsBlock(updated) { - statements := c.MergeEnvironment([]*ast.Statement{c.Factory.NewReturnStatement(updated)}, declarations) - return c.Factory.NewBlock(c.Factory.NewNodeList(statements), true /*multiLine*/) - } - - return c.Factory.UpdateBlock( - updated.AsBlock(), - c.MergeEnvironmentList(updated.AsBlock().Statements, declarations), - ) -} - -func (c *EmitContext) VisitIterationBody(body *ast.Statement, visitor *ast.NodeVisitor) *ast.Statement { - if body == nil { - return nil - } - - c.StartLexicalEnvironment() - updated := c.VisitEmbeddedStatement(body, visitor) - if updated == nil { - panic("Expected visitor to return a statement.") - } - - statements := c.EndLexicalEnvironment() - if len(statements) > 0 { - if ast.IsBlock(updated) { - statements = append(statements, updated.AsBlock().Statements.Nodes...) - statementsList := c.Factory.NewNodeList(statements) - statementsList.Loc = updated.AsBlock().Statements.Loc - return c.Factory.UpdateBlock(updated.AsBlock(), statementsList) - } - statements = append(statements, updated) - return c.Factory.NewBlock(c.Factory.NewNodeList(statements), true /*multiLine*/) - } - - return updated -} - -func (c *EmitContext) VisitEmbeddedStatement(node *ast.Statement, visitor *ast.NodeVisitor) *ast.Statement { - embeddedStatement := visitor.VisitEmbeddedStatement(node) - if embeddedStatement == nil { - return nil - } - if ast.IsNotEmittedStatement(embeddedStatement) { - emptyStatement := visitor.Factory.NewEmptyStatement() - emptyStatement.Loc = node.Loc - c.SetOriginal(emptyStatement, node) - c.AssignCommentRange(emptyStatement, node) - return emptyStatement - } - return embeddedStatement -} - -func (c *EmitContext) SetSyntheticLeadingComments(node *ast.Node, comments []SynthesizedComment) *ast.Node { - c.emitNodes.Get(node).leadingComments = comments - return node -} - -func (c *EmitContext) AddSyntheticLeadingComment(node *ast.Node, kind ast.Kind, text string, hasTrailingNewLine bool) *ast.Node { - c.emitNodes.Get(node).leadingComments = append(c.emitNodes.Get(node).leadingComments, SynthesizedComment{Kind: kind, Loc: core.NewTextRange(-1, -1), HasTrailingNewLine: hasTrailingNewLine, Text: text}) - return node -} - -func (c *EmitContext) GetSyntheticLeadingComments(node *ast.Node) []SynthesizedComment { - if c.emitNodes.Has(node) { - return c.emitNodes.Get(node).leadingComments - } - return nil -} - -func (c *EmitContext) SetSyntheticTrailingComments(node *ast.Node, comments []SynthesizedComment) *ast.Node { - c.emitNodes.Get(node).trailingComments = comments - return node -} - -func (c *EmitContext) AddSyntheticTrailingComment(node *ast.Node, kind ast.Kind, text string, hasTrailingNewLine bool) *ast.Node { - c.emitNodes.Get(node).trailingComments = append(c.emitNodes.Get(node).trailingComments, SynthesizedComment{Kind: kind, Loc: core.NewTextRange(-1, -1), HasTrailingNewLine: hasTrailingNewLine, Text: text}) - return node -} - -func (c *EmitContext) GetSyntheticTrailingComments(node *ast.Node) []SynthesizedComment { - if c.emitNodes.Has(node) { - return c.emitNodes.Get(node).trailingComments - } - return nil -} - -func (c *EmitContext) NewNotEmittedStatement(node *ast.Node) *ast.Statement { - statement := c.Factory.NewNotEmittedStatement() - statement.Loc = node.Loc - c.SetOriginal(statement, node) - c.AssignCommentRange(statement, node) - return statement -} diff --git a/kitcom/internal/tsgo/printer/emitflags.go b/kitcom/internal/tsgo/printer/emitflags.go deleted file mode 100644 index bd4f7c1..0000000 --- a/kitcom/internal/tsgo/printer/emitflags.go +++ /dev/null @@ -1,37 +0,0 @@ -package printer - -type EmitFlags uint32 - -const ( - EFSingleLine EmitFlags = 1 << iota // The contents of this node should be emitted on a single line. - EFMultiLine // The contents of this node should be emitted on multiple lines. - EFNoLeadingSourceMap // Do not emit a leading source map location for this node. - EFNoTrailingSourceMap // Do not emit a trailing source map location for this node. - EFNoNestedSourceMaps // Do not emit source map locations for children of this node. - EFNoTokenLeadingSourceMaps // Do not emit leading source map location for token nodes. - EFNoTokenTrailingSourceMaps // Do not emit trailing source map location for token nodes. - EFNoLeadingComments // Do not emit leading comments for this node. - EFNoTrailingComments // Do not emit trailing comments for this node. - EFNoNestedComments // Do not emit nested comments for children of this node. - EFHelperName // The Identifier refers to an *unscoped* emit helper (one that is emitted at the top of the file) - EFExportName // Ensure an export prefix is added for an identifier that points to an exported declaration with a local name (see SymbolFlags.ExportHasLocal). - EFLocalName // Ensure an export prefix is not added for an identifier that points to an exported declaration. - EFInternalName // The name is internal to an ES5 class body function. - EFIndented // Adds an explicit extra indentation level for class and function bodies when printing (used to match old emitter). - EFNoIndentation // Do not indent the node. - EFReuseTempVariableScope // Reuse the existing temp variable scope during emit. - EFCustomPrologue // Treat the statement as if it were a prologue directive (NOTE: Prologue directives are *not* transformed). - EFNoHoisting // Do not hoist this declaration in --module system - EFNoAsciiEscaping // When synthesizing nodes that lack an original node or textSourceNode, we want to write the text on the node with ASCII escaping substitutions. - EFExternalHelpers // This source file has external helpers - EFNeverApplyImportHelper // Do not apply an import helper to this node - EFStartOnNewLine // Start this node on a new line - EFIndirectCall // Emit CallExpression as an indirect call: `(0, f)()` -) - -const ( - EFNone EmitFlags = 0 - EFNoSourceMap = EFNoLeadingSourceMap | EFNoTrailingSourceMap // Do not emit a source map location for this node. - EFNoTokenSourceMaps = EFNoTokenLeadingSourceMaps | EFNoTokenTrailingSourceMaps // Do not emit source map locations for tokens of this node. - EFNoComments = EFNoLeadingComments | EFNoTrailingComments // Do not emit comments for this node. -) diff --git a/kitcom/internal/tsgo/printer/emithost.go b/kitcom/internal/tsgo/printer/emithost.go deleted file mode 100644 index ff07a61..0000000 --- a/kitcom/internal/tsgo/printer/emithost.go +++ /dev/null @@ -1,23 +0,0 @@ -package printer - -import ( - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tsoptions" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath" -) - -// NOTE: EmitHost operations must be thread-safe -type EmitHost interface { - Options() *core.CompilerOptions - SourceFiles() []*ast.SourceFile - UseCaseSensitiveFileNames() bool - GetCurrentDirectory() string - CommonSourceDirectory() string - IsEmitBlocked(file string) bool - WriteFile(fileName string, text string, writeByteOrderMark bool) error - GetEmitModuleFormatOfFile(file ast.HasFileName) core.ModuleKind - GetEmitResolver() EmitResolver - GetProjectReferenceFromSource(path tspath.Path) *tsoptions.SourceOutputAndProjectReference - IsSourceFileFromExternalLibrary(file *ast.SourceFile) bool -} diff --git a/kitcom/internal/tsgo/printer/emitresolver.go b/kitcom/internal/tsgo/printer/emitresolver.go deleted file mode 100644 index 5348979..0000000 --- a/kitcom/internal/tsgo/printer/emitresolver.go +++ /dev/null @@ -1,68 +0,0 @@ -package printer - -import ( - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/binder" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/evaluator" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/nodebuilder" -) - -type SymbolAccessibility int32 - -const ( - SymbolAccessibilityAccessible SymbolAccessibility = iota - SymbolAccessibilityNotAccessible - SymbolAccessibilityCannotBeNamed - SymbolAccessibilityNotResolved -) - -type SymbolAccessibilityResult struct { - Accessibility SymbolAccessibility - AliasesToMakeVisible []*ast.Node // aliases that need to have this symbol visible - ErrorSymbolName string // Optional - symbol name that results in error - ErrorNode *ast.Node // Optional - node that results in error - ErrorModuleName string // Optional - If the symbol is not visible from module, module's name -} - -type EmitResolver interface { - binder.ReferenceResolver - IsReferencedAliasDeclaration(node *ast.Node) bool - IsValueAliasDeclaration(node *ast.Node) bool - IsTopLevelValueImportEqualsWithEntityName(node *ast.Node) bool - MarkLinkedReferencesRecursively(file *ast.SourceFile) - GetExternalModuleFileFromDeclaration(node *ast.Node) *ast.SourceFile - GetEffectiveDeclarationFlags(node *ast.Node, flags ast.ModifierFlags) ast.ModifierFlags - GetResolutionModeOverride(node *ast.Node) core.ResolutionMode - - // const enum inlining - GetConstantValue(node *ast.Node) any - - // JSX Emit - GetJsxFactoryEntity(location *ast.Node) *ast.Node - GetJsxFragmentFactoryEntity(location *ast.Node) *ast.Node - SetReferencedImportDeclaration(node *ast.IdentifierNode, ref *ast.Declaration) // for overriding the reference resolver behavior for generated identifiers - - // declaration emit checker functionality projections - PrecalculateDeclarationEmitVisibility(file *ast.SourceFile) - IsSymbolAccessible(symbol *ast.Symbol, enclosingDeclaration *ast.Node, meaning ast.SymbolFlags, shouldComputeAliasToMarkVisible bool) SymbolAccessibilityResult - IsEntityNameVisible(entityName *ast.Node, enclosingDeclaration *ast.Node) SymbolAccessibilityResult // previously SymbolVisibilityResult in strada - ErrorModuleName never set - IsExpandoFunctionDeclaration(node *ast.Node) bool - IsLiteralConstDeclaration(node *ast.Node) bool - RequiresAddingImplicitUndefined(node *ast.Node, symbol *ast.Symbol, enclosingDeclaration *ast.Node) bool - IsDeclarationVisible(node *ast.Node) bool - IsImportRequiredByAugmentation(decl *ast.ImportDeclaration) bool - IsDefinitelyReferenceToGlobalSymbolObject(node *ast.Node) bool - IsImplementationOfOverload(node *ast.SignatureDeclaration) bool - GetEnumMemberValue(node *ast.Node) evaluator.Result - IsLateBound(node *ast.Node) bool - IsOptionalParameter(node *ast.Node) bool - - // Node construction for declaration emit - CreateTypeOfDeclaration(emitContext *EmitContext, declaration *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node - CreateReturnTypeOfSignatureDeclaration(emitContext *EmitContext, signatureDeclaration *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node - CreateTypeParametersOfSignatureDeclaration(emitContext *EmitContext, signatureDeclaration *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) []*ast.Node - CreateLiteralConstValue(emitContext *EmitContext, node *ast.Node, tracker nodebuilder.SymbolTracker) *ast.Node - CreateTypeOfExpression(emitContext *EmitContext, expression *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node - CreateLateBoundIndexSignatures(emitContext *EmitContext, container *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) []*ast.Node -} diff --git a/kitcom/internal/tsgo/printer/emittextwriter.go b/kitcom/internal/tsgo/printer/emittextwriter.go deleted file mode 100644 index a6a3d5e..0000000 --- a/kitcom/internal/tsgo/printer/emittextwriter.go +++ /dev/null @@ -1,33 +0,0 @@ -package printer - -import "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - -// Externally opaque interface for printing text -type EmitTextWriter interface { - Write(s string) - WriteTrailingSemicolon(text string) - WriteComment(text string) - WriteKeyword(text string) - WriteOperator(text string) - WritePunctuation(text string) - WriteSpace(text string) - WriteStringLiteral(text string) - WriteParameter(text string) - WriteProperty(text string) - WriteSymbol(text string, symbol *ast.Symbol) - WriteLine() - WriteLineForce(force bool) - IncreaseIndent() - DecreaseIndent() - Clear() - String() string - RawWrite(s string) - WriteLiteral(s string) - GetTextPos() int - GetLine() int - GetColumn() int - GetIndent() int - IsAtStartOfLine() bool - HasTrailingComment() bool - HasTrailingWhitespace() bool -} diff --git a/kitcom/internal/tsgo/printer/factory.go b/kitcom/internal/tsgo/printer/factory.go deleted file mode 100644 index 733af38..0000000 --- a/kitcom/internal/tsgo/printer/factory.go +++ /dev/null @@ -1,693 +0,0 @@ -package printer - -import ( - "fmt" - "strconv" - "strings" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/debug" -) - -type NodeFactory struct { - ast.NodeFactory - emitContext *EmitContext -} - -func NewNodeFactory(context *EmitContext) *NodeFactory { - return &NodeFactory{ - NodeFactory: *ast.NewNodeFactory(ast.NodeFactoryHooks{ - OnCreate: context.onCreate, - OnUpdate: context.onUpdate, - OnClone: context.onClone, - }), - emitContext: context, - } -} - -func (f *NodeFactory) newGeneratedIdentifier(kind GeneratedIdentifierFlags, text string, node *ast.Node, options AutoGenerateOptions) *ast.IdentifierNode { - id := AutoGenerateId(nextAutoGenerateId.Add(1)) - - if len(text) == 0 { - switch { - case node == nil: - text = fmt.Sprintf("(auto@%d)", id) - case ast.IsMemberName(node): - text = node.Text() - default: - text = fmt.Sprintf("(generated@%v)", ast.GetNodeId(f.emitContext.getNodeForGeneratedNameWorker(node, id))) - } - text = FormatGeneratedName(false /*privateName*/, options.Prefix, text, options.Suffix) - } - - name := f.NewIdentifier(text) - autoGenerate := &AutoGenerateInfo{ - Id: id, - Flags: kind | (options.Flags & ^GeneratedIdentifierFlagsKindMask), - Prefix: options.Prefix, - Suffix: options.Suffix, - Node: node, - } - if f.emitContext.autoGenerate == nil { - f.emitContext.autoGenerate = make(map[*ast.MemberName]*AutoGenerateInfo) - } - f.emitContext.autoGenerate[name] = autoGenerate - return name -} - -// Allocates a new temp variable name, but does not record it in the environment. It is recommended to pass this to either -// `AddVariableDeclaration` or `AddLexicalDeclaration` to ensure it is properly tracked, if you are not otherwise handling -// it yourself. -func (f *NodeFactory) NewTempVariable() *ast.IdentifierNode { - return f.NewTempVariableEx(AutoGenerateOptions{}) -} - -// Allocates a new temp variable name, but does not record it in the environment. It is recommended to pass this to either -// `AddVariableDeclaration` or `AddLexicalDeclaration` to ensure it is properly tracked, if you are not otherwise handling -// it yourself. -func (f *NodeFactory) NewTempVariableEx(options AutoGenerateOptions) *ast.IdentifierNode { - return f.newGeneratedIdentifier(GeneratedIdentifierFlagsAuto, "", nil /*node*/, options) -} - -// Allocates a new loop variable name. -func (f *NodeFactory) NewLoopVariable() *ast.IdentifierNode { - return f.NewLoopVariableEx(AutoGenerateOptions{}) -} - -// Allocates a new loop variable name. -func (f *NodeFactory) NewLoopVariableEx(options AutoGenerateOptions) *ast.IdentifierNode { - return f.newGeneratedIdentifier(GeneratedIdentifierFlagsLoop, "", nil /*node*/, options) -} - -// Allocates a new unique name based on the provided text. -func (f *NodeFactory) NewUniqueName(text string) *ast.IdentifierNode { - return f.NewUniqueNameEx(text, AutoGenerateOptions{}) -} - -// Allocates a new unique name based on the provided text. -func (f *NodeFactory) NewUniqueNameEx(text string, options AutoGenerateOptions) *ast.IdentifierNode { - return f.newGeneratedIdentifier(GeneratedIdentifierFlagsUnique, text, nil /*node*/, options) -} - -// Allocates a new unique name based on the provided node. -func (f *NodeFactory) NewGeneratedNameForNode(node *ast.Node) *ast.IdentifierNode { - return f.NewGeneratedNameForNodeEx(node, AutoGenerateOptions{}) -} - -// Allocates a new unique name based on the provided node. -func (f *NodeFactory) NewGeneratedNameForNodeEx(node *ast.Node, options AutoGenerateOptions) *ast.IdentifierNode { - if len(options.Prefix) > 0 || len(options.Suffix) > 0 { - options.Flags |= GeneratedIdentifierFlagsOptimistic - } - - return f.newGeneratedIdentifier(GeneratedIdentifierFlagsNode, "", node, options) -} - -func (f *NodeFactory) newGeneratedPrivateIdentifier(kind GeneratedIdentifierFlags, text string, node *ast.Node, options AutoGenerateOptions) *ast.PrivateIdentifierNode { - id := AutoGenerateId(nextAutoGenerateId.Add(1)) - - if len(text) == 0 { - switch { - case node == nil: - text = fmt.Sprintf("(auto@%d)", id) - case ast.IsMemberName(node): - text = node.Text() - default: - text = fmt.Sprintf("(generated@%v)", ast.GetNodeId(f.emitContext.getNodeForGeneratedNameWorker(node, id))) - } - text = FormatGeneratedName(true /*privateName*/, options.Prefix, text, options.Suffix) - } else if !strings.HasPrefix(text, "#") { - panic("First character of private identifier must be #: " + text) - } - - name := f.NewPrivateIdentifier(text) - autoGenerate := &AutoGenerateInfo{ - Id: id, - Flags: kind | (options.Flags &^ GeneratedIdentifierFlagsKindMask), - Prefix: options.Prefix, - Suffix: options.Suffix, - Node: node, - } - if f.emitContext.autoGenerate == nil { - f.emitContext.autoGenerate = make(map[*ast.MemberName]*AutoGenerateInfo) - } - f.emitContext.autoGenerate[name] = autoGenerate - return name -} - -// Allocates a new unique private name based on the provided text. -func (f *NodeFactory) NewUniquePrivateName(text string) *ast.PrivateIdentifierNode { - return f.NewUniquePrivateNameEx(text, AutoGenerateOptions{}) -} - -// Allocates a new unique private name based on the provided text. -func (f *NodeFactory) NewUniquePrivateNameEx(text string, options AutoGenerateOptions) *ast.PrivateIdentifierNode { - return f.newGeneratedPrivateIdentifier(GeneratedIdentifierFlagsUnique, text, nil /*node*/, options) -} - -// Allocates a new unique private name based on the provided node. -func (f *NodeFactory) NewGeneratedPrivateNameForNode(node *ast.Node) *ast.PrivateIdentifierNode { - return f.NewGeneratedPrivateNameForNodeEx(node, AutoGenerateOptions{}) -} - -// Allocates a new unique private name based on the provided node. -func (f *NodeFactory) NewGeneratedPrivateNameForNodeEx(node *ast.Node, options AutoGenerateOptions) *ast.PrivateIdentifierNode { - if len(options.Prefix) > 0 || len(options.Suffix) > 0 { - options.Flags |= GeneratedIdentifierFlagsOptimistic - } - - return f.newGeneratedPrivateIdentifier(GeneratedIdentifierFlagsNode, "", node, options) -} - -// Allocates a new StringLiteral whose source text is derived from the provided node. This is often used to create a -// string representation of an Identifier or NumericLiteral. -func (f *NodeFactory) NewStringLiteralFromNode(textSourceNode *ast.Node) *ast.Node { - var text string - switch textSourceNode.Kind { - case ast.KindIdentifier, - ast.KindPrivateIdentifier, - ast.KindJsxNamespacedName, - ast.KindNumericLiteral, - ast.KindBigIntLiteral, - ast.KindNoSubstitutionTemplateLiteral, - ast.KindTemplateHead, - ast.KindTemplateMiddle, - ast.KindTemplateTail, - ast.KindRegularExpressionLiteral: - text = textSourceNode.Text() - } - node := f.NewStringLiteral(text) - if f.emitContext.textSource == nil { - f.emitContext.textSource = make(map[*ast.StringLiteralNode]*ast.Node) - } - f.emitContext.textSource[node] = textSourceNode - return node -} - -// -// Common Tokens -// - -func (f *NodeFactory) NewThisExpression() *ast.Expression { - return f.NewKeywordExpression(ast.KindThisKeyword) -} - -func (f *NodeFactory) NewTrueExpression() *ast.Expression { - return f.NewKeywordExpression(ast.KindTrueKeyword) -} - -func (f *NodeFactory) NewFalseExpression() *ast.Expression { - return f.NewKeywordExpression(ast.KindFalseKeyword) -} - -// -// Common Operators -// - -func (f *NodeFactory) NewCommaExpression(left *ast.Expression, right *ast.Expression) *ast.Expression { - return f.NewBinaryExpression(nil /*modifiers*/, left, nil /*typeNode*/, f.NewToken(ast.KindCommaToken), right) -} - -func (f *NodeFactory) NewAssignmentExpression(left *ast.Expression, right *ast.Expression) *ast.Expression { - return f.NewBinaryExpression(nil /*modifiers*/, left, nil /*typeNode*/, f.NewToken(ast.KindEqualsToken), right) -} - -func (f *NodeFactory) NewLogicalORExpression(left *ast.Expression, right *ast.Expression) *ast.Expression { - return f.NewBinaryExpression(nil /*modifiers*/, left, nil /*typeNode*/, f.NewToken(ast.KindBarBarToken), right) -} - -// func (f *NodeFactory) NewLogicalANDExpression(left *ast.Expression, right *ast.Expression) *ast.Expression -// func (f *NodeFactory) NewBitwiseORExpression(left *ast.Expression, right *ast.Expression) *ast.Expression -// func (f *NodeFactory) NewBitwiseXORExpression(left *ast.Expression, right *ast.Expression) *ast.Expression -// func (f *NodeFactory) NewBitwiseANDExpression(left *ast.Expression, right *ast.Expression) *ast.Expression -func (f *NodeFactory) NewStrictEqualityExpression(left *ast.Expression, right *ast.Expression) *ast.Expression { - return f.NewBinaryExpression(nil /*modifiers*/, left, nil /*typeNode*/, f.NewToken(ast.KindEqualsEqualsEqualsToken), right) -} - -func (f *NodeFactory) NewStrictInequalityExpression(left *ast.Expression, right *ast.Expression) *ast.Expression { - return f.NewBinaryExpression(nil /*modifiers*/, left, nil /*typeNode*/, f.NewToken(ast.KindExclamationEqualsEqualsToken), right) -} - -// -// Compound Nodes -// - -func (f *NodeFactory) NewVoidZeroExpression() *ast.Expression { - return f.NewVoidExpression(f.NewNumericLiteral("0")) -} - -func flattenCommaElement(node *ast.Expression, expressions []*ast.Expression) []*ast.Expression { - if ast.IsBinaryExpression(node) && ast.NodeIsSynthesized(node) && node.AsBinaryExpression().OperatorToken.Kind == ast.KindCommaToken { - expressions = flattenCommaElement(node.AsBinaryExpression().Left, expressions) - expressions = flattenCommaElement(node.AsBinaryExpression().Right, expressions) - } else { - expressions = append(expressions, node) - } - return expressions -} - -func flattenCommaElements(expressions []*ast.Expression) []*ast.Expression { - var result []*ast.Expression - for _, expression := range expressions { - result = flattenCommaElement(expression, result) - } - return result -} - -// Converts a slice of expressions into a single comma-delimited expression. Returns nil if expressions is nil or empty. -// NOTE: Unlike Strada, the Corsa implementation does not currently use `ast.KindCommaListExpression`. -func (f *NodeFactory) InlineExpressions(expressions []*ast.Expression) *ast.Expression { - if len(expressions) == 0 { - return nil - } - if len(expressions) == 1 { - return expressions[0] - } - expressions = flattenCommaElements(expressions) - expression := expressions[0] - for _, next := range expressions[1:] { - expression = f.NewCommaExpression(expression, next) - } - return expression -} - -// -// Utilities -// - -func (f *NodeFactory) NewTypeCheck(value *ast.Node, tag string) *ast.Node { - if tag == "null" { - return f.NewStrictEqualityExpression(value, f.NewKeywordExpression(ast.KindNullKeyword)) - } else if tag == "undefined" { - return f.NewStrictEqualityExpression(value, f.NewVoidZeroExpression()) - } else { - return f.NewStrictEqualityExpression(f.NewTypeOfExpression(value), f.NewStringLiteral(tag)) - } -} - -func (f *NodeFactory) NewMethodCall(object *ast.Node, methodName *ast.Node, argumentsList []*ast.Node) *ast.Node { - // Preserve the optionality of `object`. - if ast.IsCallExpression(object) && (object.Flags&ast.NodeFlagsOptionalChain != 0) { - return f.NewCallExpression( - f.NewPropertyAccessExpression(object, nil, methodName, ast.NodeFlagsNone), - nil, - nil, - f.NewNodeList(argumentsList), - ast.NodeFlagsOptionalChain, - ) - } - return f.NewCallExpression( - f.NewPropertyAccessExpression(object, nil, methodName, ast.NodeFlagsNone), - nil, - nil, - f.NewNodeList(argumentsList), - ast.NodeFlagsNone, - ) -} - -func (f *NodeFactory) NewGlobalMethodCall(globalObjectName string, methodName string, argumentsList []*ast.Node) *ast.Node { - return f.NewMethodCall(f.NewIdentifier(globalObjectName), f.NewIdentifier(methodName), argumentsList) -} - -func (f *NodeFactory) NewFunctionCallCall(target *ast.Expression, thisArg *ast.Expression, argumentsList []*ast.Node) *ast.Node { - if thisArg == nil { - panic("Attempted to construct function call call without this argument expression") - } - args := append([]*ast.Expression{thisArg}, argumentsList...) - return f.NewMethodCall(target, f.NewIdentifier("call"), args) -} - -func (f *NodeFactory) NewArraySliceCall(array *ast.Expression, start int) *ast.Node { - var args []*ast.Node - if start != 0 { - args = append(args, f.NewNumericLiteral(strconv.Itoa(start))) - } - return f.NewMethodCall(array, f.NewIdentifier("slice"), args) -} - -// Determines whether a node is a parenthesized expression that can be ignored when recreating outer expressions. -// -// A parenthesized expression can be ignored when all of the following are true: -// -// - It's `pos` and `end` are not -1 -// - It does not have a custom source map range -// - It does not have a custom comment range -// - It does not have synthetic leading or trailing comments -// -// If an outermost parenthesized expression is ignored, but the containing expression requires a parentheses around -// the expression to maintain precedence, a new parenthesized expression should be created automatically when -// the containing expression is created/updated. -func (f *NodeFactory) isIgnorableParen(node *ast.Expression) bool { - return ast.IsParenthesizedExpression(node) && - ast.NodeIsSynthesized(node) && - ast.RangeIsSynthesized(f.emitContext.SourceMapRange(node)) && - ast.RangeIsSynthesized(f.emitContext.CommentRange(node)) // && - // len(emitContext.SyntheticLeadingComments(node)) == 0 && - // len(emitContext.SyntheticTrailingComments(node)) == 0 -} - -func (f *NodeFactory) updateOuterExpression(outerExpression *ast.Expression /*OuterExpression*/, expression *ast.Expression) *ast.Expression { - switch outerExpression.Kind { - case ast.KindParenthesizedExpression: - return f.UpdateParenthesizedExpression(outerExpression.AsParenthesizedExpression(), expression) - case ast.KindTypeAssertionExpression: - return f.UpdateTypeAssertion(outerExpression.AsTypeAssertion(), outerExpression.Type(), expression) - case ast.KindAsExpression: - return f.UpdateAsExpression(outerExpression.AsAsExpression(), expression, outerExpression.Type()) - case ast.KindSatisfiesExpression: - return f.UpdateSatisfiesExpression(outerExpression.AsSatisfiesExpression(), expression, outerExpression.Type()) - case ast.KindNonNullExpression: - return f.UpdateNonNullExpression(outerExpression.AsNonNullExpression(), expression) - case ast.KindExpressionWithTypeArguments: - return f.UpdateExpressionWithTypeArguments(outerExpression.AsExpressionWithTypeArguments(), expression, outerExpression.TypeArgumentList()) - case ast.KindPartiallyEmittedExpression: - return f.UpdatePartiallyEmittedExpression(outerExpression.AsPartiallyEmittedExpression(), expression) - default: - panic(fmt.Sprintf("Unexpected outer expression kind: %s", outerExpression.Kind)) - } -} - -func (f *NodeFactory) RestoreOuterExpressions(outerExpression *ast.Expression, innerExpression *ast.Expression, kinds ast.OuterExpressionKinds) *ast.Expression { - if outerExpression != nil && ast.IsOuterExpression(outerExpression, kinds) && !f.isIgnorableParen(outerExpression) { - return f.updateOuterExpression( - outerExpression, - f.RestoreOuterExpressions(outerExpression.Expression(), innerExpression, ast.OEKAll), - ) - } - return innerExpression -} - -// Ensures `"use strict"` is the first statement of a slice of statements. -func (f *NodeFactory) EnsureUseStrict(statements []*ast.Statement) []*ast.Statement { - for _, statement := range statements { - if ast.IsPrologueDirective(statement) && statement.AsExpressionStatement().Expression.Text() == "use strict" { - return statements - } else { - break - } - } - useStrictPrologue := f.NewExpressionStatement(f.NewStringLiteral("use strict")) - statements = append([]*ast.Statement{useStrictPrologue}, statements...) - return statements -} - -// Splits a slice of statements into two parts: standard prologue statements and the rest of the statements -func (f *NodeFactory) SplitStandardPrologue(source []*ast.Statement) (prologue []*ast.Statement, rest []*ast.Statement) { - for i, statement := range source { - if !ast.IsPrologueDirective(statement) { - return source[:i], source[i:] - } - } - return nil, source -} - -// Splits a slice of statements into two parts: custom prologue statements (e.g., with `EFCustomPrologue` set) and the rest of the statements -func (f *NodeFactory) SplitCustomPrologue(source []*ast.Statement) (prologue []*ast.Statement, rest []*ast.Statement) { - for i, statement := range source { - if ast.IsPrologueDirective(statement) || f.emitContext.EmitFlags(statement)&EFCustomPrologue == 0 { - return source[:i], source[i:] - } - } - return nil, source -} - -// -// Declaration Names -// - -type NameOptions struct { - AllowComments bool // indicates whether comments may be emitted for the name. - AllowSourceMaps bool // indicates whether source maps may be emitted for the name. -} - -type AssignedNameOptions struct { - AllowComments bool // indicates whether comments may be emitted for the name. - AllowSourceMaps bool // indicates whether source maps may be emitted for the name. - IgnoreAssignedName bool // indicates whether the assigned name of a declaration shouldn't be considered. -} - -func (f *NodeFactory) getName(node *ast.Declaration, emitFlags EmitFlags, opts AssignedNameOptions) *ast.IdentifierNode { - var nodeName *ast.IdentifierNode - if node != nil { - if opts.IgnoreAssignedName { - nodeName = ast.GetNonAssignedNameOfDeclaration(node) - } else { - nodeName = ast.GetNameOfDeclaration(node) - } - } - - if nodeName != nil { - name := nodeName.Clone(f) - if !opts.AllowComments { - emitFlags |= EFNoComments - } - if !opts.AllowSourceMaps { - emitFlags |= EFNoSourceMap - } - f.emitContext.AddEmitFlags(name, emitFlags) - return name - } - - return f.NewGeneratedNameForNode(node) -} - -// Gets the local name of a declaration. This is primarily used for declarations that can be referred to by name in the -// declaration's immediate scope (classes, enums, namespaces). A local name will *never* be prefixed with a module or -// namespace export modifier like "exports." when emitted as an expression. -func (f *NodeFactory) GetLocalName(node *ast.Declaration) *ast.IdentifierNode { - return f.GetLocalNameEx(node, AssignedNameOptions{}) -} - -// Gets the local name of a declaration. This is primarily used for declarations that can be referred to by name in the -// declaration's immediate scope (classes, enums, namespaces). A local name will *never* be prefixed with a module or -// namespace export modifier like "exports." when emitted as an expression. -func (f *NodeFactory) GetLocalNameEx(node *ast.Declaration, opts AssignedNameOptions) *ast.IdentifierNode { - return f.getName(node, EFLocalName, opts) -} - -// Gets the export name of a declaration. This is primarily used for declarations that can be -// referred to by name in the declaration's immediate scope (classes, enums, namespaces). An -// export name will *always* be prefixed with an module or namespace export modifier like -// `"exports."` when emitted as an expression if the name points to an exported symbol. -func (f *NodeFactory) GetExportName(node *ast.Declaration) *ast.IdentifierNode { - return f.GetExportNameEx(node, AssignedNameOptions{}) -} - -// Gets the export name of a declaration. This is primarily used for declarations that can be -// referred to by name in the declaration's immediate scope (classes, enums, namespaces). An -// export name will *always* be prefixed with an module or namespace export modifier like -// `"exports."` when emitted as an expression if the name points to an exported symbol. -func (f *NodeFactory) GetExportNameEx(node *ast.Declaration, opts AssignedNameOptions) *ast.IdentifierNode { - return f.getName(node, EFExportName, opts) -} - -// Gets the name of a declaration to use during emit. -func (f *NodeFactory) GetDeclarationName(node *ast.Declaration) *ast.IdentifierNode { - return f.GetDeclarationNameEx(node, NameOptions{}) -} - -// Gets the name of a declaration to use during emit. -func (f *NodeFactory) GetDeclarationNameEx(node *ast.Declaration, opts NameOptions) *ast.IdentifierNode { - return f.getName(node, EFNone, AssignedNameOptions{AllowComments: opts.AllowComments, AllowSourceMaps: opts.AllowSourceMaps}) -} - -func (f *NodeFactory) GetNamespaceMemberName(ns *ast.IdentifierNode, name *ast.IdentifierNode, opts NameOptions) *ast.IdentifierNode { - if !f.emitContext.HasAutoGenerateInfo(name) { - name = name.Clone(f) - } - qualifiedName := f.NewPropertyAccessExpression(ns, nil /*questionDotToken*/, name, ast.NodeFlagsNone) - f.emitContext.AssignCommentAndSourceMapRanges(qualifiedName, name) - if !opts.AllowComments { - f.emitContext.AddEmitFlags(qualifiedName, EFNoComments) - } - if !opts.AllowSourceMaps { - f.emitContext.AddEmitFlags(qualifiedName, EFNoSourceMap) - } - return qualifiedName -} - -// -// Emit Helpers -// - -// Allocates a new Identifier representing a reference to a helper function. -func (f *NodeFactory) NewUnscopedHelperName(name string) *ast.IdentifierNode { - node := f.NewIdentifier(name) - f.emitContext.SetEmitFlags(node, EFHelperName) - return node -} - -// !!! TypeScript Helpers - -// ESNext Helpers - -func (f *NodeFactory) NewAddDisposableResourceHelper(envBinding *ast.Expression, value *ast.Expression, async bool) *ast.Expression { - f.emitContext.RequestEmitHelper(addDisposableResourceHelper) - return f.NewCallExpression( - f.NewUnscopedHelperName("__addDisposableResource"), - nil, /*questionDotToken*/ - nil, /*typeArguments*/ - f.NewNodeList([]*ast.Expression{envBinding, value, f.NewKeywordExpression(core.IfElse(async, ast.KindTrueKeyword, ast.KindFalseKeyword))}), - ast.NodeFlagsNone, - ) -} - -func (f *NodeFactory) NewDisposeResourcesHelper(envBinding *ast.Expression) *ast.Expression { - f.emitContext.RequestEmitHelper(disposeResourcesHelper) - return f.NewCallExpression( - f.NewUnscopedHelperName("__disposeResources"), - nil, /*questionDotToken*/ - nil, /*typeArguments*/ - f.NewNodeList([]*ast.Expression{envBinding}), - ast.NodeFlagsNone, - ) -} - -// !!! Class Fields Helpers -// !!! ES2018 Helpers -// Chains a sequence of expressions using the __assign helper or Object.assign if available in the target -func (f *NodeFactory) NewAssignHelper(attributesSegments []*ast.Expression, scriptTarget core.ScriptTarget) *ast.Expression { - if scriptTarget >= core.ScriptTargetES2015 { - return f.NewCallExpression(f.NewPropertyAccessExpression(f.NewIdentifier("Object"), nil, f.NewIdentifier("assign"), ast.NodeFlagsNone), nil, nil, f.NewNodeList(attributesSegments), ast.NodeFlagsNone) - } - f.emitContext.RequestEmitHelper(assignHelper) - return f.NewCallExpression( - f.NewUnscopedHelperName("__assign"), - nil, - nil, - f.NewNodeList(attributesSegments), - ast.NodeFlagsNone, - ) -} - -// ES2018 Destructuring Helpers - -func (f *NodeFactory) NewRestHelper(value *ast.Expression, elements []*ast.Node, computedTempVariables []*ast.Node, location core.TextRange) *ast.Expression { - f.emitContext.RequestEmitHelper(restHelper) - var propertyNames []*ast.Node - computedTempVariableOffset := 0 - for i, element := range elements { - if i == len(elements)-1 { - break - } - propertyName := ast.TryGetPropertyNameOfBindingOrAssignmentElement(element) - if propertyName != nil { - if ast.IsComputedPropertyName(propertyName) { - debug.AssertIsDefined(computedTempVariables, "Encountered computed property name but 'computedTempVariables' argument was not provided.") - temp := computedTempVariables[computedTempVariableOffset] - computedTempVariableOffset++ - // typeof _tmp === "symbol" ? _tmp : _tmp + "" - propertyNames = append(propertyNames, f.NewConditionalExpression( - f.NewTypeCheck(temp, "symbol"), - f.NewToken(ast.KindQuestionToken), - temp, - f.NewToken(ast.KindColonToken), - f.NewBinaryExpression(nil, temp, nil, f.NewToken(ast.KindPlusToken), f.NewStringLiteral("")), - )) - } else { - propertyNames = append(propertyNames, f.NewStringLiteralFromNode(propertyName)) - } - } - } - propNames := f.NewArrayLiteralExpression(f.NewNodeList(propertyNames), false) - propNames.Loc = location - return f.NewCallExpression( - f.NewUnscopedHelperName("__rest"), - nil, - nil, - f.NewNodeList([]*ast.Node{ - value, - propNames, - }), - ast.NodeFlagsNone, - ) -} - -// !!! ES2017 Helpers - -// ES2015 Helpers - -func (f *NodeFactory) NewPropKeyHelper(expr *ast.Expression) *ast.Expression { - f.emitContext.RequestEmitHelper(propKeyHelper) - return f.NewCallExpression( - f.NewUnscopedHelperName("__propKey"), - nil, /*questionDotToken*/ - nil, /*typeArguments*/ - f.NewNodeList([]*ast.Expression{expr}), - ast.NodeFlagsNone, - ) -} - -func (f *NodeFactory) NewSetFunctionNameHelper(fn *ast.Expression, name *ast.Expression, prefix string) *ast.Expression { - f.emitContext.RequestEmitHelper(setFunctionNameHelper) - var arguments []*ast.Expression - if len(prefix) > 0 { - arguments = []*ast.Expression{fn, name, f.NewStringLiteral(prefix)} - } else { - arguments = []*ast.Expression{fn, name} - } - return f.NewCallExpression( - f.NewUnscopedHelperName("__setFunctionName"), - nil, /*questionDotToken*/ - nil, /*typeArguments*/ - f.NewNodeList(arguments), - ast.NodeFlagsNone, - ) -} - -// ES Module Helpers - -// Allocates a new Call expression to the `__importDefault` helper. -func (f *NodeFactory) NewImportDefaultHelper(expression *ast.Expression) *ast.Expression { - f.emitContext.RequestEmitHelper(importDefaultHelper) - return f.NewCallExpression( - f.NewUnscopedHelperName("__importDefault"), - nil, /*questionDotToken*/ - nil, /*typeArguments*/ - f.NewNodeList([]*ast.Expression{expression}), - ast.NodeFlagsNone, - ) -} - -// Allocates a new Call expression to the `__importStar` helper. -func (f *NodeFactory) NewImportStarHelper(expression *ast.Expression) *ast.Expression { - f.emitContext.RequestEmitHelper(importStarHelper) - return f.NewCallExpression( - f.NewUnscopedHelperName("__importStar"), - nil, /*questionDotToken*/ - nil, /*typeArguments*/ - f.NewNodeList([]*ast.Expression{expression}), - ast.NodeFlagsNone, - ) -} - -// Allocates a new Call expression to the `__exportStar` helper. -func (f *NodeFactory) NewExportStarHelper(moduleExpression *ast.Expression, exportsExpression *ast.Expression) *ast.Expression { - f.emitContext.RequestEmitHelper(exportStarHelper) - return f.NewCallExpression( - f.NewUnscopedHelperName("__exportStar"), - nil, /*questionDotToken*/ - nil, /*typeArguments*/ - f.NewNodeList([]*ast.Expression{moduleExpression, exportsExpression}), - ast.NodeFlagsNone, - ) -} - -// Allocates a new Call expression to the `__rewriteRelativeImportExtension` helper. -func (f *NodeFactory) NewRewriteRelativeImportExtensionsHelper(firstArgument *ast.Node, preserveJsx bool) *ast.Expression { - f.emitContext.RequestEmitHelper(rewriteRelativeImportExtensionsHelper) - var arguments []*ast.Expression - if preserveJsx { - arguments = []*ast.Expression{firstArgument, f.NewToken(ast.KindTrueKeyword)} - } else { - arguments = []*ast.Expression{firstArgument} - } - return f.NewCallExpression( - f.NewUnscopedHelperName("__rewriteRelativeImportExtension"), - nil, /*questionDotToken*/ - nil, /*typeArguments*/ - f.NewNodeList(arguments), - ast.NodeFlagsNone, - ) -} diff --git a/kitcom/internal/tsgo/printer/generatedidentifierflags.go b/kitcom/internal/tsgo/printer/generatedidentifierflags.go deleted file mode 100644 index 0a6cfce..0000000 --- a/kitcom/internal/tsgo/printer/generatedidentifierflags.go +++ /dev/null @@ -1,57 +0,0 @@ -package printer - -type GeneratedIdentifierFlags int - -const ( - // Kind - - GeneratedIdentifierFlagsNone = iota // Not automatically generated. - GeneratedIdentifierFlagsAuto // Automatically generated identifier. - GeneratedIdentifierFlagsLoop // Automatically generated identifier with a preference for '_i'. - GeneratedIdentifierFlagsUnique // Unique name based on the 'text' property. - GeneratedIdentifierFlagsNode // Unique name based on the node in the 'Node' property. - GeneratedIdentifierFlagsKindMask = 7 // Mask to extract the kind of identifier from its flags. - - // Flags - - GeneratedIdentifierFlagsReservedInNestedScopes = 1 << 3 // Reserve the generated name in nested scopes - GeneratedIdentifierFlagsOptimistic = 1 << 4 // First instance won't use '_#' if there's no conflict - GeneratedIdentifierFlagsFileLevel = 1 << 5 // Use only the file identifiers list and not generated names to search for conflicts - GeneratedIdentifierFlagsAllowNameSubstitution = 1 << 6 // Used by `module.ts` to indicate generated nodes which can have substitutions performed upon them (as they were generated by an earlier transform phase) -) - -func (f GeneratedIdentifierFlags) Kind() GeneratedIdentifierFlags { - return f & GeneratedIdentifierFlagsKindMask -} - -func (f GeneratedIdentifierFlags) IsAuto() bool { - return f.Kind() == GeneratedIdentifierFlagsAuto -} - -func (f GeneratedIdentifierFlags) IsLoop() bool { - return f.Kind() == GeneratedIdentifierFlagsLoop -} - -func (f GeneratedIdentifierFlags) IsUnique() bool { - return f.Kind() == GeneratedIdentifierFlagsUnique -} - -func (f GeneratedIdentifierFlags) IsNode() bool { - return f.Kind() == GeneratedIdentifierFlagsNode -} - -func (f GeneratedIdentifierFlags) IsReservedInNestedScopes() bool { - return f&GeneratedIdentifierFlagsReservedInNestedScopes != 0 -} - -func (f GeneratedIdentifierFlags) IsOptimistic() bool { - return f&GeneratedIdentifierFlagsOptimistic != 0 -} - -func (f GeneratedIdentifierFlags) IsFileLevel() bool { - return f&GeneratedIdentifierFlagsFileLevel != 0 -} - -func (f GeneratedIdentifierFlags) HasAllowNameSubstitution() bool { - return f&GeneratedIdentifierFlagsAllowNameSubstitution != 0 -} diff --git a/kitcom/internal/tsgo/printer/helpers.go b/kitcom/internal/tsgo/printer/helpers.go deleted file mode 100644 index 0b3918a..0000000 --- a/kitcom/internal/tsgo/printer/helpers.go +++ /dev/null @@ -1,252 +0,0 @@ -package printer - -type Priority struct { - Value int -} - -type EmitHelper struct { - Name string // A unique name for this helper. - Scoped bool // Indicates whether the helper MUST be emitted in the current scope. - Text string // ES3-compatible raw script text - TextCallback func(makeUniqueName func(string) string) string // A function yielding an ES3-compatible raw script text. - Priority *Priority // Helpers with a higher priority are emitted earlier than other helpers on the node. - Dependencies []*EmitHelper // Emit helpers this helper depends on - ImportName string // The name of the helper to use when importing via `--importHelpers`. -} - -func compareEmitHelpers(x *EmitHelper, y *EmitHelper) int { - if x == y { - return 0 - } - if x.Priority == y.Priority { - return 0 - } - if x.Priority == nil { - return 1 - } - if y.Priority == nil { - return -1 - } - return x.Priority.Value - y.Priority.Value -} - -// !!! TypeScript Helpers - -// ESNext Helpers - -var addDisposableResourceHelper = &EmitHelper{ - Name: "typescript:addDisposableResource", - ImportName: "__addDisposableResource", - Scoped: false, - Text: `var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) { - if (value !== null && value !== void 0) { - if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected."); - var dispose, inner; - if (async) { - if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined."); - dispose = value[Symbol.asyncDispose]; - } - if (dispose === void 0) { - if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined."); - dispose = value[Symbol.dispose]; - if (async) inner = dispose; - } - if (typeof dispose !== "function") throw new TypeError("Object not disposable."); - if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } }; - env.stack.push({ value: value, dispose: dispose, async: async }); - } - else if (async) { - env.stack.push({ async: true }); - } - return value; -};`, -} - -var disposeResourcesHelper = &EmitHelper{ - Name: "typescript:disposeResources", - ImportName: "__disposeResources", - Scoped: false, - Text: `var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) { - return function (env) { - function fail(e) { - env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e; - env.hasError = true; - } - var r, s = 0; - function next() { - while (r = env.stack.pop()) { - try { - if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next); - if (r.dispose) { - var result = r.dispose.call(r.value); - if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); }); - } - else s |= 1; - } - catch (e) { - fail(e); - } - } - if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve(); - if (env.hasError) throw env.error; - } - return next(); - }; -})(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) { - var e = new Error(message); - return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; -});`, -} - -// !!! Class Fields Helpers -// !!! ES2018 Helpers -var assignHelper = &EmitHelper{ - Name: "typescript:assign", - ImportName: "__assign", - Scoped: false, - Priority: &Priority{1}, - Text: `var __assign = (this && this.__assign) || function () { - __assign = Object.assign || function(t) { - for (var s, i = 1, n = arguments.length; i < n; i++) { - s = arguments[i]; - for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) - t[p] = s[p]; - } - return t; - }; - return __assign.apply(this, arguments); -};`, -} - -// !!! ES2018 Destructuring Helpers -var restHelper = &EmitHelper{ - Name: "typescript:rest", - ImportName: "__rest", - Scoped: false, - Text: `var __rest = (this && this.__rest) || function (s, e) { - var t = {}; - for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) - t[p] = s[p]; - if (s != null && typeof Object.getOwnPropertySymbols === "function") - for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { - if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) - t[p[i]] = s[p[i]]; - } - return t; -};`, -} - -// !!! ES2017 Helpers - -// ES2015 Helpers - -var propKeyHelper = &EmitHelper{ - Name: "typescript:propKey", - ImportName: "__propKey", - Scoped: false, - Text: `var __propKey = (this && this.__propKey) || function (x) { - return typeof x === "symbol" ? x : "".concat(x); -};`, -} - -// https://tc39.es/ecma262/#sec-setfunctionname -var setFunctionNameHelper = &EmitHelper{ - Name: "typescript:setFunctionName", - ImportName: "__setFunctionName", - Scoped: false, - Text: `var __setFunctionName = (this && this.__setFunctionName) || function (f, name, prefix) { - if (typeof name === "symbol") name = name.description ? "[".concat(name.description, "]") : ""; - return Object.defineProperty(f, "name", { configurable: true, value: prefix ? "".concat(prefix, " ", name) : name }); -};`, -} - -// ES Module Helpers - -var createBindingHelper = &EmitHelper{ - Name: "typescript:commonjscreatebinding", - ImportName: "__createBinding", - Scoped: false, - Priority: &Priority{1}, - Text: `var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -}));`, -} - -var setModuleDefaultHelper = &EmitHelper{ - Name: "typescript:commonjscreatevalue", - ImportName: "__setModuleDefault", - Scoped: false, - Priority: &Priority{1}, - Text: `var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -});`, -} - -var importStarHelper = &EmitHelper{ - Name: "typescript:commonjsimportstar", - ImportName: "__importStar", - Scoped: false, - Dependencies: []*EmitHelper{createBindingHelper, setModuleDefaultHelper}, - Priority: &Priority{2}, - Text: `var __importStar = (this && this.__importStar) || (function () { - var ownKeys = function(o) { - ownKeys = Object.getOwnPropertyNames || function (o) { - var ar = []; - for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; - return ar; - }; - return ownKeys(o); - }; - return function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); - __setModuleDefault(result, mod); - return result; - }; -})();`, -} - -var importDefaultHelper = &EmitHelper{ - Name: "typescript:commonjsimportdefault", - ImportName: "__importDefault", - Scoped: false, - Text: `var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -};`, -} - -var exportStarHelper = &EmitHelper{ - Name: "typescript:export-star", - ImportName: "__exportStar", - Scoped: false, - Dependencies: []*EmitHelper{createBindingHelper}, - Priority: &Priority{2}, - Text: `var __exportStar = (this && this.__exportStar) || function(m, exports) { - for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); -};`, -} - -var rewriteRelativeImportExtensionsHelper = &EmitHelper{ - Name: "typescript:rewriteRelativeImportExtensions", - ImportName: "__rewriteRelativeImportExtension", - Scoped: false, - Text: `var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) { - if (typeof path === "string" && /^\.\.?\//.test(path)) { - return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) { - return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js"); - }); - } - return path; -};`, -} diff --git a/kitcom/internal/tsgo/printer/namegenerator.go b/kitcom/internal/tsgo/printer/namegenerator.go deleted file mode 100644 index e29f510..0000000 --- a/kitcom/internal/tsgo/printer/namegenerator.go +++ /dev/null @@ -1,401 +0,0 @@ -package printer - -import ( - "fmt" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/collections" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/debug" -) - -// Flags enum to track count of temp variables and a few dedicated names -type tempFlags int - -const ( - tempFlagsAuto tempFlags = 0x00000000 // No preferred name - tempFlagsCountMask tempFlags = 0x0FFFFFFF // Temp variable counter - tempFlags_i tempFlags = 0x10000000 // Use/preference flag for '_i' -) - -type NameGenerator struct { - Context *EmitContext - IsFileLevelUniqueNameInCurrentFile func(string, bool) bool // callback for Printer.isFileLevelUniqueNameInCurrentFile - GetTextOfNode func(*ast.Node) string // callback for Printer.getTextOfNode - nodeIdToGeneratedName map[ast.NodeId]string // Map of generated names for specific nodes - nodeIdToGeneratedPrivateName map[ast.NodeId]string // Map of generated private names for specific nodes - autoGeneratedIdToGeneratedName map[AutoGenerateId]string // Map of generated names for temp and loop variables - nameGenerationScope *nameGenerationScope - privateNameGenerationScope *nameGenerationScope - generatedNames collections.Set[string] // NOTE: Used to match Strada, but should be moved to nameGenerationScope after port is complete. -} - -type nameGenerationScope struct { - next *nameGenerationScope // The next nameGenerationScope in the stack - tempFlags tempFlags // TempFlags for the current name generation scope. - formattedNameTempFlags map[string]tempFlags // TempFlags for the current name generation scope. - reservedNames collections.Set[string] // Names reserved in nested name generation scopes. - // generatedNames collections.Set[string] // NOTE: generated names should be scoped after Strada port is complete. -} - -func (g *NameGenerator) PushScope(reuseTempVariableScope bool) { - g.privateNameGenerationScope = &nameGenerationScope{next: g.privateNameGenerationScope} - if !reuseTempVariableScope { - g.nameGenerationScope = &nameGenerationScope{next: g.nameGenerationScope} - } -} - -func (g *NameGenerator) PopScope(reuseTempVariableScope bool) { - if g.privateNameGenerationScope != nil { - g.privateNameGenerationScope = g.privateNameGenerationScope.next - } - if !reuseTempVariableScope && g.nameGenerationScope != nil { - g.nameGenerationScope = g.nameGenerationScope.next - } -} - -func (g *NameGenerator) getScope(privateName bool) **nameGenerationScope { - return core.IfElse(privateName, &g.privateNameGenerationScope, &g.nameGenerationScope) -} - -func (g *NameGenerator) getTempFlags(privateName bool) tempFlags { - scope := g.getScope(privateName) - if *scope != nil { - return (*scope).tempFlags - } - return tempFlagsAuto -} - -func (g *NameGenerator) setTempFlags(privateName bool, flags tempFlags) { - scope := g.getScope(privateName) - if *scope == nil { - *scope = &nameGenerationScope{} - } - (*scope).tempFlags = flags -} - -// Gets the TempFlags to use in the current nameGenerationScope for the given key -func (g *NameGenerator) getTempFlagsForFormattedName(privateName bool, formattedNameKey string) tempFlags { - scope := g.getScope(privateName) - if *scope != nil { - if flags, ok := (*scope).formattedNameTempFlags[formattedNameKey]; ok { - return flags - } - } - return tempFlagsAuto -} - -// Sets the TempFlags to use in the current nameGenerationScope for the given key -func (g *NameGenerator) setTempFlagsForFormattedName(privateName bool, formattedNameKey string, flags tempFlags) { - scope := g.getScope(privateName) - if *scope == nil { - *scope = &nameGenerationScope{} - } - if (*scope).formattedNameTempFlags == nil { - (*scope).formattedNameTempFlags = make(map[string]tempFlags) - } - (*scope).formattedNameTempFlags[formattedNameKey] = flags -} - -func (g *NameGenerator) reserveName(name string, privateName bool, scoped bool, temp bool) { - scope := g.getScope(privateName) - if *scope == nil { - *scope = &nameGenerationScope{} - } - if privateName || scoped { - (*scope).reservedNames.Add(name) - } else if !temp { - g.generatedNames.Add(name) // NOTE: Matches Strada, but is incorrect. - // (*scope).generatedNames.Add(name) // TODO: generated names should be scoped after Strada port is complete. - } -} - -// Generate the text for a generated identifier or private identifier -func (g *NameGenerator) GenerateName(name *ast.MemberName) string { - if g.Context != nil { - if autoGenerate, ok := g.Context.autoGenerate[name]; ok { - if autoGenerate.Flags.IsNode() { - // Node names generate unique names based on their original node - // and are cached based on that node's id. - return g.generateNameForNodeCached(g.Context.GetNodeForGeneratedName(name), ast.IsPrivateIdentifier(name), autoGenerate.Flags, autoGenerate.Prefix, autoGenerate.Suffix) - } else { - // Auto, Loop, and Unique names are cached based on their unique autoGenerateId. - if autoGeneratedName, ok := g.autoGeneratedIdToGeneratedName[autoGenerate.Id]; ok { - return autoGeneratedName - } - if g.autoGeneratedIdToGeneratedName == nil { - g.autoGeneratedIdToGeneratedName = make(map[AutoGenerateId]string) - } - autoGeneratedName := g.makeName(name) - g.autoGeneratedIdToGeneratedName[autoGenerate.Id] = autoGeneratedName - return autoGeneratedName - } - } - } - return g.GetTextOfNode(name) -} - -func (g *NameGenerator) generateNameForNodeCached(node *ast.Node, privateName bool, flags GeneratedIdentifierFlags, prefix string, suffix string) string { - nodeId := ast.GetNodeId(node) - cache := core.IfElse(privateName, &g.nodeIdToGeneratedPrivateName, &g.nodeIdToGeneratedName) - if *cache == nil { - *cache = make(map[ast.NodeId]string) - } - - if name, ok := (*cache)[nodeId]; ok { - return name - } - - name := g.generateNameForNode(node, privateName, flags, prefix, suffix) - (*cache)[nodeId] = name - return name -} - -func (g *NameGenerator) generateNameForNode(node *ast.Node, privateName bool, flags GeneratedIdentifierFlags, prefix string, suffix string) string { - switch node.Kind { - case ast.KindIdentifier, ast.KindPrivateIdentifier: - return g.makeUniqueName(g.GetTextOfNode(node), nil /*checkFn*/, flags.IsOptimistic(), flags.IsReservedInNestedScopes(), privateName, prefix, suffix) - case ast.KindModuleDeclaration, ast.KindEnumDeclaration: - if privateName || len(prefix) > 0 || len(suffix) > 0 { - panic("Generated name for a module or enum cannot be private and may have neither a prefix nor suffix") - } - return g.generateNameForModuleOrEnum(node) - case ast.KindImportDeclaration, ast.KindJSImportDeclaration, ast.KindExportDeclaration: - if privateName || len(prefix) > 0 || len(suffix) > 0 { - panic("Generated name for an import or export cannot be private and may have neither a prefix nor suffix") - } - return g.generateNameForImportOrExportDeclaration(node) - case ast.KindFunctionDeclaration, ast.KindClassDeclaration: - if privateName || len(prefix) > 0 || len(suffix) > 0 { - panic("Generated name for a class or function declaration cannot be private and may have neither a prefix nor suffix") - } - name := node.Name() - if name != nil && !(g.Context == nil && g.Context.HasAutoGenerateInfo(name)) { - return g.generateNameForNode(name, false /*privateName*/, flags, "" /*prefix*/, "" /*suffix*/) - } - return g.generateNameForExportDefault() - case ast.KindExportAssignment: - if privateName || len(prefix) > 0 || len(suffix) > 0 { - panic("Generated name for an export assignment cannot be private and may have neither a prefix nor suffix") - } - return g.generateNameForExportDefault() - case ast.KindClassExpression: - if privateName || len(prefix) > 0 || len(suffix) > 0 { - panic("Generated name for a class expression cannot be private and may have neither a prefix nor suffix") - } - return g.generateNameForClassExpression() - case ast.KindMethodDeclaration, ast.KindGetAccessor, ast.KindSetAccessor: - return g.generateNameForMethodOrAccessor(node, privateName, prefix, suffix) - case ast.KindComputedPropertyName: - return g.makeTempVariableName(tempFlagsAuto, true /*reservedInNestedScopes*/, privateName, prefix, suffix) - default: - return g.makeTempVariableName(tempFlagsAuto, false /*reservedInNestedScopes*/, privateName, prefix, suffix) - } -} - -func (g *NameGenerator) generateNameForModuleOrEnum(node *ast.Node /* ModuleDeclaration | EnumDeclaration */) string { - name := g.GetTextOfNode(node.Name()) - // Use module/enum name itself if it is unique, otherwise make a unique variation - if isUniqueLocalName(name, node) { - return name - } else { - return g.makeUniqueName(name, nil /*checkFn*/, false /*optimistic*/, false /*scoped*/, false /*privateName*/, "" /*prefix*/, "" /*suffix*/) - } -} - -func (g *NameGenerator) generateNameForImportOrExportDeclaration(node *ast.Node /* ImportDeclaration | ExportDeclaration */) string { - expr := ast.GetExternalModuleName(node) - baseName := "module" - if ast.IsStringLiteral(expr) { - baseName = makeIdentifierFromModuleName(expr.Text()) - } - return g.makeUniqueName(baseName, nil /*checkFn*/, false /*optimistic*/, false /*scoped*/, false /*privateName*/, "" /*prefix*/, "" /*suffix*/) -} - -func (g *NameGenerator) generateNameForExportDefault() string { - return g.makeUniqueName("default", nil /*checkFn*/, false /*optimistic*/, false /*scoped*/, false /*privateName*/, "" /*prefix*/, "" /*suffix*/) -} - -func (g *NameGenerator) generateNameForClassExpression() string { - return g.makeUniqueName("class", nil /*checkFn*/, false /*optimistic*/, false /*scoped*/, false /*privateName*/, "" /*prefix*/, "" /*suffix*/) -} - -func (g *NameGenerator) generateNameForMethodOrAccessor(node *ast.Node /* MethodDeclaration | AccessorDeclaration */, privateName bool, prefix string, suffix string) string { - if ast.IsIdentifier(node.Name()) { - return g.generateNameForNodeCached(node.Name(), privateName, GeneratedIdentifierFlagsNone, prefix, suffix) - } - return g.makeTempVariableName(tempFlagsAuto, false /*reservedInNestedScopes*/, privateName, prefix, suffix) -} - -func (g *NameGenerator) makeName(name *ast.Node) string { - if g.Context != nil { - if autoGenerate, ok := g.Context.autoGenerate[name]; ok { - switch autoGenerate.Flags.Kind() { - case GeneratedIdentifierFlagsAuto: - return g.makeTempVariableName(tempFlagsAuto, autoGenerate.Flags.IsReservedInNestedScopes(), ast.IsPrivateIdentifier(name), autoGenerate.Prefix, autoGenerate.Suffix) - case GeneratedIdentifierFlagsLoop: - debug.AssertNode(name, ast.IsIdentifier) - return g.makeTempVariableName(tempFlags_i, autoGenerate.Flags.IsReservedInNestedScopes(), false /*privateName*/, autoGenerate.Prefix, autoGenerate.Suffix) - case GeneratedIdentifierFlagsUnique: - return g.makeUniqueName( - name.Text(), - core.IfElse(autoGenerate.Flags.IsFileLevel(), g.IsFileLevelUniqueNameInCurrentFile, nil), - autoGenerate.Flags.IsOptimistic(), - autoGenerate.Flags.IsReservedInNestedScopes(), - ast.IsPrivateIdentifier(name), - autoGenerate.Prefix, - autoGenerate.Suffix, - ) - } - } - } - return g.GetTextOfNode(name) -} - -// Return the next available name in the pattern _a ... _z, _0, _1, ... -// TempFlags._i may be used to express a preference for that dedicated name. -// Note that names generated by makeTempVariableName and makeUniqueName will never conflict. -func (g *NameGenerator) makeTempVariableName(flags tempFlags, reservedInNestedScopes bool, privateName bool, prefix string, suffix string) string { - var tempFlags tempFlags - var key string - simple := len(prefix) == 0 && len(suffix) == 0 - if simple { - tempFlags = g.getTempFlags(privateName) - } else { - // Generate a key to use to acquire a TempFlags counter based on the fixed portions of the generated name. - key = FormatGeneratedName(privateName, prefix, "" /*base*/, suffix) - if privateName { - key = ensureLeadingHash(key) - } - tempFlags = g.getTempFlagsForFormattedName(privateName, key) - } - - if flags != 0 && tempFlags&flags == 0 { - fullName := FormatGeneratedName(privateName, prefix, "_i", suffix) - if g.isUniqueName(fullName, privateName) { - tempFlags |= flags - g.reserveName(fullName, privateName, reservedInNestedScopes, true /*temp*/) - if simple { - g.setTempFlags(privateName, tempFlags) - } else { - g.setTempFlagsForFormattedName(privateName, key, tempFlags) - } - return fullName - } - } - - for { - count := tempFlags & tempFlagsCountMask - tempFlags++ - // Skip over 'i' and 'n' - if count != 8 && count != 13 { - var name string - if count < 26 { - name = fmt.Sprintf("_%c", 'a'+byte(count)) - } else { - name = fmt.Sprintf("_%d", count-26) - } - fullName := FormatGeneratedName(privateName, prefix, name, suffix) - if g.isUniqueName(fullName, privateName) { - g.reserveName(fullName, privateName, reservedInNestedScopes, true /*temp*/) - if simple { - g.setTempFlags(privateName, tempFlags) - } else { - g.setTempFlagsForFormattedName(privateName, key, tempFlags) - } - return fullName - } - } - } -} - -// Generate a name that is unique within the current file and doesn't conflict with any names -// in global scope. The name is formed by adding an '_n' suffix to the specified base name, -// where n is a positive integer. Note that names generated by makeTempVariableName and -// makeUniqueName are guaranteed to never conflict. -// If `optimistic` is set, the first instance will use 'baseName' verbatim instead of 'baseName_1' -func (g *NameGenerator) makeUniqueName(baseName string, checkFn func(name string, privateName bool) bool, optimistic bool, scoped bool, privateName bool, prefix string, suffix string) string { - baseName = removeLeadingHash(baseName) - if optimistic { - fullName := FormatGeneratedName(privateName, prefix, baseName, suffix) - if g.checkUniqueName(fullName, privateName, checkFn) { - g.reserveName(fullName, privateName, scoped, false /*temp*/) - return fullName - } - } - - // Find the first unique 'name_n', where n is a positive integer - if len(baseName) > 0 && baseName[len(baseName)-1] != '_' { - baseName += "_" - } - - i := 1 - for { - fullName := FormatGeneratedName(privateName, prefix, fmt.Sprintf("%s%d", baseName, i), suffix) - if g.checkUniqueName(fullName, privateName, checkFn) { - g.reserveName(fullName, privateName, scoped, false /*temp*/) - return fullName - } - i++ - } -} - -func (g *NameGenerator) checkUniqueName(name string, privateName bool, checkFn func(name string, privateName bool) bool) bool { - if checkFn != nil { - return checkFn(name, privateName) - } else { - return g.isUniqueName(name, privateName) - } -} - -func nextContainer(node *ast.Node) *ast.Node { - data := node.LocalsContainerData() - if data != nil { - return data.NextContainer - } - return nil -} - -func isUniqueLocalName(name string, container *ast.Node) bool { - node := container - for node != nil && ast.IsNodeDescendantOf(node, container) && node.LocalsContainerData() != nil { - locals := node.Locals() - if locals != nil { - // We conservatively include alias symbols to cover cases where they're emitted as locals - if local, ok := locals[name]; ok && local.Flags&(ast.SymbolFlagsValue|ast.SymbolFlagsExportValue|ast.SymbolFlagsAlias) != 0 { - return false - } - } - node = nextContainer(node) - } - return true -} - -func (g *NameGenerator) isUniqueName(name string, privateName bool) bool { - return (g.IsFileLevelUniqueNameInCurrentFile == nil || g.IsFileLevelUniqueNameInCurrentFile(name, privateName)) && - !g.isReservedName(name, privateName) -} - -func (g *NameGenerator) isReservedName(name string, privateName bool) bool { - scope := g.getScope(privateName) - - // NOTE: The following matches Strada, but is incorrect. - if g.generatedNames.Has(name) { - return true - } - - // TODO: generated names should be scoped after Strada port is complete. - ////if *scope != nil { - //// if (*scope).generatedNames.Has(name) { - //// return true - //// } - ////} - - for *scope != nil { - if (*scope).reservedNames.Has(name) { - return true - } - scope = &(*scope).next - } - return false -} diff --git a/kitcom/internal/tsgo/printer/namegenerator_test.go b/kitcom/internal/tsgo/printer/namegenerator_test.go deleted file mode 100644 index b146329..0000000 --- a/kitcom/internal/tsgo/printer/namegenerator_test.go +++ /dev/null @@ -1,640 +0,0 @@ -package printer_test - -import ( - "testing" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/binder" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/printer" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/testutil/parsetestutil" - "gotest.tools/v3/assert" -) - -func TestTempVariable1(t *testing.T) { - t.Parallel() - - ec := printer.NewEmitContext() - name1 := ec.Factory.NewTempVariable() - name2 := ec.Factory.NewTempVariable() - - g := &printer.NameGenerator{Context: ec} - text1 := g.GenerateName(name1) - text2 := g.GenerateName(name2) - - assert.Equal(t, "_a", text1) - assert.Equal(t, "_b", text2) -} - -func TestTempVariable2(t *testing.T) { - t.Parallel() - - ec := printer.NewEmitContext() - name1 := ec.Factory.NewTempVariableEx(printer.AutoGenerateOptions{ - Prefix: "A", - Suffix: "B", - }) - name2 := ec.Factory.NewTempVariableEx(printer.AutoGenerateOptions{ - Prefix: "A", - Suffix: "B", - }) - - g := &printer.NameGenerator{Context: ec} - text1 := g.GenerateName(name1) - text2 := g.GenerateName(name2) - - assert.Equal(t, "A_aB", text1) - assert.Equal(t, "A_bB", text2) -} - -func TestTempVariable3(t *testing.T) { - t.Parallel() - - ec := printer.NewEmitContext() - name1 := ec.Factory.NewTempVariable() - - g := &printer.NameGenerator{Context: ec} - text1 := g.GenerateName(name1) - text2 := g.GenerateName(name1) - - assert.Equal(t, "_a", text1) - assert.Equal(t, "_a", text2) -} - -func TestTempVariableScoped(t *testing.T) { - t.Parallel() - - ec := printer.NewEmitContext() - name1 := ec.Factory.NewTempVariable() - name2 := ec.Factory.NewTempVariable() - - g := &printer.NameGenerator{Context: ec} - text1 := g.GenerateName(name1) - g.PushScope(false) - text2 := g.GenerateName(name2) - g.PopScope(false) - - assert.Equal(t, "_a", text1) - assert.Equal(t, "_a", text2) -} - -func TestTempVariableScopedReserved(t *testing.T) { - t.Parallel() - - ec := printer.NewEmitContext() - name1 := ec.Factory.NewTempVariableEx(printer.AutoGenerateOptions{Flags: printer.GeneratedIdentifierFlagsReservedInNestedScopes}) - name2 := ec.Factory.NewTempVariable() - - g := &printer.NameGenerator{Context: ec} - text1 := g.GenerateName(name1) - g.PushScope(false) - text2 := g.GenerateName(name2) - g.PopScope(false) - - assert.Equal(t, "_a", text1) - assert.Equal(t, "_b", text2) -} - -func TestLoopVariable1(t *testing.T) { - t.Parallel() - - ec := printer.NewEmitContext() - name1 := ec.Factory.NewLoopVariable() - name2 := ec.Factory.NewLoopVariable() - - g := &printer.NameGenerator{Context: ec} - text1 := g.GenerateName(name1) - text2 := g.GenerateName(name2) - - assert.Equal(t, "_i", text1) - assert.Equal(t, "_a", text2) -} - -func TestLoopVariable2(t *testing.T) { - t.Parallel() - - ec := printer.NewEmitContext() - name1 := ec.Factory.NewLoopVariableEx(printer.AutoGenerateOptions{ - Prefix: "A", - Suffix: "B", - }) - name2 := ec.Factory.NewLoopVariableEx(printer.AutoGenerateOptions{ - Prefix: "A", - Suffix: "B", - }) - - g := &printer.NameGenerator{Context: ec} - text1 := g.GenerateName(name1) - text2 := g.GenerateName(name2) - - assert.Equal(t, "A_iB", text1) - assert.Equal(t, "A_aB", text2) -} - -func TestLoopVariable3(t *testing.T) { - t.Parallel() - - ec := printer.NewEmitContext() - name1 := ec.Factory.NewLoopVariable() - - g := &printer.NameGenerator{Context: ec} - text1 := g.GenerateName(name1) - text2 := g.GenerateName(name1) - - assert.Equal(t, "_i", text1) - assert.Equal(t, "_i", text2) -} - -func TestLoopVariableScoped(t *testing.T) { - t.Parallel() - - ec := printer.NewEmitContext() - name1 := ec.Factory.NewLoopVariable() - name2 := ec.Factory.NewLoopVariable() - - g := &printer.NameGenerator{Context: ec} - text1 := g.GenerateName(name1) - g.PushScope(false) - text2 := g.GenerateName(name2) - g.PopScope(false) - - assert.Equal(t, "_i", text1) - assert.Equal(t, "_i", text2) -} - -func TestUniqueName1(t *testing.T) { - t.Parallel() - - ec := printer.NewEmitContext() - name1 := ec.Factory.NewUniqueName("foo") - name2 := ec.Factory.NewUniqueName("foo") - - g := &printer.NameGenerator{Context: ec} - text1 := g.GenerateName(name1) - text2 := g.GenerateName(name2) - - assert.Equal(t, "foo_1", text1) - assert.Equal(t, "foo_2", text2) -} - -func TestUniqueName2(t *testing.T) { - t.Parallel() - - ec := printer.NewEmitContext() - name1 := ec.Factory.NewUniqueName("foo") - - g := &printer.NameGenerator{Context: ec} - text1 := g.GenerateName(name1) - text2 := g.GenerateName(name1) - - assert.Equal(t, "foo_1", text1) - // Expected to be same because GenerateName goes off object identity - assert.Equal(t, "foo_1", text2) -} - -func TestUniqueNameScoped(t *testing.T) { - t.Parallel() - - ec := printer.NewEmitContext() - name1 := ec.Factory.NewUniqueName("foo") - name2 := ec.Factory.NewUniqueName("foo") - - g := &printer.NameGenerator{Context: ec} - assert.Equal(t, "foo_1", g.GenerateName(name1)) - - g.PushScope(false) - assert.Equal(t, "foo_2", g.GenerateName(name2)) // Matches Strada, but is incorrect - // assert.Equal(t, "foo_1", g.GenerateName(name2)) // TODO: Fix after Strada port is complete. - g.PopScope(false) -} - -func TestUniquePrivateName1(t *testing.T) { - t.Parallel() - - ec := printer.NewEmitContext() - name1 := ec.Factory.NewUniquePrivateName("#foo") - name2 := ec.Factory.NewUniquePrivateName("#foo") - - g := &printer.NameGenerator{Context: ec} - text1 := g.GenerateName(name1) - text2 := g.GenerateName(name2) - - assert.Equal(t, "#foo_1", text1) - assert.Equal(t, "#foo_2", text2) -} - -func TestUniquePrivateName2(t *testing.T) { - t.Parallel() - - ec := printer.NewEmitContext() - name1 := ec.Factory.NewUniquePrivateName("#foo") - - g := &printer.NameGenerator{Context: ec} - text1 := g.GenerateName(name1) - text2 := g.GenerateName(name1) - - assert.Equal(t, "#foo_1", text1) - assert.Equal(t, "#foo_1", text2) -} - -func TestUniquePrivateNameScoped(t *testing.T) { - t.Parallel() - - ec := printer.NewEmitContext() - name1 := ec.Factory.NewUniquePrivateName("#foo") - name2 := ec.Factory.NewUniquePrivateName("#foo") - - g := &printer.NameGenerator{Context: ec} - assert.Equal(t, "#foo_1", g.GenerateName(name1)) - - g.PushScope(false) // private names are always reserved in nested scopes - assert.Equal(t, "#foo_2", g.GenerateName(name2)) - g.PopScope(false) -} - -func TestGeneratedNameForIdentifier1(t *testing.T) { - t.Parallel() - - ec := printer.NewEmitContext() - - file := parsetestutil.ParseTypeScript("function f() {}", false /*jsx*/) - binder.BindSourceFile(file) - - n := file.Statements.Nodes[0].Name() - name1 := ec.Factory.NewGeneratedNameForNode(n) - - g := &printer.NameGenerator{Context: ec, GetTextOfNode: (*ast.Node).Text} - text1 := g.GenerateName(name1) - - assert.Equal(t, "f_1", text1) -} - -func TestGeneratedNameForIdentifier2(t *testing.T) { - t.Parallel() - - ec := printer.NewEmitContext() - - file := parsetestutil.ParseTypeScript("function f() {}", false /*jsx*/) - binder.BindSourceFile(file) - - n := file.Statements.Nodes[0].Name() - name1 := ec.Factory.NewGeneratedNameForNodeEx(n, printer.AutoGenerateOptions{ - Prefix: "a", - Suffix: "b", - }) - - g := &printer.NameGenerator{Context: ec, GetTextOfNode: (*ast.Node).Text} - text1 := g.GenerateName(name1) - - assert.Equal(t, "afb", text1) -} - -func TestGeneratedNameForIdentifier3(t *testing.T) { - t.Parallel() - - ec := printer.NewEmitContext() - - file := parsetestutil.ParseTypeScript("function f() {}", false /*jsx*/) - binder.BindSourceFile(file) - - n := file.Statements.Nodes[0].Name() - name1 := ec.Factory.NewGeneratedNameForNodeEx(n, printer.AutoGenerateOptions{ - Prefix: "a", - Suffix: "b", - }) - name2 := ec.Factory.NewGeneratedNameForNode(name1) - - g := &printer.NameGenerator{Context: ec, GetTextOfNode: (*ast.Node).Text} - text1 := g.GenerateName(name2) - - assert.Equal(t, "afb_1", text1) -} - -// namespace reuses name if it does not collide with locals -func TestGeneratedNameForNamespace1(t *testing.T) { - t.Parallel() - - ec := printer.NewEmitContext() - - file := parsetestutil.ParseTypeScript("namespace foo { }", false /*jsx*/) - binder.BindSourceFile(file) - - ns1 := file.Statements.Nodes[0] - name1 := ec.Factory.NewGeneratedNameForNode(ns1) - - g := &printer.NameGenerator{Context: ec, GetTextOfNode: (*ast.Node).Text} - text1 := g.GenerateName(name1) - - assert.Equal(t, "foo", text1) -} - -// namespace uses generated name if it collides with locals -func TestGeneratedNameForNamespace2(t *testing.T) { - t.Parallel() - - ec := printer.NewEmitContext() - - file := parsetestutil.ParseTypeScript("namespace foo { var foo; }", false /*jsx*/) - binder.BindSourceFile(file) - - ns1 := file.Statements.Nodes[0] - name1 := ec.Factory.NewGeneratedNameForNode(ns1) - - g := &printer.NameGenerator{Context: ec, GetTextOfNode: (*ast.Node).Text} - text1 := g.GenerateName(name1) - - assert.Equal(t, "foo_1", text1) -} - -// avoids collisions when unscoped -func TestGeneratedNameForNamespace3(t *testing.T) { - t.Parallel() - - ec := printer.NewEmitContext() - - file := parsetestutil.ParseTypeScript("namespace ns1 { namespace foo { var foo; } } namespace ns2 { namespace foo { var foo; } }", false /*jsx*/) - binder.BindSourceFile(file) - - ns1 := file.Statements.Nodes[0].AsModuleDeclaration().Body.AsModuleBlock().Statements.Nodes[0] - ns2 := file.Statements.Nodes[1].AsModuleDeclaration().Body.AsModuleBlock().Statements.Nodes[0] - name1 := ec.Factory.NewGeneratedNameForNode(ns1) - name2 := ec.Factory.NewGeneratedNameForNode(ns2) - - g := &printer.NameGenerator{Context: ec, GetTextOfNode: (*ast.Node).Text} - text1 := g.GenerateName(name1) - text2 := g.GenerateName(name2) - - assert.Equal(t, "foo_1", text1) - assert.Equal(t, "foo_2", text2) -} - -// reuse name when scoped -func TestGeneratedNameForNamespace4(t *testing.T) { - t.Parallel() - - ec := printer.NewEmitContext() - - file := parsetestutil.ParseTypeScript("namespace ns1 { namespace foo { var foo; } } namespace ns2 { namespace foo { var foo; } }", false /*jsx*/) - binder.BindSourceFile(file) - - ns1 := file.Statements.Nodes[0].AsModuleDeclaration().Body.AsModuleBlock().Statements.Nodes[0] - ns2 := file.Statements.Nodes[1].AsModuleDeclaration().Body.AsModuleBlock().Statements.Nodes[0] - name1 := ec.Factory.NewGeneratedNameForNode(ns1) - name2 := ec.Factory.NewGeneratedNameForNode(ns2) - - g := &printer.NameGenerator{Context: ec, GetTextOfNode: (*ast.Node).Text} - g.PushScope(false) - text1 := g.GenerateName(name1) - g.PopScope(false) - - g.PushScope(false) - text2 := g.GenerateName(name2) - g.PopScope(false) - - assert.Equal(t, "foo_1", text1) - assert.Equal(t, "foo_2", text2) // Matches Strada, but is incorrect - // assert.Equal(t, "foo_1", text2) // TODO: Fix after Strada port is complete. -} - -func TestGeneratedNameForNodeCached(t *testing.T) { - t.Parallel() - - ec := printer.NewEmitContext() - - file := parsetestutil.ParseTypeScript("namespace foo { var foo; }", false /*jsx*/) - binder.BindSourceFile(file) - - ns1 := file.Statements.Nodes[0] - name1 := ec.Factory.NewGeneratedNameForNode(ns1) - name2 := ec.Factory.NewGeneratedNameForNode(ns1) - - g := &printer.NameGenerator{Context: ec, GetTextOfNode: (*ast.Node).Text} - text1 := g.GenerateName(name1) - text2 := g.GenerateName(name2) - - assert.Equal(t, "foo_1", text1) - assert.Equal(t, "foo_1", text2) -} - -func TestGeneratedNameForImport(t *testing.T) { - t.Parallel() - - ec := printer.NewEmitContext() - - file := parsetestutil.ParseTypeScript("import * as foo from 'foo'", false /*jsx*/) - binder.BindSourceFile(file) - - n := file.Statements.Nodes[0] - name1 := ec.Factory.NewGeneratedNameForNode(n) - - g := &printer.NameGenerator{Context: ec, GetTextOfNode: (*ast.Node).Text} - text1 := g.GenerateName(name1) - - assert.Equal(t, "foo_1", text1) -} - -func TestGeneratedNameForExport(t *testing.T) { - t.Parallel() - - ec := printer.NewEmitContext() - - file := parsetestutil.ParseTypeScript("export * as foo from 'foo'", false /*jsx*/) - binder.BindSourceFile(file) - - n := file.Statements.Nodes[0] - name1 := ec.Factory.NewGeneratedNameForNode(n) - - g := &printer.NameGenerator{Context: ec, GetTextOfNode: (*ast.Node).Text} - text1 := g.GenerateName(name1) - - assert.Equal(t, "foo_1", text1) -} - -func TestGeneratedNameForFunctionDeclaration1(t *testing.T) { - t.Parallel() - - ec := printer.NewEmitContext() - - file := parsetestutil.ParseTypeScript("export function f() {}", false /*jsx*/) - binder.BindSourceFile(file) - - n := file.Statements.Nodes[0] - name1 := ec.Factory.NewGeneratedNameForNode(n) - - g := &printer.NameGenerator{Context: ec, GetTextOfNode: (*ast.Node).Text} - text1 := g.GenerateName(name1) - - assert.Equal(t, "f_1", text1) -} - -func TestGeneratedNameForFunctionDeclaration2(t *testing.T) { - t.Parallel() - - ec := printer.NewEmitContext() - - file := parsetestutil.ParseTypeScript("export default function () {}", false /*jsx*/) - binder.BindSourceFile(file) - - n := file.Statements.Nodes[0] - name1 := ec.Factory.NewGeneratedNameForNode(n) - - g := &printer.NameGenerator{Context: ec, GetTextOfNode: (*ast.Node).Text} - text1 := g.GenerateName(name1) - - assert.Equal(t, "default_1", text1) -} - -func TestGeneratedNameForClassDeclaration1(t *testing.T) { - t.Parallel() - - ec := printer.NewEmitContext() - - file := parsetestutil.ParseTypeScript("export class C {}", false /*jsx*/) - binder.BindSourceFile(file) - - n := file.Statements.Nodes[0] - name1 := ec.Factory.NewGeneratedNameForNode(n) - - g := &printer.NameGenerator{Context: ec, GetTextOfNode: (*ast.Node).Text} - text1 := g.GenerateName(name1) - - assert.Equal(t, "C_1", text1) -} - -func TestGeneratedNameForClassDeclaration2(t *testing.T) { - t.Parallel() - - ec := printer.NewEmitContext() - - file := parsetestutil.ParseTypeScript("export default class {}", false /*jsx*/) - binder.BindSourceFile(file) - - n := file.Statements.Nodes[0] - name1 := ec.Factory.NewGeneratedNameForNode(n) - - g := &printer.NameGenerator{Context: ec, GetTextOfNode: (*ast.Node).Text} - text1 := g.GenerateName(name1) - - assert.Equal(t, "default_1", text1) -} - -func TestGeneratedNameForExportAssignment(t *testing.T) { - t.Parallel() - - ec := printer.NewEmitContext() - - file := parsetestutil.ParseTypeScript("export default 0", false /*jsx*/) - binder.BindSourceFile(file) - - n := file.Statements.Nodes[0] - name1 := ec.Factory.NewGeneratedNameForNode(n) - - g := &printer.NameGenerator{Context: ec, GetTextOfNode: (*ast.Node).Text} - text1 := g.GenerateName(name1) - - assert.Equal(t, "default_1", text1) -} - -func TestGeneratedNameForClassExpression(t *testing.T) { - t.Parallel() - - ec := printer.NewEmitContext() - - file := parsetestutil.ParseTypeScript("(class {})", false /*jsx*/) - binder.BindSourceFile(file) - - n := file.Statements.Nodes[0].AsExpressionStatement().Expression.AsParenthesizedExpression().Expression - name1 := ec.Factory.NewGeneratedNameForNode(n) - - g := &printer.NameGenerator{Context: ec, GetTextOfNode: (*ast.Node).Text} - text1 := g.GenerateName(name1) - - assert.Equal(t, "class_1", text1) -} - -func TestGeneratedNameForMethod1(t *testing.T) { - t.Parallel() - - ec := printer.NewEmitContext() - - file := parsetestutil.ParseTypeScript("class C { m() {} }", false /*jsx*/) - binder.BindSourceFile(file) - - n := file.Statements.Nodes[0].AsClassDeclaration().Members.Nodes[0] - name1 := ec.Factory.NewGeneratedNameForNode(n) - - g := &printer.NameGenerator{Context: ec, GetTextOfNode: (*ast.Node).Text} - text1 := g.GenerateName(name1) - - assert.Equal(t, "m_1", text1) -} - -func TestGeneratedNameForMethod2(t *testing.T) { - t.Parallel() - - ec := printer.NewEmitContext() - - file := parsetestutil.ParseTypeScript("class C { 0() {} }", false /*jsx*/) - binder.BindSourceFile(file) - - n := file.Statements.Nodes[0].AsClassDeclaration().Members.Nodes[0] - name1 := ec.Factory.NewGeneratedNameForNode(n) - - g := &printer.NameGenerator{Context: ec, GetTextOfNode: (*ast.Node).Text} - text1 := g.GenerateName(name1) - - assert.Equal(t, "_a", text1) -} - -func TestGeneratedPrivateNameForMethod(t *testing.T) { - t.Parallel() - - ec := printer.NewEmitContext() - - file := parsetestutil.ParseTypeScript("class C { m() {} }", false /*jsx*/) - binder.BindSourceFile(file) - - n := file.Statements.Nodes[0].AsClassDeclaration().Members.Nodes[0] - name1 := ec.Factory.NewGeneratedPrivateNameForNode(n) - - g := &printer.NameGenerator{Context: ec, GetTextOfNode: (*ast.Node).Text} - text1 := g.GenerateName(name1) - - assert.Equal(t, "#m_1", text1) -} - -func TestGeneratedNameForComputedPropertyName(t *testing.T) { - t.Parallel() - - ec := printer.NewEmitContext() - - file := parsetestutil.ParseTypeScript("class C { [x] }", false /*jsx*/) - binder.BindSourceFile(file) - - n := file.Statements.Nodes[0].AsClassDeclaration().Members.Nodes[0].Name() - name1 := ec.Factory.NewGeneratedNameForNode(n) - - g := &printer.NameGenerator{Context: ec, GetTextOfNode: (*ast.Node).Text} - text1 := g.GenerateName(name1) - - assert.Equal(t, "_a", text1) -} - -func TestGeneratedNameForOther(t *testing.T) { - t.Parallel() - - ec := printer.NewEmitContext() - - file := parsetestutil.ParseTypeScript("class C { [x] }", false /*jsx*/) - binder.BindSourceFile(file) - - n := ec.Factory.NewObjectLiteralExpression( - ec.Factory.NewNodeList([]*ast.Node{}), - false, /*multiLine*/ - ) - name1 := ec.Factory.NewGeneratedNameForNode(n) - - g := &printer.NameGenerator{Context: ec, GetTextOfNode: (*ast.Node).Text} - text1 := g.GenerateName(name1) - - assert.Equal(t, "_a", text1) -} diff --git a/kitcom/internal/tsgo/printer/printer.go b/kitcom/internal/tsgo/printer/printer.go deleted file mode 100644 index 3f88b0e..0000000 --- a/kitcom/internal/tsgo/printer/printer.go +++ /dev/null @@ -1,5953 +0,0 @@ -// 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 = ; -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.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.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("/// ", 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: - // /// - // declare var x; - // /// - // 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)) - } -} diff --git a/kitcom/internal/tsgo/printer/printer_test.go b/kitcom/internal/tsgo/printer/printer_test.go deleted file mode 100644 index 7e50ac6..0000000 --- a/kitcom/internal/tsgo/printer/printer_test.go +++ /dev/null @@ -1,2519 +0,0 @@ -package printer_test - -import ( - "testing" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/printer" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/testutil/emittestutil" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/testutil/parsetestutil" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/transformers" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/transformers/tstransforms" -) - -func TestEmit(t *testing.T) { - t.Parallel() - data := []struct { - title string - input string - output string - jsx bool - }{ - {title: "StringLiteral#1", input: `;"test"`, output: ";\n\"test\";"}, - {title: "StringLiteral#2", input: `;'test'`, output: ";\n'test';"}, - {title: "NumericLiteral#1", input: `0`, output: `0;`}, - {title: "NumericLiteral#2", input: `10_000`, output: `10_000;`}, - {title: "BigIntLiteral#1", input: `0n`, output: `0n;`}, - {title: "BigIntLiteral#2", input: `10_000n`, output: `10000n;`}, // TODO: Preserve numeric literal separators after Strada migration - {title: "BooleanLiteral#1", input: `true`, output: `true;`}, - {title: "BooleanLiteral#2", input: `false`, output: `false;`}, - {title: "NoSubstitutionTemplateLiteral", input: "``", output: "``;"}, - {title: "NoSubstitutionTemplateLiteral#2", input: "`\n`", output: "`\n`;"}, - - {title: "RegularExpressionLiteral#1", input: `/a/`, output: `/a/;`}, - {title: "RegularExpressionLiteral#2", input: `/a/g`, output: `/a/g;`}, - {title: "NullLiteral", input: `null`, output: `null;`}, - {title: "ThisExpression", input: `this`, output: `this;`}, - {title: "SuperExpression", input: `super()`, output: `super();`}, - {title: "ImportExpression", input: `import()`, output: `import();`}, - {title: "PropertyAccess#1", input: `a.b`, output: `a.b;`}, - {title: "PropertyAccess#2", input: `a.#b`, output: `a.#b;`}, - {title: "PropertyAccess#3", input: `a?.b`, output: `a?.b;`}, - {title: "PropertyAccess#4", input: `a?.b.c`, output: `a?.b.c;`}, - {title: "PropertyAccess#5", input: `1..b`, output: `1..b;`}, - {title: "PropertyAccess#6", input: `1.0.b`, output: `1.0.b;`}, - {title: "PropertyAccess#7", input: `0x1.b`, output: `0x1.b;`}, - {title: "PropertyAccess#8", input: `0b1.b`, output: `0b1.b;`}, - {title: "PropertyAccess#9", input: `0o1.b`, output: `0o1.b;`}, - {title: "PropertyAccess#10", input: `10e1.b`, output: `10e1.b;`}, - {title: "PropertyAccess#11", input: `10E1.b`, output: `10E1.b;`}, - {title: "PropertyAccess#12", input: `a.b?.c`, output: `a.b?.c;`}, - {title: "PropertyAccess#13", input: "a\n.b", output: "a\n .b;"}, - {title: "PropertyAccess#14", input: "a.\nb", output: "a.\n b;"}, - {title: "ElementAccess#1", input: `a[b]`, output: `a[b];`}, - {title: "ElementAccess#2", input: `a?.[b]`, output: `a?.[b];`}, - {title: "ElementAccess#3", input: `a?.[b].c`, output: `a?.[b].c;`}, - {title: "CallExpression#1", input: `a()`, output: `a();`}, - {title: "CallExpression#2", input: `a()`, output: `a();`}, - {title: "CallExpression#3", input: `a(b)`, output: `a(b);`}, - {title: "CallExpression#4", input: `a(b)`, output: `a(b);`}, - {title: "CallExpression#5", input: `a(b).c`, output: `a(b).c;`}, - {title: "CallExpression#6", input: `a(b).c`, output: `a(b).c;`}, - {title: "CallExpression#7", input: `a?.(b)`, output: `a?.(b);`}, - {title: "CallExpression#8", input: `a?.(b)`, output: `a?.(b);`}, - {title: "CallExpression#9", input: `a?.(b).c`, output: `a?.(b).c;`}, - {title: "CallExpression#10", input: `a?.(b).c`, output: `a?.(b).c;`}, - {title: "CallExpression#11", input: `a()`, output: `a();`}, - // {title: "CallExpression#12", input: `a()`, output: `a();`}, // TODO: preserve trailing comma after Strada migration - {title: "CallExpression#13", input: `a?.b()`, output: `a?.b();`}, - {title: "NewExpression#1", input: `new a`, output: `new a;`}, - {title: "NewExpression#2", input: `new a.b`, output: `new a.b;`}, - {title: "NewExpression#3", input: `new a()`, output: `new a();`}, - {title: "NewExpression#4", input: `new a.b()`, output: `new a.b();`}, - {title: "NewExpression#5", input: `new a()`, output: `new a();`}, - {title: "NewExpression#6", input: `new a.b()`, output: `new a.b();`}, - {title: "NewExpression#7", input: `new a(b)`, output: `new a(b);`}, - {title: "NewExpression#8", input: `new a.b(c)`, output: `new a.b(c);`}, - {title: "NewExpression#9", input: `new a(b)`, output: `new a(b);`}, - {title: "NewExpression#10", input: `new a.b(c)`, output: `new a.b(c);`}, - {title: "NewExpression#11", input: `new a(b).c`, output: `new a(b).c;`}, - {title: "NewExpression#12", input: `new a(b).c`, output: `new a(b).c;`}, - {title: "TaggedTemplateExpression#1", input: "tag``", output: "tag ``;"}, - {title: "TaggedTemplateExpression#2", input: "tag``", output: "tag ``;"}, - {title: "TypeAssertionExpression#1", input: `a`, output: `a;`}, - {title: "FunctionExpression#1", input: `(function(){})`, output: `(function () { });`}, - {title: "FunctionExpression#2", input: `(function f(){})`, output: `(function f() { });`}, - {title: "FunctionExpression#3", input: `(function*f(){})`, output: `(function* f() { });`}, - {title: "FunctionExpression#4", input: `(async function f(){})`, output: `(async function f() { });`}, - {title: "FunctionExpression#5", input: `(async function*f(){})`, output: `(async function* f() { });`}, - {title: "FunctionExpression#6", input: `(function(){})`, output: `(function () { });`}, - {title: "FunctionExpression#7", input: `(function(a){})`, output: `(function (a) { });`}, - {title: "FunctionExpression#8", input: `(function():T{})`, output: `(function (): T { });`}, - {title: "ArrowFunction#1", input: `a=>{}`, output: `a => { };`}, - {title: "ArrowFunction#2", input: `()=>{}`, output: `() => { };`}, - {title: "ArrowFunction#3", input: `(a)=>{}`, output: `(a) => { };`}, - {title: "ArrowFunction#4", input: `(a)=>{}`, output: `(a) => { };`}, - {title: "ArrowFunction#5", input: `async a=>{}`, output: `async a => { };`}, - {title: "ArrowFunction#6", input: `async()=>{}`, output: `async () => { };`}, - {title: "ArrowFunction#7", input: `async()=>{}`, output: `async () => { };`}, - {title: "ArrowFunction#8", input: `():T=>{}`, output: `(): T => { };`}, - {title: "ArrowFunction#9", input: `()=>a`, output: `() => a;`}, - {title: "DeleteExpression", input: `delete a`, output: `delete a;`}, - {title: "TypeOfExpression", input: `typeof a`, output: `typeof a;`}, - {title: "VoidExpression", input: `void a`, output: `void a;`}, - {title: "AwaitExpression", input: `await a`, output: `await a;`}, - {title: "PrefixUnaryExpression#1", input: `+a`, output: `+a;`}, - {title: "PrefixUnaryExpression#2", input: `++a`, output: `++a;`}, - {title: "PrefixUnaryExpression#3", input: `+ +a`, output: `+ +a;`}, - {title: "PrefixUnaryExpression#4", input: `+ ++a`, output: `+ ++a;`}, - {title: "PrefixUnaryExpression#5", input: `-a`, output: `-a;`}, - {title: "PrefixUnaryExpression#6", input: `--a`, output: `--a;`}, - {title: "PrefixUnaryExpression#7", input: `- -a`, output: `- -a;`}, - {title: "PrefixUnaryExpression#8", input: `- --a`, output: `- --a;`}, - {title: "PrefixUnaryExpression#9", input: `+-a`, output: `+-a;`}, - {title: "PrefixUnaryExpression#10", input: `+--a`, output: `+--a;`}, - {title: "PrefixUnaryExpression#11", input: `-+a`, output: `-+a;`}, - {title: "PrefixUnaryExpression#12", input: `-++a`, output: `-++a;`}, - {title: "PrefixUnaryExpression#13", input: `~a`, output: `~a;`}, - {title: "PrefixUnaryExpression#14", input: `!a`, output: `!a;`}, - {title: "PostfixUnaryExpression#1", input: `a++`, output: `a++;`}, - {title: "PostfixUnaryExpression#2", input: `a--`, output: `a--;`}, - {title: "BinaryExpression#1", input: `a,b`, output: `a, b;`}, - {title: "BinaryExpression#2", input: `a+b`, output: `a + b;`}, - {title: "BinaryExpression#3", input: `a**b`, output: `a ** b;`}, - {title: "BinaryExpression#4", input: `a instanceof b`, output: `a instanceof b;`}, - {title: "BinaryExpression#5", input: `a in b`, output: `a in b;`}, - {title: "BinaryExpression#6", input: "a\n&& b", output: "a\n && b;"}, - {title: "BinaryExpression#7", input: "a &&\nb", output: "a &&\n b;"}, - {title: "ConditionalExpression#1", input: `a?b:c`, output: `a ? b : c;`}, - {title: "ConditionalExpression#2", input: "a\n?b:c", output: "a\n ? b : c;"}, - {title: "ConditionalExpression#3", input: "a?\nb:c", output: "a ?\n b : c;"}, - {title: "ConditionalExpression#4", input: "a?b\n:c", output: "a ? b\n : c;"}, - {title: "ConditionalExpression#5", input: "a?b:\nc", output: "a ? b :\n c;"}, - {title: "TemplateExpression#1", input: "`a${b}c`", output: "`a${b}c`;"}, - {title: "TemplateExpression#2", input: "`a${b}c${d}e`", output: "`a${b}c${d}e`;"}, - {title: "YieldExpression#1", input: `(function*() { yield })`, output: `(function* () { yield; });`}, - {title: "YieldExpression#2", input: `(function*() { yield a })`, output: `(function* () { yield a; });`}, - {title: "YieldExpression#3", input: `(function*() { yield*a })`, output: `(function* () { yield* a; });`}, - {title: "SpreadElement", input: `[...a]`, output: `[...a];`}, - {title: "ClassExpression#1", input: `(class {})`, output: "(class {\n});"}, - {title: "ClassExpression#2", input: `(class a {})`, output: "(class a {\n});"}, - {title: "ClassExpression#3", input: `(class{})`, output: "(class {\n});"}, - {title: "ClassExpression#4", input: `(class a{})`, output: "(class a {\n});"}, - {title: "ClassExpression#5", input: `(class extends b {})`, output: "(class extends b {\n});"}, - {title: "ClassExpression#6", input: `(class a extends b {})`, output: "(class a extends b {\n});"}, - {title: "ClassExpression#7", input: `(class implements b {})`, output: "(class implements b {\n});"}, - {title: "ClassExpression#8", input: `(class a implements b {})`, output: "(class a implements b {\n});"}, - {title: "ClassExpression#9", input: `(class implements b, c {})`, output: "(class implements b, c {\n});"}, - {title: "ClassExpression#10", input: `(class a implements b, c {})`, output: "(class a implements b, c {\n});"}, - {title: "ClassExpression#11", input: `(class extends b implements c, d {})`, output: "(class extends b implements c, d {\n});"}, - {title: "ClassExpression#12", input: `(class a extends b implements c, d {})`, output: "(class a extends b implements c, d {\n});"}, - {title: "ClassExpression#13", input: `(@a class {})`, output: "(\n@a\nclass {\n});"}, - {title: "OmittedExpression", input: `[,]`, output: `[,];`}, - {title: "ExpressionWithTypeArguments", input: `a`, output: `a;`}, - {title: "AsExpression", input: `a as T`, output: `a as T;`}, - {title: "SatisfiesExpression", input: `a satisfies T`, output: `a satisfies T;`}, - {title: "NonNullExpression", input: `a!`, output: `a!;`}, - {title: "MetaProperty#1", input: `new.target`, output: `new.target;`}, - {title: "MetaProperty#2", input: `import.meta`, output: `import.meta;`}, - {title: "ArrayLiteralExpression#1", input: `[]`, output: `[];`}, - {title: "ArrayLiteralExpression#2", input: `[a]`, output: `[a];`}, - {title: "ArrayLiteralExpression#3", input: `[a,]`, output: `[a,];`}, - {title: "ArrayLiteralExpression#4", input: `[,a]`, output: `[, a];`}, - {title: "ArrayLiteralExpression#5", input: `[...a]`, output: `[...a];`}, - {title: "ObjectLiteralExpression#1", input: `({})`, output: `({});`}, - {title: "ObjectLiteralExpression#2", input: `({a,})`, output: `({ a, });`}, - {title: "ShorthandPropertyAssignment", input: `({a})`, output: `({ a });`}, - {title: "PropertyAssignment", input: `({a:b})`, output: `({ a: b });`}, - {title: "SpreadAssignment", input: `({...a})`, output: `({ ...a });`}, - {title: "Block", input: `{}`, output: `{ }`}, - {title: "VariableStatement#1", input: `var a`, output: `var a;`}, - {title: "VariableStatement#2", input: `let a`, output: `let a;`}, - {title: "VariableStatement#3", input: `const a = b`, output: `const a = b;`}, - {title: "VariableStatement#4", input: `using a = b`, output: `using a = b;`}, - {title: "VariableStatement#5", input: `await using a = b`, output: `await using a = b;`}, - {title: "EmptyStatement", input: `;`, output: `;`}, - {title: "IfStatement#1", input: `if(a);`, output: "if (a)\n ;"}, - {title: "IfStatement#2", input: `if(a);else;`, output: "if (a)\n ;\nelse\n ;"}, - {title: "IfStatement#3", input: `if(a);else{}`, output: "if (a)\n ;\nelse { }"}, - {title: "IfStatement#4", input: `if(a);else if(b);`, output: "if (a)\n ;\nelse if (b)\n ;"}, - {title: "IfStatement#5", input: `if(a);else if(b) {}`, output: "if (a)\n ;\nelse if (b) { }"}, - {title: "IfStatement#6", input: `if(a) {}`, output: "if (a) { }"}, - {title: "IfStatement#7", input: `if(a) {} else;`, output: "if (a) { }\nelse\n ;"}, - {title: "IfStatement#8", input: `if(a) {} else {}`, output: "if (a) { }\nelse { }"}, - {title: "IfStatement#9", input: `if(a) {} else if(b);`, output: "if (a) { }\nelse if (b)\n ;"}, - {title: "IfStatement#10", input: `if(a) {} else if(b){}`, output: "if (a) { }\nelse if (b) { }"}, - {title: "DoStatement#1", input: `do;while(a);`, output: "do\n ;\nwhile (a);"}, - {title: "DoStatement#2", input: `do {} while(a);`, output: "do { } while (a);"}, - {title: "WhileStatement#1", input: `while(a);`, output: "while (a)\n ;"}, - {title: "WhileStatement#2", input: `while(a) {}`, output: "while (a) { }"}, - {title: "ForStatement#1", input: `for(;;);`, output: "for (;;)\n ;"}, - {title: "ForStatement#2", input: `for(a;;);`, output: "for (a;;)\n ;"}, - {title: "ForStatement#3", input: `for(var a;;);`, output: "for (var a;;)\n ;"}, - {title: "ForStatement#4", input: `for(;a;);`, output: "for (; a;)\n ;"}, - {title: "ForStatement#5", input: `for(;;a);`, output: "for (;; a)\n ;"}, - {title: "ForStatement#6", input: `for(;;){}`, output: "for (;;) { }"}, - {title: "ForInStatement#1", input: `for(a in b);`, output: "for (a in b)\n ;"}, - {title: "ForInStatement#2", input: `for(var a in b);`, output: "for (var a in b)\n ;"}, - {title: "ForInStatement#3", input: `for(a in b){}`, output: "for (a in b) { }"}, - {title: "ForOfStatement#1", input: `for(a of b);`, output: "for (a of b)\n ;"}, - {title: "ForOfStatement#2", input: `for(var a of b);`, output: "for (var a of b)\n ;"}, - {title: "ForOfStatement#3", input: `for(a of b){}`, output: "for (a of b) { }"}, - {title: "ForOfStatement#4", input: `for await(a of b);`, output: "for await (a of b)\n ;"}, - {title: "ForOfStatement#5", input: `for await(var a of b);`, output: "for await (var a of b)\n ;"}, - {title: "ForOfStatement#6", input: `for await(a of b){}`, output: "for await (a of b) { }"}, - {title: "ContinueStatement#1", input: `continue`, output: "continue;"}, - {title: "ContinueStatement#2", input: `continue a`, output: "continue a;"}, - {title: "BreakStatement#1", input: `break`, output: "break;"}, - {title: "BreakStatement#2", input: `break a`, output: "break a;"}, - {title: "ReturnStatement#1", input: `return`, output: "return;"}, - {title: "ReturnStatement#2", input: `return a`, output: "return a;"}, - {title: "WithStatement#1", input: `with(a);`, output: "with (a)\n ;"}, - {title: "WithStatement#2", input: `with(a){}`, output: "with (a) { }"}, - {title: "SwitchStatement", input: `switch (a) {}`, output: "switch (a) {\n}"}, - {title: "CaseClause#1", input: `switch (a) {case b:}`, output: "switch (a) {\n case b:\n}"}, - {title: "CaseClause#2", input: `switch (a) {case b:;}`, output: "switch (a) {\n case b: ;\n}"}, - {title: "DefaultClause#1", input: `switch (a) {default:}`, output: "switch (a) {\n default:\n}"}, - {title: "DefaultClause#2", input: `switch (a) {default:;}`, output: "switch (a) {\n default: ;\n}"}, - {title: "LabeledStatement", input: `a:;`, output: "a: ;"}, - {title: "ThrowStatement", input: `throw a`, output: "throw a;"}, - {title: "TryStatement#1", input: `try {} catch {}`, output: "try { }\ncatch { }"}, - {title: "TryStatement#2", input: `try {} finally {}`, output: "try { }\nfinally { }"}, - {title: "TryStatement#3", input: `try {} catch {} finally {}`, output: "try { }\ncatch { }\nfinally { }"}, - {title: "DebuggerStatement", input: `debugger`, output: "debugger;"}, - {title: "FunctionDeclaration#1", input: `export default function(){}`, output: `export default function () { }`}, - {title: "FunctionDeclaration#2", input: `function f(){}`, output: `function f() { }`}, - {title: "FunctionDeclaration#3", input: `function*f(){}`, output: `function* f() { }`}, - {title: "FunctionDeclaration#4", input: `async function f(){}`, output: `async function f() { }`}, - {title: "FunctionDeclaration#5", input: `async function*f(){}`, output: `async function* f() { }`}, - {title: "FunctionDeclaration#6", input: `function f(){}`, output: `function f() { }`}, - {title: "FunctionDeclaration#7", input: `function f(a){}`, output: `function f(a) { }`}, - {title: "FunctionDeclaration#8", input: `function f():T{}`, output: `function f(): T { }`}, - {title: "FunctionDeclaration#9", input: `function f();`, output: `function f();`}, - {title: "ClassDeclaration#1", input: `class a {}`, output: "class a {\n}"}, - {title: "ClassDeclaration#2", input: `class a{}`, output: "class a {\n}"}, - {title: "ClassDeclaration#3", input: `class a extends b {}`, output: "class a extends b {\n}"}, - {title: "ClassDeclaration#4", input: `class a implements b {}`, output: "class a implements b {\n}"}, - {title: "ClassDeclaration#5", input: `class a implements b, c {}`, output: "class a implements b, c {\n}"}, - {title: "ClassDeclaration#6", input: `class a extends b implements c, d {}`, output: "class a extends b implements c, d {\n}"}, - {title: "ClassDeclaration#7", input: `export default class {}`, output: "export default class {\n}"}, - {title: "ClassDeclaration#8", input: `export default class{}`, output: "export default class {\n}"}, - {title: "ClassDeclaration#9", input: `export default class extends b {}`, output: "export default class extends b {\n}"}, - {title: "ClassDeclaration#10", input: `export default class implements b {}`, output: "export default class implements b {\n}"}, - {title: "ClassDeclaration#11", input: `export default class implements b, c {}`, output: "export default class implements b, c {\n}"}, - {title: "ClassDeclaration#12", input: `export default class extends b implements c, d {}`, output: "export default class extends b implements c, d {\n}"}, - {title: "ClassDeclaration#13", input: `@a class b {}`, output: "@a\nclass b {\n}"}, - {title: "ClassDeclaration#14", input: `@a export class b {}`, output: "@a\nexport class b {\n}"}, - {title: "ClassDeclaration#15", input: `export @a class b {}`, output: "export \n@a\nclass b {\n}"}, - {title: "InterfaceDeclaration#1", input: `interface a {}`, output: "interface a {\n}"}, - {title: "InterfaceDeclaration#2", input: `interface a{}`, output: "interface a {\n}"}, - {title: "InterfaceDeclaration#3", input: `interface a extends b {}`, output: "interface a extends b {\n}"}, - {title: "InterfaceDeclaration#4", input: `interface a extends b, c {}`, output: "interface a extends b, c {\n}"}, - {title: "TypeAliasDeclaration#1", input: `type a = b`, output: "type a = b;"}, - {title: "TypeAliasDeclaration#2", input: `type a = b`, output: "type a = b;"}, - {title: "EnumDeclaration#1", input: `enum a{}`, output: "enum a {\n}"}, - {title: "EnumDeclaration#2", input: `enum a{b}`, output: "enum a {\n b\n}"}, - {title: "EnumDeclaration#3", input: `enum a{b=c}`, output: "enum a {\n b = c\n}"}, - {title: "ModuleDeclaration#1", input: `module a{}`, output: "module a { }"}, - {title: "ModuleDeclaration#2", input: `module a.b{}`, output: "module a.b { }"}, - {title: "ModuleDeclaration#3", input: `module "a";`, output: "module \"a\";"}, - {title: "ModuleDeclaration#4", input: `module "a"{}`, output: "module \"a\" { }"}, - {title: "ModuleDeclaration#5", input: `namespace a{}`, output: "namespace a { }"}, - {title: "ModuleDeclaration#6", input: `namespace a.b{}`, output: "namespace a.b { }"}, - {title: "ModuleDeclaration#7", input: `global;`, output: "global;"}, - {title: "ModuleDeclaration#8", input: `global{}`, output: "global { }"}, - {title: "ImportEqualsDeclaration#1", input: `import a = b`, output: "import a = b;"}, - {title: "ImportEqualsDeclaration#2", input: `import a = b.c`, output: "import a = b.c;"}, - {title: "ImportEqualsDeclaration#3", input: `import a = require("b")`, output: "import a = require(\"b\");"}, - {title: "ImportEqualsDeclaration#4", input: `export import a = b`, output: "export import a = b;"}, - {title: "ImportEqualsDeclaration#5", input: `export import a = require("b")`, output: "export import a = require(\"b\");"}, - {title: "ImportEqualsDeclaration#6", input: `import type a = b`, output: "import type a = b;"}, - {title: "ImportEqualsDeclaration#7", input: `import type a = b.c`, output: "import type a = b.c;"}, - {title: "ImportEqualsDeclaration#8", input: `import type a = require("b")`, output: "import type a = require(\"b\");"}, - {title: "ImportDeclaration#1", input: `import "a"`, output: "import \"a\";"}, - {title: "ImportDeclaration#2", input: `import a from "b"`, output: "import a from \"b\";"}, - {title: "ImportDeclaration#3", input: `import type a from "b"`, output: "import type a from \"b\";"}, - {title: "ImportDeclaration#4", input: `import * as a from "b"`, output: "import * as a from \"b\";"}, - {title: "ImportDeclaration#5", input: `import type * as a from "b"`, output: "import type * as a from \"b\";"}, - {title: "ImportDeclaration#6", input: `import {} from "b"`, output: "import {} from \"b\";"}, - {title: "ImportDeclaration#7", input: `import type {} from "b"`, output: "import type {} from \"b\";"}, - {title: "ImportDeclaration#8", input: `import { a } from "b"`, output: "import { a } from \"b\";"}, - {title: "ImportDeclaration#9", input: `import type { a } from "b"`, output: "import type { a } from \"b\";"}, - {title: "ImportDeclaration#8", input: `import { a as b } from "c"`, output: "import { a as b } from \"c\";"}, - {title: "ImportDeclaration#9", input: `import type { a as b } from "c"`, output: "import type { a as b } from \"c\";"}, - {title: "ImportDeclaration#10", input: `import { "a" as b } from "c"`, output: "import { \"a\" as b } from \"c\";"}, - {title: "ImportDeclaration#11", input: `import type { "a" as b } from "c"`, output: "import type { \"a\" as b } from \"c\";"}, - {title: "ImportDeclaration#12", input: `import a, {} from "b"`, output: "import a, {} from \"b\";"}, - {title: "ImportDeclaration#13", input: `import a, * as b from "c"`, output: "import a, * as b from \"c\";"}, - {title: "ImportDeclaration#14", input: `import {} from "a" with {}`, output: "import {} from \"a\" with {};"}, - {title: "ImportDeclaration#15", input: `import {} from "a" with { b: "c" }`, output: "import {} from \"a\" with { b: \"c\" };"}, - {title: "ImportDeclaration#16", input: `import {} from "a" with { "b": "c" }`, output: "import {} from \"a\" with { \"b\": \"c\" };"}, - {title: "ExportAssignment#1", input: `export = a`, output: "export = a;"}, - {title: "ExportAssignment#2", input: `export default a`, output: "export default a;"}, - {title: "NamespaceExportDeclaration", input: `export as namespace a`, output: "export as namespace a;"}, - {title: "ExportDeclaration#1", input: `export * from "a"`, output: "export * from \"a\";"}, - {title: "ExportDeclaration#2", input: `export type * from "a"`, output: "export type * from \"a\";"}, - {title: "ExportDeclaration#3", input: `export * as a from "b"`, output: "export * as a from \"b\";"}, - {title: "ExportDeclaration#4", input: `export type * as a from "b"`, output: "export type * as a from \"b\";"}, - {title: "ExportDeclaration#5", input: `export { } from "a"`, output: "export {} from \"a\";"}, - {title: "ExportDeclaration#6", input: `export type { } from "a"`, output: "export type {} from \"a\";"}, - {title: "ExportDeclaration#7", input: `export { a } from "b"`, output: "export { a } from \"b\";"}, - {title: "ExportDeclaration#8", input: `export { type a } from "b"`, output: "export { type a } from \"b\";"}, - {title: "ExportDeclaration#9", input: `export type { a } from "b"`, output: "export type { a } from \"b\";"}, - {title: "ExportDeclaration#10", input: `export { a as b } from "c"`, output: "export { a as b } from \"c\";"}, - {title: "ExportDeclaration#11", input: `export { type a as b } from "c"`, output: "export { type a as b } from \"c\";"}, - {title: "ExportDeclaration#12", input: `export type { a as b } from "c"`, output: "export type { a as b } from \"c\";"}, - {title: "ExportDeclaration#13", input: `export { a as "b" } from "c"`, output: "export { a as \"b\" } from \"c\";"}, - {title: "ExportDeclaration#14", input: `export { type a as "b" } from "c"`, output: "export { type a as \"b\" } from \"c\";"}, - {title: "ExportDeclaration#15", input: `export type { a as "b" } from "c"`, output: "export type { a as \"b\" } from \"c\";"}, - {title: "ExportDeclaration#16", input: `export { "a" } from "b"`, output: "export { \"a\" } from \"b\";"}, - {title: "ExportDeclaration#17", input: `export { type "a" } from "b"`, output: "export { type \"a\" } from \"b\";"}, - {title: "ExportDeclaration#18", input: `export type { "a" } from "b"`, output: "export type { \"a\" } from \"b\";"}, - {title: "ExportDeclaration#19", input: `export { "a" as b } from "c"`, output: "export { \"a\" as b } from \"c\";"}, - {title: "ExportDeclaration#20", input: `export { type "a" as b } from "c"`, output: "export { type \"a\" as b } from \"c\";"}, - {title: "ExportDeclaration#21", input: `export type { "a" as b } from "c"`, output: "export type { \"a\" as b } from \"c\";"}, - {title: "ExportDeclaration#22", input: `export { "a" as "b" } from "c"`, output: "export { \"a\" as \"b\" } from \"c\";"}, - {title: "ExportDeclaration#23", input: `export { type "a" as "b" } from "c"`, output: "export { type \"a\" as \"b\" } from \"c\";"}, - {title: "ExportDeclaration#24", input: `export type { "a" as "b" } from "c"`, output: "export type { \"a\" as \"b\" } from \"c\";"}, - {title: "ExportDeclaration#25", input: `export { }`, output: "export {};"}, - {title: "ExportDeclaration#26", input: `export type { }`, output: "export type {};"}, - {title: "ExportDeclaration#27", input: `export { a }`, output: "export { a };"}, - {title: "ExportDeclaration#28", input: `export { type a }`, output: "export { type a };"}, - {title: "ExportDeclaration#29", input: `export type { a }`, output: "export type { a };"}, - {title: "ExportDeclaration#30", input: `export { a as b }`, output: "export { a as b };"}, - {title: "ExportDeclaration#31", input: `export { type a as b }`, output: "export { type a as b };"}, - {title: "ExportDeclaration#32", input: `export type { a as b }`, output: "export type { a as b };"}, - {title: "ExportDeclaration#33", input: `export { a as "b" }`, output: "export { a as \"b\" };"}, - {title: "ExportDeclaration#34", input: `export { type a as "b" }`, output: "export { type a as \"b\" };"}, - {title: "ExportDeclaration#35", input: `export type { a as "b" }`, output: "export type { a as \"b\" };"}, - {title: "ExportDeclaration#36", input: `export {} from "a" with {}`, output: "export {} from \"a\" with {};"}, - {title: "ExportDeclaration#37", input: `export {} from "a" with { b: "c" }`, output: "export {} from \"a\" with { b: \"c\" };"}, - {title: "ExportDeclaration#38", input: `export {} from "a" with { "b": "c" }`, output: "export {} from \"a\" with { \"b\": \"c\" };"}, - {title: "KeywordTypeNode#1", input: `type T = any`, output: `type T = any;`}, - {title: "KeywordTypeNode#2", input: `type T = unknown`, output: `type T = unknown;`}, - {title: "KeywordTypeNode#3", input: `type T = never`, output: `type T = never;`}, - {title: "KeywordTypeNode#4", input: `type T = void`, output: `type T = void;`}, - {title: "KeywordTypeNode#5", input: `type T = undefined`, output: `type T = undefined;`}, - {title: "KeywordTypeNode#6", input: `type T = null`, output: `type T = null;`}, - {title: "KeywordTypeNode#7", input: `type T = object`, output: `type T = object;`}, - {title: "KeywordTypeNode#8", input: `type T = string`, output: `type T = string;`}, - {title: "KeywordTypeNode#9", input: `type T = symbol`, output: `type T = symbol;`}, - {title: "KeywordTypeNode#10", input: `type T = number`, output: `type T = number;`}, - {title: "KeywordTypeNode#11", input: `type T = bigint`, output: `type T = bigint;`}, - {title: "KeywordTypeNode#12", input: `type T = boolean`, output: `type T = boolean;`}, - {title: "KeywordTypeNode#13", input: `type T = intrinsic`, output: `type T = intrinsic;`}, - {title: "TypePredicateNode#1", input: `function f(): asserts a`, output: `function f(): asserts a;`}, - {title: "TypePredicateNode#2", input: `function f(): asserts a is b`, output: `function f(): asserts a is b;`}, - {title: "TypePredicateNode#3", input: `function f(): asserts this`, output: `function f(): asserts this;`}, - {title: "TypePredicateNode#4", input: `function f(): asserts this is b`, output: `function f(): asserts this is b;`}, - {title: "TypeReferenceNode#1", input: `type T = a`, output: `type T = a;`}, - {title: "TypeReferenceNode#2", input: `type T = a.b`, output: `type T = a.b;`}, - {title: "TypeReferenceNode#3", input: `type T = a`, output: `type T = a;`}, - {title: "TypeReferenceNode#4", input: `type T = a.b`, output: `type T = a.b;`}, - {title: "FunctionTypeNode#1", input: `type T = () => a`, output: `type T = () => a;`}, - {title: "FunctionTypeNode#2", input: `type T = () => a`, output: `type T = () => a;`}, - {title: "FunctionTypeNode#3", input: `type T = (a) => b`, output: `type T = (a) => b;`}, - {title: "ConstructorTypeNode#1", input: `type T = new () => a`, output: `type T = new () => a;`}, - {title: "ConstructorTypeNode#2", input: `type T = new () => a`, output: `type T = new () => a;`}, - {title: "ConstructorTypeNode#3", input: `type T = new (a) => b`, output: `type T = new (a) => b;`}, - {title: "ConstructorTypeNode#4", input: `type T = abstract new () => a`, output: `type T = abstract new () => a;`}, - {title: "TypeQueryNode#1", input: `type T = typeof a`, output: `type T = typeof a;`}, - {title: "TypeQueryNode#2", input: `type T = typeof a.b`, output: `type T = typeof a.b;`}, - {title: "TypeQueryNode#3", input: `type T = typeof a`, output: `type T = typeof a;`}, - {title: "TypeLiteralNode#1", input: `type T = {}`, output: `type T = {};`}, - {title: "TypeLiteralNode#2", input: `type T = {a}`, output: "type T = {\n a;\n};"}, - {title: "ArrayTypeNode", input: `type T = a[]`, output: "type T = a[];"}, - {title: "TupleTypeNode#1", input: `type T = []`, output: "type T = [\n];"}, - {title: "TupleTypeNode#2", input: `type T = [a]`, output: "type T = [\n a\n];"}, - {title: "TupleTypeNode#3", input: `type T = [a,]`, output: "type T = [\n a\n];"}, - {title: "RestTypeNode", input: `type T = [...a]`, output: "type T = [\n ...a\n];"}, - {title: "OptionalTypeNode", input: `type T = [a?]`, output: "type T = [\n a?\n];"}, - {title: "NamedTupleMember#1", input: `type T = [a: b]`, output: "type T = [\n a: b\n];"}, - {title: "NamedTupleMember#2", input: `type T = [a?: b]`, output: "type T = [\n a?: b\n];"}, - {title: "NamedTupleMember#3", input: `type T = [...a: b]`, output: "type T = [\n ...a: b\n];"}, - {title: "UnionTypeNode#1", input: `type T = a | b`, output: "type T = a | b;"}, - {title: "UnionTypeNode#2", input: `type T = a | b | c`, output: "type T = a | b | c;"}, - {title: "UnionTypeNode#3", input: `type T = | a | b`, output: "type T = a | b;"}, - {title: "IntersectionTypeNode#1", input: `type T = a & b`, output: "type T = a & b;"}, - {title: "IntersectionTypeNode#2", input: `type T = a & b & c`, output: "type T = a & b & c;"}, - {title: "IntersectionTypeNode#3", input: `type T = & a & b`, output: "type T = a & b;"}, - {title: "ConditionalTypeNode", input: `type T = a extends b ? c : d`, output: "type T = a extends b ? c : d;"}, - {title: "InferTypeNode#1", input: `type T = a extends infer b ? c : d`, output: "type T = a extends infer b ? c : d;"}, - {title: "InferTypeNode#2", input: `type T = a extends infer b extends c ? d : e`, output: "type T = a extends infer b extends c ? d : e;"}, - {title: "ParenthesizedTypeNode", input: `type T = (U)`, output: "type T = (U);"}, - {title: "ThisTypeNode", input: `type T = this`, output: "type T = this;"}, - {title: "TypeOperatorNode#1", input: `type T = keyof U`, output: "type T = keyof U;"}, - {title: "TypeOperatorNode#2", input: `type T = readonly U[]`, output: "type T = readonly U[];"}, - {title: "TypeOperatorNode#3", input: `type T = unique symbol`, output: "type T = unique symbol;"}, - {title: "IndexedAccessTypeNode", input: `type T = a[b]`, output: "type T = a[b];"}, - {title: "MappedTypeNode#1", input: `type T = { [a in b]: c }`, output: "type T = {\n [a in b]: c;\n};"}, - {title: "MappedTypeNode#2", input: `type T = { [a in b as c]: d }`, output: "type T = {\n [a in b as c]: d;\n};"}, - {title: "MappedTypeNode#3", input: `type T = { readonly [a in b]: c }`, output: "type T = {\n readonly [a in b]: c;\n};"}, - {title: "MappedTypeNode#4", input: `type T = { +readonly [a in b]: c }`, output: "type T = {\n +readonly [a in b]: c;\n};"}, - {title: "MappedTypeNode#5", input: `type T = { -readonly [a in b]: c }`, output: "type T = {\n -readonly [a in b]: c;\n};"}, - {title: "MappedTypeNode#6", input: `type T = { [a in b]?: c }`, output: "type T = {\n [a in b]?: c;\n};"}, - {title: "MappedTypeNode#7", input: `type T = { [a in b]+?: c }`, output: "type T = {\n [a in b]+?: c;\n};"}, - {title: "MappedTypeNode#8", input: `type T = { [a in b]-?: c }`, output: "type T = {\n [a in b]-?: c;\n};"}, - {title: "MappedTypeNode#9", input: `type T = { [a in b]: c; d }`, output: "type T = {\n [a in b]: c;\n d;\n};"}, - {title: "LiteralTypeNode#1", input: `type T = null`, output: "type T = null;"}, - {title: "LiteralTypeNode#2", input: `type T = true`, output: "type T = true;"}, - {title: "LiteralTypeNode#3", input: `type T = false`, output: "type T = false;"}, - {title: "LiteralTypeNode#4", input: `type T = ""`, output: "type T = \"\";"}, - {title: "LiteralTypeNode#5", input: "type T = ''", output: "type T = '';"}, - {title: "LiteralTypeNode#6", input: "type T = ``", output: "type T = ``;"}, - {title: "LiteralTypeNode#7", input: `type T = 0`, output: "type T = 0;"}, - {title: "LiteralTypeNode#8", input: `type T = 0n`, output: "type T = 0n;"}, - {title: "LiteralTypeNode#9", input: `type T = -0`, output: "type T = -0;"}, - {title: "LiteralTypeNode#10", input: `type T = -0n`, output: "type T = -0n;"}, - {title: "TemplateTypeNode#1", input: "type T = `a${b}c`", output: "type T = `a${b}c`;"}, - {title: "TemplateTypeNode#2", input: "type T = `a${b}c${d}e`", output: "type T = `a${b}c${d}e`;"}, - {title: "ImportTypeNode#1", input: `type T = import(a)`, output: "type T = import(a);"}, - {title: "ImportTypeNode#2", input: `type T = import(a).b`, output: "type T = import(a).b;"}, - {title: "ImportTypeNode#3", input: `type T = import(a).b`, output: "type T = import(a).b;"}, - {title: "ImportTypeNode#4", input: `type T = typeof import(a)`, output: "type T = typeof import(a);"}, - {title: "ImportTypeNode#5", input: `type T = typeof import(a).b`, output: "type T = typeof import(a).b;"}, - {title: "ImportTypeNode#6", input: `type T = import(a, { with: { } })`, output: "type T = import(a, { with: {} });"}, - {title: "ImportTypeNode#6", input: `type T = import(a, { with: { b: "c" } })`, output: "type T = import(a, { with: { b: \"c\" } });"}, - {title: "ImportTypeNode#7", input: `type T = import(a, { with: { "b": "c" } })`, output: "type T = import(a, { with: { \"b\": \"c\" } });"}, - {title: "PropertySignature#1", input: "interface I {a}", output: "interface I {\n a;\n}"}, - {title: "PropertySignature#2", input: "interface I {readonly a}", output: "interface I {\n readonly a;\n}"}, - {title: "PropertySignature#3", input: "interface I {\"a\"}", output: "interface I {\n \"a\";\n}"}, - {title: "PropertySignature#4", input: "interface I {'a'}", output: "interface I {\n 'a';\n}"}, - {title: "PropertySignature#5", input: "interface I {0}", output: "interface I {\n 0;\n}"}, - {title: "PropertySignature#6", input: "interface I {0n}", output: "interface I {\n 0n;\n}"}, - {title: "PropertySignature#7", input: "interface I {[a]}", output: "interface I {\n [a];\n}"}, - {title: "PropertySignature#8", input: "interface I {a?}", output: "interface I {\n a?;\n}"}, - {title: "PropertySignature#9", input: "interface I {a: b}", output: "interface I {\n a: b;\n}"}, - {title: "MethodSignature#1", input: "interface I {a()}", output: "interface I {\n a();\n}"}, - {title: "MethodSignature#2", input: "interface I {\"a\"()}", output: "interface I {\n \"a\"();\n}"}, - {title: "MethodSignature#3", input: "interface I {'a'()}", output: "interface I {\n 'a'();\n}"}, - {title: "MethodSignature#4", input: "interface I {0()}", output: "interface I {\n 0();\n}"}, - {title: "MethodSignature#5", input: "interface I {0n()}", output: "interface I {\n 0n();\n}"}, - {title: "MethodSignature#6", input: "interface I {[a]()}", output: "interface I {\n [a]();\n}"}, - {title: "MethodSignature#7", input: "interface I {a?()}", output: "interface I {\n a?();\n}"}, - {title: "MethodSignature#8", input: "interface I {a()}", output: "interface I {\n a();\n}"}, - {title: "MethodSignature#9", input: "interface I {a(): b}", output: "interface I {\n a(): b;\n}"}, - {title: "MethodSignature#10", input: "interface I {a(b): c}", output: "interface I {\n a(b): c;\n}"}, - {title: "CallSignature#1", input: "interface I {()}", output: "interface I {\n ();\n}"}, - {title: "CallSignature#2", input: "interface I {():a}", output: "interface I {\n (): a;\n}"}, - {title: "CallSignature#3", input: "interface I {(p)}", output: "interface I {\n (p);\n}"}, - {title: "CallSignature#4", input: "interface I {()}", output: "interface I {\n ();\n}"}, - {title: "ConstructSignature#1", input: "interface I {new ()}", output: "interface I {\n new ();\n}"}, - {title: "ConstructSignature#2", input: "interface I {new ():a}", output: "interface I {\n new (): a;\n}"}, - {title: "ConstructSignature#3", input: "interface I {new (p)}", output: "interface I {\n new (p);\n}"}, - {title: "ConstructSignature#4", input: "interface I {new ()}", output: "interface I {\n new ();\n}"}, - {title: "IndexSignatureDeclaration#1", input: "interface I {[a]}", output: "interface I {\n [a];\n}"}, - {title: "IndexSignatureDeclaration#2", input: "interface I {[a: b]}", output: "interface I {\n [a: b];\n}"}, - {title: "IndexSignatureDeclaration#3", input: "interface I {[a: b]: c}", output: "interface I {\n [a: b]: c;\n}"}, - {title: "PropertyDeclaration#1", input: "class C {a}", output: "class C {\n a;\n}"}, - {title: "PropertyDeclaration#2", input: "class C {readonly a}", output: "class C {\n readonly a;\n}"}, - {title: "PropertyDeclaration#3", input: "class C {static a}", output: "class C {\n static a;\n}"}, - {title: "PropertyDeclaration#4", input: "class C {accessor a}", output: "class C {\n accessor a;\n}"}, - {title: "PropertyDeclaration#5", input: "class C {\"a\"}", output: "class C {\n \"a\";\n}"}, - {title: "PropertyDeclaration#6", input: "class C {'a'}", output: "class C {\n 'a';\n}"}, - {title: "PropertyDeclaration#7", input: "class C {0}", output: "class C {\n 0;\n}"}, - {title: "PropertyDeclaration#8", input: "class C {0n}", output: "class C {\n 0n;\n}"}, - {title: "PropertyDeclaration#9", input: "class C {[a]}", output: "class C {\n [a];\n}"}, - {title: "PropertyDeclaration#10", input: "class C {#a}", output: "class C {\n #a;\n}"}, - {title: "PropertyDeclaration#11", input: "class C {a?}", output: "class C {\n a?;\n}"}, - {title: "PropertyDeclaration#12", input: "class C {a!}", output: "class C {\n a!;\n}"}, - {title: "PropertyDeclaration#13", input: "class C {a: b}", output: "class C {\n a: b;\n}"}, - {title: "PropertyDeclaration#14", input: "class C {a = b}", output: "class C {\n a = b;\n}"}, - {title: "PropertyDeclaration#15", input: "class C {@a b}", output: "class C {\n @a\n b;\n}"}, - {title: "MethodDeclaration#1", input: "class C {a()}", output: "class C {\n a();\n}"}, - {title: "MethodDeclaration#2", input: "class C {\"a\"()}", output: "class C {\n \"a\"();\n}"}, - {title: "MethodDeclaration#3", input: "class C {'a'()}", output: "class C {\n 'a'();\n}"}, - {title: "MethodDeclaration#4", input: "class C {0()}", output: "class C {\n 0();\n}"}, - {title: "MethodDeclaration#5", input: "class C {0n()}", output: "class C {\n 0n();\n}"}, - {title: "MethodDeclaration#6", input: "class C {[a]()}", output: "class C {\n [a]();\n}"}, - {title: "MethodDeclaration#7", input: "class C {#a()}", output: "class C {\n #a();\n}"}, - {title: "MethodDeclaration#8", input: "class C {a?()}", output: "class C {\n a?();\n}"}, - {title: "MethodDeclaration#9", input: "class C {a()}", output: "class C {\n a();\n}"}, - {title: "MethodDeclaration#10", input: "class C {a(): b}", output: "class C {\n a(): b;\n}"}, - {title: "MethodDeclaration#11", input: "class C {a(b): c}", output: "class C {\n a(b): c;\n}"}, - {title: "MethodDeclaration#12", input: "class C {a() {} }", output: "class C {\n a() { }\n}"}, - {title: "MethodDeclaration#13", input: "class C {@a b() {} }", output: "class C {\n @a\n b() { }\n}"}, - {title: "MethodDeclaration#14", input: "class C {static a() {} }", output: "class C {\n static a() { }\n}"}, - {title: "MethodDeclaration#15", input: "class C {async a() {} }", output: "class C {\n async a() { }\n}"}, - {title: "GetAccessorDeclaration#1", input: "class C {get a()}", output: "class C {\n get a();\n}"}, - {title: "GetAccessorDeclaration#2", input: "class C {get \"a\"()}", output: "class C {\n get \"a\"();\n}"}, - {title: "GetAccessorDeclaration#3", input: "class C {get 'a'()}", output: "class C {\n get 'a'();\n}"}, - {title: "GetAccessorDeclaration#4", input: "class C {get 0()}", output: "class C {\n get 0();\n}"}, - {title: "GetAccessorDeclaration#5", input: "class C {get 0n()}", output: "class C {\n get 0n();\n}"}, - {title: "GetAccessorDeclaration#6", input: "class C {get [a]()}", output: "class C {\n get [a]();\n}"}, - {title: "GetAccessorDeclaration#7", input: "class C {get #a()}", output: "class C {\n get #a();\n}"}, - {title: "GetAccessorDeclaration#8", input: "class C {get a(): b}", output: "class C {\n get a(): b;\n}"}, - {title: "GetAccessorDeclaration#9", input: "class C {get a(b): c}", output: "class C {\n get a(b): c;\n}"}, - {title: "GetAccessorDeclaration#10", input: "class C {get a() {} }", output: "class C {\n get a() { }\n}"}, - {title: "GetAccessorDeclaration#11", input: "class C {@a get b() {} }", output: "class C {\n @a\n get b() { }\n}"}, - {title: "GetAccessorDeclaration#12", input: "class C {static get a() {} }", output: "class C {\n static get a() { }\n}"}, - {title: "SetAccessorDeclaration#1", input: "class C {set a()}", output: "class C {\n set a();\n}"}, - {title: "SetAccessorDeclaration#2", input: "class C {set \"a\"()}", output: "class C {\n set \"a\"();\n}"}, - {title: "SetAccessorDeclaration#3", input: "class C {set 'a'()}", output: "class C {\n set 'a'();\n}"}, - {title: "SetAccessorDeclaration#4", input: "class C {set 0()}", output: "class C {\n set 0();\n}"}, - {title: "SetAccessorDeclaration#5", input: "class C {set 0n()}", output: "class C {\n set 0n();\n}"}, - {title: "SetAccessorDeclaration#6", input: "class C {set [a]()}", output: "class C {\n set [a]();\n}"}, - {title: "SetAccessorDeclaration#7", input: "class C {set #a()}", output: "class C {\n set #a();\n}"}, - {title: "SetAccessorDeclaration#8", input: "class C {set a(): b}", output: "class C {\n set a(): b;\n}"}, - {title: "SetAccessorDeclaration#9", input: "class C {set a(b): c}", output: "class C {\n set a(b): c;\n}"}, - {title: "SetAccessorDeclaration#10", input: "class C {set a() {} }", output: "class C {\n set a() { }\n}"}, - {title: "SetAccessorDeclaration#11", input: "class C {@a set b() {} }", output: "class C {\n @a\n set b() { }\n}"}, - {title: "SetAccessorDeclaration#12", input: "class C {static set a() {} }", output: "class C {\n static set a() { }\n}"}, - {title: "ConstructorDeclaration#1", input: "class C {constructor()}", output: "class C {\n constructor();\n}"}, - {title: "ConstructorDeclaration#2", input: "class C {constructor(): b}", output: "class C {\n constructor(): b;\n}"}, - {title: "ConstructorDeclaration#3", input: "class C {constructor(b): c}", output: "class C {\n constructor(b): c;\n}"}, - {title: "ConstructorDeclaration#4", input: "class C {constructor() {} }", output: "class C {\n constructor() { }\n}"}, - {title: "ConstructorDeclaration#5", input: "class C {@a constructor() {} }", output: "class C {\n constructor() { }\n}"}, - {title: "ConstructorDeclaration#6", input: "class C {private constructor() {} }", output: "class C {\n private constructor() { }\n}"}, - {title: "ClassStaticBlockDeclaration", input: "class C {static { }}", output: "class C {\n static { }\n}"}, - {title: "SemicolonClassElement#1", input: "class C {;}", output: "class C {\n ;\n}"}, - {title: "ParameterDeclaration#1", input: "function f(a)", output: "function f(a);"}, - {title: "ParameterDeclaration#2", input: "function f(a: b)", output: "function f(a: b);"}, - {title: "ParameterDeclaration#3", input: "function f(a = b)", output: "function f(a = b);"}, - {title: "ParameterDeclaration#4", input: "function f(a?)", output: "function f(a?);"}, - {title: "ParameterDeclaration#5", input: "function f(...a)", output: "function f(...a);"}, - {title: "ParameterDeclaration#6", input: "function f(this)", output: "function f(this);"}, - // {title: "ParameterDeclaration#7", input: "function f(a,)", output: "function f(a,);"}, // TODO: preserve trailing comma after Strada migration - {title: "ObjectBindingPattern#1", input: "function f({})", output: "function f({});"}, - {title: "ObjectBindingPattern#2", input: "function f({a})", output: "function f({ a });"}, - {title: "ObjectBindingPattern#3", input: "function f({a = b})", output: "function f({ a = b });"}, - {title: "ObjectBindingPattern#4", input: "function f({a: b})", output: "function f({ a: b });"}, - {title: "ObjectBindingPattern#5", input: "function f({a: b = c})", output: "function f({ a: b = c });"}, - {title: "ObjectBindingPattern#6", input: "function f({\"a\": b})", output: "function f({ \"a\": b });"}, - {title: "ObjectBindingPattern#7", input: "function f({'a': b})", output: "function f({ 'a': b });"}, - {title: "ObjectBindingPattern#8", input: "function f({0: b})", output: "function f({ 0: b });"}, - {title: "ObjectBindingPattern#9", input: "function f({[a]: b})", output: "function f({ [a]: b });"}, - {title: "ObjectBindingPattern#10", input: "function f({...a})", output: "function f({ ...a });"}, - {title: "ObjectBindingPattern#11", input: "function f({a: {}})", output: "function f({ a: {} });"}, - {title: "ObjectBindingPattern#12", input: "function f({a: []})", output: "function f({ a: [] });"}, - {title: "ArrayBindingPattern#1", input: "function f([])", output: "function f([]);"}, - {title: "ArrayBindingPattern#2", input: "function f([,])", output: "function f([,]);"}, - {title: "ArrayBindingPattern#3", input: "function f([a])", output: "function f([a]);"}, - {title: "ArrayBindingPattern#4", input: "function f([a, b])", output: "function f([a, b]);"}, - {title: "ArrayBindingPattern#5", input: "function f([a, , b])", output: "function f([a, , b]);"}, - {title: "ArrayBindingPattern#6", input: "function f([a = b])", output: "function f([a = b]);"}, - {title: "ArrayBindingPattern#7", input: "function f([...a])", output: "function f([...a]);"}, - {title: "ArrayBindingPattern#8", input: "function f([{}])", output: "function f([{}]);"}, - {title: "ArrayBindingPattern#9", input: "function f([[]])", output: "function f([[]]);"}, - {title: "TypeParameterDeclaration#1", input: "function f();", output: "function f();"}, - {title: "TypeParameterDeclaration#2", input: "function f();", output: "function f();"}, - {title: "TypeParameterDeclaration#3", input: "function f();", output: "function f();"}, - {title: "TypeParameterDeclaration#4", input: "function f();", output: "function f();"}, - {title: "TypeParameterDeclaration#5", input: "function f();", output: "function f();"}, - {title: "TypeParameterDeclaration#6", input: "function f();", output: "function f();"}, - // {title: "TypeParameterDeclaration#7", input: "function f();", output: "function f();"}, // TODO: preserve trailing comma after Strada migration - {title: "JsxElement1", input: "", output: ";", jsx: true}, - {title: "JsxElement2", input: "", output: ";", jsx: true}, - {title: "JsxElement3", input: "", output: ";", jsx: true}, - {title: "JsxElement4", input: "", output: ";", jsx: true}, - {title: "JsxElement5", input: ">", output: ">;", jsx: true}, - {title: "JsxElement6", input: "", output: ";", jsx: true}, - {title: "JsxElement7", input: "b", output: "b;", jsx: true}, - {title: "JsxElement8", input: "{b}", output: "{b};", jsx: true}, - {title: "JsxElement9", input: "", output: ";", jsx: true}, - {title: "JsxElement10", input: "", output: ";", jsx: true}, - {title: "JsxElement11", input: "<>", output: "<>;", jsx: true}, - {title: "JsxSelfClosingElement1", input: "", output: ";", jsx: true}, - {title: "JsxSelfClosingElement2", input: "", output: ";", jsx: true}, - {title: "JsxSelfClosingElement3", input: "", output: ";", jsx: true}, - {title: "JsxSelfClosingElement4", input: "", output: ";", jsx: true}, - {title: "JsxSelfClosingElement5", input: " />", output: " />;", jsx: true}, - {title: "JsxSelfClosingElement6", input: "", output: ";", jsx: true}, - {title: "JsxFragment1", input: "<>", output: "<>;", jsx: true}, - {title: "JsxFragment2", input: "<>b", output: "<>b;", jsx: true}, - {title: "JsxFragment3", input: "<>{b}", output: "<>{b};", jsx: true}, - {title: "JsxFragment4", input: "<>", output: "<>;", jsx: true}, - {title: "JsxFragment5", input: "<>", output: "<>;", jsx: true}, - {title: "JsxFragment6", input: "<><>", output: "<><>;", jsx: true}, - {title: "JsxAttribute1", input: "", output: ";", jsx: true}, - {title: "JsxAttribute2", input: "", output: ";", jsx: true}, - {title: "JsxAttribute3", input: "", output: ";", jsx: true}, - {title: "JsxAttribute4", input: "", output: ";", jsx: true}, - {title: "JsxAttribute5", input: "", output: ";", jsx: true}, - {title: "JsxAttribute6", input: "/>", output: "/>;", jsx: true}, - {title: "JsxAttribute7", input: "/>", output: "/>;", jsx: true}, - {title: "JsxAttribute8", input: "/>", output: "/>;", jsx: true}, - {title: "JsxSpreadAttribute", input: "", output: ";", jsx: true}, - } - - for _, rec := range data { - t.Run(rec.title, func(t *testing.T) { - t.Parallel() - file := parsetestutil.ParseTypeScript(rec.input, rec.jsx) - parsetestutil.CheckDiagnostics(t, file) - emittestutil.CheckEmit(t, nil, file, rec.output) - }) - } -} - -func TestParenthesizeDecorator(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( - []*ast.Node{ - factory.NewClassDeclaration( - factory.NewModifierList( - []*ast.Node{ - factory.NewDecorator( - factory.NewBinaryExpression( - nil, /*modifiers*/ - factory.NewIdentifier("a"), - nil, /*typeNode*/ - factory.NewToken(ast.KindPlusToken), - factory.NewIdentifier("b"), - ), - ), - }, - ), - factory.NewIdentifier("C"), - nil, - nil, - factory.NewNodeList([]*ast.Node{}), - ), - }, - ), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "@(a + b)\nclass C {\n}") -} - -func TestParenthesizeComputedPropertyName(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( - []*ast.Node{ - factory.NewClassDeclaration( - nil, /*modifiers*/ - factory.NewIdentifier("C"), - nil, /*typeParameters*/ - nil, /*heritageClauses*/ - factory.NewNodeList([]*ast.Node{ - factory.NewPropertyDeclaration( - nil, /*modifiers*/ - factory.NewComputedPropertyName( - // will be parenthesized on emit: - factory.NewBinaryExpression( - nil, /*modifiers*/ - factory.NewIdentifier("a"), - nil, /*typeNode*/ - factory.NewToken(ast.KindCommaToken), - factory.NewIdentifier("b"), - ), - ), - nil, /*postfixToken*/ - nil, /*typeNode*/ - nil, /*initializer*/ - ), - }), - ), - }, - ), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "class C {\n [(a, b)];\n}") -} - -func TestParenthesizeArrayLiteral(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( - []*ast.Node{ - factory.NewExpressionStatement( - factory.NewArrayLiteralExpression( - factory.NewNodeList( - []*ast.Node{ - // will be parenthesized on emit: - factory.NewBinaryExpression( - nil, /*modifiers*/ - factory.NewIdentifier("a"), - nil, /*typeNode*/ - factory.NewToken(ast.KindCommaToken), - factory.NewIdentifier("b"), - ), - }, - ), - false, /*multiLine*/ - ), - ), - }, - ), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "[(a, b)];") -} - -func TestParenthesizePropertyAccess1(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( - []*ast.Node{ - factory.NewExpressionStatement( - factory.NewPropertyAccessExpression( - // will be parenthesized on emit: - factory.NewBinaryExpression( - nil, /*modifiers*/ - factory.NewIdentifier("a"), - nil, /*typeNode*/ - factory.NewToken(ast.KindCommaToken), - factory.NewIdentifier("b"), - ), - nil, /*questionDotToken*/ - factory.NewIdentifier("c"), - ast.NodeFlagsNone, - ), - ), - }, - ), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "(a, b).c;") -} - -func TestParenthesizePropertyAccess2(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( - []*ast.Node{ - factory.NewExpressionStatement( - factory.NewPropertyAccessExpression( - // will be parenthesized on emit: - factory.NewPropertyAccessExpression( - factory.NewIdentifier("a"), - factory.NewToken(ast.KindQuestionDotToken), - factory.NewIdentifier("b"), - ast.NodeFlagsOptionalChain, - ), - nil, /*questionDotToken*/ - factory.NewIdentifier("c"), - ast.NodeFlagsNone, - ), - ), - }, - ), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "(a?.b).c;") -} - -func TestParenthesizePropertyAccess3(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( - []*ast.Node{ - factory.NewExpressionStatement( - factory.NewPropertyAccessExpression( - // will be parenthesized on emit: - factory.NewNewExpression( - factory.NewIdentifier("a"), - nil, /*typeArguments*/ - nil, /*arguments*/ - ), - nil, /*questionDotToken*/ - factory.NewIdentifier("b"), - ast.NodeFlagsNone, - ), - ), - }, - ), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "(new a).b;") -} - -func TestParenthesizeElementAccess1(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( - []*ast.Node{ - factory.NewExpressionStatement( - factory.NewElementAccessExpression( - // will be parenthesized on emit: - factory.NewBinaryExpression( - nil, /*modifiers*/ - factory.NewIdentifier("a"), - nil, /*typeNode*/ - factory.NewToken(ast.KindCommaToken), - factory.NewIdentifier("b"), - ), - nil, /*questionDotToken*/ - factory.NewIdentifier("c"), - ast.NodeFlagsNone, - ), - ), - }, - ), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "(a, b)[c];") -} - -func TestParenthesizeElementAccess2(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( - []*ast.Node{ - factory.NewExpressionStatement( - factory.NewElementAccessExpression( - // will be parenthesized on emit: - factory.NewPropertyAccessExpression( - factory.NewIdentifier("a"), - factory.NewToken(ast.KindQuestionDotToken), - factory.NewIdentifier("b"), - ast.NodeFlagsOptionalChain, - ), - nil, /*questionDotToken*/ - factory.NewIdentifier("c"), - ast.NodeFlagsNone, - ), - ), - }, - ), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "(a?.b)[c];") -} - -func TestParenthesizeElementAccess3(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( - []*ast.Node{ - factory.NewExpressionStatement( - factory.NewElementAccessExpression( - // will be parenthesized on emit: - factory.NewNewExpression( - factory.NewIdentifier("a"), - nil, /*typeArguments*/ - nil, /*arguments*/ - ), - nil, /*questionDotToken*/ - factory.NewIdentifier("b"), - ast.NodeFlagsNone, - ), - ), - }, - ), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "(new a)[b];") -} - -func TestParenthesizeCall1(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( - []*ast.Node{ - factory.NewExpressionStatement( - factory.NewCallExpression( - // will be parenthesized on emit: - factory.NewBinaryExpression( - nil, /*modifiers*/ - factory.NewIdentifier("a"), - nil, /*typeNode*/ - factory.NewToken(ast.KindCommaToken), - factory.NewIdentifier("b"), - ), - nil, /*questionDotToken*/ - nil, /*typeArguments*/ - factory.NewNodeList([]*ast.Node{}), - ast.NodeFlagsNone, - ), - ), - }, - ), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "(a, b)();") -} - -func TestParenthesizeCall2(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( - []*ast.Node{ - factory.NewExpressionStatement( - factory.NewCallExpression( - // will be parenthesized on emit: - factory.NewPropertyAccessExpression( - factory.NewIdentifier("a"), - factory.NewToken(ast.KindQuestionDotToken), - factory.NewIdentifier("b"), - ast.NodeFlagsOptionalChain, - ), - nil, /*questionDotToken*/ - nil, /*typeArguments*/ - factory.NewNodeList([]*ast.Node{}), - ast.NodeFlagsNone, - ), - ), - }, - ), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "(a?.b)();") -} - -func TestParenthesizeCall3(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( - []*ast.Node{ - factory.NewExpressionStatement( - factory.NewCallExpression( - // will be parenthesized on emit: - factory.NewNewExpression( - factory.NewIdentifier("C"), - nil, /*typeArguments*/ - nil, /*arguments*/ - ), - nil, /*questionDotToken*/ - nil, /*typeArguments*/ - factory.NewNodeList([]*ast.Node{}), - ast.NodeFlagsNone, - ), - ), - }, - ), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "(new C)();") -} - -func TestParenthesizeCall4(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( - []*ast.Node{ - factory.NewExpressionStatement( - factory.NewCallExpression( - factory.NewIdentifier("a"), - nil, /*questionDotToken*/ - nil, /*typeArguments*/ - factory.NewNodeList([]*ast.Node{ - factory.NewBinaryExpression( - nil, /*modifiers*/ - factory.NewIdentifier("b"), - nil, /*typeNode*/ - factory.NewToken(ast.KindCommaToken), - factory.NewIdentifier("c"), - ), - }), - ast.NodeFlagsNone, - ), - ), - }, - ), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "a((b, c));") -} - -func TestParenthesizeNew1(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( - []*ast.Node{ - factory.NewExpressionStatement( - factory.NewNewExpression( - // will be parenthesized on emit: - factory.NewBinaryExpression( - nil, /*modifiers*/ - factory.NewIdentifier("a"), - nil, /*typeNode*/ - factory.NewToken(ast.KindCommaToken), - factory.NewIdentifier("b"), - ), - nil, /*typeArguments*/ - factory.NewNodeList([]*ast.Node{}), - ), - ), - }, - ), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "new (a, b)();") -} - -func TestParenthesizeNew2(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( - []*ast.Node{ - factory.NewExpressionStatement( - factory.NewNewExpression( - // will be parenthesized on emit: - factory.NewCallExpression( - factory.NewIdentifier("C"), - nil, /*questionDotToken*/ - nil, /*typeArguments*/ - factory.NewNodeList([]*ast.Node{}), - ast.NodeFlagsNone, - ), - nil, /*typeArguments*/ - nil, /*arguments*/ - ), - ), - }, - ), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "new (C());") -} - -func TestParenthesizeNew3(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( - []*ast.Node{ - factory.NewExpressionStatement( - factory.NewNewExpression( - factory.NewIdentifier("C"), - nil, /*typeArguments*/ - factory.NewNodeList([]*ast.Node{ - factory.NewBinaryExpression( - nil, /*modifiers*/ - factory.NewIdentifier("a"), - nil, /*typeNode*/ - factory.NewToken(ast.KindCommaToken), - factory.NewIdentifier("b"), - ), - }), - ), - ), - }, - ), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "new C((a, b));") -} - -func TestParenthesizeTaggedTemplate1(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( - []*ast.Node{ - factory.NewExpressionStatement( - factory.NewTaggedTemplateExpression( - // will be parenthesized on emit: - factory.NewBinaryExpression( - nil, /*modifiers*/ - factory.NewIdentifier("a"), - nil, /*typeNode*/ - factory.NewToken(ast.KindCommaToken), - factory.NewIdentifier("b"), - ), - nil, /*questionDotToken*/ - nil, /*typeArguments*/ - factory.NewNoSubstitutionTemplateLiteral(""), - ast.NodeFlagsNone, - ), - ), - }, - ), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "(a, b) ``;") -} - -func TestParenthesizeTaggedTemplate2(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( - []*ast.Node{ - factory.NewExpressionStatement( - factory.NewTaggedTemplateExpression( - // will be parenthesized on emit: - factory.NewPropertyAccessExpression( - factory.NewIdentifier("a"), - factory.NewToken(ast.KindQuestionDotToken), - factory.NewIdentifier("b"), - ast.NodeFlagsOptionalChain, - ), - nil, /*questionDotToken*/ - nil, /*typeArguments*/ - factory.NewNoSubstitutionTemplateLiteral(""), - ast.NodeFlagsNone, - ), - ), - }, - ), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "(a?.b) ``;") -} - -func TestParenthesizeTypeAssertion1(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( - []*ast.Node{ - factory.NewExpressionStatement( - factory.NewTypeAssertion( - factory.NewTypeReferenceNode( - factory.NewIdentifier("T"), - nil, /*typeArguments*/ - ), - // will be parenthesized on emit: - factory.NewBinaryExpression( - nil, /*modifiers*/ - factory.NewIdentifier("a"), - nil, /*typeNode*/ - factory.NewToken(ast.KindPlusToken), - factory.NewIdentifier("b"), - ), - ), - ), - }, - ), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "(a + b);") -} - -func TestParenthesizeArrowFunction1(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( - []*ast.Node{ - factory.NewExpressionStatement( - factory.NewArrowFunction( - nil, /*modifiers*/ - nil, /*typeParameters*/ - factory.NewNodeList([]*ast.Node{}), - nil, /*returnType*/ - nil, /*fullSignature*/ - factory.NewToken(ast.KindEqualsGreaterThanToken), - // will be parenthesized on emit: - factory.NewObjectLiteralExpression( - factory.NewNodeList([]*ast.Node{}), - false, /*multiLine*/ - ), - ), - ), - }, - ), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "() => ({});") -} - -func TestParenthesizeArrowFunction2(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( - []*ast.Node{ - factory.NewExpressionStatement( - factory.NewArrowFunction( - nil, /*modifiers*/ - nil, /*typeParameters*/ - factory.NewNodeList([]*ast.Node{}), - nil, /*returnType*/ - nil, /*fullSignature*/ - factory.NewToken(ast.KindEqualsGreaterThanToken), - // will be parenthesized on emit: - factory.NewPropertyAccessExpression( - factory.NewObjectLiteralExpression( - factory.NewNodeList([]*ast.Node{}), - false, /*multiLine*/ - ), - nil, /*questionDotToken*/ - factory.NewIdentifier("a"), - ast.NodeFlagsNone, - ), - ), - ), - }, - ), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "() => ({}.a);") -} - -func TestParenthesizeDelete(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( - []*ast.Node{ - factory.NewExpressionStatement( - factory.NewDeleteExpression( - // will be parenthesized on emit: - factory.NewBinaryExpression( - nil, /*modifiers*/ - factory.NewIdentifier("a"), - nil, /*typeNode*/ - factory.NewToken(ast.KindPlusToken), - factory.NewIdentifier("b"), - ), - ), - ), - }, - ), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "delete (a + b);") -} - -func TestParenthesizeVoid(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( - []*ast.Node{ - factory.NewExpressionStatement( - factory.NewVoidExpression( - // will be parenthesized on emit: - factory.NewBinaryExpression( - nil, /*modifiers*/ - factory.NewIdentifier("a"), - nil, /*typeNode*/ - factory.NewToken(ast.KindPlusToken), - factory.NewIdentifier("b"), - ), - ), - ), - }, - ), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "void (a + b);") -} - -func TestParenthesizeTypeOf(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( - []*ast.Node{ - factory.NewExpressionStatement( - factory.NewTypeOfExpression( - // will be parenthesized on emit: - factory.NewBinaryExpression( - nil, /*modifiers*/ - factory.NewIdentifier("a"), - nil, /*typeNode*/ - factory.NewToken(ast.KindPlusToken), - factory.NewIdentifier("b"), - ), - ), - ), - }, - ), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "typeof (a + b);") -} - -func TestParenthesizeAwait(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( - []*ast.Node{ - factory.NewExpressionStatement( - factory.NewAwaitExpression( - // will be parenthesized on emit: - factory.NewBinaryExpression( - nil, /*modifiers*/ - factory.NewIdentifier("a"), - nil, /*typeNode*/ - factory.NewToken(ast.KindPlusToken), - factory.NewIdentifier("b"), - ), - ), - ), - }, - ), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "await (a + b);") -} - -func isBinaryOperator(token ast.Kind) bool { - switch token { - case ast.KindCommaToken, - ast.KindLessThanToken, - ast.KindGreaterThanToken, - ast.KindLessThanEqualsToken, - ast.KindGreaterThanEqualsToken, - ast.KindEqualsEqualsToken, - ast.KindEqualsEqualsEqualsToken, - ast.KindExclamationEqualsToken, - ast.KindExclamationEqualsEqualsToken, - ast.KindPlusToken, - ast.KindMinusToken, - ast.KindAsteriskToken, - ast.KindAsteriskAsteriskToken, - ast.KindSlashToken, - ast.KindPercentToken, - ast.KindLessThanLessThanToken, - ast.KindGreaterThanGreaterThanToken, - ast.KindGreaterThanGreaterThanGreaterThanToken, - ast.KindAmpersandToken, - ast.KindBarToken, - ast.KindCaretToken, - ast.KindAmpersandAmpersandToken, - ast.KindBarBarToken, - ast.KindQuestionQuestionToken, - ast.KindEqualsToken, - ast.KindPlusEqualsToken, - ast.KindMinusEqualsToken, - ast.KindAsteriskEqualsToken, - ast.KindAsteriskAsteriskEqualsToken, - ast.KindSlashEqualsToken, - ast.KindPercentEqualsToken, - ast.KindLessThanLessThanEqualsToken, - ast.KindGreaterThanGreaterThanEqualsToken, - ast.KindGreaterThanGreaterThanGreaterThanEqualsToken, - ast.KindAmpersandEqualsToken, - ast.KindBarEqualsToken, - ast.KindBarBarEqualsToken, - ast.KindAmpersandAmpersandEqualsToken, - ast.KindQuestionQuestionEqualsToken, - ast.KindCaretEqualsToken, - ast.KindInKeyword, - ast.KindInstanceOfKeyword: - return true - } - return false -} - -func makeSide(label string, kind ast.Kind, factory *ast.NodeFactory) *ast.Node { - switch { - case kind == ast.KindIdentifier || kind == ast.KindUnknown: - return factory.NewIdentifier(label) - case kind == ast.KindArrowFunction: - return factory.NewArrowFunction( - nil, /*modifiers*/ - nil, /*typeParameters*/ - factory.NewNodeList([]*ast.Node{}), - nil, /*returnType*/ - nil, /*fullSignature*/ - factory.NewToken(ast.KindEqualsGreaterThanToken), - factory.NewBlock(factory.NewNodeList([]*ast.Node{}), false /*multiLine*/), - ) - case isBinaryOperator(kind): - return factory.NewBinaryExpression( - nil, /*modifiers*/ - factory.NewIdentifier(label+"l"), - nil, /*typeNode*/ - factory.NewToken(kind), - factory.NewIdentifier(label+"r"), - ) - default: - panic("unsupported kind") - } -} - -func TestParenthesizeBinary(t *testing.T) { - t.Parallel() - - data := []struct { - left ast.Kind - operator ast.Kind - right ast.Kind - output string - }{ - {operator: ast.KindCommaToken, output: "l, r"}, - {operator: ast.KindCommaToken, left: ast.KindPlusToken, output: "ll + lr, r"}, - {operator: ast.KindAsteriskToken, left: ast.KindPlusToken, output: "(ll + lr) * r"}, - {operator: ast.KindAsteriskToken, right: ast.KindPlusToken, output: "l * (rl + rr)"}, - {operator: ast.KindPlusToken, left: ast.KindAsteriskToken, output: "ll * lr + r"}, - {operator: ast.KindPlusToken, right: ast.KindAsteriskToken, output: "l + rl * rr"}, - {operator: ast.KindSlashToken, left: ast.KindAsteriskToken, output: "ll * lr / r"}, - {operator: ast.KindSlashToken, left: ast.KindAsteriskAsteriskToken, output: "ll ** lr / r"}, - {operator: ast.KindAsteriskAsteriskToken, left: ast.KindAsteriskToken, output: "(ll * lr) ** r"}, - {operator: ast.KindAsteriskAsteriskToken, left: ast.KindAsteriskAsteriskToken, output: "(ll ** lr) ** r"}, - {operator: ast.KindAsteriskToken, right: ast.KindAsteriskToken, output: "l * rl * rr"}, - {operator: ast.KindBarToken, right: ast.KindBarToken, output: "l | rl | rr"}, - {operator: ast.KindAmpersandToken, right: ast.KindAmpersandToken, output: "l & rl & rr"}, - {operator: ast.KindCaretToken, right: ast.KindCaretToken, output: "l ^ rl ^ rr"}, - {operator: ast.KindAmpersandAmpersandToken, right: ast.KindArrowFunction, output: "l && (() => { })"}, - } - for _, rec := range data { - t.Run(rec.output, func(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( - []*ast.Node{ - factory.NewExpressionStatement( - factory.NewBinaryExpression( - nil, /*modifiers*/ - makeSide("l", rec.left, &factory), - nil, /*typeNode*/ - factory.NewToken(rec.operator), - makeSide("r", rec.right, &factory), - ), - ), - }, - ), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), rec.output+";") - }) - } -} - -func TestParenthesizeConditional1(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( - []*ast.Node{ - factory.NewExpressionStatement( - factory.NewConditionalExpression( - // will be parenthesized on emit: - factory.NewBinaryExpression( - nil, /*modifiers*/ - factory.NewIdentifier("a"), - nil, /*typeNode*/ - factory.NewToken(ast.KindCommaToken), - factory.NewIdentifier("b"), - ), - factory.NewToken(ast.KindQuestionToken), - factory.NewIdentifier("c"), - factory.NewToken(ast.KindColonToken), - factory.NewIdentifier("d"), - ), - ), - }, - ), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "(a, b) ? c : d;") -} - -func TestParenthesizeConditional2(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( - []*ast.Node{ - factory.NewExpressionStatement( - factory.NewConditionalExpression( - // will be parenthesized on emit: - factory.NewBinaryExpression( - nil, /*modifiers*/ - factory.NewIdentifier("a"), - nil, /*typeNode*/ - factory.NewToken(ast.KindEqualsToken), - factory.NewIdentifier("b"), - ), - factory.NewToken(ast.KindQuestionToken), - factory.NewIdentifier("c"), - factory.NewToken(ast.KindColonToken), - factory.NewIdentifier("d"), - ), - ), - }, - ), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "(a = b) ? c : d;") -} - -func TestParenthesizeConditional3(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( - []*ast.Node{ - factory.NewExpressionStatement( - factory.NewConditionalExpression( - // will be parenthesized on emit: - factory.NewArrowFunction( - nil, /*modifiers*/ - nil, /*typeParameters*/ - factory.NewNodeList([]*ast.Node{}), - nil, /*returnType*/ - nil, /*fullSignature*/ - factory.NewToken(ast.KindEqualsGreaterThanToken), - factory.NewBlock( - factory.NewNodeList([]*ast.Node{}), - false, /*multiLine*/ - ), - ), - factory.NewToken(ast.KindQuestionToken), - factory.NewIdentifier("a"), - factory.NewToken(ast.KindColonToken), - factory.NewIdentifier("b"), - ), - ), - }, - ), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "(() => { }) ? a : b;") -} - -func TestParenthesizeConditional4(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( - []*ast.Node{ - factory.NewExpressionStatement( - factory.NewConditionalExpression( - // will be parenthesized on emit: - factory.NewYieldExpression(nil, nil), - factory.NewToken(ast.KindQuestionToken), - factory.NewIdentifier("a"), - factory.NewToken(ast.KindColonToken), - factory.NewIdentifier("b"), - ), - ), - }, - ), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "(yield) ? a : b;") -} - -func TestParenthesizeConditional5(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( - []*ast.Node{ - factory.NewExpressionStatement( - factory.NewConditionalExpression( - factory.NewIdentifier("a"), - factory.NewToken(ast.KindQuestionToken), - // will be parenthesized on emit: - factory.NewBinaryExpression( - nil, /*modifiers*/ - factory.NewIdentifier("b"), - nil, /*typeNode*/ - factory.NewToken(ast.KindCommaToken), - factory.NewIdentifier("c"), - ), - factory.NewToken(ast.KindColonToken), - factory.NewIdentifier("d"), - ), - ), - }, - ), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "a ? (b, c) : d;") -} - -func TestParenthesizeConditional6(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( - []*ast.Node{ - factory.NewExpressionStatement( - factory.NewConditionalExpression( - factory.NewIdentifier("a"), - factory.NewToken(ast.KindQuestionToken), - factory.NewIdentifier("b"), - factory.NewToken(ast.KindColonToken), - // will be parenthesized on emit: - factory.NewBinaryExpression( - nil, /*modifiers*/ - factory.NewIdentifier("c"), - nil, /*typeNode*/ - factory.NewToken(ast.KindCommaToken), - factory.NewIdentifier("d"), - ), - ), - ), - }, - ), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "a ? b : (c, d);") -} - -func TestParenthesizeYield1(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( - []*ast.Node{ - factory.NewExpressionStatement( - factory.NewYieldExpression( - nil, /*asteriskToken*/ - // will be parenthesized on emit: - factory.NewBinaryExpression( - nil, /*modifiers*/ - factory.NewIdentifier("a"), - nil, /*typeNode*/ - factory.NewToken(ast.KindCommaToken), - factory.NewIdentifier("b"), - ), - ), - ), - }, - ), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "yield (a, b);") -} - -// !!! test ASI avoidance from emitExpressionNoASI -////func TestParenthesizeYield2(t *testing.T) { -////} - -func TestParenthesizeSpreadElement1(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( - []*ast.Node{ - factory.NewExpressionStatement( - factory.NewArrayLiteralExpression( - factory.NewNodeList( - []*ast.Node{ - factory.NewSpreadElement( - // will be parenthesized on emit: - factory.NewBinaryExpression( - nil, /*modifiers*/ - factory.NewIdentifier("a"), - nil, /*typeNode*/ - factory.NewToken(ast.KindCommaToken), - factory.NewIdentifier("b"), - ), - ), - }, - ), - false, /*multiLine*/ - ), - ), - }, - ), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "[...(a, b)];") -} - -func TestParenthesizeSpreadElement2(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( - []*ast.Node{ - factory.NewExpressionStatement( - factory.NewCallExpression( - factory.NewIdentifier("a"), - nil, /*questionDotToken*/ - nil, /*typeArguments*/ - factory.NewNodeList( - []*ast.Node{ - factory.NewSpreadElement( - // will be parenthesized on emit: - factory.NewBinaryExpression( - nil, /*modifiers*/ - factory.NewIdentifier("b"), - nil, /*typeNode*/ - factory.NewToken(ast.KindCommaToken), - factory.NewIdentifier("c"), - ), - ), - }, - ), - ast.NodeFlagsNone, - ), - ), - }, - ), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "a(...(b, c));") -} - -func TestParenthesizeSpreadElement3(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( - []*ast.Node{ - factory.NewExpressionStatement( - factory.NewNewExpression( - factory.NewIdentifier("a"), - nil, /*typeArguments*/ - factory.NewNodeList( - []*ast.Node{ - factory.NewSpreadElement( - // will be parenthesized on emit: - factory.NewBinaryExpression( - nil, /*modifiers*/ - factory.NewIdentifier("b"), - nil, /*typeNode*/ - factory.NewToken(ast.KindCommaToken), - factory.NewIdentifier("c"), - ), - ), - }, - ), - ), - ), - }, - ), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "new a(...(b, c));") -} - -func TestParenthesizeExpressionWithTypeArguments(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( - []*ast.Node{ - factory.NewExpressionStatement( - factory.NewExpressionWithTypeArguments( - // will be parenthesized on emit: - factory.NewBinaryExpression( - nil, /*modifiers*/ - factory.NewIdentifier("a"), - nil, /*typeNode*/ - factory.NewToken(ast.KindCommaToken), - factory.NewIdentifier("b"), - ), - factory.NewNodeList( - []*ast.Node{ - factory.NewTypeReferenceNode( - factory.NewIdentifier("c"), - nil, - ), - }, - ), - ), - ), - }, - ), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "(a, b);") -} - -func TestParenthesizeAsExpression(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( - []*ast.Node{ - factory.NewExpressionStatement( - factory.NewAsExpression( - // will be parenthesized on emit: - factory.NewBinaryExpression( - nil, /*modifiers*/ - factory.NewIdentifier("a"), - nil, /*typeNode*/ - factory.NewToken(ast.KindCommaToken), - factory.NewIdentifier("b"), - ), - factory.NewTypeReferenceNode( - factory.NewIdentifier("c"), - nil, - ), - ), - ), - }, - ), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "(a, b) as c;") -} - -func TestParenthesizeSatisfiesExpression(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( - []*ast.Node{ - factory.NewExpressionStatement( - factory.NewSatisfiesExpression( - // will be parenthesized on emit: - factory.NewBinaryExpression( - nil, /*modifiers*/ - factory.NewIdentifier("a"), - nil, /*typeNode*/ - factory.NewToken(ast.KindCommaToken), - factory.NewIdentifier("b"), - ), - factory.NewTypeReferenceNode( - factory.NewIdentifier("c"), - nil, - ), - ), - ), - }, - ), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "(a, b) satisfies c;") -} - -func TestParenthesizeNonNullExpression(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( - []*ast.Node{ - factory.NewExpressionStatement( - factory.NewNonNullExpression( - // will be parenthesized on emit: - factory.NewBinaryExpression( - nil, /*modifiers*/ - factory.NewIdentifier("a"), - nil, /*typeNode*/ - factory.NewToken(ast.KindCommaToken), - factory.NewIdentifier("b"), - ), - ast.NodeFlagsNone, - ), - ), - }, - ), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "(a, b)!;") -} - -func TestParenthesizeExpressionStatement1(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( - []*ast.Node{ - factory.NewExpressionStatement( - factory.NewObjectLiteralExpression( - factory.NewNodeList( - []*ast.Node{}, - ), - false, /*multiLine*/ - ), - ), - }, - ), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "({});") -} - -func TestParenthesizeExpressionStatement2(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( - []*ast.Node{ - factory.NewExpressionStatement( - factory.NewFunctionExpression( - nil, /*modifiers*/ - nil, /*asteriskToken*/ - nil, /*name*/ - nil, /*typeParameters*/ - factory.NewNodeList( - []*ast.Node{}, - ), - nil, /*returnType*/ - nil, /*fullSignature*/ - factory.NewBlock( - factory.NewNodeList([]*ast.Node{}), - false, /*multiLine*/ - ), - ), - ), - }, - ), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "(function () { });") -} - -func TestParenthesizeExpressionStatement3(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( - []*ast.Node{ - factory.NewExpressionStatement( - factory.NewClassExpression( - nil, /*modifiers*/ - nil, /*name*/ - nil, /*typeParameters*/ - nil, /*heritageClauses*/ - factory.NewNodeList( - []*ast.Node{}, - ), - ), - ), - }, - ), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "(class {\n});") -} - -func TestParenthesizeExpressionDefault1(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( - []*ast.Node{ - factory.NewExportAssignment( - nil, /*modifiers*/ - false, /*isExportEquals*/ - nil, /*typeNode*/ - // will be parenthesized on emit: - factory.NewClassExpression( - nil, /*modifiers*/ - nil, /*name*/ - nil, /*typeParameters*/ - nil, /*heritageClauses*/ - factory.NewNodeList( - []*ast.Node{}, - ), - ), - ), - }, - ), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "export default (class {\n});") -} - -func TestParenthesizeExpressionDefault2(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( - []*ast.Node{ - factory.NewExportAssignment( - nil, /*modifiers*/ - false, /*isExportEquals*/ - nil, /*typeNode*/ - // will be parenthesized on emit: - factory.NewFunctionExpression( - nil, /*modifiers*/ - nil, /*asteriskToken*/ - nil, /*name*/ - nil, /*typeParameters*/ - factory.NewNodeList( - []*ast.Node{}, - ), - nil, /*returnType*/ - nil, /*fullSignature*/ - factory.NewBlock( - factory.NewNodeList( - []*ast.Node{}, - ), - false, /*multiLine*/ - ), - ), - ), - }, - ), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "export default (function () { });") -} - -func TestParenthesizeExpressionDefault3(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( - []*ast.Node{ - factory.NewExportAssignment( - nil, /*modifiers*/ - false, /*isExportEquals*/ - nil, /*typeNode*/ - // will be parenthesized on emit: - factory.NewBinaryExpression( - nil, /*modifiers*/ - factory.NewIdentifier("a"), - nil, /*typeNode*/ - factory.NewToken(ast.KindCommaToken), - factory.NewIdentifier("b"), - ), - ), - }, - ), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "export default (a, b);") -} - -func TestParenthesizeArrayType(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( - []*ast.Node{ - factory.NewTypeAliasDeclaration( - nil, /*modifiers*/ - factory.NewIdentifier("_"), /*name*/ - nil, /*typeParameters*/ - factory.NewArrayTypeNode( - // will be parenthesized on emit: - factory.NewUnionTypeNode( - factory.NewNodeList( - []*ast.Node{ - factory.NewTypeReferenceNode(factory.NewIdentifier("a"), nil /*typeArguments*/), - factory.NewTypeReferenceNode(factory.NewIdentifier("b"), nil /*typeArguments*/), - }, - ), - ), - ), - ), - }, - ), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "type _ = (a | b)[];") -} - -func TestParenthesizeOptionalType(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( - []*ast.Node{ - factory.NewTypeAliasDeclaration( - nil, /*modifiers*/ - factory.NewIdentifier("_"), /*name*/ - nil, /*typeParameters*/ - factory.NewTupleTypeNode( - factory.NewNodeList( - []*ast.Node{ - factory.NewOptionalTypeNode( - // will be parenthesized on emit: - factory.NewUnionTypeNode( - factory.NewNodeList( - []*ast.Node{ - factory.NewTypeReferenceNode(factory.NewIdentifier("a"), nil /*typeArguments*/), - factory.NewTypeReferenceNode(factory.NewIdentifier("b"), nil /*typeArguments*/), - }, - ), - ), - ), - }, - ), - ), - ), - }, - ), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "type _ = [\n (a | b)?\n];") -} - -func TestParenthesizeUnionType1(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( - []*ast.Node{ - factory.NewTypeAliasDeclaration( - nil, /*modifiers*/ - factory.NewIdentifier("_"), /*name*/ - nil, /*typeParameters*/ - factory.NewUnionTypeNode( - factory.NewNodeList( - []*ast.Node{ - factory.NewTypeReferenceNode(factory.NewIdentifier("a"), nil /*typeArguments*/), - // will be parenthesized on emit: - factory.NewFunctionTypeNode( - nil, /*typeParameters*/ - factory.NewNodeList( - []*ast.Node{}, - ), - factory.NewTypeReferenceNode(factory.NewIdentifier("b"), nil /*typeArguments*/), - ), - }, - ), - ), - ), - }, - ), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "type _ = a | (() => b);") -} - -func TestParenthesizeUnionType2(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( - []*ast.Node{ - factory.NewTypeAliasDeclaration( - nil, /*modifiers*/ - factory.NewIdentifier("_"), /*name*/ - nil, /*typeParameters*/ - factory.NewUnionTypeNode( - factory.NewNodeList( - []*ast.Node{ - // will be parenthesized on emit: - factory.NewInferTypeNode( - factory.NewTypeParameterDeclaration( - nil, - factory.NewIdentifier("a"), - factory.NewTypeReferenceNode(factory.NewIdentifier("b"), nil /*typeArguments*/), - nil, /*defaultType*/ - ), - ), - factory.NewTypeReferenceNode(factory.NewIdentifier("c"), nil /*typeArguments*/), - }, - ), - ), - ), - }, - ), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "type _ = (infer a extends b) | c;") -} - -func TestParenthesizeIntersectionType(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( - []*ast.Node{ - factory.NewTypeAliasDeclaration( - nil, /*modifiers*/ - factory.NewIdentifier("_"), /*name*/ - nil, /*typeParameters*/ - factory.NewIntersectionTypeNode( - factory.NewNodeList( - []*ast.Node{ - factory.NewTypeReferenceNode(factory.NewIdentifier("a"), nil /*typeArguments*/), - // will be parenthesized on emit: - factory.NewUnionTypeNode( - factory.NewNodeList( - []*ast.Node{ - factory.NewTypeReferenceNode(factory.NewIdentifier("b"), nil /*typeArguments*/), - factory.NewTypeReferenceNode(factory.NewIdentifier("c"), nil /*typeArguments*/), - }, - ), - ), - }, - ), - ), - ), - }, - ), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "type _ = a & (b | c);") -} - -func TestParenthesizeReadonlyTypeOperator1(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( - []*ast.Node{ - factory.NewTypeAliasDeclaration( - nil, /*modifiers*/ - factory.NewIdentifier("_"), /*name*/ - nil, /*typeParameters*/ - factory.NewTypeOperatorNode( - ast.KindReadonlyKeyword, - // will be parenthesized on emit: - factory.NewUnionTypeNode( - factory.NewNodeList( - []*ast.Node{ - factory.NewTypeReferenceNode(factory.NewIdentifier("a"), nil /*typeArguments*/), - factory.NewTypeReferenceNode(factory.NewIdentifier("b"), nil /*typeArguments*/), - }, - ), - ), - ), - ), - }, - ), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "type _ = readonly (a | b);") -} - -func TestParenthesizeReadonlyTypeOperator2(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( - []*ast.Node{ - factory.NewTypeAliasDeclaration( - nil, /*modifiers*/ - factory.NewIdentifier("_"), /*name*/ - nil, /*typeParameters*/ - factory.NewTypeOperatorNode( - ast.KindReadonlyKeyword, - // will be parenthesized on emit: - factory.NewTypeOperatorNode( - ast.KindKeyOfKeyword, - factory.NewTypeReferenceNode(factory.NewIdentifier("a"), nil /*typeArguments*/), - ), - ), - ), - }, - ), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "type _ = readonly (keyof a);") -} - -func TestParenthesizeKeyofTypeOperator(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( - []*ast.Node{ - factory.NewTypeAliasDeclaration( - nil, /*modifiers*/ - factory.NewIdentifier("_"), /*name*/ - nil, /*typeParameters*/ - factory.NewTypeOperatorNode( - ast.KindKeyOfKeyword, - // will be parenthesized on emit: - factory.NewUnionTypeNode( - factory.NewNodeList( - []*ast.Node{ - factory.NewTypeReferenceNode(factory.NewIdentifier("a"), nil /*typeArguments*/), - factory.NewTypeReferenceNode(factory.NewIdentifier("b"), nil /*typeArguments*/), - }, - ), - ), - ), - ), - }, - ), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "type _ = keyof (a | b);") -} - -func TestParenthesizeIndexedAccessType(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( - []*ast.Node{ - factory.NewTypeAliasDeclaration( - nil, /*modifiers*/ - factory.NewIdentifier("_"), /*name*/ - nil, /*typeParameters*/ - factory.NewIndexedAccessTypeNode( - // will be parenthesized on emit: - factory.NewUnionTypeNode( - factory.NewNodeList( - []*ast.Node{ - factory.NewTypeReferenceNode(factory.NewIdentifier("a"), nil /*typeArguments*/), - factory.NewTypeReferenceNode(factory.NewIdentifier("b"), nil /*typeArguments*/), - }, - ), - ), - factory.NewTypeReferenceNode(factory.NewIdentifier("c"), nil /*typeArguments*/), - ), - ), - }, - ), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "type _ = (a | b)[c];") -} - -func TestParenthesizeConditionalType1(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( - []*ast.Node{ - factory.NewTypeAliasDeclaration( - nil, /*modifiers*/ - factory.NewIdentifier("_"), /*name*/ - nil, /*typeParameters*/ - factory.NewConditionalTypeNode( - // will be parenthesized on emit: - factory.NewFunctionTypeNode( - nil, /*typeParameters*/ - factory.NewNodeList( - []*ast.Node{}, - ), - factory.NewTypeReferenceNode(factory.NewIdentifier("a"), nil /*typeArguments*/), - ), - factory.NewTypeReferenceNode(factory.NewIdentifier("b"), nil /*typeArguments*/), - factory.NewTypeReferenceNode(factory.NewIdentifier("c"), nil /*typeArguments*/), - factory.NewTypeReferenceNode(factory.NewIdentifier("d"), nil /*typeArguments*/), - ), - ), - }, - ), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "type _ = (() => a) extends b ? c : d;") -} - -func TestParenthesizeConditionalType2(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( - []*ast.Node{ - factory.NewTypeAliasDeclaration( - nil, /*modifiers*/ - factory.NewIdentifier("_"), /*name*/ - nil, /*typeParameters*/ - factory.NewConditionalTypeNode( - factory.NewTypeReferenceNode(factory.NewIdentifier("a"), nil /*typeArguments*/), - // will be parenthesized on emit: - factory.NewConditionalTypeNode( - factory.NewTypeReferenceNode(factory.NewIdentifier("b"), nil /*typeArguments*/), - factory.NewTypeReferenceNode(factory.NewIdentifier("c"), nil /*typeArguments*/), - factory.NewTypeReferenceNode(factory.NewIdentifier("d"), nil /*typeArguments*/), - factory.NewTypeReferenceNode(factory.NewIdentifier("e"), nil /*typeArguments*/), - ), - factory.NewTypeReferenceNode(factory.NewIdentifier("f"), nil /*typeArguments*/), - factory.NewTypeReferenceNode(factory.NewIdentifier("g"), nil /*typeArguments*/), - ), - ), - }, - ), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "type _ = a extends (b extends c ? d : e) ? f : g;") -} - -func TestParenthesizeConditionalType3(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( - []*ast.Node{ - factory.NewTypeAliasDeclaration( - nil, /*modifiers*/ - factory.NewIdentifier("_"), /*name*/ - nil, /*typeParameters*/ - factory.NewConditionalTypeNode( - factory.NewTypeReferenceNode(factory.NewIdentifier("a"), nil /*typeArguments*/), - factory.NewFunctionTypeNode( - nil, /*typeParameters*/ - factory.NewNodeList( - []*ast.Node{}, - ), - // will be parenthesized on emit: - factory.NewInferTypeNode( - factory.NewTypeParameterDeclaration( - nil, - factory.NewIdentifier("b"), - factory.NewTypeReferenceNode(factory.NewIdentifier("c"), nil /*typeArguments*/), - nil, /*defaultType*/ - ), - ), - ), - factory.NewTypeReferenceNode(factory.NewIdentifier("d"), nil /*typeArguments*/), - factory.NewTypeReferenceNode(factory.NewIdentifier("e"), nil /*typeArguments*/), - ), - ), - }, - ), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "type _ = a extends () => (infer b extends c) ? d : e;") -} - -func TestParenthesizeConditionalType4(t *testing.T) { - t.Parallel() - - var factory ast.NodeFactory - file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList([]*ast.Node{ - factory.NewTypeAliasDeclaration( - nil, /*modifiers*/ - factory.NewIdentifier("_"), /*name*/ - nil, /*typeParameters*/ - factory.NewConditionalTypeNode( - factory.NewTypeReferenceNode(factory.NewIdentifier("a"), nil /*typeArguments*/), - factory.NewFunctionTypeNode( - nil, /*typeParameters*/ - factory.NewNodeList( - []*ast.Node{}, - ), - // will be parenthesized on emit: - factory.NewUnionTypeNode( - factory.NewNodeList( - []*ast.Node{ - factory.NewInferTypeNode( - factory.NewTypeParameterDeclaration( - nil, - factory.NewIdentifier("b"), - factory.NewTypeReferenceNode(factory.NewIdentifier("c"), nil /*typeArguments*/), - nil, /*defaultType*/ - ), - ), - factory.NewTypeReferenceNode(factory.NewIdentifier("d"), nil /*typeArguments*/), - }, - ), - ), - ), - factory.NewTypeReferenceNode(factory.NewIdentifier("e"), nil /*typeArguments*/), - factory.NewTypeReferenceNode(factory.NewIdentifier("f"), nil /*typeArguments*/), - ), - ), - }), factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "type _ = a extends () => (infer b extends c) | d ? e : f;") -} - -func TestNameGeneration(t *testing.T) { - t.Parallel() - ec := printer.NewEmitContext() - file := ec.Factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", ec.Factory.NewNodeList([]*ast.Node{ - ec.Factory.NewVariableStatement(nil, ec.Factory.NewVariableDeclarationList( - ast.NodeFlagsNone, - ec.Factory.NewNodeList([]*ast.Node{ - ec.Factory.NewVariableDeclaration(ec.Factory.NewTempVariable(), nil, nil, nil), - }), - )), - ec.Factory.NewFunctionDeclaration( - nil, - nil, - ec.Factory.NewIdentifier("f"), - nil, - ec.Factory.NewNodeList([]*ast.Node{}), - nil, - nil, - ec.Factory.NewBlock(ec.Factory.NewNodeList([]*ast.Node{ - ec.Factory.NewVariableStatement(nil, ec.Factory.NewVariableDeclarationList( - ast.NodeFlagsNone, - ec.Factory.NewNodeList([]*ast.Node{ - ec.Factory.NewVariableDeclaration(ec.Factory.NewTempVariable(), nil, nil, nil), - }), - )), - }), true), - ), - }), ec.Factory.NewToken(ast.KindEndOfFile)) - - parsetestutil.MarkSyntheticRecursive(file) - emittestutil.CheckEmit(t, ec, file.AsSourceFile(), "var _a;\nfunction f() {\n var _a;\n}") -} - -func TestNoTrailingCommaAfterTransform(t *testing.T) { - t.Parallel() - - file := parsetestutil.ParseTypeScript("[a!]", false /*jsx*/) - emitContext := printer.NewEmitContext() - - var visitor *ast.NodeVisitor - visitor = emitContext.NewNodeVisitor(func(node *ast.Node) *ast.Node { - switch node.Kind { - case ast.KindNonNullExpression: - node = node.AsNonNullExpression().Expression - default: - node = node.VisitEachChild(visitor) - } - return node - }) - file = visitor.VisitSourceFile(file) - - emittestutil.CheckEmit(t, emitContext, file.AsSourceFile(), "[a];") -} - -func TestTrailingCommaAfterTransform(t *testing.T) { - t.Parallel() - - file := parsetestutil.ParseTypeScript("[a!,]", false /*jsx*/) - emitContext := printer.NewEmitContext() - - var visitor *ast.NodeVisitor - visitor = emitContext.NewNodeVisitor(func(node *ast.Node) *ast.Node { - switch node.Kind { - case ast.KindNonNullExpression: - node = node.AsNonNullExpression().Expression - default: - node = node.VisitEachChild(visitor) - } - return node - }) - file = visitor.VisitSourceFile(file) - - emittestutil.CheckEmit(t, emitContext, file.AsSourceFile(), "[a,];") -} - -func TestPartiallyEmittedExpression(t *testing.T) { - t.Parallel() - - compilerOptions := &core.CompilerOptions{} - - file := parsetestutil.ParseTypeScript(`return ((container.parent - .left as PropertyAccessExpression) - .expression as PropertyAccessExpression) - .expression;`, false /*jsx*/) - - emitContext := printer.NewEmitContext() - file = tstransforms.NewTypeEraserTransformer(&transformers.TransformOptions{CompilerOptions: compilerOptions, Context: emitContext}).TransformSourceFile(file) - emittestutil.CheckEmit(t, emitContext, file.AsSourceFile(), `return container.parent - .left - .expression - .expression;`) -} diff --git a/kitcom/internal/tsgo/printer/singlelinestringwriter.go b/kitcom/internal/tsgo/printer/singlelinestringwriter.go deleted file mode 100644 index 2c7888d..0000000 --- a/kitcom/internal/tsgo/printer/singlelinestringwriter.go +++ /dev/null @@ -1,158 +0,0 @@ -package printer - -import ( - "strings" - "sync" - "unicode/utf8" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/stringutil" -) - -var singleLineStringWriterPool sync.Pool = sync.Pool{ - New: func() any { - return &singleLineStringWriter{} - }, -} - -var _ EmitTextWriter = &singleLineStringWriter{} - -func GetSingleLineStringWriter() (EmitTextWriter, func()) { - w := singleLineStringWriterPool.Get().(*singleLineStringWriter) - w.Clear() - return w, func() { - singleLineStringWriterPool.Put(w) - } -} - -type singleLineStringWriter struct { - builder strings.Builder - lastWritten string -} - -func (w *singleLineStringWriter) Clear() { - w.lastWritten = "" - w.builder.Reset() -} - -func (w singleLineStringWriter) DecreaseIndent() { - // Do Nothing -} - -func (w singleLineStringWriter) GetColumn() int { - return 0 -} - -func (w singleLineStringWriter) GetIndent() int { - return 0 -} - -func (w singleLineStringWriter) GetLine() int { - return 0 -} - -func (w singleLineStringWriter) String() string { - return w.builder.String() -} - -func (w singleLineStringWriter) GetTextPos() int { - return w.builder.Len() -} - -func (w singleLineStringWriter) HasTrailingComment() bool { - return false -} - -func (w singleLineStringWriter) HasTrailingWhitespace() bool { - if w.builder.Len() == 0 { - return false - } - ch, _ := utf8.DecodeLastRuneInString(w.lastWritten) - if ch == utf8.RuneError { - return false - } - return stringutil.IsWhiteSpaceLike(ch) -} - -func (w singleLineStringWriter) IncreaseIndent() { - // Do Nothing -} - -func (w singleLineStringWriter) IsAtStartOfLine() bool { - return false -} - -func (w *singleLineStringWriter) RawWrite(s string) { - w.lastWritten = s - w.builder.WriteString(s) -} - -func (w *singleLineStringWriter) Write(s string) { - w.lastWritten = s - w.builder.WriteString(s) -} - -func (w *singleLineStringWriter) WriteComment(text string) { - w.lastWritten = text - w.builder.WriteString(text) -} - -func (w *singleLineStringWriter) WriteKeyword(text string) { - w.lastWritten = text - w.builder.WriteString(text) -} - -func (w *singleLineStringWriter) WriteLine() { - w.lastWritten = " " - w.builder.WriteString(" ") -} - -func (w *singleLineStringWriter) WriteLineForce(force bool) { - w.lastWritten = " " - w.builder.WriteString(" ") -} - -func (w *singleLineStringWriter) WriteLiteral(s string) { - w.lastWritten = s - w.builder.WriteString(s) -} - -func (w *singleLineStringWriter) WriteOperator(text string) { - w.lastWritten = text - w.builder.WriteString(text) -} - -func (w *singleLineStringWriter) WriteParameter(text string) { - w.lastWritten = text - w.builder.WriteString(text) -} - -func (w *singleLineStringWriter) WriteProperty(text string) { - w.lastWritten = text - w.builder.WriteString(text) -} - -func (w *singleLineStringWriter) WritePunctuation(text string) { - w.lastWritten = text - w.builder.WriteString(text) -} - -func (w *singleLineStringWriter) WriteSpace(text string) { - w.lastWritten = text - w.builder.WriteString(text) -} - -func (w *singleLineStringWriter) WriteStringLiteral(text string) { - w.lastWritten = text - w.builder.WriteString(text) -} - -func (w *singleLineStringWriter) WriteSymbol(text string, symbol *ast.Symbol) { - w.lastWritten = text - w.builder.WriteString(text) -} - -func (w *singleLineStringWriter) WriteTrailingSemicolon(text string) { - w.lastWritten = text - w.builder.WriteString(text) -} diff --git a/kitcom/internal/tsgo/printer/sourcefilemetadataprovider.go b/kitcom/internal/tsgo/printer/sourcefilemetadataprovider.go deleted file mode 100644 index 81ad56f..0000000 --- a/kitcom/internal/tsgo/printer/sourcefilemetadataprovider.go +++ /dev/null @@ -1,10 +0,0 @@ -package printer - -import ( - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath" -) - -type SourceFileMetaDataProvider interface { - GetSourceFileMetaData(path tspath.Path) *ast.SourceFileMetaData -} diff --git a/kitcom/internal/tsgo/printer/textwriter.go b/kitcom/internal/tsgo/printer/textwriter.go deleted file mode 100644 index 973518f..0000000 --- a/kitcom/internal/tsgo/printer/textwriter.go +++ /dev/null @@ -1,211 +0,0 @@ -package printer - -import ( - "strings" - "unicode/utf8" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/stringutil" -) - -var _ EmitTextWriter = &textWriter{} - -type textWriter struct { - newLine string - builder strings.Builder - lastWritten string - indent int - lineStart bool - lineCount int - linePos int - hasTrailingCommentState bool -} - -func (w *textWriter) Clear() { - *w = textWriter{newLine: w.newLine, lineStart: true} -} - -func (w *textWriter) DecreaseIndent() { - w.indent-- -} - -func (w *textWriter) GetColumn() int { - if w.lineStart { - return w.indent * 4 - } - return w.builder.Len() - w.linePos -} - -func (w *textWriter) GetIndent() int { - return w.indent -} - -func (w *textWriter) GetLine() int { - return w.lineCount -} - -func (w *textWriter) String() string { - return w.builder.String() -} - -func (w *textWriter) GetTextPos() int { - return w.builder.Len() -} - -func (w textWriter) HasTrailingComment() bool { - return w.hasTrailingCommentState -} - -func (w *textWriter) HasTrailingWhitespace() bool { - if w.builder.Len() == 0 { - return false - } - ch, _ := utf8.DecodeLastRuneInString(w.lastWritten) - if ch == utf8.RuneError { - return false - } - return stringutil.IsWhiteSpaceLike(ch) -} - -func (w *textWriter) IncreaseIndent() { - w.indent++ -} - -func (w *textWriter) IsAtStartOfLine() bool { - return w.lineStart -} - -func (w *textWriter) RawWrite(s string) { - if s != "" { - w.builder.WriteString(s) - w.lastWritten = s - w.updateLineCountAndPosFor(s) - w.hasTrailingCommentState = false - } -} - -func (w *textWriter) updateLineCountAndPosFor(s string) { - var count int - var lastLineStart core.TextPos - - for lineStart := range core.ComputeECMALineStartsSeq(s) { - count++ - lastLineStart = lineStart - } - - if count > 1 { - w.lineCount += count - 1 - curLen := w.builder.Len() - w.linePos = curLen - len(s) + int(lastLineStart) - w.lineStart = (w.linePos - curLen) == 0 - return - } - w.lineStart = false -} - -func getIndentString(indent int) string { - switch indent { - case 0: - return "" - case 1: - return " " - default: - // TODO: This is cached in tsc - should it be cached here? - return strings.Repeat(" ", indent) - } -} - -func (w *textWriter) writeText(s string) { - if s != "" { - if w.lineStart { - w.builder.WriteString(getIndentString(w.indent)) - w.lineStart = false - } - w.builder.WriteString(s) - w.lastWritten = s - w.updateLineCountAndPosFor(s) - } -} - -func (w *textWriter) Write(s string) { - if s != "" { - w.hasTrailingCommentState = false - } - w.writeText(s) -} - -func (w *textWriter) WriteComment(text string) { - if text != "" { - w.hasTrailingCommentState = true - } - w.writeText(text) -} - -func (w *textWriter) WriteKeyword(text string) { - w.Write(text) -} - -func (w *textWriter) writeLineRaw() { - w.builder.WriteString(w.newLine) - w.lastWritten = w.newLine - w.lineCount++ - w.linePos = w.builder.Len() - w.lineStart = true - w.hasTrailingCommentState = false -} - -func (w *textWriter) WriteLine() { - if !w.lineStart { - w.writeLineRaw() - } -} - -func (w *textWriter) WriteLineForce(force bool) { - if !w.lineStart || force { - w.writeLineRaw() - } -} - -func (w *textWriter) WriteLiteral(s string) { - w.Write(s) -} - -func (w *textWriter) WriteOperator(text string) { - w.Write(text) -} - -func (w *textWriter) WriteParameter(text string) { - w.Write(text) -} - -func (w *textWriter) WriteProperty(text string) { - w.Write(text) -} - -func (w *textWriter) WritePunctuation(text string) { - w.Write(text) -} - -func (w *textWriter) WriteSpace(text string) { - w.Write(text) -} - -func (w *textWriter) WriteStringLiteral(text string) { - w.Write(text) -} - -func (w *textWriter) WriteSymbol(text string, symbol *ast.Symbol) { - w.Write(text) -} - -func (w *textWriter) WriteTrailingSemicolon(text string) { - w.Write(text) -} - -func NewTextWriter(newLine string) EmitTextWriter { - var w textWriter - w.newLine = newLine - w.Clear() - return &w -} diff --git a/kitcom/internal/tsgo/printer/utilities.go b/kitcom/internal/tsgo/printer/utilities.go deleted file mode 100644 index 1abb709..0000000 --- a/kitcom/internal/tsgo/printer/utilities.go +++ /dev/null @@ -1,909 +0,0 @@ -package printer - -import ( - "fmt" - "slices" - "strconv" - "strings" - "unicode/utf8" - - "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/stringutil" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath" -) - -type getLiteralTextFlags int - -const ( - getLiteralTextFlagsNone getLiteralTextFlags = 0 - getLiteralTextFlagsNeverAsciiEscape getLiteralTextFlags = 1 << 0 - getLiteralTextFlagsJsxAttributeEscape getLiteralTextFlags = 1 << 1 - getLiteralTextFlagsTerminateUnterminatedLiterals getLiteralTextFlags = 1 << 2 - getLiteralTextFlagsAllowNumericSeparator getLiteralTextFlags = 1 << 3 -) - -type QuoteChar rune - -const ( - QuoteCharSingleQuote QuoteChar = '\'' - QuoteCharDoubleQuote QuoteChar = '"' - QuoteCharBacktick QuoteChar = '`' -) - -var jsxEscapedCharsMap = map[rune]string{ - '"': """, - '\'': "'", -} - -var escapedCharsMap = map[rune]string{ - '\t': `\t`, - '\v': `\v`, - '\f': `\f`, - '\b': `\b`, - '\r': `\r`, - '\n': `\n`, - '\\': `\\`, - '"': `\"`, - '\'': `\'`, - '`': "\\`", - '$': `\$`, // when quoteChar == '`' - '\u2028': `\u2028`, // lineSeparator - '\u2029': `\u2029`, // paragraphSeparator - '\u0085': `\u0085`, // nextLine -} - -func encodeJsxCharacterEntity(b *strings.Builder, charCode rune) { - hexCharCode := strings.ToUpper(strconv.FormatUint(uint64(charCode), 16)) - b.WriteString("&#x") - b.WriteString(hexCharCode) - b.WriteByte(';') -} - -func encodeUtf16EscapeSequence(b *strings.Builder, charCode rune) { - hexCharCode := strings.ToUpper(strconv.FormatUint(uint64(charCode), 16)) - b.WriteString(`\u`) - for i := len(hexCharCode); i < 4; i++ { - b.WriteByte('0') - } - b.WriteString(hexCharCode) -} - -// Based heavily on the abstract 'Quote'/'QuoteJSONString' operation from ECMA-262 (24.3.2.2), -// but augmented for a few select characters (e.g. lineSeparator, paragraphSeparator, nextLine) -// Note that this doesn't actually wrap the input in double quotes. -func escapeStringWorker(s string, quoteChar QuoteChar, flags getLiteralTextFlags, b *strings.Builder) { - pos := 0 - i := 0 - for i < len(s) { - ch, size := utf8.DecodeRuneInString(s[i:]) - - escape := false - - // This consists of the first 19 unprintable ASCII characters, canonical escapes, lineSeparator, - // paragraphSeparator, and nextLine. The latter three are just desirable to suppress new lines in - // the language service. These characters should be escaped when printing, and if any characters are added, - // `escapedCharsMap` and/or `jsxEscapedCharsMap` must be updated. Note that this *does not* include the 'delete' - // character. There is no reason for this other than that JSON.stringify does not handle it either. - switch ch { - case '\\': - if flags&getLiteralTextFlagsJsxAttributeEscape == 0 { - escape = true - } - case '$': - if quoteChar == QuoteCharBacktick && i+1 < len(s) && s[i+1] == '{' { - escape = true - } - case rune(quoteChar), '\u2028', '\u2029', '\u0085', '\r': - escape = true - case '\n': - if quoteChar != QuoteCharBacktick { - // Template strings preserve simple LF newlines, still encode CRLF (or CR). - escape = true - } - default: - if ch <= '\u001f' || flags&getLiteralTextFlagsNeverAsciiEscape == 0 && ch > '\u007f' { - escape = true - } - } - - if escape { - if pos < i { - // Write string up to this point - b.WriteString(s[pos:i]) - } - - switch { - case flags&getLiteralTextFlagsJsxAttributeEscape != 0: - if ch == 0 { - b.WriteString("�") - } else if match, ok := jsxEscapedCharsMap[ch]; ok { - b.WriteString(match) - } else { - encodeJsxCharacterEntity(b, ch) - } - - default: - if ch == '\r' && quoteChar == QuoteCharBacktick && i+1 < len(s) && s[i+1] == '\n' { - // Template strings preserve simple LF newlines, but still must escape CRLF. Left alone, the - // above cases for `\r` and `\n` would inadvertently escape CRLF as two independent characters. - size++ - b.WriteString(`\r\n`) - } else if ch > 0xffff { - // encode as surrogate pair - ch -= 0x10000 - encodeUtf16EscapeSequence(b, (ch&0b11111111110000000000>>10)+0xD800) - encodeUtf16EscapeSequence(b, (ch&0b00000000001111111111)+0xDC00) - } else if ch == 0 { - if i+1 < len(s) && stringutil.IsDigit(rune(s[i+1])) { - // If the null character is followed by digits, print as a hex escape to prevent the result from - // parsing as an octal (which is forbidden in strict mode) - b.WriteString(`\x00`) - } else { - // Otherwise, keep printing a literal \0 for the null character - b.WriteString(`\0`) - } - } else { - if match, ok := escapedCharsMap[ch]; ok { - b.WriteString(match) - } else { - encodeUtf16EscapeSequence(b, ch) - } - } - } - pos = i + size - } - - i += size - } - - if pos < i { - b.WriteString(s[pos:]) - } -} - -func EscapeString(s string, quoteChar QuoteChar) string { - var b strings.Builder - b.Grow(len(s) + 2) - escapeStringWorker(s, quoteChar, getLiteralTextFlagsNeverAsciiEscape, &b) - return b.String() -} - -func escapeNonAsciiString(s string, quoteChar QuoteChar) string { - var b strings.Builder - b.Grow(len(s) + 2) - escapeStringWorker(s, quoteChar, getLiteralTextFlagsNone, &b) - return b.String() -} - -func escapeJsxAttributeString(s string, quoteChar QuoteChar) string { - var b strings.Builder - b.Grow(len(s) + 2) - escapeStringWorker(s, quoteChar, getLiteralTextFlagsJsxAttributeEscape|getLiteralTextFlagsNeverAsciiEscape, &b) - return b.String() -} - -func canUseOriginalText(node *ast.LiteralLikeNode, flags getLiteralTextFlags) bool { - // A synthetic node has no original text, nor does a node without a parent as we would be unable to find the - // containing SourceFile. We also cannot use the original text if the literal was unterminated and the caller has - // requested proper termination of unterminated literals - if ast.NodeIsSynthesized(node) || node.Parent == nil || flags&getLiteralTextFlagsTerminateUnterminatedLiterals != 0 && ast.IsUnterminatedLiteral(node) { - return false - } - - if node.Kind == ast.KindNumericLiteral { - tokenFlags := node.AsNumericLiteral().TokenFlags - // For a numeric literal, we cannot use the original text if the original text was an invalid literal - if tokenFlags&ast.TokenFlagsIsInvalid != 0 { - return false - } - // We also cannot use the original text if the literal contains numeric separators, but numeric separators - // are not permitted - if tokenFlags&ast.TokenFlagsContainsSeparator != 0 { - return flags&getLiteralTextFlagsAllowNumericSeparator != 0 - } - } - - // Finally, we do not use the original text of a BigInt literal - // TODO(rbuckton): The reason as to why we do not use the original text for bigints is not mentioned in the - // original compiler source. It could be that this is no longer necessary, in which case bigint literals should - // use the same code path as numeric literals, above - return node.Kind != ast.KindBigIntLiteral -} - -func getLiteralText(node *ast.LiteralLikeNode, sourceFile *ast.SourceFile, flags getLiteralTextFlags) string { - // If we don't need to downlevel and we can reach the original source text using - // the node's parent reference, then simply get the text as it was originally written. - if sourceFile != nil && canUseOriginalText(node, flags) { - return scanner.GetSourceTextOfNodeFromSourceFile(sourceFile, node, false /*includeTrivia*/) - } - - // If we can't reach the original source text, use the canonical form if it's a number, - // or a (possibly escaped) quoted form of the original text if it's string-like. - switch node.Kind { - case ast.KindStringLiteral: - var b strings.Builder - var quoteChar QuoteChar - if node.AsStringLiteral().TokenFlags&ast.TokenFlagsSingleQuote != 0 { - quoteChar = QuoteCharSingleQuote - } else { - quoteChar = QuoteCharDoubleQuote - } - - text := node.Text() - - // Write leading quote character - b.Grow(len(text) + 2) - b.WriteRune(rune(quoteChar)) - - // Write text - escapeStringWorker(text, quoteChar, flags, &b) - - // Write trailing quote character - b.WriteRune(rune(quoteChar)) - return b.String() - - case ast.KindNoSubstitutionTemplateLiteral, - ast.KindTemplateHead, - ast.KindTemplateMiddle, - ast.KindTemplateTail: - - // If a NoSubstitutionTemplateLiteral appears to have a substitution in it, the original text - // had to include a backslash: `not \${a} substitution`. - var b strings.Builder - text := node.TemplateLiteralLikeData().Text - rawText := node.TemplateLiteralLikeData().RawText - raw := len(rawText) > 0 || len(text) == 0 - - var textLen int - if raw { - textLen = len(rawText) - } else { - textLen = len(text) - } - - // Write leading quote character - switch node.Kind { - case ast.KindNoSubstitutionTemplateLiteral: - b.Grow(2 + textLen) - b.WriteRune('`') - case ast.KindTemplateHead: - b.Grow(3 + textLen) - b.WriteRune('`') - case ast.KindTemplateMiddle: - b.Grow(3 + textLen) - b.WriteRune('}') - case ast.KindTemplateTail: - b.Grow(2 + textLen) - b.WriteRune('}') - } - - // Write text - switch { - case len(rawText) > 0 || len(text) == 0: - // If rawText is set, it is expected to be valid. - b.WriteString(rawText) - default: - escapeStringWorker(text, QuoteCharBacktick, flags, &b) - } - - // Write trailing quote character - switch node.Kind { - case ast.KindNoSubstitutionTemplateLiteral: - b.WriteRune('`') - case ast.KindTemplateHead: - b.WriteString("${") - case ast.KindTemplateMiddle: - b.WriteString("${") - case ast.KindTemplateTail: - b.WriteRune('`') - } - return b.String() - - case ast.KindNumericLiteral, ast.KindBigIntLiteral: - return node.Text() - - case ast.KindRegularExpressionLiteral: - if flags&getLiteralTextFlagsTerminateUnterminatedLiterals != 0 && ast.IsUnterminatedLiteral(node) { - var b strings.Builder - text := node.Text() - if len(text) > 0 && text[len(text)-1] == '\\' { - b.Grow(2 + len(text)) - b.WriteString(text) - b.WriteString(" /") - } else { - b.Grow(1 + len(text)) - b.WriteString(text) - b.WriteString("/") - } - return b.String() - } - return node.Text() - - default: - panic("Unsupported LiteralLikeNode") - } -} - -func isNotPrologueDirective(node *ast.Node) bool { - return !ast.IsPrologueDirective(node) -} - -func rangeIsOnSingleLine(r core.TextRange, sourceFile *ast.SourceFile) bool { - return rangeStartIsOnSameLineAsRangeEnd(r, r, sourceFile) -} - -func rangeStartPositionsAreOnSameLine(range1 core.TextRange, range2 core.TextRange, sourceFile *ast.SourceFile) bool { - return positionsAreOnSameLine( - getStartPositionOfRange(range1, sourceFile, false /*includeComments*/), - getStartPositionOfRange(range2, sourceFile, false /*includeComments*/), - sourceFile, - ) -} - -func rangeEndPositionsAreOnSameLine(range1 core.TextRange, range2 core.TextRange, sourceFile *ast.SourceFile) bool { - return positionsAreOnSameLine(range1.End(), range2.End(), sourceFile) -} - -func rangeStartIsOnSameLineAsRangeEnd(range1 core.TextRange, range2 core.TextRange, sourceFile *ast.SourceFile) bool { - return positionsAreOnSameLine(getStartPositionOfRange(range1, sourceFile, false /*includeComments*/), range2.End(), sourceFile) -} - -func rangeEndIsOnSameLineAsRangeStart(range1 core.TextRange, range2 core.TextRange, sourceFile *ast.SourceFile) bool { - return positionsAreOnSameLine(range1.End(), getStartPositionOfRange(range2, sourceFile, false /*includeComments*/), sourceFile) -} - -func getStartPositionOfRange(r core.TextRange, sourceFile *ast.SourceFile, includeComments bool) int { - if ast.PositionIsSynthesized(r.Pos()) { - return -1 - } - return scanner.SkipTriviaEx(sourceFile.Text(), r.Pos(), &scanner.SkipTriviaOptions{StopAtComments: includeComments}) -} - -func positionsAreOnSameLine(pos1 int, pos2 int, sourceFile *ast.SourceFile) bool { - return GetLinesBetweenPositions(sourceFile, pos1, pos2) == 0 -} - -func GetLinesBetweenPositions(sourceFile *ast.SourceFile, pos1 int, pos2 int) int { - if pos1 == pos2 { - return 0 - } - lineStarts := scanner.GetECMALineStarts(sourceFile) - lower := core.IfElse(pos1 < pos2, pos1, pos2) - isNegative := lower == pos2 - upper := core.IfElse(isNegative, pos1, pos2) - lowerLine := scanner.ComputeLineOfPosition(lineStarts, lower) - upperLine := lowerLine + scanner.ComputeLineOfPosition(lineStarts[lowerLine:], upper) - if isNegative { - return lowerLine - upperLine - } else { - return upperLine - lowerLine - } -} - -func getLinesBetweenRangeEndAndRangeStart(range1 core.TextRange, range2 core.TextRange, sourceFile *ast.SourceFile, includeSecondRangeComments bool) int { - range2Start := getStartPositionOfRange(range2, sourceFile, includeSecondRangeComments) - return GetLinesBetweenPositions(sourceFile, range1.End(), range2Start) -} - -func getLinesBetweenPositionAndPrecedingNonWhitespaceCharacter(pos int, stopPos int, sourceFile *ast.SourceFile, includeComments bool) int { - startPos := scanner.SkipTriviaEx(sourceFile.Text(), pos, &scanner.SkipTriviaOptions{StopAtComments: includeComments}) - prevPos := getPreviousNonWhitespacePosition(startPos, stopPos, sourceFile) - return GetLinesBetweenPositions(sourceFile, core.IfElse(prevPos >= 0, prevPos, stopPos), startPos) -} - -func getLinesBetweenPositionAndNextNonWhitespaceCharacter(pos int, stopPos int, sourceFile *ast.SourceFile, includeComments bool) int { - nextPos := scanner.SkipTriviaEx(sourceFile.Text(), pos, &scanner.SkipTriviaOptions{StopAtComments: includeComments}) - return GetLinesBetweenPositions(sourceFile, pos, core.IfElse(stopPos < nextPos, stopPos, nextPos)) -} - -func getPreviousNonWhitespacePosition(pos int, stopPos int, sourceFile *ast.SourceFile) int { - for ; pos >= stopPos; pos-- { - if !stringutil.IsWhiteSpaceLike(rune(sourceFile.Text()[pos])) { - return pos - } - } - return -1 -} - -func getCommentRange(node *ast.Node) core.TextRange { - // TODO(rbuckton) - return node.Loc -} - -func siblingNodePositionsAreComparable(previousNode *ast.Node, nextNode *ast.Node) bool { - if nextNode.Pos() < previousNode.End() { - return false - } - - // TODO(rbuckton) - // previousNode = getOriginalNode(previousNode); - // nextNode = getOriginalNode(nextNode); - parent := previousNode.Parent - if parent == nil || parent != nextNode.Parent { - return false - } - - parentNodeArray := getContainingNodeArray(previousNode) - if parentNodeArray != nil { - prevNodeIndex := slices.Index(parentNodeArray.Nodes, previousNode) - return prevNodeIndex >= 0 && slices.Index(parentNodeArray.Nodes, nextNode) == prevNodeIndex+1 - } - - return false -} - -func getContainingNodeArray(node *ast.Node) *ast.NodeList { - parent := node.Parent - if parent == nil { - return nil - } - - switch node.Kind { - case ast.KindTypeParameter: - switch { - case ast.IsFunctionLike(parent): - return parent.FunctionLikeData().TypeParameters - case ast.IsClassLike(parent): - return parent.ClassLikeData().TypeParameters - case ast.IsInterfaceDeclaration(parent): - return parent.AsInterfaceDeclaration().TypeParameters - case ast.IsTypeOrJSTypeAliasDeclaration(parent): - return parent.AsTypeAliasDeclaration().TypeParameters - case ast.IsInferTypeNode(parent): - break - default: - panic(fmt.Sprintf("Unexpected TypeParameter parent: %#v", parent.Kind)) - } - - case ast.KindParameter: - return node.Parent.FunctionLikeData().Parameters - case ast.KindTemplateLiteralTypeSpan: - return node.Parent.AsTemplateLiteralTypeNode().TemplateSpans - case ast.KindTemplateSpan: - return node.Parent.AsTemplateExpression().TemplateSpans - case ast.KindDecorator: - if canHaveDecorators(node.Parent) { - if modifiers := node.Parent.Modifiers(); modifiers != nil { - return &modifiers.NodeList - } - } - return nil - case ast.KindHeritageClause: - if ast.IsClassLike(node.Parent) { - return node.Parent.ClassLikeData().HeritageClauses - } else { - return node.Parent.AsInterfaceDeclaration().HeritageClauses - } - } - - // TODO(rbuckton) - // if ast.IsJSDocTag(node) { - // if ast.IsJSDocTypeLiteral(node.parent) { - // return nil - // } - // return node.parent.tags - // } - - switch parent.Kind { - case ast.KindTypeLiteral: - if ast.IsTypeElement(node) { - return parent.AsTypeLiteralNode().Members - } - case ast.KindInterfaceDeclaration: - if ast.IsTypeElement(node) { - return parent.AsInterfaceDeclaration().Members - } - case ast.KindUnionType: - return parent.AsUnionTypeNode().Types - case ast.KindIntersectionType: - return parent.AsIntersectionTypeNode().Types - case ast.KindTupleType: - return parent.AsTupleTypeNode().Elements - case ast.KindArrayLiteralExpression: - return parent.AsArrayLiteralExpression().Elements - case ast.KindCommaListExpression: - panic("not implemented") - case ast.KindNamedImports: - return parent.AsNamedImports().Elements - case ast.KindNamedExports: - return parent.AsNamedExports().Elements - case ast.KindObjectLiteralExpression: - return parent.AsObjectLiteralExpression().Properties - case ast.KindJsxAttributes: - return parent.AsJsxAttributes().Properties - case ast.KindCallExpression: - p := parent.AsCallExpression() - switch { - case ast.IsTypeNode(node): - return p.TypeArguments - case node != p.Expression: - return p.Arguments - } - case ast.KindNewExpression: - p := parent.AsNewExpression() - switch { - case ast.IsTypeNode(node): - return p.TypeArguments - case node != p.Expression: - return p.Arguments - } - case ast.KindJsxElement: - if ast.IsJsxChild(node) { - return parent.AsJsxElement().Children - } - case ast.KindJsxFragment: - if ast.IsJsxChild(node) { - return parent.AsJsxFragment().Children - } - case ast.KindJsxOpeningElement: - if ast.IsTypeNode(node) { - return parent.AsJsxOpeningElement().TypeArguments - } - case ast.KindJsxSelfClosingElement: - if ast.IsTypeNode(node) { - return parent.AsJsxSelfClosingElement().TypeArguments - } - case ast.KindBlock: - return parent.AsBlock().Statements - case ast.KindCaseClause, ast.KindDefaultClause: - return parent.AsCaseOrDefaultClause().Statements - case ast.KindModuleBlock: - return parent.AsModuleBlock().Statements - case ast.KindCaseBlock: - return parent.AsCaseBlock().Clauses - case ast.KindClassDeclaration, ast.KindClassExpression: - if ast.IsClassElement(node) { - return parent.ClassLikeData().Members - } - case ast.KindEnumDeclaration: - if ast.IsEnumMember(node) { - return parent.AsEnumDeclaration().Members - } - case ast.KindSourceFile: - if ast.IsStatement(node) { - return parent.AsSourceFile().Statements - } - } - - if ast.IsModifier(node) { - if modifiers := parent.Modifiers(); modifiers != nil { - return &modifiers.NodeList - } - } - - return nil -} - -func canHaveDecorators(node *ast.Node) bool { - switch node.Kind { - case ast.KindParameter, - ast.KindPropertyDeclaration, - ast.KindMethodDeclaration, - ast.KindGetAccessor, - ast.KindSetAccessor, - ast.KindClassExpression, - ast.KindClassDeclaration: - return true - } - return false -} - -func originalNodesHaveSameParent(nodeA *ast.Node, nodeB *ast.Node) bool { - // TODO(rbuckton): nodeA = getOriginalNode(nodeA) - if nodeA.Parent != nil { - // For performance, do not call `getOriginalNode` for `nodeB` if `nodeA` doesn't even - // have a parent node. - // TODO(rbuckton): nodeB = getOriginalNode(nodeB) - return nodeA.Parent == nodeB.Parent - } - return false -} - -func tryGetEnd(node interface{ End() int }) (int, bool) { - // avoid using reflect (via core.IsNil) for common cases - switch v := node.(type) { - case (*ast.Node): - if v != nil { - return v.End(), true - } - case (*ast.NodeList): - if v != nil { - return v.End(), true - } - case (*ast.ModifierList): - if v != nil { - return v.End(), true - } - case (*core.TextRange): - if v != nil { - return v.End(), true - } - case (core.TextRange): - return v.End(), true - default: - panic(fmt.Sprintf("unhandled type: %T", node)) - } - return 0, false -} - -func greatestEnd(end int, nodes ...interface{ End() int }) int { - for i := len(nodes) - 1; i >= 0; i-- { - node := nodes[i] - if nodeEnd, ok := tryGetEnd(node); ok && end < nodeEnd { - end = nodeEnd - } - } - return end -} - -func skipSynthesizedParentheses(node *ast.Node) *ast.Node { - for node.Kind == ast.KindParenthesizedExpression && ast.NodeIsSynthesized(node) { - node = node.AsParenthesizedExpression().Expression - } - return node -} - -func isNewExpressionWithoutArguments(node *ast.Node) bool { - return node.Kind == ast.KindNewExpression && node.AsNewExpression().Arguments == nil -} - -func isBinaryOperation(node *ast.Node, token ast.Kind) bool { - node = ast.SkipPartiallyEmittedExpressions(node) - return node.Kind == ast.KindBinaryExpression && - node.AsBinaryExpression().OperatorToken.Kind == token -} - -func isImmediatelyInvokedFunctionExpressionOrArrowFunction(node *ast.Expression) bool { - node = ast.SkipPartiallyEmittedExpressions(node) - if !ast.IsCallExpression(node) { - return false - } - node = ast.SkipPartiallyEmittedExpressions(node.AsCallExpression().Expression) - return ast.IsFunctionExpression(node) || ast.IsArrowFunction(node) -} - -func IsFileLevelUniqueName(sourceFile *ast.SourceFile, name string, hasGlobalName func(string) bool) bool { - if hasGlobalName != nil && hasGlobalName(name) { - return false - } - _, ok := sourceFile.Identifiers[name] - return !ok -} - -func hasLeadingHash(text string) bool { - return len(text) > 0 && text[0] == '#' -} - -func removeLeadingHash(text string) string { - if hasLeadingHash(text) { - return text[1:] - } else { - return text - } -} - -func ensureLeadingHash(text string) string { - if hasLeadingHash(text) { - return text - } else { - return "#" + text - } -} - -func FormatGeneratedName(privateName bool, prefix string, base string, suffix string) string { - name := removeLeadingHash(prefix) + removeLeadingHash(base) + removeLeadingHash(suffix) - if privateName { - return ensureLeadingHash(name) - } - return name -} - -func isASCIIWordCharacter(ch rune) bool { - return stringutil.IsASCIILetter(ch) || stringutil.IsDigit(ch) || ch == '_' -} - -func makeIdentifierFromModuleName(moduleName string) string { - moduleName = tspath.GetBaseFileName(moduleName) - var builder strings.Builder - start := 0 - pos := 0 - for pos < len(moduleName) { - ch := rune(moduleName[pos]) - if pos == 0 && stringutil.IsDigit(ch) { - builder.WriteByte('_') - } else if !isASCIIWordCharacter(ch) { - if start < pos { - builder.WriteString(moduleName[start:pos]) - } - builder.WriteByte('_') - start = pos + 1 - } - pos++ - } - if start < pos { - builder.WriteString(moduleName[start:pos]) - } - return builder.String() -} - -func findSpanEndWithEmitContext[T any](c *EmitContext, array []T, test func(c *EmitContext, value T) bool, start int) int { - i := start - for i < len(array) && test(c, array[i]) { - i++ - } - return i -} - -func findSpanEnd[T any](array []T, test func(value T) bool, start int) int { - i := start - for i < len(array) && test(array[i]) { - i++ - } - return i -} - -func skipWhiteSpaceSingleLine(text string, pos *int) { - for *pos < len(text) { - ch, size := utf8.DecodeRuneInString(text[*pos:]) - if !stringutil.IsWhiteSpaceSingleLine(ch) { - break - } - *pos += size - } -} - -func matchWhiteSpaceSingleLine(text string, pos *int) bool { - startPos := *pos - skipWhiteSpaceSingleLine(text, pos) - return *pos != startPos -} - -func matchRune(text string, pos *int, expected rune) bool { - ch, size := utf8.DecodeRuneInString(text[*pos:]) - if ch == expected { - *pos += size - return true - } - return false -} - -func matchString(text string, pos *int, expected string) bool { - textPos := *pos - expectedPos := 0 - for expectedPos < len(expected) { - if textPos >= len(text) { - return false - } - - expectedRune, expectedSize := utf8.DecodeRuneInString(expected[expectedPos:]) - if !matchRune(text, &textPos, expectedRune) { - return false - } - - expectedPos += expectedSize - } - - *pos = textPos - return true -} - -func matchQuotedString(text string, pos *int) bool { - textPos := *pos - var quoteChar rune - switch { - case matchRune(text, &textPos, '\''): - quoteChar = '\'' - case matchRune(text, &textPos, '"'): - quoteChar = '"' - default: - return false - } - for textPos < len(text) { - ch, size := utf8.DecodeRuneInString(text[textPos:]) - textPos += size - if ch == quoteChar { - *pos = textPos - return true - } - } - return false -} - -// /// -// /// -// /// -// /// -// /// -// /// -func IsRecognizedTripleSlashComment(text string, commentRange ast.CommentRange) bool { - if commentRange.Kind == ast.KindSingleLineCommentTrivia && - commentRange.Len() > 2 && - text[commentRange.Pos()+1] == '/' && - text[commentRange.Pos()+2] == '/' { - text = text[commentRange.Pos()+3 : commentRange.End()] - pos := 0 - skipWhiteSpaceSingleLine(text, &pos) - if !matchRune(text, &pos, '<') { - return false - } - switch { - case matchString(text, &pos, "reference"): - if !matchWhiteSpaceSingleLine(text, &pos) { - return false - } - if !matchString(text, &pos, "path") && - !matchString(text, &pos, "types") && - !matchString(text, &pos, "lib") && - !matchString(text, &pos, "no-default-lib") { - return false - } - skipWhiteSpaceSingleLine(text, &pos) - if !matchRune(text, &pos, '=') { - return false - } - skipWhiteSpaceSingleLine(text, &pos) - if !matchQuotedString(text, &pos) { - return false - } - case matchString(text, &pos, "amd-dependency"): - if !matchWhiteSpaceSingleLine(text, &pos) { - return false - } - if !matchString(text, &pos, "path") { - return false - } - skipWhiteSpaceSingleLine(text, &pos) - if !matchRune(text, &pos, '=') { - return false - } - skipWhiteSpaceSingleLine(text, &pos) - if !matchQuotedString(text, &pos) { - return false - } - case matchString(text, &pos, "amd-module"): - skipWhiteSpaceSingleLine(text, &pos) - default: - return false - } - index := strings.Index(text[pos:], "/>") - return index != -1 - } - - return false -} - -func isJSDocLikeText(text string, comment ast.CommentRange) bool { - return comment.Kind == ast.KindMultiLineCommentTrivia && - comment.Len() > 5 && - text[comment.Pos()+2] == '*' && - text[comment.Pos()+3] != '/' -} - -func IsPinnedComment(text string, comment ast.CommentRange) bool { - return comment.Kind == ast.KindMultiLineCommentTrivia && - comment.Len() > 5 && - text[comment.Pos()+2] == '!' -} - -func calculateIndent(text string, pos int, end int) int { - currentLineIndent := 0 - indentSize := len(getIndentString(1)) - for pos < end { - ch, size := utf8.DecodeRuneInString(text[pos:]) - if !stringutil.IsWhiteSpaceSingleLine(ch) { - break - } - if ch == '\t' { - // Tabs = TabSize = indent size and go to next tabStop - currentLineIndent += indentSize - (currentLineIndent % indentSize) - } else { - // Single space - currentLineIndent++ - } - pos += size - } - - return currentLineIndent -} diff --git a/kitcom/internal/tsgo/printer/utilities_test.go b/kitcom/internal/tsgo/printer/utilities_test.go deleted file mode 100644 index a94f4f8..0000000 --- a/kitcom/internal/tsgo/printer/utilities_test.go +++ /dev/null @@ -1,153 +0,0 @@ -package printer - -import ( - "fmt" - "testing" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "gotest.tools/v3/assert" -) - -func TestEscapeString(t *testing.T) { - t.Parallel() - data := []struct { - s string - quoteChar QuoteChar - expected string - }{ - {s: "", quoteChar: QuoteCharDoubleQuote, expected: ``}, - {s: "abc", quoteChar: QuoteCharDoubleQuote, expected: `abc`}, - {s: "ab\"c", quoteChar: QuoteCharDoubleQuote, expected: `ab\"c`}, - {s: "ab\tc", quoteChar: QuoteCharDoubleQuote, expected: `ab\tc`}, - {s: "ab\nc", quoteChar: QuoteCharDoubleQuote, expected: `ab\nc`}, - {s: "ab'c", quoteChar: QuoteCharDoubleQuote, expected: `ab'c`}, - {s: "ab'c", quoteChar: QuoteCharSingleQuote, expected: `ab\'c`}, - {s: "ab\"c", quoteChar: QuoteCharSingleQuote, expected: `ab"c`}, - {s: "ab`c", quoteChar: QuoteCharBacktick, expected: "ab\\`c"}, - {s: "\u001f", quoteChar: QuoteCharBacktick, expected: "\\u001F"}, - } - for i, rec := range data { - t.Run(fmt.Sprintf("[%d] escapeString(%q, %v)", i, rec.s, rec.quoteChar), func(t *testing.T) { - t.Parallel() - actual := EscapeString(rec.s, rec.quoteChar) - assert.Equal(t, actual, rec.expected) - }) - } -} - -func TestEscapeNonAsciiString(t *testing.T) { - t.Parallel() - data := []struct { - s string - quoteChar QuoteChar - expected string - }{ - {s: "", quoteChar: QuoteCharDoubleQuote, expected: ``}, - {s: "abc", quoteChar: QuoteCharDoubleQuote, expected: `abc`}, - {s: "ab\"c", quoteChar: QuoteCharDoubleQuote, expected: `ab\"c`}, - {s: "ab\tc", quoteChar: QuoteCharDoubleQuote, expected: `ab\tc`}, - {s: "ab\nc", quoteChar: QuoteCharDoubleQuote, expected: `ab\nc`}, - {s: "ab'c", quoteChar: QuoteCharDoubleQuote, expected: `ab'c`}, - {s: "ab'c", quoteChar: QuoteCharSingleQuote, expected: `ab\'c`}, - {s: "ab\"c", quoteChar: QuoteCharSingleQuote, expected: `ab"c`}, - {s: "ab`c", quoteChar: QuoteCharBacktick, expected: "ab\\`c"}, - {s: "ab\u008fc", quoteChar: QuoteCharDoubleQuote, expected: `ab\u008Fc`}, - {s: "𝟘𝟙", quoteChar: QuoteCharDoubleQuote, expected: `\uD835\uDFD8\uD835\uDFD9`}, - } - for i, rec := range data { - t.Run(fmt.Sprintf("[%d] escapeNonAsciiString(%q, %v)", i, rec.s, rec.quoteChar), func(t *testing.T) { - t.Parallel() - actual := escapeNonAsciiString(rec.s, rec.quoteChar) - assert.Equal(t, actual, rec.expected) - }) - } -} - -func TestEscapeJsxAttributeString(t *testing.T) { - t.Parallel() - data := []struct { - s string - quoteChar QuoteChar - expected string - }{ - {s: "", quoteChar: QuoteCharDoubleQuote, expected: ""}, - {s: "abc", quoteChar: QuoteCharDoubleQuote, expected: "abc"}, - {s: "ab\"c", quoteChar: QuoteCharDoubleQuote, expected: "ab"c"}, - {s: "ab\tc", quoteChar: QuoteCharDoubleQuote, expected: "ab c"}, - {s: "ab\nc", quoteChar: QuoteCharDoubleQuote, expected: "ab c"}, - {s: "ab'c", quoteChar: QuoteCharDoubleQuote, expected: "ab'c"}, - {s: "ab'c", quoteChar: QuoteCharSingleQuote, expected: "ab'c"}, - {s: "ab\"c", quoteChar: QuoteCharSingleQuote, expected: "ab\"c"}, - {s: "ab\u008fc", quoteChar: QuoteCharDoubleQuote, expected: "ab\u008Fc"}, - {s: "𝟘𝟙", quoteChar: QuoteCharDoubleQuote, expected: "𝟘𝟙"}, - } - for i, rec := range data { - t.Run(fmt.Sprintf("[%d] escapeJsxAttributeString(%q, %v)", i, rec.s, rec.quoteChar), func(t *testing.T) { - t.Parallel() - actual := escapeJsxAttributeString(rec.s, rec.quoteChar) - assert.Equal(t, actual, rec.expected) - }) - } -} - -func TestIsRecognizedTripleSlashComment(t *testing.T) { - t.Parallel() - data := []struct { - s string - commentRange ast.CommentRange - expected bool - }{ - {s: "", commentRange: ast.CommentRange{Kind: ast.KindMultiLineCommentTrivia}, expected: false}, - {s: "", commentRange: ast.CommentRange{Kind: ast.KindSingleLineCommentTrivia}, expected: false}, - {s: "/a", expected: false}, - {s: "//", expected: false}, - {s: "//a", expected: false}, - {s: "///", expected: false}, - {s: "///a", expected: false}, - {s: "///", expected: true}, - {s: "///", expected: true}, - {s: "///", expected: true}, - {s: "///", expected: true}, - {s: "///", expected: true}, - {s: "///", expected: true}, - {s: "/// ", expected: true}, - {s: "/// ", expected: true}, - {s: "/// ", expected: true}, - {s: "/// ", expected: true}, - {s: "/// ", expected: true}, - {s: "/// ", expected: true}, - {s: "/// ", expected: true}, - {s: "/// ", expected: true}, - {s: "/// ", expected: true}, - {s: "/// ", expected: true}, - {s: "/// ", expected: true}, - {s: "/// ", expected: true}, - {s: "/// ", expected: true}, - {s: "/// ", expected: true}, - {s: "/// ", expected: true}, - {s: "/// ", expected: true}, - {s: "/// ", expected: true}, - {s: "/// ", expected: true}, - {s: "/// ", expected: true}, - {s: "/// ", expected: true}, - {s: "/// ", expected: true}, - {s: "/// ", expected: true}, - {s: "/// ", expected: true}, - {s: "/// ", expected: false}, - {s: "/// ", expected: false}, - {s: "/// ", expected: false}, - } - for i, rec := range data { - t.Run(fmt.Sprintf("[%d] isRecognizedTripleSlashComment()", i), func(t *testing.T) { - t.Parallel() - commentRange := rec.commentRange - if commentRange.Kind == ast.KindUnknown { - commentRange.Kind = ast.KindSingleLineCommentTrivia - commentRange.TextRange = core.NewTextRange(0, len(rec.s)) - } - actual := IsRecognizedTripleSlashComment(rec.s, commentRange) - assert.Equal(t, actual, rec.expected) - }) - } -}