vendor tsgo

This commit is contained in:
2026-07-09 16:50:43 -04:00
parent c06ea2e5a4
commit 98978e4930
5804 changed files with 1556156 additions and 101 deletions

View File

@@ -0,0 +1,510 @@
package transformers
import (
"slices"
"strconv"
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/core"
)
// FlattenLevel controls how deeply binding/assignment patterns are decomposed.
type FlattenLevel int
const (
FlattenLevelAll FlattenLevel = iota // Fully decompose all patterns into individual assignments/bindings
FlattenLevelObjectRest // Only decompose patterns containing object rest elements
)
// CreateAssignmentCallback is a callback used to create custom assignment expressions during destructuring flattening.
// When provided, the target will always be an Identifier, and the callback can wrap the assignment with additional logic
// (e.g., export expressions in CJS modules or namespace member assignments).
type CreateAssignmentCallback func(name *ast.IdentifierNode, value *ast.Expression, location *core.TextRange) *ast.Expression
// FlattenDestructuringAssignment flattens a destructuring assignment expression into a sequence of
// individual property/element access assignments. Supports custom assignment callbacks for module
// export or namespace member expressions.
func FlattenDestructuringAssignment(
tx *Transformer,
node *ast.Node, // VariableDeclaration | DestructuringAssignment
needsValue bool,
level FlattenLevel,
createAssignmentCallback CreateAssignmentCallback,
) *ast.Expression {
f := newFlattener(tx, level)
f.createAssignmentCallback = createAssignmentCallback
f.hoistTempVariables = true
// Assignment mode callbacks
f.emitBindingOrAssignment = (*flattener).emitAssignment
f.createArrayBindingOrAssignmentPattern = (*flattener).createArrayAssignmentPattern
f.createObjectBindingOrAssignmentPattern = (*flattener).createObjectAssignmentPattern
f.createArrayBindingOrAssignmentElement = (*flattener).createArrayAssignmentElement
return f.flattenDestructuringAssignment(node, needsValue)
}
// pendingDecl tracks a pending variable declaration during binding flattening.
type pendingDecl struct {
pendingExpressions []*ast.Node
name *ast.Node
value *ast.Node
location core.TextRange
original *ast.Node
}
// FlattenDestructuringBinding flattens a binding pattern in a variable declaration or parameter
// into individual variable declarations. Returns a single VariableDeclaration, a SyntaxList of
// declarations, or nil.
func FlattenDestructuringBinding(
tx *Transformer,
node *ast.Node, // VariableDeclaration | ParameterDeclaration | BindingElement
rval *ast.Node,
level FlattenLevel,
hoistTempVariables bool,
skipInitializer bool,
) *ast.Node {
f := newFlattener(tx, level)
f.hoistTempVariables = hoistTempVariables
// Binding mode callbacks
f.emitBindingOrAssignment = (*flattener).emitBinding
f.createArrayBindingOrAssignmentPattern = (*flattener).createArrayBindingPattern
f.createObjectBindingOrAssignmentPattern = (*flattener).createObjectBindingPattern
f.createArrayBindingOrAssignmentElement = (*flattener).createArrayBindingElement
return f.flattenDestructuringBinding(node, rval, skipInitializer)
}
// flattener encapsulates the state and logic for flattening destructuring patterns.
// It is equivalent to TypeScript's FlattenContext in destructuring.ts.
type flattener struct {
tx *Transformer
level FlattenLevel
createAssignmentCallback CreateAssignmentCallback
// State
expressions []*ast.Node
declarations []pendingDecl
hasTransformedPriorElement bool
hoistTempVariables bool
// Mode callbacks (set by FlattenDestructuringAssignment or FlattenDestructuringBinding)
emitBindingOrAssignment func(f *flattener, target *ast.Node, value *ast.Node, location core.TextRange, original *ast.Node)
createArrayBindingOrAssignmentPattern func(f *flattener, elements []*ast.Node) *ast.Node
createObjectBindingOrAssignmentPattern func(f *flattener, elements []*ast.Node) *ast.Node
createArrayBindingOrAssignmentElement func(f *flattener, expr *ast.Node) *ast.Node
}
func newFlattener(tx *Transformer, level FlattenLevel) *flattener {
return &flattener{
tx: tx,
level: level,
}
}
// --- Assignment mode callbacks ---
func (f *flattener) createArrayAssignmentPattern(elements []*ast.Node) *ast.Node {
return f.tx.Factory().NewArrayLiteralExpression(f.tx.Factory().NewNodeList(elements), false)
}
func (f *flattener) createObjectAssignmentPattern(elements []*ast.Node) *ast.Node {
return f.tx.Factory().NewObjectLiteralExpression(f.tx.Factory().NewNodeList(elements), false)
}
func (f *flattener) createArrayAssignmentElement(expr *ast.Node) *ast.Node {
return expr
}
func (f *flattener) emitAssignment(target *ast.Node, value *ast.Node, location core.TextRange, original *ast.Node) {
var expression *ast.Expression
if f.createAssignmentCallback != nil && ast.IsIdentifier(target) {
expression = f.createAssignmentCallback(target, value, &location)
} else {
expression = f.tx.Factory().NewAssignmentExpression(f.tx.Visitor().VisitNode(target), value)
expression.Loc = location
}
f.tx.EmitContext().SetOriginal(expression, original)
f.emitExpression(expression)
}
// --- Binding mode callbacks ---
func (f *flattener) createArrayBindingPattern(elements []*ast.Node) *ast.Node {
return f.tx.Factory().NewBindingPattern(ast.KindArrayBindingPattern, f.tx.Factory().NewNodeList(elements))
}
func (f *flattener) createObjectBindingPattern(elements []*ast.Node) *ast.Node {
return f.tx.Factory().NewBindingPattern(ast.KindObjectBindingPattern, f.tx.Factory().NewNodeList(elements))
}
func (f *flattener) createArrayBindingElement(expr *ast.Node) *ast.Node {
return f.tx.Factory().NewBindingElement(nil, nil, expr, nil)
}
func (f *flattener) emitBinding(target *ast.Node, value *ast.Node, location core.TextRange, original *ast.Node) {
if len(f.expressions) > 0 {
value = f.tx.Factory().InlineExpressions(append(f.expressions, value))
f.expressions = nil
}
f.declarations = append(f.declarations, pendingDecl{
name: target,
value: value,
location: location,
original: original,
})
}
// --- Shared helpers ---
func (f *flattener) emitExpression(expr *ast.Node) {
f.expressions = append(f.expressions, expr)
}
func (f *flattener) ensureIdentifier(value *ast.Node, reuseIdentifierExpressions bool, location core.TextRange) *ast.Node {
if reuseIdentifierExpressions && ast.IsIdentifier(value) {
return value
}
temp := f.tx.Factory().NewTempVariable()
if f.hoistTempVariables {
f.tx.EmitContext().AddVariableDeclaration(temp)
assign := f.tx.Factory().NewAssignmentExpression(temp, value)
assign.Loc = location
f.emitExpression(assign)
} else {
f.emitBindingOrAssignment(f, temp, value, location, nil)
}
return temp
}
func (f *flattener) createDefaultValueCheck(value *ast.Expression, defaultValue *ast.Expression, location core.TextRange) *ast.Node {
value = f.ensureIdentifier(value, true, location)
return f.tx.Factory().NewConditionalExpression(
f.tx.Factory().NewTypeCheck(value, "undefined"),
f.tx.Factory().NewToken(ast.KindQuestionToken),
defaultValue,
f.tx.Factory().NewToken(ast.KindColonToken),
value,
)
}
func (f *flattener) createDestructuringPropertyAccess(value *ast.Node, propertyName *ast.Node) *ast.Node {
if ast.IsComputedPropertyName(propertyName) {
argumentExpression := f.ensureIdentifier(f.tx.Visitor().VisitNode(propertyName.Expression()), false, propertyName.Loc)
return f.tx.Factory().NewElementAccessExpression(value, nil, argumentExpression, ast.NodeFlagsNone)
} else if ast.IsStringOrNumericLiteralLike(propertyName) || ast.IsBigIntLiteral(propertyName) {
argumentExpression := propertyName.Clone(f.tx.Factory())
return f.tx.Factory().NewElementAccessExpression(value, nil, argumentExpression, ast.NodeFlagsNone)
} else {
name := f.tx.Factory().NewIdentifier(propertyName.Text())
return f.tx.Factory().NewPropertyAccessExpression(value, nil, name, ast.NodeFlagsNone)
}
}
// --- Entry points ---
func (f *flattener) flattenDestructuringAssignment(node *ast.Node, needsValue bool) *ast.Expression {
location := node.Loc
var value *ast.Node
if ast.IsDestructuringAssignment(node) {
value = node.AsBinaryExpression().Right
for ast.IsEmptyArrayLiteral(node.AsBinaryExpression().Left) || ast.IsEmptyObjectLiteral(node.AsBinaryExpression().Left) {
if ast.IsDestructuringAssignment(value) {
node = value
location = node.Loc
value = node.AsBinaryExpression().Right
} else {
return f.tx.Visitor().VisitNode(value)
}
}
}
if value != nil {
value = f.tx.Visitor().VisitNode(value)
if ast.IsIdentifier(value) && BindingOrAssignmentElementAssignsToName(node, value.Text()) || BindingOrAssignmentElementContainsNonLiteralComputedName(node) {
value = f.ensureIdentifier(value, false, location)
} else if needsValue {
value = f.ensureIdentifier(value, true, location)
} else if ast.NodeIsSynthesized(node) {
location = value.Loc
}
}
f.flattenBindingOrAssignmentElement(node, value, location, ast.IsDestructuringAssignment(node))
if value != nil && needsValue {
if len(f.expressions) == 0 {
return value
}
f.expressions = append(f.expressions, value)
}
res := f.tx.Factory().InlineExpressions(f.expressions)
if res != nil {
return res
}
return f.tx.Factory().NewOmittedExpression()
}
func (f *flattener) flattenDestructuringBinding(node *ast.Node, rval *ast.Node, skipInitializer bool) *ast.Node {
if ast.IsVariableDeclaration(node) {
initializer := GetInitializerOfBindingOrAssignmentElement(node)
if initializer != nil && (ast.IsIdentifier(initializer) && BindingOrAssignmentElementAssignsToName(node, initializer.Text()) || BindingOrAssignmentElementContainsNonLiteralComputedName(node)) {
initializer = f.ensureIdentifier(f.tx.Visitor().VisitNode(initializer), false, initializer.Loc)
node = f.tx.Factory().UpdateVariableDeclaration(node.AsVariableDeclaration(), node.Name(), nil, nil, initializer)
}
}
f.flattenBindingOrAssignmentElement(node, rval, node.Loc, skipInitializer)
if len(f.expressions) > 0 {
temp := f.tx.Factory().NewTempVariable()
if f.hoistTempVariables {
value := f.tx.Factory().InlineExpressions(f.expressions)
f.expressions = nil
f.emitBindingOrAssignment(f, temp, value, core.TextRange{}, nil)
} else {
f.tx.EmitContext().AddVariableDeclaration(temp)
last := &f.declarations[len(f.declarations)-1]
last.pendingExpressions = append(last.pendingExpressions, f.tx.Factory().NewAssignmentExpression(temp, last.value))
last.pendingExpressions = append(last.pendingExpressions, f.expressions...)
last.value = temp
}
}
decls := make([]*ast.Node, 0, len(f.declarations))
for _, pending := range f.declarations {
expr := pending.value
if len(pending.pendingExpressions) > 0 {
expr = f.tx.Factory().InlineExpressions(append(pending.pendingExpressions, pending.value))
}
decl := f.tx.Factory().NewVariableDeclaration(pending.name, nil, nil, expr)
decl.Loc = pending.location
if pending.original != nil {
f.tx.EmitContext().SetOriginal(decl, pending.original)
}
decls = append(decls, decl)
}
if len(decls) == 1 {
return decls[0]
}
if len(decls) == 0 {
return nil
}
return f.tx.Factory().NewSyntaxList(decls)
}
// --- Core flattening ---
func (f *flattener) flattenBindingOrAssignmentElement(element *ast.Node, value *ast.Node, location core.TextRange, skipInitializer bool) {
bindingTarget := ast.GetTargetOfBindingOrAssignmentElement(element)
if bindingTarget == nil {
return
}
if !skipInitializer {
initializer := f.tx.Visitor().VisitNode(GetInitializerOfBindingOrAssignmentElement(element))
if initializer != nil {
if value != nil {
value = f.createDefaultValueCheck(value, initializer, location)
if !IsSimpleCopiableExpression(initializer) && (ast.IsBindingPattern(bindingTarget) || ast.IsAssignmentPattern(bindingTarget)) {
value = f.ensureIdentifier(value, true, location)
}
} else {
value = initializer
}
} else if value == nil {
value = f.tx.Factory().NewVoidZeroExpression()
}
}
if isObjectBindingOrAssignmentPattern(bindingTarget) {
f.flattenObjectBindingOrAssignmentPattern(element, bindingTarget, value, location)
} else if isArrayBindingOrAssignmentPattern(bindingTarget) {
f.flattenArrayBindingOrAssignmentPattern(element, bindingTarget, value, location)
} else {
f.emitBindingOrAssignment(f, bindingTarget, value, location, element)
}
}
func (f *flattener) flattenObjectBindingOrAssignmentPattern(parent *ast.Node, pattern *ast.Node, value *ast.Node, location core.TextRange) {
elements := ast.GetElementsOfBindingOrAssignmentPattern(pattern)
numElements := len(elements)
if numElements != 1 {
reuseIdentifierExpressions := !ast.IsDeclarationBindingElement(parent) || numElements != 0
value = f.ensureIdentifier(value, reuseIdentifierExpressions, location)
}
var bindingElements []*ast.Node
var computedTempVariables []*ast.Node
for i, element := range elements {
if ast.GetRestIndicatorOfBindingOrAssignmentElement(element) == nil {
propertyName := ast.TryGetPropertyNameOfBindingOrAssignmentElement(element)
if f.level >= FlattenLevelObjectRest &&
element.SubtreeFacts()&(ast.SubtreeContainsRestOrSpread|ast.SubtreeContainsObjectRestOrSpread) == 0 &&
ast.GetTargetOfBindingOrAssignmentElement(element).SubtreeFacts()&(ast.SubtreeContainsRestOrSpread|ast.SubtreeContainsObjectRestOrSpread) == 0 &&
!ast.IsComputedPropertyName(propertyName) {
bindingElements = append(bindingElements, f.tx.Visitor().VisitNode(element))
} else {
if len(bindingElements) > 0 {
f.emitBindingOrAssignment(f, f.createObjectBindingOrAssignmentPattern(f, bindingElements), value, location, pattern)
bindingElements = nil
}
rhsValue := f.createDestructuringPropertyAccess(value, propertyName)
if ast.IsComputedPropertyName(propertyName) {
computedTempVariables = append(computedTempVariables, rhsValue.AsElementAccessExpression().ArgumentExpression)
}
f.flattenBindingOrAssignmentElement(element, rhsValue, element.Loc, false)
}
} else if i == numElements-1 {
if len(bindingElements) > 0 {
f.emitBindingOrAssignment(f, f.createObjectBindingOrAssignmentPattern(f, bindingElements), value, location, pattern)
bindingElements = nil
}
rhsValue := f.tx.Factory().NewRestHelper(value, elements, computedTempVariables, pattern.Loc)
f.flattenBindingOrAssignmentElement(element, rhsValue, element.Loc, false)
}
}
if len(bindingElements) > 0 {
f.emitBindingOrAssignment(f, f.createObjectBindingOrAssignmentPattern(f, bindingElements), value, location, pattern)
}
}
type restIdElemPair struct {
id *ast.Node
element *ast.Node
}
func (f *flattener) flattenArrayBindingOrAssignmentPattern(parent *ast.Node, pattern *ast.Node, value *ast.Node, location core.TextRange) {
elements := ast.GetElementsOfBindingOrAssignmentPattern(pattern)
numElements := len(elements)
if numElements != 1 && (f.level < FlattenLevelObjectRest || numElements == 0) || core.Every(elements, ast.IsOmittedExpression) {
reuseIdentifierExpressions := !ast.IsDeclarationBindingElement(parent) || numElements != 0
value = f.ensureIdentifier(value, reuseIdentifierExpressions, location)
}
var bindingElements []*ast.Node
var restContainingElements []restIdElemPair
for i, element := range elements {
if f.level >= FlattenLevelObjectRest {
if element.SubtreeFacts()&ast.SubtreeContainsObjectRestOrSpread != 0 || f.hasTransformedPriorElement && !isSimpleBindingOrAssignmentElement(element) {
f.hasTransformedPriorElement = true
temp := f.tx.Factory().NewTempVariable()
if f.hoistTempVariables {
f.tx.EmitContext().AddVariableDeclaration(temp)
}
restContainingElements = append(restContainingElements, restIdElemPair{temp, element})
bindingElements = append(bindingElements, f.createArrayBindingOrAssignmentElement(f, temp))
} else {
bindingElements = append(bindingElements, element)
}
} else if ast.IsOmittedExpression(element) {
continue
} else if ast.GetRestIndicatorOfBindingOrAssignmentElement(element) == nil {
rhsValue := f.tx.Factory().NewElementAccessExpression(value, nil, f.tx.Factory().NewNumericLiteral(strconv.Itoa(i), ast.TokenFlagsNone), ast.NodeFlagsNone)
f.flattenBindingOrAssignmentElement(element, rhsValue, element.Loc, false)
} else if i == numElements-1 {
rhsValue := f.tx.Factory().NewArraySliceCall(value, i)
f.flattenBindingOrAssignmentElement(element, rhsValue, element.Loc, false)
}
}
if len(bindingElements) > 0 {
f.emitBindingOrAssignment(f, f.createArrayBindingOrAssignmentPattern(f, bindingElements), value, location, pattern)
}
if len(restContainingElements) > 0 {
for _, pair := range restContainingElements {
f.flattenBindingOrAssignmentElement(pair.element, pair.id, pair.element.Loc, false)
}
}
}
// --- Exported helper functions ---
// BindingOrAssignmentElementAssignsToName checks if any target in a binding/assignment pattern assigns to the given name.
func BindingOrAssignmentElementAssignsToName(element *ast.Node, name string) bool {
target := ast.GetTargetOfBindingOrAssignmentElement(element)
if target == nil {
return false
}
if ast.IsBindingPattern(target) || ast.IsAssignmentPattern(target) {
return bindingOrAssignmentPatternAssignsToName(target, name)
} else if ast.IsIdentifier(target) {
return target.Text() == name
}
return false
}
func bindingOrAssignmentPatternAssignsToName(pattern *ast.Node, name string) bool {
elements := ast.GetElementsOfBindingOrAssignmentPattern(pattern)
for _, element := range elements {
if BindingOrAssignmentElementAssignsToName(element, name) {
return true
}
}
return false
}
// BindingOrAssignmentElementContainsNonLiteralComputedName checks if any element has a non-literal computed property name.
func BindingOrAssignmentElementContainsNonLiteralComputedName(element *ast.Node) bool {
propertyName := ast.TryGetPropertyNameOfBindingOrAssignmentElement(element)
if propertyName != nil && ast.IsComputedPropertyName(propertyName) && !ast.IsLiteralExpression(propertyName.Expression()) {
return true
}
target := ast.GetTargetOfBindingOrAssignmentElement(element)
return target != nil && (ast.IsBindingPattern(target) || ast.IsAssignmentPattern(target)) && bindingOrAssignmentPatternContainsNonLiteralComputedName(target)
}
func bindingOrAssignmentPatternContainsNonLiteralComputedName(pattern *ast.Node) bool {
elements := ast.GetElementsOfBindingOrAssignmentPattern(pattern)
return slices.ContainsFunc(elements, BindingOrAssignmentElementContainsNonLiteralComputedName)
}
// GetInitializerOfBindingOrAssignmentElement returns the initializer/default value of a binding or assignment element.
func GetInitializerOfBindingOrAssignmentElement(bindingElement *ast.Node) *ast.Node {
if bindingElement == nil {
return nil
}
if ast.IsDeclarationBindingElement(bindingElement) {
return bindingElement.Initializer()
}
if ast.IsPropertyAssignment(bindingElement) {
initializer := bindingElement.Initializer()
if ast.IsAssignmentExpression(initializer, true) {
return initializer.AsBinaryExpression().Right
}
return nil
}
if ast.IsShorthandPropertyAssignment(bindingElement) {
return bindingElement.AsShorthandPropertyAssignment().ObjectAssignmentInitializer
}
if ast.IsAssignmentExpression(bindingElement, true) {
return bindingElement.AsBinaryExpression().Right
}
if ast.IsSpreadElement(bindingElement) {
return GetInitializerOfBindingOrAssignmentElement(bindingElement.Expression())
}
return nil
}
func isObjectBindingOrAssignmentPattern(node *ast.Node) bool {
return node != nil && (node.Kind == ast.KindObjectBindingPattern || node.Kind == ast.KindObjectLiteralExpression)
}
func isArrayBindingOrAssignmentPattern(node *ast.Node) bool {
return node != nil && (node.Kind == ast.KindArrayBindingPattern || node.Kind == ast.KindArrayLiteralExpression)
}
func isSimpleBindingOrAssignmentElement(element *ast.Node) bool {
target := ast.GetTargetOfBindingOrAssignmentElement(element)
if target == nil || ast.IsOmittedExpression(target) {
return true
}
propertyName := ast.TryGetPropertyNameOfBindingOrAssignmentElement(element)
if propertyName != nil && !ast.IsPropertyNameLiteral(propertyName) {
return false
}
initializer := GetInitializerOfBindingOrAssignmentElement(element)
if initializer != nil && !IsSimpleInlineableExpression(initializer) {
return false
}
if ast.IsBindingPattern(target) || ast.IsAssignmentPattern(target) {
return core.Every(ast.GetElementsOfBindingOrAssignmentPattern(target), isSimpleBindingOrAssignmentElement)
}
return ast.IsIdentifier(target)
}