remove unused packages
This commit is contained in:
parent
d14eb0a9b7
commit
87a7c7e9f5
@ -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()
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -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.
|
||||
)
|
||||
@ -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
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -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,
|
||||
)
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -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;
|
||||
};`,
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -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)
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
|
||||
// /// <reference path="..." />
|
||||
// /// <reference types="..." />
|
||||
// /// <reference lib="..." />
|
||||
// /// <reference no-default-lib="..." />
|
||||
// /// <amd-dependency path="..." />
|
||||
// /// <amd-module />
|
||||
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
|
||||
}
|
||||
@ -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: "///<reference path=\"foo\" />", expected: true},
|
||||
{s: "///<reference types=\"foo\" />", expected: true},
|
||||
{s: "///<reference lib=\"foo\" />", expected: true},
|
||||
{s: "///<reference no-default-lib=\"foo\" />", expected: true},
|
||||
{s: "///<amd-dependency path=\"foo\" />", expected: true},
|
||||
{s: "///<amd-module />", expected: true},
|
||||
{s: "/// <reference path=\"foo\" />", expected: true},
|
||||
{s: "/// <reference types=\"foo\" />", expected: true},
|
||||
{s: "/// <reference lib=\"foo\" />", expected: true},
|
||||
{s: "/// <reference no-default-lib=\"foo\" />", expected: true},
|
||||
{s: "/// <amd-dependency path=\"foo\" />", expected: true},
|
||||
{s: "/// <amd-module />", expected: true},
|
||||
{s: "/// <reference path=\"foo\"/>", expected: true},
|
||||
{s: "/// <reference types=\"foo\"/>", expected: true},
|
||||
{s: "/// <reference lib=\"foo\"/>", expected: true},
|
||||
{s: "/// <reference no-default-lib=\"foo\"/>", expected: true},
|
||||
{s: "/// <amd-dependency path=\"foo\"/>", expected: true},
|
||||
{s: "/// <amd-module/>", expected: true},
|
||||
{s: "/// <reference path='foo' />", expected: true},
|
||||
{s: "/// <reference types='foo' />", expected: true},
|
||||
{s: "/// <reference lib='foo' />", expected: true},
|
||||
{s: "/// <reference no-default-lib='foo' />", expected: true},
|
||||
{s: "/// <amd-dependency path='foo' />", expected: true},
|
||||
{s: "/// <reference path=\"foo\" /> ", expected: true},
|
||||
{s: "/// <reference types=\"foo\" /> ", expected: true},
|
||||
{s: "/// <reference lib=\"foo\" /> ", expected: true},
|
||||
{s: "/// <reference no-default-lib=\"foo\" /> ", expected: true},
|
||||
{s: "/// <amd-dependency path=\"foo\" /> ", expected: true},
|
||||
{s: "/// <amd-module /> ", expected: true},
|
||||
{s: "/// <foo />", expected: false},
|
||||
{s: "/// <reference />", expected: false},
|
||||
{s: "/// <amd-dependency />", 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)
|
||||
})
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user